C ++:如何将函数(不知道其参数)传递给另一个函数?

时间:2011-01-29 15:14:37

标签: c++ pointers timer function-pointers parameter-passing

我正在尝试创建一个函数来存储和重复另一个函数,该函数作为特定时间或给定重复的参数给出。 但是当你想要将函数作为参数传递时,你必须事先知道它的所有参数。 如果我想将函数作为一个参数传递,将参数作为另一个参数传递,我该怎么办?

void AddTimer(float time, int repeats, void (*func), params); // I know params has no type and that (*func) is missing parameters but it is just to show you what I mean

提前致谢

5 个答案:

答案 0 :(得分:15)

您可以做的最好的事情是使用std::functionboost::function作为参数,同时使用std::bindboost::bind将参数与函数绑定:< / p>

void foo() { std::cout << "foo" << std::endl; }
void bar( int x ) { std::cout << "bar(" << x << ")" << std::endl; }
struct test {
   void foo() { std::cout << "test::foo" << std::endl; }
};
void call( int times, boost::function< void() > f )
{
   for ( int i = 0; i < times; ++i )
      f();
}
int main() {
   call( 1, &foo );                   // no need to bind any argument
   call( 2, boost::bind( &bar, 5 ) );
   test t;
   call( 1, boost::bind( &test::foo, &t ) ); // note the &t
}

请注意,传递完全通用的函数指针存在一些固有的错误:你如何使用它?调用函数的主体如何能够传递未定义数量的未知类型的参数?这就是bind模板解析的内容,它们创建了一个类函数,它存储函数指针(具体函数指针)以及调用时要使用的参数副本(注意示例中的&t以便指针而不是对象被复制)。 bind的结果是一个可以通过已知接口调用的仿函数,在这种情况下,它可以绑定在function< void() >内并且不带参数调用。

答案 1 :(得分:3)

就现代C ++而言,

dribeas的回答是正确的。

为了感兴趣,还有一个来自C世界的简单的低技术解决方案,就其而言,可以在C ++中运行。而不是允许任意参数,将函数定义为void (*func)(void*),并使“params”void*。然后调用者的工作是定义一些包含参数的结构,并管理其生命周期。通常调用者也会为真正需要调用的函数编写一个简单的包装器:

void myfunc(int, float); // defined elsewhere

typedef struct {
    int foo;
    float bar;
} myfunc_params;

void myfunc_wrapper(void *p) {
    myfunc_params *params = (myfunc_params *)p;
    myfunc(params->foo, params->bar);
}

int main() {
    myfunc_params x = {1, 2};
    AddTimer(23, 5, myfunc_wrapper, &x);
    sleep(23*5 + 1);
}

实际上你想要“发射并忘记”计时器,所以如果你使用这个方案,你可能还需要一种方法让计时器管理在所有的点火完成后释放用户数据指针。

显然,这种类型的安全性有限。原则上应该没关系,因为提供函数指针和用户数据指针的人不应该有很大的困难来确保它们匹配。在实践中,当然人们会找到编写错误的方法,以及责备你的方法,因为他们的编译器没有告诉他们错误; - )

答案 2 :(得分:2)

这只是一个例子,你可以将函数指针传递给另一个函数,然后调用它:

void AddTimer(float time, int repeats, void (*func)(int), int params)
{
    //call the func
    func(params);
}

void myfunction(int param)
{
   //...
}

AddTimer(1000.0, 10, myfunction, 10);

同样,如果您的函数采用不同类型或/和数量的参数,您可以编写代码!

答案 3 :(得分:1)

如果根本没有关于函数指针的规则,只需使用void *。

答案 4 :(得分:0)

在C ++ 11中,事情变得非常简单 - 您可以获得实现计时器所需的一切。

传递绑定函数调用的最简洁方法是传递使用lambda语法生成的仿函数,例如:[]{ std::cout << "Hello, world!" << std::endl; }。这样生成的对象只有编译器知道的类型,但类型可以转换为std::function<void()>

#include <functional>
#include <list>
#include <chrono>
#include <thread>
#include <iostream>

template <typename Clock = std::chrono::high_resolution_clock>
class Timers {
public:
   using clock = Clock;
   using duration = typename clock::duration;
   using time_point = typename clock::time_point;
private:
   struct Timer {
      duration const period;
      std::function<void()> const call;
      int repeats;
      time_point next;
      Timer(duration $period, int $repeats, std::function<void()> && $call) :
         period($period), call(std::move($call)), repeats($repeats) {}
   };
   std::list<Timer> m_timers;
public:
   Timers() {}
   Timers(const Timers &) = delete;
   Timers & operator=(const Timers &) = delete;
   template <typename C> void add(std::chrono::milliseconds period,
                                  int repeats, C && callable)
   {
      if (repeats) m_timers.push_back(Timer(period, repeats, callable));
   }
   enum class Missed { Skip, Emit };
   void run(Missed missed = Missed::Emit) {
      for (auto & timer : m_timers) timer.next = clock::now() + timer.period;
      while (! m_timers.empty()) {
         auto next = time_point::max();
         auto ti = std::begin(m_timers);
         while (ti != std::end(m_timers)) {
            while (ti->next <= clock::now()) {
               ti->call();
               if (--ti->repeats <= 0) {
                  ti = m_timers.erase(ti);
                  continue;
               }
               do {
                  ti->next += ti->period;
               } while (missed == Missed::Skip && ti->next <= clock::now());
            }
            next = std::min(next, ti->next);
            ++ ti;
         }
         if (! m_timers.empty()) std::this_thread::sleep_until(next);
      }
   }
};

int main(void)
{
   Timers<> timers;
   using ms = std::chrono::milliseconds;
   timers.add(ms(1000), 2, []{ std::cout << "Hello, world!" << std::endl; });
   timers.add(ms(100), 20, []{ std::cout << "*" << std::endl; });
   timers.run();
   std::cout << std::endl;
   return 0;
}