2011-01-29 67 views
9

我试图创建一个函数,将存储和重复给出的特定时间或重复给定的参数给出的另一个函数。 但是,当你想传递一个函数作为参数时,你必须事先知道它的所有参数。 如果我想将该功能作为一个参数传递,并将参数作为另一个参数传递,我该怎么办?C++:我如何将一个函数(不知道它的参数)传递给另一个函数?

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 

在此先感谢

回答

3

就现代C++而言,dribeas的答案是正确的。

出于利益的考虑,C世界还有一个简单的高科技解决方案,只要它在C++中工作。不要允许任意参数,请将函数定义为void (*func)(void*),并设置“参数”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); 
} 

在实践中要“射后不理”的定时器,因此,如果您使用此方案,您可能还需要一个方式一旦所有的发射完成,定时器就设法释放用户数据指针。

显然这限制了类型的安全性。原则上应该不重要,因为提供函数指针和用户数据指针的人不应该很难确保它们匹配。在实践中,当然人们会想方设法编写错误,并且因为编译器没有告诉他们错误而错误地指责你;-)

15

,你可以做的最好的是用std::functionboost::function作为参数,连同std::bindboost::bind,好了,用函数绑定的参数:

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() >之内并且不带任何参数调用。

+0

会打电话那么只能使用void函数吗? – Ninja 2011-01-29 16:15:21

+0

@Ninja当然可以。作为调用者,如果您甚至不知道返回类型是什么,那么如何使用该函数?你可以使用`boost :: function `并将返回类型转换为你想要的。如果你想要现代的C++替代品,你可以使用`boost :: function `来玩某种类型的擦除。 – kizzx2 2011-01-29 16:54:36

1

如果真的没有关于函数指针的规则,那就使用void *。

+5

`void(*)(void)`是比`void *`更好的“泛型”函数指针类型,因为如果您将类型为“void(*)(void)”的值转换回正确的函数指针,保证原始函数指针值将被保留。如果您将函数指针值转换为void *`并返回,则无法保证。 – 2011-01-29 15:22:47

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

同样,你可以写你的代码,如果你的函数有不同的类型和/或参数的数字!

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; 
} 
相关问题