Обертка над boost::signals

261
16 мая 2018, 20:10

Недавно начал изучать boost. Обратил внимание на сигналы/слоты. Подумал, что было бы неплохо написать функцию connect для облегчения синтаксиса - без необходимости постоянно вызывать boost::bind. Аргументами функции являлись бы - название слота, указатель на объект, указатель на сигнал и число аргументов у сигнала и слота. Код:

#include <iostream>
#include <boost/signals2.hpp>
#include <boost/bind.hpp>
class BaseClass
{
public:
    //Функция соединения
    template< class A1, class M, class T, typename... Args, class B>
    static boost::signals2::connection connect( M T::*f, A1 a1, B &connection, int num)
    {
            return connection.connect(getBind(f, a1, _1, _2));
    }
    template< class A1, class M, class T, typename... Args >
    static boost::function<M> getBind( M T::*f, A1 a1, Args&& ...arg) {
        return boost::bind(f, a1, boost::forward<Args>(arg)...);
    }
};
class OtherClass
{
public:
    boost::signals2::signal<void (int, int)> myConnection;
    void start() {
        myConnection(2, 2);
    }
};
class MyClass : public BaseClass
{
public:
    void myFunc(int a, int b)
    {
        std::cout << "hello " << a << " " << b;
    }
};
int main(int argc, char** argv)
{
    MyClass *cl = new MyClass;
    OtherClass *myOtherClass = new OtherClass;
    BaseClass::connect(&MyClass::myFunc, cl, myOtherClass->myConnection, 2);
    myOtherClass->start();
    return 0;
}

В таком варианте оно компилируется и работает только если не менять аргументы у сигнала и слота. А мне бы хотелось создать универсальный вариант. Дополненный код функции connect:

static boost::signals2::connection connect( M T::*f, A1 a1, B &connection, int num)
    {
        if (num == 1)
            return connection.connect(getBind(f, a1, _1));
        if (num == 2)
            return connection.connect(getBind(f, a1, _1, _2));
    }

И вот так оно не компилируется, даже если не менять аргументы - вроде как если num равно 2, то должен был выбраться нужный вызов bind.

Ошибки следующие:

C:\Libs\boost_1_67_0\boost\bind\bind.hpp:75: ошибка: C2825: 'F': must be a class or namespace when followed by '::' C:\Libs\boost_1_67_0\boost\bind\bind.hpp:75: ошибка: C2510: 'F': left of '::' must be a class/struct/union C:\Libs\boost_1_67_0\boost\bind\bind.hpp:75: ошибка: C3646: 'type': unknown override specifier C:\Libs\boost_1_67_0\boost\bind\bind.hpp:75: ошибка: C4430: missing type specifier - int assumed. Note: C++ does not support default-int C:\Libs\boost_1_67_0\boost\bind\bind.hpp:1284: ошибка: C2039: 'type': is not a member of 'boost::_bi::result_traits' with [ R=boost::_bi::unspecified, F=void (__thiscall MyClass::* )(int,int) ]

Answer 1

У вас ошибка в синтаксисе для использования указателя на нестатический метод класса. Запись M T::*f не позволяет объявить (и вывести тип) для такого указателя. Еще базовый класс тут ни к чему, а переданное в connect число никак не используется. Исправленный вариант:

#include <iostream>
#include <boost/signals2.hpp>
#include <boost/bind.hpp>
template< class A1, typename TF, typename... Args >
static auto make_bind( TF f, A1 a1, Args&& ...arg)
{
    return boost::bind(f, a1, boost::forward<Args>(arg)...);
}
template< class A1, typename TF, class B>
static boost::signals2::connection my_connect( TF f, A1 a1, B &connection, int num)
{
    return connection.connect(make_bind(f, a1, num, _1));
}
class OtherClass
{
public:
    boost::signals2::signal<void (int)> myConnection;
    void start() {
        myConnection(2);
    }
};
class MyClass
{
public:
    void myFunc(int a, int b)
    {
        std::cout << "hello " << a << " " << b;
    }
};
int main(int argc, char** argv)
{
    MyClass *cl = new MyClass;
    OtherClass *myOtherClass = new OtherClass;
    my_connect(&MyClass::myFunc, cl, myOtherClass->myConnection, 1);
    myOtherClass->start();
    return 0;
}

А вообще вместо доп функций можно использовать лямду:

myOtherClass->myConnection.connect([cl, a{3}](int b){ cl->myFunc(a, b); });

online compiler

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

#include <iostream>
#include <boost/signals2.hpp>
#include <boost/bind.hpp>
template< class A1, class B, class TResult, class T, typename... TArgs>
static boost::signals2::connection my_connect(TResult ( T::* f )(TArgs...), A1 a1, B &connection)
{
    return connection.connect([f, a1](TArgs... args){ (a1->*f)(::boost::forward<TArgs>(args)...); });
}
class OtherClass
{
public:
    boost::signals2::signal<void (int, int)> myConnection;
    void start() {
        myConnection(1, 2);
    }
};
class MyClass
{
public:
    void myFunc(int a, int b)
    {
        std::cout << "hello " << a << " " << b;
    }
};
int main(int argc, char** argv)
{
    MyClass *cl = new MyClass;
    OtherClass *myOtherClass = new OtherClass;
    my_connect(&MyClass::myFunc, cl, myOtherClass->myConnection);
    myOtherClass->start();
    return 0;
}

online compiler

READ ALSO
Перегруженная функция чистильщика

Перегруженная функция чистильщика

Как исправить данную ошибку

569
C++ массивы байт переменной (неизвестной) длины для представления данных и SHA256

C++ массивы байт переменной (неизвестной) длины для представления данных и SHA256

Хочу переписать этот код с Go на C++/Boost ради опыта работы с библиотеками типа asio

261
Цикличный сдвиг элементов списка [требует правки]

Цикличный сдвиг элементов списка [требует правки]

Как реализовать цикличный сдвиг элементов списка?

236
Расшифровка CryptoStream в с++

Расшифровка CryptoStream в с++

Есть файл зашифрованный с помощью класса CryptoStream (NET Framework)

263