Callback with variadic template to ambiguous overloads

这一生的挚爱 提交于 2020-01-06 08:58:07

问题


In C++0X, I want to write generic caller/callback functions using variadic templates. First hurdle: The callees are member functions. So far so good. Second hurdle: There are many overloaded member functions of the same name.

How would I solve this? My primary reference is this fine article, but I can't quite make it work.

OK, let's dive in:

Class Foo
{
    void bar(int ID, int, int) { ... }
    void bar(int ID) { ... }
    void bar(int ID, double, float, void(Baz::*)()) const { /* jikes */ }

    template<typename ... Args>
    void sendBarToID_15(std::function<void(int, Args...)> refB, Args ... args)
    {
        refB(15, args...);
    }

    void yum()
    {
        sendBarToID_15(&Foo::bar, this, 17, 29); // want first version
    }
};

However, I cannot compile the call in yum() because the overload prevents template resolution. According to the referenced article, I should explicitly create a function object

f = magic::make_function<help, me>(&Foo::bar)

and then leisurely call sendBarToID_15(f, this, 17, 29).

  1. How can I make this work?

  2. Bonus points for std::bind magic that obviates "this" in the last line.

  3. Extra bonus points for making 15 parametric in a useful way.

Lots of thanks!!


回答1:


Is this what you're looking for?

#include <functional>
#include <iostream>

class Baz;

class Foo
{
    void bar(int ID, int, int) { std::cout << "here\n"; }
    void bar(int ID) { /*...*/ }
    void bar(int ID, double, float, void(Baz::*)()) const { /* jikes */ }

    template<int ID, typename ... Args>
    void sendBarToID(std::function<void(int, Args...)> refB, Args&& ... args)
    {
        refB(ID, std::forward<Args>(args)...);
    }

public:
    void yum()
    {
        using namespace std::placeholders;
        void (Foo::*mfp)(int, int, int) = &Foo::bar;
        sendBarToID<15>(std::function<void(int, int, int)>
            (std::bind(mfp, this, _1, _2, _3)), 17, 29); // want first version
    }
};

int main()
{
    Foo foo;
    foo.yum();
}



回答2:


Use a lambda function- this kind of thing isn't necessary anymore.

class Foo
{
    void bar(int ID, int, int) { ... }
    void bar(int ID) { ... }
    void bar(int ID, double, float, void(Baz::*)()) const { /* jikes */ }

    template<typename ... Args>
    void sendBarToID_15(std::function<void(int, Args...)> refB, Args ... args)
    {
        refB(15, args...);
    }

    void yum()
    {
        sendBarToID_15([&, this](int i) {
            this->bar(i, 17, 29);
        });
    }
};



回答3:


You are trying to call bar with the following arguments in order : 15, this, 17, 29.

You want : this, 15, 17, 29

template<typename ... Args>
void sendBarToID_15(std::function<void(int, Args...)> refB, Args ... args)
{
    refB(15, args...);
}

So &Foo::bar can't be a std::function

If you can use lambda I'd use :

 void yum()
 {
    // EDITED: was "Foo* t"
    // (I don't remember of capture works with this, you may not need this)
    Foo* p = this;
    sendBarToID_15([p](int x, int y, int z){ p->bar(x, y, z); }, 17, 29);
 }

if you can't implement it with an helper class:

class helper {
private:
   Foo* p;
public:
   [...]
   void operator(int x, int y, int z) {
      p->bar(x,y,z);
   }
}

or using bind:

// EDITED: wrong return type was used
void (Fred::*f)(char x, float y) = &Foo::bar;
sendBarToID_15(std::bind(f, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), 17, 29);



回答4:


To follow up Howard's fine answer, let me just state that in the end I conclude that making the sendBarToID function templated doesn't really improve the logic of the setup in the way I had hoped. Since we have to bind() anyway, there's no reason to first bind and then unbind placeholders, we might as well just bind everything right in place. Here's the non-templated version:

void sendBarToID_15(std::function<void(int)> f)
{
    f(15);
}

void yum()
{
    // No avoiding this in the presence of overloads
    void (Foo::*mfp)(int, int, int) = &Foo::bar;

    sendBarToID_15(std::bind(mfp, this, std::placeholder::_1, 17, 29));
}

I was hoping that the variadic template solution could somehow make the client code simpler, but now I don't see how it can get any simpler than this. Variadic #define macros take care of the rest.

Thank you for the contributions!

Update: OK, here's what I finally came up with, thanks to preprocessor macros:

#include <functional>
#include <iostream>

class Baz;

class Foo
{
    void bar(int ID, const int &, int)
    { std::cout << "v1 called with ID " << ID << "\n"; }
    void bar(int ID)
    { std::cout << "v2 called with ID " << ID << "\n"; }
    void bar(int ID, double, float, void(Baz::*)()) const
    { std::cout << "v3 called with ID " << ID << "\n"; }

    void innocent(int ID, double)
    { std::cout << "innocent called with ID " << ID << "\n"; }

    void very_innocent(int ID, double) const
    { std::cout << "very innocent called with ID " << ID << "\n"; }

    template<int ID> void sendBarToID(std::function<void(int)> refB) { refB(ID); }
    template<int ID> void sendConstBarToID(std::function<void(int)> refB) const { refB(ID); }

#define MAKE_CALLBACK(f, ...) std::bind(&Foo::f, this, std::placeholders::_1, __VA_ARGS__)
#define MAKE_EXPLICIT_CALLBACK(g, ...) std::bind(g, this, std::placeholders::_1, __VA_ARGS__)

#define MAKE_SIGNED_CALLBACK(h, SIGNATURE, ...) MAKE_EXPLICIT_CALLBACK(static_cast<void (Foo::*)SIGNATURE>(&Foo::h), __VA_ARGS__)
#define MAKE_CONST_SIGNED_CALLBACK(h, SIGNATURE, ...) MAKE_EXPLICIT_CALLBACK(static_cast<void (Foo::*)SIGNATURE const>(&Foo::h), __VA_ARGS__)

public:
    void gobble()
    {
      double q = .5;
      int    n = 2875;
      void(Baz::*why)();

      sendBarToID<5>(MAKE_CALLBACK(innocent, q));
      sendConstBarToID<7>(MAKE_CALLBACK(very_innocent, q));
      // sendBarToID<11>(MAKE_SIGNED_CALLBACK(bar, (int))); // can't do, too much commas
      sendBarToID<13>(MAKE_SIGNED_CALLBACK(bar, (int, const int &, int), n, 1729));
      sendConstBarToID<17>(MAKE_CONST_SIGNED_CALLBACK(bar, (int, double, float, void(Baz::*)()), q, q, why));
    }

    void yum() const
    {
      double q = .5;
      int    n = 2875;
      void(Baz::*why)();

      sendConstBarToID<2>(MAKE_CALLBACK(very_innocent, q));
      // sendBarToID<-1>(MAKE_CALLBACK(innocent, q)); // Illegal in const function

      sendConstBarToID<3>(MAKE_CONST_SIGNED_CALLBACK(bar, (int, double, float, void(Baz::*)()), q, q, why));
    }
 };

int main()
{
    Foo foo;
    foo.yum();
    foo.gobble();
}

There is one inconvenience: I need to define two separate functions and macros for constant and non-constant member functions. Also, I can't handle the empty argument list (Foo::bar(int)).



来源:https://stackoverflow.com/questions/5608606/callback-with-variadic-template-to-ambiguous-overloads

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!