「C++の応用 - D-Bus」の版間の差分

提供:MochiuWiki - SUSE, Electronic Circuit, PCB
ナビゲーションに移動 検索に移動
155行目: 155行目:
std::stringクラスの参照渡しは明示的に行っていないが、これは、sdbus-c++ライブラリが内部で適切に処理するためである。<br>
std::stringクラスの参照渡しは明示的に行っていないが、これは、sdbus-c++ライブラリが内部で適切に処理するためである。<br>
<u>sdbus-c++ライブラリは効率的な引数の受け渡しを行うよう設計されているため、大きなオブジェクトは自動的に参照として扱われる。</u><br>
<u>sdbus-c++ライブラリは効率的な引数の受け渡しを行うよう設計されているため、大きなオブジェクトは自動的に参照として扱われる。</u><br>
<br>
<u>まず、上記のDBusインターフェース定義ファイルからC++コードを生成する。</u><br>
sdbus-c++ライブラリには、XMLファイルからC++のソースコードを生成するツールが付属している。 (sdbus-c++-xml2cppファイル)<br>
sdbus-c++-xml2cppで生成されたソースコードを使用することにより、型安全性が向上して、D-Busインターフェースの使用がより簡単になる。<br>
<br>
以下に示すコマンドを実行して、<クライアント側のソースコード>と<サーバ側のソースコード) を生成する。<br>
sdbus-c++-xml2cpp <D-Busインターフェース定義ファイル名 (XMLファイル)> \
                  --proxy=<D-Busインターフェース定義ファイルが記述されたクライアント側のソースコードファイル名 (プロキシヘッダ)> \
                  --adaptor=<D-Busインターフェース定義ファイルが記述されたサーバ側のソースコードファイル名>
# 例 :
sdbus-c++-xml2cpp org.example.mochiu.xml \
                  --proxy=mochiu_proxy.h \
                  --adaptor=mochiu_adaptor.h
<br>
<u>次に、生成されたソースコードをプロジェクトに統合する。</u>
  <syntaxhighlight lang="c++">
  <syntaxhighlight lang="c++">
  #include <sdbus-c++/sdbus-c++.h>
  #include <sdbus-c++/sdbus-c++.h>
  #include <iostream>
  #include <iostream>
  #include <cstring>
  #include <cstring>
#include "mochiu_proxy.h"  // 生成されたプロキシヘッダをインクルード
   
   
  int main()
  int main()
198行目: 215行目:
     return 0;
     return 0;
  }
  }
</syntaxhighlight>
<br>
* CMakeを使用する場合
<syntaxhighlight lang="cmake">
cmake_minimum_required(VERSION 3.16)
project(<プロジェクト名>)
set(CMAKE_CXX_STANDARD 17)
find_package(sdbus-c++ REQUIRED)
add_executable(<ターゲット名>
    main.cpp
)
target_link_libraries(<ターゲット名> PRIVATE
    SDBusCpp::sdbus-c++
)
  </syntaxhighlight>
  </syntaxhighlight>
<br><br>
<br><br>

2024年7月29日 (月) 06:58時点における版

概要

D-Busは、オープンソースのプロセス間通信(IPC:Inter Process Communication)機構であり、freedesktop.orgプロジェクトの一部である。
IPCとは、1台のコンピュータ上で動作する複数のプログラムの間で情報を交換するシステムのことである。

IPCには、パイプ、名前付きパイプ、シグナル、共有メモリ、Unixソケット、ループバックソケット等がある。
D-Busもリンク層はUnixソケットで動作しているが、手順とフォーマット(プレゼンテーション層)が既定されていることが、「生の」Unixソケットとは異なる。

開発当初はGNOME等のGUIの制御を目的としていが、今日では、GUIに限らず幅広いソフトウェアで使用されており、
デスクトップの通知、メディアプレーヤー制御、XDGポータル等、多くのfreedesktop.org標準がD-Busをベースに構築されている。

IPCとは、あるプロセスから別のプロセスへ情報を取得する方法を説明するために使用することができる。
これは、データの交換、メソッドの呼び出し、イベントのリスニング等がある。

  • デスクトップにおけるIPCの使用例
    • スクリプト(ユーザが共通の環境でスクリプトを実行して、実行中の様々なソフトウェアと対話または制御する)
    • 集中型サービスへのアクセスの提供
    • 協調型ソフトウェアの複数のインスタンス間の調整


  • D-Busの使用例
    • freedesktop.orgのnotification仕様
      これは、ソフトウェアは通知を中央サーバ(Plasma等)に送信して、中央サーバは通知を表示して、通知が閉じられたりアクションが実行されたりといったイベントを送り返すものである。


  • IPCの他の使用例
    • ユニークなソフトウェアのサポート
      これは、ソフトウェアの起動時に、まず、同じソフトウェアの他の実行中のインスタンスを確認して、
      もし存在すれば、IPCを介して実行中のインスタンスにメッセージを送信して、自分自身を表示して終了させる。


D-Busは、言語やツールキットに囚われないため、あらゆるプロバイダのソフトウェアやサービスが相互作用することができる。
デーモン(軽量サービスプロバイダ)とそれを利用したいソフトウェアが、必要なサービス以上のことを知らなくても通信できるようにするためによく利用される。

D-Busに仕様を詳しく知りたい場合は、freedesktopの公式Webサイトを参照すること。


sdbus-c++ライブラリ

sdbus-c++ライブラリとは

sdbus-c++ライブラリは、C++で理解できるAPIを提供するように設計されたLinux向けの高水準C++ D-Busライブラリである。
SystemdによるC D-Bus実装であるsd-busの上に、抽象化の別のレイヤーを追加する。

sdbus-c++ライブラリは、主にdbus-c++を置き換えるものとして開発されている。
dbus-c++は、現在多くの未解決のバグ、並行性の問題、固有の設計の複雑さと制限に悩まされている。

sdbus-c++ライブラリはsd-busライブラリを使用しているが、必ずしもSystemdに制約されているわけではなく、Systemd以外の環境でも完璧に使用することができる。

sdbus-c++ライブラリのインストール

sdbus-c++ライブラリのビルドに必要なライブラリをインストールする。

# SUSE
sudo zypper install pkg-config make cmake gcc gcc-c++ expat-devel systemd-devel


sdbus-c++ライブラリのGithubにアクセスして、ソースコードをダウンロードする。
ダウンロードしたファイルを解凍する。

tar xf sdbus-cpp-<バージョン>.tar.gz
cd sdbus-cpp-<バージョン>


または、git cloneコマンドを実行して、ソースコードをダウンロードすることもできる。

git clone https://github.com/Kistler-Group/sdbus-cpp.git
cd sdbus-cpp


sdbus-c++ライブラリをビルドおよびインストールする。

cmake -DCMAKE_INSTALL_PREFIX=<sdbus-c++ライブラリのインストールディレクトリ>  \
      -DCMAKE_BUILD_TYPE=Release \
      -DBUILD_CODE_GEN=ON        \
      -DBUILD_DOC=ON             \
      -DBUILD_EXAMPLES=ON        \
      ..

make -j $(nproc)
make install


使用例 : Systemdサービスユニットの開始

以下の例では、sdbus-c++ライブラリを使用して、sudo systemctl start smbコマンドと同等の操作を行っている。
D-Bus経由でSystemdと直接通信するため、より柔軟で、プログラム内から制御できるようになっている。

ただし、さんぷるコードを実行するには、適切な権限が必要となる。

 #include <sdbus-c++/sdbus-c++.h>
 #include <iostream>
 
 int main()
 {
    try {
        // D-Busへの接続を作成
        auto connection = sdbus::createSystemBusConnection();
 
        // Systemdのマネージャーインターフェースへのプロキシオブジェクトを作成
        auto proxy = sdbus::createProxy(*connection,
                                        "org.freedesktop.systemd1",
                                        "/org/freedesktop/systemd1");
 
        // StartUnitメソッドを呼び出してsmbサービスを開始
        // "StartUnit"はSystemdサービスのメソッド名
        // "org.freedesktop.systemd1.Manager"はD-Busインターフェース名
        // "smb.service"は開始するSystemdサービス名
        // "replace"はSystemdサービスの起動モード
        proxy->callMethod("StartUnit")
               .onInterface("org.freedesktop.systemd1.Manager")
               .withArguments("smb.service", "replace")
               .storeResultsTo();
 
        std::cout << "smb service started successfully." << std::endl;
    }
    catch (const sdbus::Error &e) {
       // sdbus::Errorをキャッチしてエラーメッセージを表示
       std::cerr << "D-Bus error: " << e.what() << std::endl;
       return -1;
    }
    catch (const std::exception &e) {
       // std::exceptionをキャッチして一般エラーを処理
       std::cerr << "Error: " << e.what() << std::endl;
       return -1;
    }
 
    return 0;
 }


使用例 : ユーザ定義のD-Busサービスの実行

以下に示すようなユーザ定義のD-Busサービスがあるとする。

  • D-Busサービス名
    org.example.mochiu
  • D-Busオブジェクト名
    /org/example/mochiu
  • D-Busインターフェース名
org.example.mochiu.method
  • D-Busインターフェースメソッド名
    func1
    • func1の引数
      第1引数 int型
      第2引数 std::stringクラスの参照
    • func1の戻り値
      int型


上記のD-Busサービスにおいて、指定したD-Busサービス、オブジェクト、インターフェース、メソッドを使用して実行している。

また、D-Busインターフェースの定義ファイル (XMLファイル) は、以下に示すようなものとする。

 <!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
 "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
 <node>
   <!-- D-Busインターフェースの定義 -->
   <interface name="org.example.mochiu.method">
     <!-- func1メソッドの定義 -->
     <!-- func1メソッドは、2つの入力引数 (int型とstd::string型) を受け取り、int型の値を返すことを定義している -->
     <method name="func1">
       <!-- 第1引数 : int型 -->
       <arg name="arg1" type="i" direction="in"/>
       <!-- 第2引数 : std::string型 (ここでは参照型として使用する) -->
       <arg name="arg2" type="s" direction="in"/>
       <!-- 戻り値 : int型 -->
       <arg name="result" type="i" direction="out"/>
     </method>
   </interface>
 </node>


std::stringクラスの参照渡しは明示的に行っていないが、これは、sdbus-c++ライブラリが内部で適切に処理するためである。
sdbus-c++ライブラリは効率的な引数の受け渡しを行うよう設計されているため、大きなオブジェクトは自動的に参照として扱われる。

まず、上記のDBusインターフェース定義ファイルからC++コードを生成する。
sdbus-c++ライブラリには、XMLファイルからC++のソースコードを生成するツールが付属している。 (sdbus-c++-xml2cppファイル)
sdbus-c++-xml2cppで生成されたソースコードを使用することにより、型安全性が向上して、D-Busインターフェースの使用がより簡単になる。

以下に示すコマンドを実行して、<クライアント側のソースコード>と<サーバ側のソースコード) を生成する。

sdbus-c++-xml2cpp <D-Busインターフェース定義ファイル名 (XMLファイル)> \
                  --proxy=<D-Busインターフェース定義ファイルが記述されたクライアント側のソースコードファイル名 (プロキシヘッダ)> \
                  --adaptor=<D-Busインターフェース定義ファイルが記述されたサーバ側のソースコードファイル名>

# 例 :
sdbus-c++-xml2cpp org.example.mochiu.xml \
                  --proxy=mochiu_proxy.h \
                  --adaptor=mochiu_adaptor.h


次に、生成されたソースコードをプロジェクトに統合する。

 #include <sdbus-c++/sdbus-c++.h>
 #include <iostream>
 #include <cstring>
 #include "mochiu_proxy.h"  // 生成されたプロキシヘッダをインクルード
 
 int main()
 {
    try {
       // セッションバスへの接続を作成(ユーザー定義サービスの場合、通常はセッションバスを使用)
       auto connection = sdbus::createSessionBusConnection();
 
       // 指定されたD-Busサービスとオブジェクトへのプロキシオブジェクトを作成
       auto proxy = sdbus::createProxy(*connection,
                                       "org.example.mochiu",  // D-Busサービス名
                                       "/org/example/mochiu"  // D-Busオブジェクト名
       );
 
       // func1メソッドの引数
       int arg1 = 42;
       std::string arg2 = "Hello, D-Bus!";
 
       // D-Busインターフェースメソッド (func1) を呼び出して、戻り値を受け取る
       auto result = proxy->callMethod("func1")                        // D-Busインターフェースメソッド名
                            .onInterface("org.example.mochiu.method")  // D-Busインターフェース名
                            .withArguments(arg1, arg2)                 // 引数を渡す
                            .returnValue<int>();                       // int型の戻り値を受け取る
 
       std::cout << "Method 'func1' called successfully." << std::endl;
       std::cout << "Result: " << result << std::endl;
    }
    catch (const sdbus::Error &e) {
       // sdbus::Errorをキャッチしてエラーメッセージを表示
       std::cerr << "D-Bus error: " << e.what() << std::endl;
       return -1;
    }
    catch (const std::exception &e) {
       // std::exceptionをキャッチして一般エラーを処理
       std::cerr << "Error: " << e.what() << std::endl;
       return -1;
    }
 
    return 0;
 }


  • CMakeを使用する場合
 cmake_minimum_required(VERSION 3.16)
 project(<プロジェクト名>)
 
 set(CMAKE_CXX_STANDARD 17)
 
 find_package(sdbus-c++ REQUIRED)
 
 add_executable(<ターゲット名>
    main.cpp
 )
 
 target_link_libraries(<ターゲット名> PRIVATE
    SDBusCpp::sdbus-c++
 )