带有不同参数的C ++成员函数指针 - 或者这是不是很糟糕?

时间:2013-06-04 09:04:52

标签: c++ function-pointers argument-passing

即使我担心你会告诉我这个话题已被多次讨论,我敢问它,因为我无法生成解决方案。可能我只是在寻找错误的东西......

假设我有一个从某个外部函数接收“模式”的函数。根据模式,该函数将调用同一对象的不同成员函数。对于我来说,这对于我没有任何参数的成员函数很有效,但我没有找到如何将它扩展到带参数的成员。在现实世界的应用程序中,参数不是int / float而是更复杂的类,并且调用嵌套在不同的循环中,所以我需要多次放置switch语句,这些都是我认为很难看的。

问题A:是否可以使用基于现有设计的参数轻松添加对成员函数的支持?如果是,那怎么做呢?如果可能,没有外部库......

问题B:这是一种完全错误/错误的做法吗?我该怎么做得更好?

非常感谢您的帮助和解释。

克里斯

标题摘录:

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?
}

7 个答案:

答案 0 :(得分:2)

看一下std :: function和std :: bind,它们似乎完全符合你的需要。

编辑:

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

Object o;
f(o);
f2(o);
据我记得,

应该在盒子里工作。 这是你需要的吗?

答案 1 :(得分:2)

将算法包装在仿函数中是正确的方法,std::function是标准库提供的一个很好的仿函数。

但是,正如Tomek所建议的那样,使用boost::bind甚至std::bind是非常丑陋的IMO,并且在绑定多个参数时会迅速失控。

如果你有一个最近的编译器,你可以使用lambda代替,这使得Tomek的例子看起来像:

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

设置lambda有几个字符,但成员访问语法非常自然,你做出更改的方式很明显。

当然,如果你真的想要,可以让参数作为对象的引用,但我更喜欢这里的指针。

答案 2 :(得分:1)

您可以使用可变参数模板函数:

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

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

以下是一个完整的例子:

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

答案 3 :(得分:0)

我看到的一种方法是使用变量参数(非常类似于printf,sprintf就是这样)。 (或者可能使用stdc库,传递不同类型的列表。)

原因是,参数列表是函数指针类型的一部分,所以你基本上需要一个带有变量参数的进程函数,然后memberFunction可能也需要是那个类型之一。

以下是如何获取变量参数的简单(非成员)示例(成员函数基本上相同)。请参阅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);

答案 4 :(得分:0)

听起来像packaged_task。另请查看Tomek的建议。

虽然IRL我会先讨论一下你为什么需要它的问题。可能使用std::future或其他更高级别的工具

可以更好地覆盖您的工作

答案 5 :(得分:0)

每个函数(memberFn **)都不能成为参数类的成员吗?

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

}

答案 6 :(得分:0)

与其他答案相同,但要显示成员方法:

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