Как создать синхронный интерфейс класса с qdbusxml2cpp?

Краткое описание проблемы: qdbusxml2cpp генерирует QDBusAbstractInterface Подкласс, методы которого выбирают D-Bus, отвечает асинхронно, но я хочу, чтобы он был синхронным (т.е. он должен блокироваться до получения ответа).

XML вход:

<?xml version="1.0"?>
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<interface name="some.interface.Foo">
    <method name="GetValues">
    <arg name="values" type="a(oa{sv})" direction="out"/>
        <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QList&lt;SomeStruct&gt;" />
    </method>
</interface>
</node>

С помощью этой команды генерируется заголовок и файл.cpp (не показан):

qdbusxml2cpp-qt5 -c InterfaceFoo -p interface_foo  foo.xml

Сгенерированный заголовок:

class InterfaceFoo: public QDBusAbstractInterface
{
    Q_OBJECT
public:
    static inline const char *staticInterfaceName()
    { return "some.interface.Foo"; }

public:
    InterfaceFoo(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent = 0);

    ~InterfaceFoo();

public Q_SLOTS: // METHODS
    inline QDBusPendingReply<QList<SomeStruct> > GetValues()
    {
        QList<QVariant> argumentList;
        // NOTICE THIS LINE.
        return asyncCallWithArgumentList(QStringLiteral("GetValues"), argumentList);
    }

Q_SIGNALS: // SIGNALS
};

namespace some {
namespace interface {
    typedef ::InterfaceFoo Foo;
}
}
#endif

Как вы можете увидеть метод, сгенерированный asyncCallWithArgumentList() асинхронный: он ожидает connect()переход к слоту, который срабатывает при получении ответа по шине D-Bus.

Вместо этого я хочу иметь возможность сделать:

 some::interface::Foo *interface =  new some::interface::Foo("some::interface", "/Foo", QDBusConnection::systemBus(), this);
 // THIS SHOULD block until a reply is received or it times out.
 QList<SomeStruct> data = interface->GetValues();

1 ответ

Решение

Вы могли бы использовать value() на возвращаемое значение для блокировки, с GetValues() как есть:

auto reply = interface->GetValues();
auto data = reply.value<QList<QVariant>>();

Увы, созданный интерфейс должен быть сгенерирован один раз, а затем стать частью ваших источников. Вы должны изменить его, чтобы использовать блокирующий вызов, и добавить преобразование из варианта в конкретный тип:

inline QDBusPendingReply<QList<SomeStruct>> GetValues()
{
    QList<QVariant> argumentList;
    auto msg = callWithArgumentList(QDBus::Block, QStringLiteral("GetValues"), argumentList);
    Q_ASSERT(msg.type() == QDBusMessage::ReplyMessage);
    QList<SomeStruct> result;
    for (auto const & arg : msg.arguments())
      result << arg.value<SomeStruct>();
    return result;
}

Наконец, вы, возможно, захотите пересмотреть вопрос о том, чтобы сделать его синхронным: реальный мир асинхронен, поэтому написание кода, как если бы его не было, часто приводит к обратным результатам. Если ваш код работает в главном потоке, и в нем есть графический интерфейс, вы будете плохо работать с пользователем. Если вы перемещаете свой код в рабочий поток, вы тратите впустую весь поток только для поддержки стиля синхронного кодирования. Даже если вы пишете, что составляет неинтерактивную утилиту / службу пакетного типа, вы потенциально увеличиваете задержку, например, не выполняя параллельные вызовы.

Другие вопросы по тегам