Ошибки «Нет совпадения» с функцией обратного вызова члена, использующей std::tr1::function

Я пытаюсь создать функцию обратного вызова, используя str::tr1::function, указывающую на общедоступную функцию-член.

std::tr1::function < int (const string& , const MessageInfo* , const void* , const int , const void* ) > dssCallBack;
dssCallBack = &ABC::mDBtoDScallback;

Этот обратный вызов будет передан функции внутри тела другой функции класса ABC. Подпись ABC::mDBtoDScallback есть

int DataserviceSubscriber::mDBtoDScallback(const string& strTopic, const MessageInfo* messageInfo, const void* data, const int dataLen, const void* callback_data)

Когда я пытаюсь скомпилировать это, я получаю следующую ошибку от g++.

In file included from /usr/lib/gcc/x86_64-redhat-linux/4.4.6/../../../../include/c++/4.4.6/tr1/functional:56,
                 from ../src/bmrk/databus/ABC.hpp:17,
                 from ../src/bmrk/databus/ABC.cpp:1:
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/../../../../include/c++/4.4.6/tr1_impl/functional: In static member function ‘static _Res std::tr1::_Function_handler<_Res(_ArgTypes ...), _Member _Class::*>::_M_invoke(const std::tr1::_Any_data&, _ArgTypes ...) [with _Class = ABC, _Member = int(const std::string&, const MessageInfo*, const void*, int, const void*), _Res = int, _ArgTypes = const std::string&, const MessageInfo*, const void*, int, const void*]’:
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/../../../../include/c++/4.4.6/tr1_impl/functional:2005:   instantiated from ‘std::tr1::function<_Res(_ArgTypes ...)>::function(_Functor, typename __gnu_cxx::__enable_if<(! std::tr1::is_integral::value), std::tr1::function<_Res(_ArgTypes ...)>::_Useless>::__type) [with _Functor = int (ABC::*)(const std::string&, const MessageInfo*, const void*, int, const void*), _Res = int, _ArgTypes = const std::string&, const MessageInfo*, const void*, int, const void*]’

/usr/lib/gcc/x86_64-redhat-linux/4.4.6/../../../../include/c++/4.4.6/tr1_impl/functional:1885:   instantiated from ‘typename __gnu_cxx::__enable_if<(! std::tr1::is_integral::value), std::tr1::function<_Res(_ArgTypes ...)>&>::__type std::tr1::function<_Res(_ArgTypes ...)>::operator=(_Functor) [with _Functor = int (ABC::*)(const std::string&, const MessageInfo*, const void*, int, const void*), _Res = int, _ArgTypes = const std::string&, const MessageInfo*, const void*, int, const void*]’
../src/bmrk/databus/dataservice_subscriber.cpp:266:   instantiated from here

/usr/lib/gcc/x86_64-redhat-linux/4.4.6/../../../../include/c++/4.4.6/tr1_impl/functional:1714: error: no match for call to ‘(std::tr1::_Mem_fn<int (ABC::*)(const std::string&, const MessageInfo*, const void*, int, const void*)>) (const std::basic_string<char, std::char_traits<char>, std::allocator<char> >&, const MessageInfo*&, const void*&, int&, const void*&)’

/usr/lib/gcc/x86_64-redhat-linux/4.4.6/../../../../include/c++/4.4.6/tr1_impl/functional:546: note: candidates are: _Res std::tr1::_Mem_fn<_Res (_Class::*)(_ArgTypes ...)>::operator()(_Class&, _ArgTypes ...) const [with _Res = int, _Class = ABC, _ArgTypes = const std::string&, const MessageInfo*, const void*, int, const void*]

/usr/lib/gcc/x86_64-redhat-linux/4.4.6/../../../../include/c++/4.4.6/tr1_impl/functional:551: note:                 _Res std::tr1::_Mem_fn<_Res (_Class::*)(_ArgTypes ...)>::operator()(_Class*, _ArgTypes ...) const [with _Res = int, _Class = ABC, _ArgTypes = const std::string&, const MessageInfo*, const void*, int, const void*]

Я пытаюсь увидеть, что я делаю неправильно здесь, но не могу это заметить. Я попытался найти, но не смог найти других с похожими проблемами. Я мог бы использовать typedef в стиле C, но я хотел использовать и сохранять вещи в стиле C++, в процессе также привыкая к некоторым новым вещам в C++11.

Спасибо.

РЕДАКТИРОВАТЬ: по просьбе Майкла Берра обратный вызов вызывается из функции в соответствии со ссылкой здесь http://en.cppreference.com/w/cpp/utility/functional/function

int ABC::subs_rt(const vector<string> &symbols, raw_callback_t raw_callback, void *app_data, Error *error)
{
    DBtoDS_callback_data cbData;
    cbData.subscriber_callback = raw_callback;
    cbData.raw_callback_app_data = app_data;
    cbData.err = error;

    // Perform processing on 'symbols'
    // dss is a member of class ABC and has been initialized in constructor
    dss->AddSubscriptionPrefix(symbols);
    b_cancel_subscription = false;

    std::tr1::function < int (const string& , const MessageInfo* , const void* , const int , const void* ) > dssCallBack;
    dssCallBack = &DataserviceSubscriber::mDBtoDScallback;

    dss->Subscribe(dssCallBack, static_cast<const void*>(&cbData));

    return 0;
}

Сам обратный вызов выглядит так

int ABC::mDBtoDScallback(const string& strTopic, const MessageInfo* messageInfo, const void* data, const int dataLen, const void* callback_data)
{
    const DBtoDS_callback_data* cbData = static_cast<const DBtoDS_callback_data*>(callback_data);

    if(0 == messageInfo) // Version 1
    {
        // Do callback Stuff
    }
    else // Version 2
    {
        Subscriber::timeval_t now;
        TimeUtils::now(now);
        std::string payload(static_cast<const char*>(data), dataLen);

        // Do callback Stuff
    }
}

Функция int ABC::mDBtoDScallback не является статической, как предположил WhozCraig. Это проблема? Я не могу сделать некоторые переменные, используемые в этой функции, статическими. Есть ли способ обойти это или мне нужно использовать указатель функции в стиле C?

Спасибо.

РЕДАКТИРОВАТЬ 2: согласно н.м. и озабоченность WhozCraig и по этой ссылке C++: назначение функции в объект tr1::function

Я изменил строки в функции ABC::subs_rt на

std::tr1::function < int (const string& , const MessageInfo* , const void* , const int , const void* ) > dssCallBack;
//dssCallBack = std::tr1::bind(&ABC::mDBtoDScallback, this, std::tr1::placeholders::_1);
dssCallBack = std::tr1::bind(&ABC::mDBtoDScallback, this);

Я попробовал вариант с комментариями и вариант без комментариев, но теперь получаю эту ошибку

In file included from /usr/lib/gcc/x86_64-redhat-linux/4.4.6/../../../../include/c++/4.4.6/tr1/functional:56,
                 from ../src/bmrk/databus/ABC.hpp:17,
                 from ../src/bmrk/databus/ABC.cpp:1:
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/../../../../include/c++/4.4.6/tr1_impl/functional: In member function ‘typename std::tr1::result_of<_Functor(typename std::tr1::result_of<std::tr1::_Mu<_Bound_args, std::tr1::is_bind_expression::value, (std::tr1::is_placeholder::value > 0)>(_Bound_args, std::tr1::tuple<_UElements ...>)>::type ...)>::type std::tr1::_Bind<_Functor(_Bound_args ...)>::__call(const std::tr1::tuple<_UElements ...>&, std::tr1::_Index_tuple<_Indexes ...>) [with _Args = const std::basic_string<char, std::char_traits<char>, std::allocator<char> >&, const MessageInfo*&, const void*&, int&, const void*&, int ..._Indexes = 0, _Functor = std::tr1::_Mem_fn<int (ABC::*)(const std::string&, const MessageInfo*, const void*, int, const void*)>, _Bound_args = ABC*]’:
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/../../../../include/c++/4.4.6/tr1_impl/functional:1191:   instantiated from ‘typename std::tr1::result_of<_Functor(typename std::tr1::result_of<std::tr1::_Mu<_Bound_args, std::tr1::is_bind_expression::value, (std::tr1::is_placeholder::value > 0)>(_Bound_args, std::tr1::tuple<_UElements ...>)>::type ...)>::type std::tr1::_Bind<_Functor(_Bound_args ...)>::operator()(_Args& ...) [with _Args = const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, const MessageInfo*, const void*, int, const void*, _Functor = std::tr1::_Mem_fn<int (ABC::*)(const std::string&, const MessageInfo*, const void*, int, const void*)>, _Bound_args = ABC*]’
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/../../../../include/c++/4.4.6/tr1_impl/functional:1654:   instantiated from ‘static _Res std::tr1::_Function_handler<_Res(_ArgTypes ...), _Functor>::_M_invoke(const std::tr1::_Any_data&, _ArgTypes ...) [with _Res = int, _Functor = std::tr1::_Bind<std::tr1::_Mem_fn<int (ABC::*)(const std::string&, const MessageInfo*, const void*, int, const void*)>(ABC*)>, _ArgTypes = const std::string&, const MessageInfo*, const void*, int, const void*]’
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/../../../../include/c++/4.4.6/tr1_impl/functional:2005:   instantiated from ‘std::tr1::function<_Res(_ArgTypes ...)>::function(_Functor, typename __gnu_cxx::__enable_if<(! std::tr1::is_integral::value), std::tr1::function<_Res(_ArgTypes ...)>::_Useless>::__type) [with _Functor = std::tr1::_Bind<std::tr1::_Mem_fn<int (ABC::*)(const std::string&, const MessageInfo*, const void*, int, const void*)>(ABC*)>, _Res = int, _ArgTypes = const std::string&, const MessageInfo*, const void*, int, const void*]’
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/../../../../include/c++/4.4.6/tr1_impl/functional:1885:   instantiated from ‘typename __gnu_cxx::__enable_if<(! std::tr1::is_integral::value), std::tr1::function<_Res(_ArgTypes ...)>&>::__type std::tr1::function<_Res(_ArgTypes ...)>::operator=(_Functor) [with _Functor = std::tr1::_Bind<std::tr1::_Mem_fn<int (ABC::*)(const std::string&, const MessageInfo*, const void*, int, const void*)>(ABC*)>, _Res = int, _ArgTypes = const std::string&, const MessageInfo*, const void*, int, const void*]’
../src/bmrk/databus/ABC.cpp:266:   instantiated from here
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/../../../../include/c++/4.4.6/tr1_impl/functional:1137: error: no match for call to ‘(std::tr1::_Mem_fn<int (ABC::*)(const std::string&, const MessageInfo*, const void*, int, const void*)>) (ABC*&)’
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/../../../../include/c++/4.4.6/tr1_impl/functional:546: note: candidates are: _Res std::tr1::_Mem_fn<_Res (_Class::*)(_ArgTypes ...)>::operator()(_Class&, _ArgTypes ...) const [with _Res = int, _Class = ABC, _ArgTypes = const std::string&, const MessageInfo*, const void*, int, const void*]
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/../../../../include/c++/4.4.6/tr1_impl/functional:551: note:                 _Res std::tr1::_Mem_fn<_Res (_Class::*)(_ArgTypes ...)>::operator()(_Class*, _ArgTypes ...) const [with _Res = int, _Class = ABC, _ArgTypes = const std::string&, const MessageInfo*, const void*, int, const void*]
make[1]: *** [ABC.lo] Error 1

РЕШЕНИЕ ПРОБЛЕМЫ: Учитывая мои требования, я решил сделать свой обратный вызов статической функцией-членом и передать указатель на объект родительского класса через const void* callback_data. Будучи статической функцией, она может обращаться к приватным функциям класса ABC и передавать параметры в raw_callback. Помощь, которую я получил от всех комментариев, была для меня большим опытом обучения и в конечном итоге привела меня к решению.

static int ABC::mDBtoDScallback(const string& strTopic, const MessageInfo* messageInfo, const void* data, const int dataLen, const void* callback_data)
{
    const DBtoDS_callback_data* cbData = static_cast<const DBtoDS_callback_data*>(callback_data);

    TimeUtils::now(now);
    std::string payload(static_cast<const char*>(data), dataLen);

    string symbol;
    string sym;
    int pri_s = 0;

    if(0 == messageInfo)
    {
        parse_topic(strTopic, symbol, pri_s, cbData->err);
    }
    else
    {
        symbol = messageInfo->key();
        pri_s = ( messageInfo->has_pri_s() ? messageInfo->pri_s() : 0 );
    }
    if (cbData->subs->symbols_need_translation())
    {
        cbData->subs->translate_symbol(cbData->subs->_translator, symbol, sym, false);
    }
    else
    {
        sym = symbol;
    }

    cbData->subscriber_callback(cbData->subs, sym, pri_s, cbData->subs->prod, payload, now, cbData->raw_callback_app_data, cbData->err);
}

Спасибо.


person Chinmay Nerurkar    schedule 14.01.2013    source источник
comment
Я так понимаю, ABC::mDBtoDScallback не является статическим членом класса? Функция пытается вывести как (ABC::*), чего ваш функциональный объект не ожидает (и будьте осторожны, пипсы, мне не особенно удобно работать с новыми функциональными объектами и им подобными в С++ 11, но это, по крайней мере, кажется ( для меня) быть проблемой).   -  person WhozCraig    schedule 14.01.2013
comment
@WhozCraig: Да, выглядит правильно.   -  person Lightness Races in Orbit    schedule 14.01.2013
comment
Было бы неплохо получить небольшой автономный пример для работы.   -  person Michael Burr    schedule 14.01.2013
comment
Я отредактировал вопрос, чтобы дать немного больше информации о том, что я делаю, как предложил @MichaelBurr.   -  person Chinmay Nerurkar    schedule 14.01.2013
comment
Функция не является статической, как догадался @LightnessRacesinOrbit, и использует некоторые нестатические переменные из класса. Что я могу с этим сделать в таком случае?   -  person Chinmay Nerurkar    schedule 14.01.2013
comment
Как бы вы передали this dssCallBack? У него нет аргумента типа ABC*.   -  person n. 1.8e9-where's-my-share m.    schedule 14.01.2013
comment
@ChinmayNerurkar Чтобы он не был статичным, вам нужно предоставить this. Я очень новичок в объектах-функциях, объектах-членах и т. д., поэтому моя помощь будет ограниченной, но я изготовил интересный образец, и я должен убедиться, что эти вещи чертовски хороши. .   -  person WhozCraig    schedule 14.01.2013
comment
@н.м. и WhozCraig, я ценю вашу помощь. Я пытался передать this с помощью bind, но, я думаю, это тоже не помогло.   -  person Chinmay Nerurkar    schedule 14.01.2013
comment
Просто добавьте ABC* в качестве первого типа параметра.   -  person n. 1.8e9-where's-my-share m.    schedule 14.01.2013
comment
+1 Я согласен с @n.m. Если у вас есть обновленный код, представленный выше, это, вероятно, решит вашу проблему, но мне все равно было интересно изучить это, так что спасибо за это!   -  person WhozCraig    schedule 14.01.2013


Ответы (2)


Как обсуждалось в комментариях, проблема, по-видимому, заключается в том, что ваш объект функции ожидает статическую функцию, не связанную с объектом, в то время как вы фактически передаете ей член. Из того, что я вижу, для этого есть несколько обходных путей, самый простой из которых вы, вероятно, уже рассматриваете; используя статический и передайте указатель вашего объекта в качестве параметра.

В качестве альтернативы, я не знаю, будет ли это работать для вашей конкретной архитектуры, но было быстро собрано следующее, как вызвать функцию-член с указанным объектом, используя std::mem_fn<> в сочетании с std::function<>. Это отдельный образец, но я надеюсь, вы поймете, как он может вам помочь.

#include <iostream>
#include <functional>
using namespace std;

class MyClass
{
public:
    MyClass() {}

    int CallMe(void *p, int a, float f)
    {
        // use params herere
        cout << "CallMe fired : " << this << " !\n" << endl;
        return 0;
    }
};

int main()
{
    MyClass obj, obj2;
    std::function<int(MyClass*,void*,int,float)> fn(std::mem_fn(&MyClass::CallMe));

    cout << "Invoking CallMe with " << &obj << " object..." << endl;
    fn(&obj, NULL, 1, 2.0);

    cout << "Invoking CallMe with " << &obj2 << " object..." << endl;
    fn(&obj2, NULL, 1, 2.0);

    return 0;
}

Вывод

Invoking CallMe with 0x7fff5fbff7d8 object...
CallMe fired : 0x7fff5fbff7d8 !

Invoking CallMe with 0x7fff5fbff7d0 object...
CallMe fired : 0x7fff5fbff7d0 !

Примечание

Информация о том, как работают объекты функций-членов, представленная в C++11, на cppreference. com меня захватывает. Не то чтобы это кого-то действительно заботит. Я понятия не имею, правильно ли они отправляются в виртуальные машины и т. д., но я совершенно поражен ими.

Я надеюсь, что вы найдете это полезным, но я полностью готов удалить его, если один из экспертов по std-lib, который действительно понимает глубины std::function, std::bind и std::men_fn, предложит более краткие объяснения. (или рвет этот фрагмент в клочья). Честно говоря, вероятно, где-то на SO есть лучшие примеры вызова элементов через std::function<>, но я должен сказать, что простота использования std::men_fn<> с std::function<> довольно увлекательна.


Виртуальная рассылка

После поиска комментария DeadMG мне было искренне любопытно, работает ли виртуальная диспетчеризация. Я думал, что это хороший шанс, так как мы предоставляем указатель this, но не затаил дыхание, так как ясно, что мы передаем адрес члена MyClass::CallMe конструктору нашего std:::mem_fn<>.

Обновленный исходный код и полученный результат были интересны. Обратите внимание на один и тот же функциональный объект, используемый в обоих экземплярах. Предназначен ли он для такой работы (а похоже, кстати) я не могу сказать, но мне было интересно, тем не менее.

#include <iostream>
#include <functional>
using namespace std;

class MyClass
{
public:
    MyClass() {}

    virtual int CallMe(void *p, int a, float f)
    {
        // use params herere
        cout << "MyClass::CallMe fired : " << this << endl;
        return 0;
    }
};

class MyDerived : public MyClass
{
public:
    MyDerived() {}

    virtual int CallMe(void *p, int a, float f)
    {
        // use params herere
        cout << "MyDerived::CallMe fired : " << this << endl;
        return 0;
    }
};

int main()
{
    MyClass obj;
    MyDerived obj2;

    std::function<int(MyClass*,void*,int,float)> fn(std::mem_fn(&MyClass::CallMe));

    cout << "Invoking CallMe with " << &obj << " object..." << endl;
    fn(&obj, NULL, 1, 2.0);

    cout << "Invoking CallMe with " << &obj2 << " object..." << endl;
    fn(&obj2, NULL, 1, 2.0);

    return 0;
}

Вывод

Invoking CallMe with 000000000021FB58 object...
MyClass::CallMe fired : 000000000021FB58
Invoking CallMe with 000000000021FB78 object...
MyDerived::CallMe fired : 000000000021FB78
person WhozCraig    schedule 14.01.2013
comment
Спасибо за объяснение. Я просматриваю страницы cppreference.com, и это действительно интересно. Моя проблема заключается в том, что сигнатура обратного вызова, используемая dssCallBack, определена в классе, который является типом «dss». Я не могу изменить эту подпись, хотя и понимаю, почему здесь важна передача this. было бы бессмысленно связывать обратный вызов класса `dss' с классом 'ABC'. Тем не менее, пожалуйста, не удаляйте этот ответ, так как он содержит полезную информацию и пример. - person Chinmay Nerurkar; 15.01.2013
comment
@ChinmayNerurkar Хорошо. Я оставлю это. Кстати, вам не нужно менять сигнатуру функции, а нужно изменить decl, переданный шаблону std::function<>. Если у вас есть возможность изменить decl шаблона объекта функции, который вы используете для вызова, а также точку вызова (для передачи объекта и параметров), вы, вероятно, сможете обойти это. Но фактическое объявление функции-члена не должно требовать таких изменений (у него уже есть указатель *this, положение в нем — это то, с чего все началось, поскольку оно не статично). Удачи, сэр. - person WhozCraig; 15.01.2013

Источник простой, если знать, где искать. Рассмотрим следующий код:

std::tr1::function < int (const string& , const MessageInfo* , const void* , const int , const void* ) > dssCallBack;
dssCallBack = &ABC::mDBtoDScallback;
dssCallBack(std::string(), nullptr, nullptr, 0, nullptr);

Что такое this? Вы никогда его не предоставляли. Таким образом, std::function никак не может выполнить эту работу - вы пытаетесь вызвать член, но не предоставили объект. Что должен делать std::function, волшебным образом решать, каким должен быть this?

Два решения: связать this с помощью std::bind или передать this в качестве аргумента, как в std::mem_fn.

person Puppy    schedule 14.01.2013
comment
+1. Эти штуки чертовски скользкие, сэр. Я получил удовольствие, потратив полчаса на их изучение. Дополнительный вопрос, который я упомянул в комментариях и моем ответе ниже (который будет удален): правильно ли это отправляет на виртуальные переопределения? Искренне любопытно. - person WhozCraig; 14.01.2013
comment
Отправка как? ABC::mDBtoDScallback — это конкретная функция, в ней нет ничего виртуального или невиртуального. Он никогда не вызовет ничего, кроме ABC::mDBtoDScallback. Я думаю. Я на самом деле не знаю. PTMF в основном плохи для всего, поэтому я редко их использую. - person Puppy; 15.01.2013
comment
@DeadMG Я понимаю, почему здесь важно this. Однако я не могу изменить подпись dssCallBack, поскольку она определена в типе класса dss. В основном мне нужно вызвать dssCallBack из функции ABC::subs_rt и сделать dssCallBack вызовом raw_callback из аргументов функции ABC::subs_rt. raw_callback необходимо получить доступ к закрытым членам объекта класса ABC, переданного ему как аргумент ABC*. - person Chinmay Nerurkar; 15.01.2013
comment
@DeadMG это было то, о чем я думал, легко проверить, но ваша оценка имеет смысл. Спасибо. - person WhozCraig; 15.01.2013
comment
@Чинмай: Что? Мне все равно. Если подпись не позволяет передать this, то вы должны связать ее, и все. - person Puppy; 15.01.2013