【问题标题】:'No Match' Errors with a member callback function using std::tr1::function使用 std::tr1::function 的成员回调函数出现“不匹配”错误
【发布时间】:2013-01-14 17:59:58
【问题描述】:

我正在尝试使用指向公共成员函数的 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*]

我想看看我在这里做错了什么,但无法发现它。我试图查找,但找不到其他有类似问题的人。我可以使用 C 风格的 typedef,但我想使用并保持 C++ 风格,在此过程中也习惯了 C++11 中的一些新内容。

谢谢。

编辑:根据 Michael Burr 的要求,回调是从函数中调用的,这里的引用 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:根据 n.m.和 WhozCraig 的关注和每个链接 C++: Assigning a function to a tr1::function object

我将 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。从所有 cmets 获得的帮助对我来说是一次重要的学习经历,并最终引导我找到了解决方案。

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);
}

谢谢。

【问题讨论】:

  • 我认为ABC::mDBtoDScallback 不是静态类成员吗?该函数试图推断为(ABC::*),这是您的函数对象所不期望的(并且要温柔,偷看,我对 C++11 中的新函数对象及其同类不是特别满意,但这至少(对我来说)似乎是问题)。
  • @WhozCraig:是的,看起来差不多。
  • 如果有一个小型的、独立的示例可以使用会很好。
  • 我已经编辑了这个问题,以便按照@MichaelBurr 的建议提供更多关于我正在做的事情的背景信息。
  • 只需添加ABC*作为第一个参数类型。

标签: c++ function tr1


【解决方案1】:

来源很简单,如果你知道去哪里找的话。考虑以下代码:

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 应该是什么?

两种解决方案是,使用std::bind 绑定this,或将this 作为参数传递,如std::mem_fn

【讨论】:

  • +1。先生,这些东西真漂亮。我花了半个小时来研究它们,这让我大吃一惊。我在 cmets 中提到的附带问题以及我在下面的答案(即将被删除):这是否正确调度到虚拟覆盖?真的很好奇。
  • 发货如何? ABC::mDBtoDScallback 是一个特定的功能——它没有任何虚拟或非虚拟的东西。除了ABC::mDBtoDScallback,它永远不会调用任何东西。我想。我其实不知道。 PTMF 基本上对所有事情都不利,所以我很少使用它们。
  • @DeadMG 我明白为什么this 在这里很重要。但是我不能更改dssCallBack 的签名,因为它是在dss 的类类型中定义的。我基本上需要从ABC::subs_rt 函数调用dssCallBack 并从ABC::subs_rt 函数的参数中调用dssCallBack raw_callbackraw_callback 需要访问作为 ABC* 争论传递给它的 ABC 类对象的私有成员。
  • @DeadMG 这就是我的想法,易于测试,但您的评估非常有意义。谢谢。
  • @Chinmay:什么?我不在乎。如果签名不允许通过this,那么你必须绑定它,就是这样。
【解决方案2】:

正如在 cmets 中所讨论的,问题似乎是因为您的函数对象需要一个静态的非对象绑定函数,而您实际上是在向它传递一个成员。据我所知,有几种解决方法,您可能已经在考虑最简单的一种;使用静态并将对象的指针作为参数传递。

另外,我不知道这是否适用于您的特定架构,但以下内容快速汇总,介绍了如何使用std::mem_fn&lt;&gt;std::function&lt;&gt; 调用具有指定对象的成员函数。这是一个独立的示例,但我希望您能看到它对您的帮助。

#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 !

注意

cppreference.com 上的 C++11 中提供的关于成员函数对象如何工作的阅读对我来说着迷。并不是说有人真正关心这一点。我不知道他们是否正确地分派到虚拟机等,但我对他们绝对感到惊讶。

我希望这对您有所帮助,但如果一位真正了解 std::function、std::bind 和 std::men_fn 深度的 std-lib 专家提出建议,我完全准备好删除它更简洁的解释(或将这个 sn-p 撕成碎片)。老实说,在通过std::function&lt;&gt; 调用成员时,SO 上可能有更好的示例,但我不得不说将std::men_fn&lt;&gt;std::function&lt;&gt; 一起使用的简单性相当吸引人。


虚拟调度

在寻求 DeadMG 的评论后,我真的很好奇虚拟调度是否有效。我认为这是一个很好的机会,因为我们提供了一个this 指针,但我并没有屏住呼吸,因为显然我们将MyClass::CallMe 成员按地址传递给了std:::mem_fn&lt;&gt; 的构造函数。

更新后的源代码和运行结果很有趣。注意两个实例中使用的同一个函数对象。我不能说它是否打算以这种方式工作(它似乎是,顺便说一句),但它对我来说很有趣,尽管如此。

#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

【讨论】:

  • 感谢您的解释。我正在浏览 cppreference.com 页面,这确实很有趣。我的问题是dssCallBack 使用的回调签名是在“dss”类型的类中定义的。即使我明白为什么在这里传递this 很重要,我也无法更改该签名。将“dss”类回调绑定到“ABC”类是没有意义的。尽管如此,请不要删除此回复,因为它提供了有用的信息和示例。
  • @ChinmayNerurkar 好的。我会离开它。首先,您不必更改 function 的签名,而是更改传递给 std::function&lt;&gt; 模板的 decl。如果您可以灵活地更改用于调用的 function object 的模板 decl 以及调用点(传递 object+params),那么您可能会克服这个问题。但是实际的成员函数声明应该不需要这样的更改(它已经有一个*this 指针,其中的规定是所有这一切的开始,因为它是非静态的)。祝你好运,先生。
猜你喜欢
  • 1970-01-01
  • 2015-06-15
  • 1970-01-01
  • 1970-01-01
  • 2013-04-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-11-24
相关资源
最近更新 更多