0

I am trying to create a map of functions with different parameters and return types. So, in order to do that, I have searched a lot through stackoverflow, and I think I got what I need, but not exactly...

Background:

From this post, the answer from pmr is the most accurate solution that I need. So, I have expanded the function call to a variadic template (I hope). So, here is my version of just that call (the rest of the AnyCaller class is exactly the same):

   template<typename Ret, typename ...Args>
   Ret call(const std::string& s, Args&&... arg) {
     // we have to assume that our users know what we are actually returning here
     const boost::any& a = calls[s];
     return boost::any_cast< std::function<Ret(Args...)> >(a)(std::forward<Args>(arg)...);
   }

And it compiles ok. Now, these are the three functions that I have used to test the class:

int foo() { MessageBoxW(nullptr, L"Helolo VOID", L"Info", NULL); return 1; }
double foo2(std::wstring str) { MessageBoxW(nullptr, str.data(), L"Info", NULL); return double(4.5); }

UINT foo3(std::wstring str1, std::wstring str2) 
{ 
  std::wstring strBuffer;

  strBuffer = str1;
  strBuffer += L"==>";
  strBuffer += str2;
  MessageBoxW(nullptr, strBuffer.data(), L"Info", NULL); 

  return 4; 
}

So, the first one has a signature of int(void), the second double(std::wstring) and the third double(std::wstring, std::wstring).

Now, this is the testing code:

  AnyCaller c;
  c.add("foo1", std::function<int(void)>(foo));
  c.add("foo2", std::function<double(std::wstring)>(foo2));
  c.add("foo3", std::function<UINT(std::wstring, std::wstring)>(foo3));

  c.call<int>("foo1");
  c.call<double, std::wstring>("foo2", std::wstring(L"foo2!!!").data());
  c.call<UINT, std::wstring, std::wstring>("foo3", std::wstring(L"foo3!!!").data(), std::wstring(L"---foo3!!!").data());

And all is running smoothly :)

So, yet this is working ok, what I really need is to add support for function members. Basically, what I have done is to create a class A with that exact same three functions and a few more for test purpose:

class A
{
public:
  int foo() { MessageBoxW(nullptr, L"Helolo VOID", L"Info", NULL); return 1; }
  double foo2(std::wstring str) { MessageBoxW(nullptr, str.data(), L"Info", NULL); return double(4.5); }

  UINT foo3(std::wstring str1, std::wstring str2)
  {
    std::wstring strBuffer;

    strBuffer = str1;
    strBuffer += L"==>";
    strBuffer += str2;
    MessageBoxW(nullptr, strBuffer.data(), L"Info", NULL);

    return 4;
  }

  std::wstring foo4(VOID) { return std::wstring(L"foo4"); }
  std::wstring foo5(std::wstring strData) { return (strData + L"--foo5"); }
  VOID foo6(VOID) { ; }
};

But I am unable to get this working. My first problem is to add the pointer to the member function:

A a;
c.add("foo1", std::function<int(void)>(&A::foo)); // => Not valid
c.add("foo1", std::function<int(void)>(&a.foo));  // => Not valid
c.add("foo1", &a.foo);                            // => Not valid
c.add("foo1", a.foo);                             // => Not valid
c.add("foo1", ?????); //What in heaven goes here?

Obviously must be some kind of conversion from a, but I cannot imagine what...

Of course, after that, I need to do the actual call:

int nRet = c.call<int>("foo1");

Thanks to anyone that can help :_)

PS: I cannot do static members, if that is a solution...

PS2: I am using VS2013...

SOLUTION:

Thanks to the comments of @Kiroxas, @Praetorian and this post, I have come out with a solution that does not involve variadic templates.

These are my test classes A and B:

class A
{
public:
  int foo1() { MessageBoxW(nullptr, L"Helolo VOID", L"Info", NULL); return 1; }
  int foo2(std::wstring str) { MessageBoxW(nullptr, str.data(), L"Info", NULL); return 5; }

  int foo3(std::wstring str1, std::wstring str2)
  {
    std::wstring strBuffer;

    strBuffer = str1;
    strBuffer += L"==>";
    strBuffer += str2;
    MessageBoxW(nullptr, strBuffer.data(), L"Info", NULL);

    return 4;
  }
};

class B
{
public:
  std::wstring foo4(VOID) { return std::wstring(L"foo4"); }
  std::wstring foo5(std::wstring strData) { return (strData + L"--foo5"); }
  VOID foo6(VOID) { ; }
  double foo7(std::wstring str1, int nNum) 
  {
    std::wstring strBuffer;

    strBuffer = str1;
    strBuffer += L"==>";
    strBuffer += std::to_wstring(nNum);
    MessageBoxW(nullptr, strBuffer.data(), L"Info", NULL);

    return double(3.1415);
  }
};

And this is the code to insert them into an array and call them :) My ideal solution would be to insert them into a map, so, as soon as I have all working, I will update ¡again! the post.

      typedef struct
      {
        UINT ID;
        std::wstring NAME;
        boost::any Func;
      } funcs;
      funcs CallBackItems[] =
      {
        //From class A
        { 0, L"foo1", std::function<int(void)>(std::bind(&A::foo1, a)) },
        { 1, L"foo2", std::function<int(std::wstring)>(std::bind(&A::foo2, a, std::placeholders::_1)) },
        { 2, L"foo3", std::function<int(std::wstring, std::wstring)>(std::bind(&A::foo3, a, std::placeholders::_1, std::placeholders::_2)) },
        //From class B
        { 3, L"foo4", std::function<std::wstring(void)>(std::bind(&B::foo4, b)) },
        { 4, L"foo5", std::function<std::wstring(std::wstring)>(std::bind(&B::foo5, b, std::placeholders::_1)) },
        { 5, L"foo6", std::function<void(void)>(std::bind(&B::foo6, b)) },
        { 6, L"foo7", std::function<double(std::wstring, int)>(std::bind(&B::foo7, b, std::placeholders::_1, std::placeholders::_2)) }
      };

      int nResult = -1;
      std::wstring wstrResult = L"";
      double dResult = 0.0;

  //class A
  nResult = boost::any_cast<std::function<int(void)>>(CallBackItems[0].Func)();
  nResult = boost::any_cast<std::function<int(std::wstring)>>(CallBackItems[1].Func)(L"foo2");
  nResult = boost::any_cast<std::function<int(std::wstring, std::wstring)>>(CallBackItems[2].Func)(L"foo", L"3");
  //class B
  wstrResult = boost::any_cast<std::function<std::wstring(void)>>(CallBackItems[3].Func)();
  wstrResult = boost::any_cast<std::function<std::wstring(std::wstring)>>(CallBackItems[4].Func)(L"foo5");
  boost::any_cast<std::function<void(void)>>(CallBackItems[5].Func)();
  dResult = boost::any_cast<std::function<double(std::wstring, int)>>(CallBackItems[6].Func)(L"foo", 7);
Community
  • 1
  • 1
  • 1
    A function member needs an object as parameter, so use std::bind to bind your object `a` to your function. like `c.add("foo1", std::function(std::bind(&A::foo, std::ref(a)));` – Kiroxas Jul 03 '14 at 15:46
  • Thanks @Kiroxas! Sadly it does not work under VS2013 :( – Jesús Del Río Jul 04 '14 at 06:53

1 Answers1

1

A::foo(1|2|3) are non-static member functions, which means they take an implicit first argument, a pointer to the object instance on which they're being invoked (the this pointer). You have two options available, either use std::bind to bind the object on which you're going to invoke the member function, or pass a pointer to the object later when you call() it.

I'm replacing your two call overloads with a single variadic template version

template<typename Ret, typename... T>
Ret call(const std::string& s, T&&... arg) {
    // we have to assume that our users know what we are actually returning here
    const boost::any& a = calls[s];
    return boost::any_cast< std::function<Ret(T...)> >(a)(std::forward<T>(arg)...);
}

Option 1: using std::bind

A a;
AnyCaller c;
c.add("foo1", std::function<int()>(std::bind(&A::foo1, &a)));
c.add("foo2", std::function<double(std::wstring)>(
                std::bind(&A::foo2, &a, std::placeholders::_1)));

c.call<int>("foo1");
c.call<double>("foo2", std::wstring(L"Calling foo2"));

Option 2: pass the object pointer when invoking the function. Note that in this case the type of the std::function is different.

A a;
AnyCaller c;
c.add("foo1", std::function<int(A*)>(&A::foo1));
c.add("foo2", std::function<double(A*, std::wstring)>(&A::foo2));

c.call<int>("foo1", &a);
c.call<double>("foo2", &a, std::wstring(L"Calling foo2"));

The second option does not work on VS2013.

Live demo of both options.

Praetorian
  • 106,671
  • 19
  • 240
  • 328
  • Thank you @Praetorian ! :D It works flawessly in the environment from coliru but, sadly, in VS2013 does not :( Could http://stackoverflow.com/a/8304873/3802137 help here? – Jesús Del Río Jul 04 '14 at 06:51
  • @JesúsDelRío There was an error in the example I posted, I didn't notice the return type of `foo2` was `double` and not `int`. Anyway, the second version will still not work on VS2013 because of [this bug](http://connect.microsoft.com/VisualStudio/feedback/details/694400). So your only option on that compiler is using `std::bind`. – Praetorian Jul 04 '14 at 18:08