Как я могу создать функцию-член С++, привязав аргументы другой функции-члена?

У меня возникают проблемы с созданием переменной типа "указатель на функцию-член" (PTMF) "на лету" (то есть путем закрепления некоторых аргументов существующей функции-члена через std::bind). Мой вопрос в том, возможно ли это когда-либо со стандартом С++ 11 или пост-С++ 11.

Преамбула: у меня есть класс, в котором хранится статический массив констант std::functions, инициализированных из PTMF, далее именуемых «обработчиками». Изначально это были обычные функции-члены с именем и реализацией, поэтому я никогда не использовал C++11 и std::function. Затем я решил, что многие из них очень похожи, и решил сгенерировать их с помощью «функции-генератора». Я хотел бы избежать использования шаблонов для генерации, потому что количество этих почти одинаковых обработчиков в будущем резко возрастет (около 200+), а шаблонизация просто приведет к раздуванию кода.

Если бы рассматриваемые PTMF были статическими, у меня не было бы проблем с генерацией обработчиков через std::bind. Упрощенный пример:

#include <iostream>
#include <functional>


using namespace std;


struct A {
    typedef function<void(int)> HandlerFn;

    static void parametrized_handler(int i, const char *param) {
        cout << "parametrized handler: " << param << endl;
    }

    static void handler(int i) { cout << "handler 1" << endl; }

    int mm;
};

static const A::HandlerFn handler2 = [](int) { cout << "handler 2" << endl; };

static const A::HandlerFn handler3 = bind(A::parametrized_handler,
                                          placeholders::_1,
                                          "test_param");


int main()
{
    A::handler(42);
    handler2(42);
    handler3(42);

    return 0;
}

Выход:

$ ./a.out 
handler 1
handler 2
parametrized handler: test_param

Проблема возникает, когда я обращаюсь к нестатическим функциям-членам. std::bind не может генерировать объект функции, который действует как PTMF. Я знаю, что могу передать реальный объект в качестве первого аргумента для привязки и получить работающую функцию, но это не то, чего я хочу: когда я инициализирую статический массив const, объектов вообще нет, и результат привязки будет в любом случае действовать как обычная функция, не являющаяся членом.

Ожидаемая реализация для нестатических функций-членов (с воображаемым связующим std::bind_mem):

#include <iostream>
#include <functional>


using namespace std;

struct A;


struct A {
    typedef function<void(int)> HandlerFn;

    void parametrized_handler(int i, const char *param) {
        mm;
        cout << "parametrized handler: " << param << endl;
    }

    void handler(int i) const { mm; cout << "handler 1" << endl; }

    const HandlerFn handler2 = [this](int i) { mm; cout << "handler 2" << endl; };

    int mm;
};


// An imaginary PTMF binder
// static const A::HandlerFn handler3 = bind_mem(A::parametrized_handler,
//                                               placeholders::_1,
//                                               "test_param");



int main()
{
    A a;

    (a.handler)(42);
    (a.handler2)(42);
    //(a.handler3)(42);

    return 0;
}

Выход:

$ ./a.out 
handler 1
handler 2

Итак, есть ли способ реализовать привязку аргумента PTMF?


person cheater    schedule 27.12.2017    source источник
comment
С . или .* нельзя. -> было бы громоздко, но как насчет ->*? Вы можете обернуть свои функции в собственный класс и для этого перегрузить operator ->*.   -  person Quentin    schedule 27.12.2017
comment
@Slava: Тогда вы предлагаете сделать все обработчики статическими. Это должно работать, но, поскольку это заставляет всю мою систему обработчиков покинуть мир указателей на члены, я по-прежнему использую это как последнее решение в дополнение к созданию собственного функционального класса (@Quentin) или использованию шаблонов. . Я все еще надеюсь на решение, которое позволит мне создать PTMF; У меня есть много потребностей в генерации указателя на член помимо предоставленного базового примера.   -  person cheater    schedule 27.12.2017


Ответы (1)


Для привязки указателя к нестатической функции-члену вам нужен объект.

#include<functional>

struct A {
    typedef std::function<void(int)> HandlerFn;
    void mem(int);
    void static static_mem(int);
};

void foo() {
    A a;
    A::HandlerFn h1 = A::static_mem;
    //This captures a by ref
    A::HandlerFn h2 = std::bind(&A::mem, std::ref(a), std::placeholders::_1);
    //This captures a by copy
    A::HandlerFn h3 = std::bind(&A::mem, a, std::placeholders::_1);
    //Change to =a for copy
    A::HandlerFn h4 = [&a](int i){
        a.mem(i);
    };
    h1(34);
    h2(42);    
}

Ссылка: https://godbolt.org/g/Mddexq

person balki    schedule 27.12.2017
comment
Это разочаровывает, учитывая тот факт, что логически объект для привязки не нужен. =( Хорошо, все равно спасибо. - person cheater; 27.12.2017
comment
@мошенник. Функции-члены нельзя вызывать без объектов. Если в классе нет состояния, рассмотрите возможность удаления класса и просто использования пространства имен для группировки связанных функций. - person balki; 27.12.2017
comment
Я говорю не об вызове обработчиков, а об их объявлении. Для состояния: к сожалению, у класса есть состояние (симулируется через член мм в моем примере). - person cheater; 27.12.2017