3

Even though I fear that you will tell me that this topic was covered several time, I dare to ask it, since I was not able to generate a solution. Probably I was just looking for the wrong thing...

Assume that I have a function which receives a "mode" from some external function. Depending on the mode, the function will call different member functions of the same object. This works well for me with member function without any argument, but I did not find out how to extend it to members with arguments. In the real world application, the arguments are not int/float but a more complex classes and the call is nested inside different loops, so I would need to put switch statements several times which I consider ugly.

Question A: Is it possible to easily add support for member functions with arguments based on the existing design? If yes, how does one do that? If possible without external libraries...

Question B: Is this a completely wrong/bad approach? How would I do it better?

Thanks a lot for your help and explanations.

Chris

header excerpt:

typedef void (Object::*memberFunction)();

class Object
{
    void memberFnNoArg();
    void memberFnWithIntArg(int arg);
    void memberFnWithFloatArg(float arg);
}

cpp excerpt:

void function()
{
    int mode = getModeFromSomewhere();

    int intArg = 33;
    float floatArg = 66.6;

    switch(mode)
    {
    case 1:
        process(&Object::memberFnNoArg);
        break;
    case 2:
        process(&Object::memberFnWithIntArg, ???); // how can I pass arg?
        break;
    case 3:
        process(&Object::memberFnWithFlaotArg, ???); // how can I pass arg?
        break;
    default:
        // do nothing;
    }

}

void process(Object::memberFunction func)
{
    Object object;
    // loops, called several times, ...
    (object.*func)(); // how do I handle different arguments?
}
Chris
  • 981
  • 4
  • 14
  • 29
  • 3
    Maybe [`std::bind`](http://en.cppreference.com/w/cpp/utility/functional/bind) will help here? – BoBTFish Jun 04 '13 at 09:08
  • Maybe use a template? – Barmar Jun 04 '13 at 09:31
  • I'm not sure how pointers to member functions are simplifying your design. It seems like turning the algorithm using the object into a functor might be a better approach. – Ben Voigt Jun 04 '13 at 22:24
  • @ben-voigt: It would simplify my design because inside process() the member function is called at several places inside the algorithm. Otherwise I would have to pass the mode and other arguments to process() and then distinguish every time. I am looking for a more transparent design... – Chris Jun 05 '13 at 09:05

7 Answers7

2

Have a look at std::function and std::bind, they seem to fit perfectly what you need.

EDIT:

std::function<void(Object &)> f = &Object::memberFnNoArg;
std::function<void(Object &)> f2 = std::bind(&Object::memberFnWithIntArg, _1, 22);

Object o;
f(o);
f2(o);

should work out of a box as far as I remember. Is this what you need?

Tomek
  • 4,554
  • 1
  • 19
  • 19
  • I'm glad to hear bind, because that was one thing I thought but was not sure about. At the moment I am fighting with passing only the member function without this pointer to bind... I edited the problem to illustrate the issue: I want to be able to pass the to-be-used member function before object is instantiated. – Chris Jun 04 '13 at 12:49
  • @Chris: You can bind any argument, not just the target object. – Ben Voigt Jun 04 '13 at 22:25
  • @Tomek: This looks promising, but I still don't see how to handle the member functions with arguments. Let's say I do the std::function assignment inside the switch statement. Then I would need to pass the function as an argument to 'void process()'. I tried to do that with assigning `auto f1 = std::bind(&Object::memberFnWithIntArg, 33);` but the type of f1 is not the same as f so I cannot handle f, f1, and f2 with the same process function... – Chris Jun 05 '13 at 08:58
  • @Chris: Check my edits. Please note I am not sure I got this right (I don't remember exact usage of std::bind). The keyword which you are probably looking for is 'placeholder'. – Tomek Jun 05 '13 at 10:06
  • Don't you need to use `mem_fn` or similar here? – Ben Voigt Jun 05 '13 at 14:36
  • @Ben: I am not sure, haven't used it for long time. I believe std::function and std::bind are to supersede, not to extend, mem_fn. There are some examples here: http://en.cppreference.com/w/cpp/utility/functional/bind – Tomek Jun 05 '13 at 21:28
2

Wrapping the algorithm in a functor is the right approach, and std::function is a nice functor provided by the Standard library.

But using boost::bind or even std::bind, as suggested by Tomek, is really ugly IMO, and rapidly gets out of control when binding multiple arguments.

If you have a recent compiler you can use a lambda instead, which makes Tomek's example look like:

std::function<void(Object*)> f  =
    [](Object* const that){ that->memberFnNoArg(); };

int int_value = 22;
std::function<void(Object*)> f2 =
    [int_value](Object* const that){ that->memberFnIntArg(int_value); };

Object o;
f(&o);
f2(&o);

There are a few characters to set up the lambda, but the member access syntax is extremely natural and it's obvious how you make changes.

Of course, you can make the parameter a reference to the object if you really want, but I prefer pointers here.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • Thanks for this very elegant suggestion! Just one more question: (How) can I make this to work also with pointer arguments? I got the error 'pointer' is not captured. Is there something like std::ref() for pointers? – Chris Jun 05 '13 at 18:53
  • @Chris: If you want to pass an argument which is a variable instead of a literal constant, list the variable between the `[]`. That way the lambda will store its value to later use during the member function call. – Ben Voigt Jun 05 '13 at 19:00
1

You could use a varadic template function:

template <typename... Args>
void process(void (Object::*func)(Args...),Args... args)
{
    Object object;

    // loops, called several times, ...
    (object.*func)(args...);
}

Here is a full example:

#include <iostream>

struct Object
{
    void memberFnNoArg()
    {
      std::cout << "Object::memberFnNoArg()\n";
    }

    void memberFnWithIntArg(int arg)
    {
      std::cout << "Object::memberFnWithIntArg(" << arg << ")\n";
    }

    void memberFnWithFloatArg(float arg)
    {
      std::cout << "Object::memberFnWithFloatArg(" << arg << ")\n";
    }
};

template <typename... Args>
void process(void (Object::*func)(Args...),Args... args)
{
    Object object;

    // loops, called several times, ...
    (object.*func)(args...);
}

int main()
{
  process(&Object::memberFnNoArg);
  process(&Object::memberFnWithIntArg,5);
  process(&Object::memberFnWithFloatArg,2.7F);
  return 0;
}
Vaughn Cato
  • 63,448
  • 5
  • 82
  • 132
0

One way I see around this would be to use a variable arguments (pretty much like printf, sprintf does it). (Or maybe with stdc libraries, passing a list of different types.)

The reason is, that the argument list is part of the function pointer type, so you'd essentially need a process function with variable arguments and then the memberFunction probably needs to be one of that type too.

Below is a plain (non member) sample of how to pick up variable arguments (member functions would essentially work the same). See stdarg.h.

typedef void (*var_function)(int typearg, ...);

void print_arg(int typearg, ...)
{
  va_list ap;
  int i;

  va_start(ap, typearg); 

  if (typearg==1) { // int 
     int i= va_arg(ap, int);
     printf("%d ", i);
  }
  else 
  if (typearg==2) { // float 
     float f= va_arg(ap, float);
     printf("%f ", f);
  }
  else 
  if (typearg==3) { // char *
     char *s= va_arg(ap, char *);
     printf("%s ", s);
  }

     ....

  va_end(ap);
}

// calling function with different types
int main()
{
   print_arg(1, 999);
   print_arg(2, 3.1415926);
   print_arg(3, "Hello");
   ....
   process(print_arg, 3, "via pointer);
Nicholaz
  • 1,419
  • 9
  • 11
  • 2
    Though it can be made to execute the desired effect, I'd rise "over my dead body" on review. – Balog Pal Jun 04 '13 at 11:05
  • Thanks for the proposal, but this solution does not seem to simplify my task... it just as in-transparent as the switch statement. – Chris Jun 04 '13 at 12:42
0

Sounds like packaged_task. Also check out Tomek's suggestion.

Though IRL I'd go ahead asking lots of questions on why you need it in the first place. Possibly your work could be better covered using std::future or other higher level facility,

Balog Pal
  • 16,195
  • 2
  • 23
  • 37
  • Thanks for the answer. Maybe I just don't understand but I think packaged_task and future are helping with multihreading issues which this question is not about. – Chris Jun 04 '13 at 12:59
  • future can be launched sync or async -- the point of it may be very close to what you want, create an "order" and at later point be able to fetch the result. What it does in between is just magic. – Balog Pal Jun 04 '13 at 13:37
0

Can't each function (memberFn**) be a member of argument classes ?

class BaseArg
{
  virtual void Fn() = 0;
};

class IntArg : public BaseArg
{
  void Fn();
};

class FloatArg : public BaseArg
{
  void Fn();
};


void function()
{
    int mode = getModeFromSomewhere();
    BaseArg* pArg;

    if ( mode ... ){
      pArg = new IntArg( 33 );
    }
    else {
      pArg = new FloatArg( 66.6 );
    }

    pArg->Fn();  // Call the right function without a switch
                 // and without knowing the arguments

}
iksess
  • 574
  • 4
  • 9
0

Same as other answers, but to show for member methods:

#include <iostream>
class Object
{
public:
    void memberFnNoArg()
    {
        std::cout << "Object::memberFnNoArg()\n";
    }

    void memberFnWithIntArg(int arg)
    {
        std::cout << "Object::memberFnWithIntArg(" << arg << ")\n";
    }

    void memberFnWithFloatArg(float arg)
    {
        std::cout << "Object::memberFnWithFloatArg(" << arg << ")\n";
    }
    bool memberFnWithBoolReturn(int)
    {
        return true;
    }
    template <typename... Args>
    void process(void (Object::*func)(Args...),Args... args);
    // overload process
    template <typename... Args>
    bool process(bool (Object::*func)(Args...),Args... args);
};
template <typename... Args>
void  process( void (Object::*func)(Args...),class Object* obj,Args... args)
{

    (obj->*func)(args...);
}
template <typename... Args>
bool  process( bool (Object::*func)(Args...),class Object* obj,Args... args)
{
    return ((obj->*func)(args...)) ;

}
int main()
{
    Object object;
    process(&Object::memberFnNoArg,&object);
    process(&Object::memberFnWithIntArg,&object,5);
    process(&Object::memberFnWithFloatArg,&object,2.7F);
    // overloaded process
    printf("%d\n",process(&Object::memberFnWithBoolReturn,&object,1));

    return 0;
}
Mohammad Kanan
  • 4,452
  • 10
  • 23
  • 47