【DBus-cxx】Related Pages翻译&理解

Related Pages翻译&理解

前言

这篇文章算是对dbus-cxx较为详细的入门指导,看这篇文章之前我建议你先看【dbux-cxx】简介及例程并且跑一下例程。
第一次尝试翻译全英教程,翻的不好请见谅…这个说实话翻译的再好也不好懂,读多几遍大概就知道什么意思了。

参考资料

dbus-cxx官网

1/Dispatching in dbus-cxx

对应页面

Dispatching in dbus-cxx(dbus-cxx中的调度)

在dbus-cxx中,“调度”的概念是指将信息读写到总线。
每个使用dbus-cxx的应用都需要实现Dispatcher(调度器),一般来说每个应用只需要一个。
如果这个Dispatcher跑在一个单独的线程中,则你必须要定义一个ThreadDispatcher(线程调度器)来调用方法和发送对应线程的信号(如果你想从非主Dispatcher线程中接收函数调用/信号的话)

Standalone Dispatcher(独立运行的/脱机 调度器)

始终可用的基础Dispatcher是"StandaloneDispatcher"类,这个类会创建新线程来处理读/写总线。
Dispatcher类的实现旨在实现线程安全地与任何库一起运作。
如果你正在使用多线程且需要从特定线程调用方法,则需要实现ThreadDispatcher对象。
example:

/* Create a new dispatcher that does the reading/writing from a separate thread */
std::shared_ptr<DBus::Dispatcher> dispatch = DBus::StandaloneDispatcher::create();
std::shared_ptr<DBus::Connection> conn = dispatch->create_connection( DBus::BusType::SESSION );

/* Create an object whos methods will be called from the DispatcherThread created by the StandaloneDispatcher */
std::shared_ptr<DBus::Object> obj = onn->create_object( "/example", DBus::ThreadForCalling::DispatcherThread );

/* The method created here will be called from the DispatcherThread */
obj->create_method<...>( "dbuscxx.interface", "example_method", sigc::[mem|ptr]_fun(...) );
Dispatching with Qt

qt中已经创建了一个运行在主线程中的Dispatcher实现,此外,还有一个ThreadDispatcher的实现,以确保你可以使用单独的QThread来多线程处理你的应用。
Example with dispatching in main thread(主线程中调度的例子):

#include <dbus-cxx-qt.h>

/*** 省略一点代码 ***/

std::shared_ptr<DBus::Dispatcher> disp = DBus::Qt::QtDispatcher::create();
std::shared_ptr<DBus::Connection> conn = disp->create_connection( DBus::BusType::SESSION );

/* Create an object whos methods will be called from the main thread(which also is the dispatcher thread) */
std::shared_ptr<DBus::Object> obj =conn->create_object( "/example", DBus::ThreadForCalling::DispatcherThread );

/* The method created here will be called from the main thread(which is also the dispatcher thread) */
obj->create_method<...>( "dbuscxx.interface", "example_method", sigc::[mem|ptr]_fun(...) );

Example with the StandaloneDispatcher:

std::shared_ptr<DBus::Dispatcher> disp = DBus::StandaloneDispatcher::create();
std::shared_ptr<DBus::Connection> conn = disp->create_connection( DBus::BusType::SESSION );
 
/* Since the StandaloneDispatcher creates a new thread, we will need a QtThreadDispatcher to ensure
 * that we have proper cross-thread communications
 */
std::shared_ptr<DBus::Qt::QtThreadDispatcher> thrDisp = DBus::Qt::QtThreadDispatcher::create();
conn->add_thread_dispatcher( thrDisp );
 
/* Create an object whos methods will be called from the main thread(which is not the dispatcher thread) */
std::shared_ptr<DBus::Object> obj =conn->create_object( "/example", DBus::ThreadForCalling::CurrentThread );
 
/* The method created here will be called from the main thread thanks to the QtThreadDispatcher we created */
obj->create_method<...>( "dbuscxx.interface", "example_method", sigc::[mem|ptr]_fun(...) );

在仔细看了以上例子后,我觉得他的意思大概就是,如果你要在单独的线程中提供可供主线程调用的方法,则必须要实现一个ThreadDispatcher,其关键是add_thread_dispatcher().

2/Local and Remote Concepts

对应页面
为了能正确地使用dbus-cxx库,我们需要讨论两个概念:本地对象和远程对象。

Bus Names,Paths,and Interfaces

在我们讨论本地/远程对象之前,我们首先需要总线名称,路径和接口的关系。

Bus Names

当一个应用链接到DBus,它一定会被分配一个唯一的名字。这个独特的名字会长得像这样:1.20。
在这里插入图片描述

这个唯一的名称可以用来向特定的应用程序发送消息,but since this may change between startups it is not useful for most purposes(这句不会翻)。通常,我们会以com.example.MyProgram的格式来请求一个“名副其实“的总线名称。这有几个”总所周知“的名字,比如org.freedesktop.Notifications(功能是为用户显示消息),org.freedesktop.DBus(DBus守护进程控制接口)。

  • 总线名称必须至少由两个由点分割的部分组成,每个部分中至少有一个字符。
  • 在Dbus-cxx中,你可以通过DBus::Connection.request_name()来申请名字。
  • 总线名称类似于顶级域名(比如example.com)。
Paths
  • 路径,在DBus-cxx中由DBus::Path类表示,可以认为是域名的子目录。
  • 路径总是用斜杠分隔,例如/org/freedesktop/DBus。应用程序可以导出同一总线名上的多条路径。
    (这玩意是dfeet↓)
    在这里插入图片描述
Interfaces
  • 接口由DBus::Iterface类或DBus::InterfaceProxy类表示,这取决于它们是我们导出的本地对象上的接口,还是我们调用的远程对象上的接口。
  • DBus的接口与Java的接口类似,都是定义一组”顾名思义“的函数,可以在给定路径的对象上执行这些函数(学过Java的我也没搞懂他在讲什么,看懂的可以留个言,原文是:Interfaces are similar to Java interfaces, in that they define a well-known set of functions that can be performed on an object at the given path.)
  • 默认情况下,每个路径必须包含org.freedesktop.DBus.Instrospectable的接口,并且可以包含任意数量的其他接口。该接口只定义了一个方法Introspect,该方法返回一个XML文档,该文档定义了该路径上该对象上的接口、方法和信号。
    在这里插入图片描述
Local Objects

本地对象(有时被称为adaptee类)是包含可能从其他应用程序调用的方法的对象,当这些对象的接口由dbus-cxx-xml2cpp自动生成时,它们被称为”adaptee“类。然后可以将这些adaptee类导出到DBus的特定路径上。
在这里插入图片描述

Remote Objects

远程对象是代表远程应用导出的方法和信号的对象。这些类被称为“proxy(代理)”类,因为它们将请求转发到远程应用程序,并从远程应用程序中返回结果,就像调用的方法在我们本地程序上一样。
这些类也能被dbus-cxx-xml2cpp生成。
在这里插入图片描述

3/Initial Concepts

对应页面

Concepts #1, #2, #3 and #4: Objects (#1), methods (#2), signals (#3) and proxies (#4)
  • 一个dbus-cxx对象是一组方法和信号的集合。方法是一种可以在对象上调用的操作,可以返回值,也可以不返回值。信号是从对象那里发出的,没有返回值。
  • 一个dbus-cxx ObjectProxy为客户端提供了远程对象的抽象。ObjectProxy提供的方法和信号代理具有与服务器端方法/代理等效的签名。(我想这就是我的上级说的:这个dbus-cxx调用远程方法就跟自己在本地调用一样 的意思吧?)
  • 方法代理将方法调用的参数组装成D-Bus消息并传输到服务器。响应(如果有的话)将以D-Bus消息的形式返回,然后将其解压缩并作为代理调用的返回值返回给应用程序。
  • 信号代理与方法代理的不同之处在于,它监视对象中的信号事件。这些信号事件以D-Bus信号消息的形式送达。当信号事件到达时,参数被解压缩并通过信号代理回调在本地发出。
Concept #5: sigc++ signal and slot library

信号和槽

  • dbus-cxx在对象及其代理方面极度依赖于"sigc++"的信号和槽系统。
  • 在服务器端,对象的方法依赖于槽来提供底层功能。当传入方法被调用时,该调用的参数会被解压缩并传递到与该方法关联的槽中。
Concepts #6, #7: Smart Pointers and create()

DBus-cxx广泛使用智能指针来帮助管理类的内存。为了使该方案正常工作,大多数类不能独立初始化(构造函数私有)。要创建类的实例,请在该类上使用create()方法,比如std::shared_ptrDBus::Dispatcher disp = DBus::StandaloneDispatcher::create();

4/dbus-cxx-xml2cpp

Summary

DBus-cxx-xml2cpp从修改后的DBus XML自省文件(不知道是啥)生成代理和适配器接口。默认情况下,将打印单个(仅头文件)类到标准输出。当使用-f发送到文件时,将创建一个.cpp和.h文件。

Commands
  • -x/–xml:要处理的xml文件
  • -p/–prefix:所有输出文件的前缀
  • -f/–file:将输出发送到名为class_proxy.h和class_adapter.h的文件,不接受任何参数,文件名将基于自省文档
  • –proxy:生成代理接口。当你想通过总线与远程应用通信时使用
  • –adapter:生成一个适配器接口。当你想实现一个接口的时候使用
  • –verbose:生成详细处理信息
Extended DBus XML attributes

dbus-xml2cpp在某些情况下需要的不仅仅是dbus内省文档提供的信息,可以通过向关键节点添加附加属性来提供此信息。
每个XML元素的扩展属性是:

  • node
    • cppname - c++类适配器和代理的基础名字(原文是base name)
    • gen-namespace - c++适配器和代理将生成到的命名空间
    • dest - 用于为代理的dest参数创建一个默认值
    • path - 用于为代理和适配器的地址参数创建一个默认值
  • interface
    • ignored - 如果设置为true,则不会为代理或适配器构建该接口
    • cppname - 生成的c++接口类的名称
Proxy Example

下面是一个来自Avahi的DBus内省文档的例子

<?xml version="1.0" standalone='no'?><!--*-nxml-*-->
<?xml-stylesheet type="text/xsl" href="introspect.xsl"?>
<!DOCTYPE node SYSTEM "introspect.dtd">

<node cppname="ServiceBrowser" gen-namespace="Avahi">

<interface name="org.freedesktop.DBus.Introspectable">
<method name="Introspect">
<arg name="data" type="s" direction="out" />
</method>
</interface>

<interface name="org.freedesktop.Avahi.ServiceBrowser">

<method name="Free"/>

<method name="Start"/>

<signal name="ItemNew">
<arg name="interface" type="i"/>
<arg name="protocol" type="i"/>
<arg name="name" type="s"/>
<arg name="type" type="s"/>
<arg name="domain" type="s"/>
<arg name="flags" type="u"/>
</signal>

<signal name="ItemRemove">
<arg name="interface" type="i"/>
<arg name="protocol" type="i"/>
<arg name="name" type="s"/>
<arg name="type" type="s"/>
<arg name="domain" type="s"/>
<arg name="flags" type="u"/>
</signal>

<signal name="Failure">
<arg name="error" type="s"/>
</signal>

<signal name="AllForNow"/>

<signal name="CacheExhausted"/>

</interface>
</node>

要创造代理,需要使用dbus-cxx-xml2cpp:

  • dbus-cxx-xml2cpp --proxy --xml=/path/to/org.freedesktop.Avahi.ServiceBrowser.xml --file
    将为这个自省文件输出多个文件,一个对象代理,一个接口代理。
    (1)对象代理
#ifndef SERVICEBROWSERPROXY_H
#define SERVICEBROWSERPROXY_H

#include <dbus-cxx.h>
#include <memory>
#include <stdint.h>
#include <string>
#include "org_freedesktop_Avahi_ServiceBrowserProxy.h"
namespace Avahi {

class ServiceBrowserProxy : public DBus::ObjectProxy {  //继承DBus::ObjectProxy类
public:
    ServiceBrowserProxy(std::shared_ptr<DBus::Connection> conn, std::string dest, std::string path, DBus::ThreadForCalling signalCallingThread = DBus::ThreadForCalling::DispatcherThread );
public:
    static std::shared_ptr<ServiceBrowserProxy> create(std::shared_ptr<DBus::Connection> conn, std::string dest,   std::string path, DBus::ThreadForCalling signalCallingThread = DBus::ThreadForCalling::DispatcherThread );
    std::shared_ptr<Avahi::org_freedesktop_Avahi_ServiceBrowserProxy> getorg_freedesktop_Avahi_ServiceBrowserInterface( );
protected:
    std::shared_ptr<Avahi::org_freedesktop_Avahi_ServiceBrowserProxy> m_org_freedesktop_Avahi_ServiceBrowserProxy;
};
} /* namespace Avahi */
#endif /* SERVICEBROWSERPROXY_H */

(2)接口代理:

#ifndef ORG_FREEDESKTOP_AVAHI_SERVICEBROWSERPROXY_H
#define ORG_FREEDESKTOP_AVAHI_SERVICEBROWSERPROXY_H

#include <dbus-cxx.h>
#include <memory>
#include <stdint.h>
#include <string>
namespace Avahi {
class org_freedesktop_Avahi_ServiceBrowserProxy : public DBus::InterfaceProxy {
protected:
org_freedesktop_Avahi_ServiceBrowserProxy(std::string name );
public:
static std::shared_ptr<Avahi::org_freedesktop_Avahi_ServiceBrowserProxy> create(std::string name = "org.freedesktop.Avahi.ServiceBrowser" );
void Free( );
void Start( );
    std::shared_ptr<DBus::SignalProxy<void(int32_t,int32_t,std::string,std::string,std::string,uint32_t)>> signal_ItemNew( );
    std::shared_ptr<DBus::SignalProxy<void(int32_t,int32_t,std::string,std::string,std::string,uint32_t)>> signal_ItemRemove( );
    std::shared_ptr<DBus::SignalProxy<void(std::string)>> signal_Failure( );
    std::shared_ptr<DBus::SignalProxy<void()>> signal_AllForNow( );
    std::shared_ptr<DBus::SignalProxy<void()>> signal_CacheExhausted( );
protected:
    std::shared_ptr<DBus::MethodProxy<void()>> m_method_Free;
    std::shared_ptr<DBus::MethodProxy<void()>> m_method_Start;
    std::shared_ptr<DBus::SignalProxy<void(int32_t,int32_t,std::string,std::string,std::string,uint32_t)>> m_signalproxy_ItemNew;
    std::shared_ptr<DBus::SignalProxy<void(int32_t,int32_t,std::string,std::string,std::string,uint32_t)>> m_signalproxy_ItemRemove;
    std::shared_ptr<DBus::SignalProxy<void(std::string)>> m_signalproxy_Failure;
    std::shared_ptr<DBus::SignalProxy<void()>> m_signalproxy_AllForNow;
    std::shared_ptr<DBus::SignalProxy<void()>> m_signalproxy_CacheExhausted;
};
} /* namespace Avahi */
#endif /* ORG_FREEDESKTOP_AVAHI_SERVICEBROWSERPROXY_H */

Introspection(内省)XML文档中的每个接口都有一个为其创建的新类,并添加到主代理类中以供使用。生成的方法将像本地应用程序中存在的对象上的普通方法一样起作用,但它们将通过总线调用。信号以signal_作为前缀,使其更明显。属性类似于方法,因为它们没有前缀。

Adapter Example

如果我们想从一个已经存在的内省文档中实现一个类,我们使用一下命令创建一个适配器:
dbus-cxx-xml2cpp --adapter --xml=/path/to/org.freedesktop.Avahi.ServerBrowser.xml --file
几个类会被生成。跟之前一样,在内省文件中,每个接口都有一个类,一个主适配器类,加上一个纯虚类,为了接收方法调用,我们必须实现这个纯虚类。
你需要导出到总线的主适配器(main adapter↓):

#ifndef SERVICEBROWSERADAPTER_H
#define SERVICEBROWSERADAPTER_H

#include <dbus-cxx.h>
#include <memory>
#include <stdint.h>
#include <string>
#include "org_freedesktop_Avahi_ServiceBrowserInterface.h"
namespace Avahi {
class ServiceBrowserAdapter
: public DBus::Object {
public:
ServiceBrowserAdapter(std::shared_ptr<org_freedesktop_Avahi_ServiceBrowserInterface> _org_freedesktop_Avahi_ServiceBrowserInterface, std::string path );
public:
    static std::shared_ptr<ServiceBrowserAdapter> create(std::shared_ptr<DBus::Connection> connection, std::shared_ptr<org_freedesktop_Avahi_ServiceBrowserInterface> _org_freedesktop_Avahi_ServiceBrowserInterface, std::string path, DBus::ThreadForCalling calling_thread = DBus::ThreadForCalling::DispatcherThread );
};
} /* namespace Avahi */
#endif /* SERVICEBROWSERADAPTER_H */

适配器类,它包含该接口的信号。你还必须给它一个指向纯虚类的指针,这个类实现了这个接口上的方法:

#ifndef ORG_FREEDESKTOP_AVAHI_SERVICEBROWSERINTERFACE_H
#define ORG_FREEDESKTOP_AVAHI_SERVICEBROWSERINTERFACE_H

#include <dbus-cxx.h>
#include <memory>
#include <stdint.h>
#include <string>
#include "org_freedesktop_Avahi_ServiceBrowser.h"
namespace Avahi {
class org_freedesktop_Avahi_ServiceBrowserInterface
: public DBus::Interface {
private:
org_freedesktop_Avahi_ServiceBrowserInterface(org_freedesktop_Avahi_ServiceBrowser* adaptee, std::string name );
public:
    static std::shared_ptr<org_freedesktop_Avahi_ServiceBrowserInterface> create(org_freedesktop_Avahi_ServiceBrowser* adaptee, std::string name = "org.freedesktop.Avahi.ServiceBrowser" );
    std::shared_ptr<DBus::Signal<void(int32_t,int32_t,std::string,std::string,std::string,uint32_t)>> signal_ItemNew( );
    void ItemNew(int32_t _interface, int32_t protocol, std::string name, std::string type, std::string domain, uint32_t flags );
    std::shared_ptr<DBus::Signal<void(int32_t,int32_t,std::string,std::string,std::string,uint32_t)>> signal_ItemRemove( );
    void ItemRemove(int32_t _interface, int32_t protocol, std::string name, std::string type, std::string domain, uint32_t flags );
    std::shared_ptr<DBus::Signal<void(std::string)>> signal_Failure( );
    void Failure(std::string error );
    std::shared_ptr<DBus::Signal<void()>> signal_AllForNow( );
    void AllForNow( );
    std::shared_ptr<DBus::Signal<void()>> signal_CacheExhausted( );
    void CacheExhausted( );
protected:
    std::shared_ptr<DBus::Signal<void(int32_t,int32_t,std::string,std::string,std::string,uint32_t)>> m_signal_ItemNew;
    std::shared_ptr<DBus::Signal<void(int32_t,int32_t,std::string,std::string,std::string,uint32_t)>> m_signal_ItemRemove;
    std::shared_ptr<DBus::Signal<void(std::string)>> m_signal_Failure;
    std::shared_ptr<DBus::Signal<void()>> m_signal_AllForNow;
    std::shared_ptr<DBus::Signal<void()>> m_signal_CacheExhausted;
};
} /* namespace Avahi */
#endif /* ORG_FREEDESKTOP_AVAHI_SERVICEBROWSERINTERFACE_H */

最终,必须实现的纯虚类,以便拥有通过DBus调用的方法。

#ifndef ORG_FREEDESKTOP_AVAHI_SERVICEBROWSER_H
#define ORG_FREEDESKTOP_AVAHI_SERVICEBROWSER_H

class org_freedesktop_Avahi_ServiceBrowser
{
public:
virtual void Free( ) = 0;
virtual void Start( ) = 0;
};
#endif /* ORG_FREEDESKTOP_AVAHI_SERVICEBROWSER_H */

纯虚类背后的思想是,任何需要通过DBus调用的类都只需要最少量的支持,就可以通过添加通过总线调用方法的能力。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值