{"id":39669402,"url":"https://github.com/martinhaefner/simppl","last_synced_at":"2026-01-18T09:36:10.930Z","repository":{"id":65266736,"uuid":"2534608","full_name":"martinhaefner/simppl","owner":"martinhaefner","description":"simppl::dbus - an easy-to-use C++ D-Bus wrapper","archived":false,"fork":false,"pushed_at":"2025-04-03T12:40:29.000Z","size":1054,"stargazers_count":68,"open_issues_count":3,"forks_count":27,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-04-03T13:39:18.086Z","etag":null,"topics":["c-plus-plus","d-bus","ipc"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/martinhaefner.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2011-10-07T19:09:52.000Z","updated_at":"2025-04-03T12:40:33.000Z","dependencies_parsed_at":"2025-01-04T20:30:06.592Z","dependency_job_id":"b098ee44-e1cf-4f11-8297-f983f8ad2a91","html_url":"https://github.com/martinhaefner/simppl","commit_stats":null,"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"purl":"pkg:github/martinhaefner/simppl","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/martinhaefner%2Fsimppl","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/martinhaefner%2Fsimppl/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/martinhaefner%2Fsimppl/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/martinhaefner%2Fsimppl/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/martinhaefner","download_url":"https://codeload.github.com/martinhaefner/simppl/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/martinhaefner%2Fsimppl/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28534168,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-18T00:39:45.795Z","status":"online","status_checked_at":"2026-01-18T02:00:07.578Z","response_time":98,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["c-plus-plus","d-bus","ipc"],"created_at":"2026-01-18T09:36:10.522Z","updated_at":"2026-01-18T09:36:10.882Z","avatar_url":"https://github.com/martinhaefner.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"The simppl/dbus C++ library provides a high-level abstraction layer\nfor DBus messaging by using solely the C++ language and compiler for\nthe definition of remote interfaces. No extra tools are necessary to\ngenerate the binding glue code.\n\nsimppl/dbus can interact with standard DBus services and also provides\nsupport for the DBus.Properties and DBus.ObjectManager APIs. See the\nexamples subfolder on how to interact with standard Linux system services\nlike WPA supplicant or NetworkManager.\n\n\n## Setup\n\nFor building the library and tests from source code you need cmake and\ngtest. I use cmake 2.8 and gtest-1.7. You may need some changes in the\nsetup for environment variables in the contributed script file update.sh.\nDevelopment libraries of libdbus-1 are also needed for a successful build.\n\n\n## Compilers\n\nI only developed on GCC \u003e=4.9. The code uses features of upto C++14 and\ng++ specific demangling functions for class symbols. Therefore, I do not\nexpect the code to work on any other compiler family without appropriate\ncode adaptions.\n\nFor structure serialization boost::fusion can be used. See benchmark example\nfor more information on how to serialize structures when sending remote messages.\nThe boost way is the formal and correct way to serialize structures. The old\nway of defining serializer_type's typdefs within structures is no longer\nrecommended since that one makes heavy use of C++ language to memory\nmappings and will definitely not work with complex data types containing\nvirtual functions or containing multiple inheritence.\n\n\n## Status\n\nThe library is used in a production project but it still can be\nregarded as 'proof of concept'. Interface design may still be under change.\nDue to the simplicity of the glue layer and the main functionality provided\nby libdbus I would recommend to give it a try in real projects.\n\n\n## First steps\n\nThe standard way to access DBus services is typically through using libdbus\nor GDBus. While the APIs are very powerful, they do not provide an\nadequate level of abstraction which could be provided by code generation\nfrom DBus interface definitions, i.e. the DBus introspection XML.\nsimppl/dbus provides such an abstraction with the help of interface\ndescriptions written entirely in C++. This results is service APIs\nwhich just looks like ordinary C++ function calls, on both sides, clients\n(aka. stub) and servers (aka. skeletons).\n\nThe easiest way to implement services is to let simppl generate\nbus names and object paths for your service interface definitions.\n\n\n### Defining an interface\n\n\nLet's start with a simple echo service. See the service description:\n\n```c++\n   // Must match CMake option `HAVE_INTROSPECTION`\n   #define SIMPPL_HAVE_INTROSPECTION 1\n\n   #include \u003csimppl/interface.h\u003e\n\n   namespace test\n   {\n\n   INTERFACE(EchoService)\n   {\n      Method\u003cin\u003cstd::string\u003e, out\u003cstd::string\u003e\u003e echo;\n   };\n\n   }   // namespace\n```\n\nRemember, this is pure C++. You see a method named 'echo' taking a\nstring as 'in' argument and a string as 'out' argument.\nThe header file with this definition can be included by the server and\nby the client. A complete interface definition also needs some hand-written\nglue code, but it's very simple. The full interface looks like this:\n\n\n```c++\n   // Must match CMake option `HAVE_INTROSPECTION`\n   #define SIMPPL_HAVE_INTROSPECTION 1\n\n   #include \u003csimppl/interface.h\u003e\n\n   namespace test\n   {\n\n   INTERFACE(EchoService)\n   {\n      Method\u003cin\u003cstd::string\u003e, out\u003cstd::string\u003e\u003e echo;\n\n      // constructor\n      EchoService()\n       : INIT(echo)\n      {\n      }\n   };\n\n   }   // namespace\n```\n\nNow the interface is complete C++. You need to implement the constructor\nto correctly initialize the members. For oneway methods, you just add\nthe keyword oneway to the method definition. oneway, in and out are\npart of the C++ namespace simppl::dbus.\n\nBut, how is the service mapped to dbus? Well, have you seen the namespace\ndefinition? Then you can already imagine how the service is mapped, can't you?\nServices are typically instantiated and each instance will have its own\ninstance name. For now, let's say the instance name was 'myEcho'.\nWith this information provided in the constructor of the service instance,\nthe dbus bus name requested will be\n\n```\n   test.EchoService.myEcho\n```\n\nThe objectpath of the service is build accordingly so the service is\nprovided under\n\n```\n   /test/EchoService/myEcho\n```\n\nThis mapping is done by simppl automatically. But, as mentioned earlier,\nyou may connect your DBus object to any bus name and object path layout\nin order to connect any available DBus service. Also, a server object\ninstance may provide multiple interfaces. But we ignore these details\nfor now and continue with our EchoService. Let's implement the\nserver by inheriting our implementation class from simppl's\nskeleton, giving the skeleton the interface definition from above:\n\n\n```c++\n   // Must match CMake option `HAVE_INTROSPECTION`\n   #define SIMPPL_HAVE_INTROSPECTION 1\n   \n   #include \u003csimppl/dispatcher.h\u003e\n   #include \u003csimppl/interface.h\u003e\n   #include \u003csimppl/skeleton.h\u003e\n\n   class MyEcho : simppl::dbus::Skeleton\u003cEchoService\u003e\n   {\n      MyEcho(simppl::dbus::Dispatcher\u0026 disp)\n       : simppl::dbus::Skeleton\u003ctest::EchoService\u003e(disp, \"myEcho\")\n      {\n      }\n   };\n```\n\nThis service instance does not implement a handler for the echo method\nyet. But you have seen how the service's instance name is provided.\nLet's provide an implementation for the echo method:\n\n\n```c++\n   // ... Same includes and SIMPPL_HAVE_INTROSPECTION \n\n   class MyEcho : simppl::dbus::Skeleton\u003cEchoService\u003e\n   {\n      MyEcho(simppl::dbus::Dispatcher\u0026 disp)\n       : simppl::dbus::Skeleton\u003ctest::EchoService\u003e(disp, \"myEcho\")\n      {\n         echo \u003e\u003e [](const std::string\u0026 echo_string)\n         {\n            std::cout \u003c\u003c \"Client says '\" \u003c\u003c echo_string \u003c\u003c \"'\" \u003c\u003c std::endl;\n         };\n      }\n   };\n```\n\nNow, simppl will receive the echo request, unmarshall the argument and\nforward the request to the provided lambda function connected via the\nright shift operator. Instead of lambda functions, you may of course\nuse any other method to bind to a std::function with an appropriate\ncall interface, e.g. via using std::bind.\n\nBut there is still no response yet. Let's send a response back to the client:\n\n\n```c++\n   // ... Same includes and SIMPPL_HAVE_INTROSPECTION \n   \n   class MyEcho : simppl::dbus::Skeleton\u003cEchoService\u003e\n   {\n      MyEcho(simppl::dbus::Dispatcher\u0026 disp)\n       : simppl::dbus::Skeleton\u003cEchoService\u003e(disp, \"myEcho\")\n      {\n         echo \u003e\u003e [this](const std::string\u0026 echo_string)\n         {\n            std::cout \u003c\u003c \"Client says '\" \u003c\u003c echo_string \u003c\u003c \"'\" \u003c\u003c std::endl;\n            respond_with(echo(echo_string));\n         };\n      }\n   };\n```\n\nThat's simppl, isn't it? simppl is designed to even provide asynchronous\nresponses with a call to defer_response. For more information, see examples\nprovided in the unittests.\n\nSetup the eventloop and the server is finished:\n\n\n   ```c++\n   int main()\n   {\n      simppl::dbus::Dispatcher disp(\"bus:session\");\n      MyEcho instance(disp);\n\n      disp.run();\n\n      return EXIT_SUCCESS;\n   }\n```\n\nNow it's time to implement a client. Clients are implemented by instantiation\nof a stub. One can either write blocking clients which is only recommended\nfor simple tools or write a full featured event-driven client. Let's start\nwith a simple blocking client:\n\n\n```c++\n   int main()\n   {\n      simppl::dbus::Dispatcher disp(\"bus:session\");\n\n      simppl::dbus::Stub\u003cEchoService\u003e stub(disp, \"myEcho\");\n\n      std::string echoed = stub.echo(\"Hello world!\");\n\n      return EXIT_SUCCESS;\n   }\n```\n\nThat's it. The method call blocks until the response is received and\nthe result is stored in the echoed variable. In case of an error, an exception is thrown.\nNote that the underlying DBus implementation will block on the fd correlated\nwith the request and therefore a blocking client shall never be running on the\nsame connection as server objects, which could easily be mixed up in\nmore complex distributed services.\n\nImportant notice: simppl currently always\nadds a signal filter to the message bus. Therefore, connections instantiated\nfor blocking clients and therefore never running any kind of event loop, will cause\nthe bus daemon to grow in size, as these bus clients will never read these signal\nnotifications from the bus.\n\nBut let's have a look on the message loop driven client. The best way to implement\nsuch a client is to derive from the stub base template and delegate the\ncallbacks to member functions. Note that the method is called via the async(...)\nmember function.\n\n\n```c++\n   class MyEchoClient : simppl::dbus::Stub\u003cEchoService\u003e\n   {\n      MyEchoClient(simppl::dbus::Dispatcher\u0026 disp)\n       : simppl::dbus::Stub\u003cEchoService\u003e(disp, \"myEcho\")\n      {\n         connected \u003e\u003e std::bind(\u0026MyEchoClient::handle_connected, this, _1);\n      }\n\n      void handle_connected(simppl::dbus::ConnectionState st)\n      {\n         if (st == simppl::dbus::ConnectionState::Connected)\n         {\n            echo.async(\"Hello World!\") \u003e\u003e [](const simppl::dbus::Callstate st, const std::string\u0026 echo_string)\n            {\n               if (st)\n               {\n                  std::cout \u003c\u003c \"Server says '\" \u003c\u003c echo_string \u003c\u003c \"'\" \u003c\u003c std::endl;\n                  respond_with(echo(echo_string));\n               }\n               else\n                  std::cout \u003c\u003c \"Got error: \" \u003c\u003c st.what() \u003c\u003c std::endl;\n            };\n         }\n      }\n   };\n```\n\nEvent loop driven clients always get callbacks called from the dbus runtime\nfor any occuring DBus event. The initial event for a client is the connected event\nwhich will be emitted as soon as the server registers itself on the bus (remember\nthe busname). After being connected, the client may start an\nasynchronous method like in the example above. The method response callbacks\ncan be any function object fullfilling the response signature, the preferred\nway nowadays is a C++ lambda function. The main program is as simple as in the\nblocking example:\n\n\n```c++\n   int main()\n   {\n      simppl::dbus::Dispatcher disp(\"bus:session\");\n\n      MyEchoClient client(disp);\n\n      disp.run();\n\n      return EXIT_SUCCESS;\n   }\n```\n\nEasy, isn't it? But with simppl/dbus it is also possible to model signals\nand properties. Moreover, any complex data can be passed between client\nand server. See the following example:\n\n\n```c++\n   namespace test\n   {\n      struct Data\n      {\n         typedef make_serializer\u003cint, std::string, std::vector\u003cstd::tuple\u003cint, double\u003e\u003e\u003e::type serializer_type;\n\n         int i;\n         std::string str;\n         std::vector\u003cstd::tuple\u003cint, double\u003e\u003e vec;\n      };\n\n\n      INTERFACE(ComplexTest)\n      {\n         Method\u003cin\u003cData\u003e, out\u003cint\u003e, out\u003cstd::string\u003e\u003e eval;\n\n         Signal\u003cstd::map\u003cint,int\u003e\u003e sig;\n\n         Property\u003cData\u003e data;\n\n         ComplexTest()\n          : INIT(eval)\n          , INIT(sig)\n          , INIT(data)\n         {\n         }\n      };\n   }\n```\n\nThe Data structure is any C/C++ struct but must not include virtual functions.\nFor structures with a more complex memory layout it is adviced to use boost::fusion\nto map the struct to a template-iteratable type sequence. For simpler types\nlike above, the make_serializer generates an adequate serialization code for\nthe structure, i.e. the structure can be used as any simple data type:\n\n\n```c++\n   int main()\n   {\n      simppl::dbus::Dispatcher disp(\"bus:session\");\n\n      simppl::dbus::Stub\u003cComplexTest\u003e teststub(disp, \"test\");\n\n      Data d({ 42, \"Hallo\", ... });\n\n      int i_ret;\n      std::string s_ret;\n\n      std::tie(i_ret, s_ret) = teststub.eval(d);\n\n      return EXIT_SUCCESS;\n   }\n```\n\nHave you noticed how methods with more than one return value are mapped\nto a tuple out parameter which can be tie'd to the local variables in\nthis blocking call? Isn't that cool?\n\nThe signal in the example above is only sensefully usable\nin an event loop driven client. There is a slight difference between\nthe properties concepts of simppl and dbus for now: the changed notification\nwill not be part of the Properties interface so only get and set may\ncurrently be used to access properties of non-simppl services. But this is\nnothing that cannot be changed in future. See how a client will typically\nregister for update notifications in order to receive changes on the properties\non server side. See the clients connected callback:\n\n\n```c++\n   class MyComplexClient : simppl::dbus::Stub\u003cComplexTest\u003e\n   {\n      MyComplexClient(simppl::dbus::Dispatcher\u0026 disp)\n       : simppl::dbus::Stub\u003cComplexTest\u003e(disp, \"test\")\n      {\n         connected \u003e\u003e [this](simppl::dbus::ConnectionState st)\n         {\n            if (st == simppl::dbus::ConnectionState::Connected)\n            {\n               sig.attach() \u003e\u003e [](const std::map\u003cint,int\u003e\u0026 m)\n               {\n                  ...\n               };\n\n               data.attach() \u003e\u003e [](const Data\u0026 d)\n               {\n                  // first callback is due to the attach!\n                  ...\n               };\n            }\n            else\n            {\n               sig.detach();\n               data.detach();\n            }\n         };\n      }\n   };\n```\n\nProperties on server side can be either implemented by providing a\ncallback function to be called whenever the property is requested by a\nclient or the property value can be stored within the server instance.\nTherefore, you have to decide and initialize the property within the\nserver's constructor. Let's see the difference:\n\n```c++\n   class MyComplexServer : simppl::dbus::Skeleton\u003cComplexTest\u003e\n   {\n      MyComplexServer(simppl::dbus::Dispatcher\u0026 disp)\n       : simppl::dbus::Skeleton\u003cComplexTest\u003e(disp, \"test\")\n      {\n         // either using the callback version...\n         data.on_read([](){\n            return Data({ 42, \"Hallo\", ... });\n         });\n\n         // ... or keep a copy in the server instance\n         data = Data({ 42, \"Hallo\", ... });\n      }\n   };\n```\n\nThe notification of property changes is either done by calling the properties\nnotify(...) method in case the property is initialized for callback access\nor by just assigning a new value to the stored property instance.\nThe version of property access to be used in your server is completely\ntransparent for the client and depends on your use-case.\n\nSimppl properties also support the GetAll interface. See unittest for an example.\n\nUser-defined exceptions may also be transferred. This feature is currently\nrestricted to method calls. To define an error you have to use the boost\nfusion binding:\n\n```c++\n   namespace test\n   {\n\n   class MyError : simppl::dbus::Error\n   {\n   public:\n\n      // needed for client side code generation\n      MyError() = default;\n\n      // used to throw error on server side\n      MyError(int rc)\n       : simppl::dbus::Error(\"My.Error\")   // make a DBus appropriate error name\n       , result(rc)\n      {\n         // NOOP\n      }\n\n      int result;\n   };\n\n   }   // namespace\n\n   BOOST_FUSION_ADAPT_STRUCT(\n      test::MyError,\n      (int, result)\n   )\n```\n\nIn the interface definition the exception class has to be added:\n\n```c++\n   namespace test\n   {\n\n   INTERFACE(HelloService)\n   {\n      Method\u003cin\u003cstd::string\u003e, _throw\u003cMyError\u003e\u003e hello;\n\n      // constructor\n      HelloService()\n       : INIT(hello)\n      {\n      }\n   };\n\n   }   // namespace\n```\n\nYou may now throw the error in a server's method callback:\n\n```c++\n   class MyHello : simppl::dbus::Skeleton\u003cHelloService\u003e\n   {\n      MyHello(simppl::dbus::Dispatcher\u0026 disp)\n       : simppl::dbus::Skeleton\u003cHelloService\u003e(disp, \"myHello\")\n      {\n         hello \u003e\u003e [](const std::string\u0026 hello_string)\n         {\n            if (hello_string == \"idiot\")\n                respond_with(MyError(EINVAL));\n\n            respond_with(hello());\n         };\n      }\n   };\n```\n\nThis was a short introduction to simppl/dbus. I hope you will like\ndeveloping client/server applications with the means of C++ and without\nthe need of a complex tool chain for glue-code generation.\n\n\n## Lifetime tracking\n\nIn the beginning, simppl was designed without DBus as the transport layer in mind.\nTherefore, the connected/disconnected handling  of the clients currently only\nworks if the server binary will exit, i.e. the DBus connection hold to the\nbus daemon will be closed. Otherwise, the bus names will not be released\nand therefore, clients will not get notified when a server object was destroyed.\nA more robust solution for this topic is to make use of the DBus ObjectManager\ninterface, which will inform you whenever DBus objects are being added or\nremoved from an object path instead of the bus name.\n\nThis can be achieved by simply creating a server object which implements\nthe standard DBus.ObjectManager interface:\n\n```c++\n   #define SIMPPL_HAVE_OBJECTMANAGER 1\n\n   #include \"simppl/...\u003e\n\n   simppl::dbus::Skeleton\u003corg::freedesktop::DBus::ObjectManager\u003e mgr(s, \"s\");\n\n   simppl::dbus::Skeleton\u003ctest::One\u003e o(s, \"s\");\n   o.Str = \"Hallo\";\n   o.Int = 42;\n\n   mgr.add_managed_object(\u0026o);\n```\n\nObjectManager support may be disabled for compatibility reasons in cmake.\nBeware, that client code has to set the macro to the same value as during\nlibrary compilation.\n\n\n## Eventloop integration\n\nThe libdbus reference implementation allows the integration of the DBus\nAPI in any thirdparty event loop. You may access the DBusConnection object\nfrom the simppl::dbus::Dispatcher in order to integrate simppl in your\nproject's event loop. For an example implementation using boost::asio\nsee the examples source code in the asio subdirectory.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmartinhaefner%2Fsimppl","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmartinhaefner%2Fsimppl","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmartinhaefner%2Fsimppl/lists"}