【问题标题】:C++: How do I pass a function(without knowing its parameters) to another function?C++:如何将一个函数(不知道其参数)传递给另一个函数?
【发布时间】:2011-06-17 18:43:54
【问题描述】:

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

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

提前致谢

【问题讨论】:

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


    【解决方案1】:

    您能做的最好的事情是使用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 模板解析的内容,它们创建了一个类函子,将函数指针(具体函数指针)连同要在调用时使用的参数副本一起存储(注意示例中的&amp;t,以便指针而不是对象被复制)。 bind 的结果是一个可以通过已知接口调用的函子,在这种情况下,它可以绑定在 function&lt; void() &gt; 内并在不带参数的情况下调用。

    【讨论】:

    • 会调用然后只使用 void 函数吗?
    • @Ninja 当然可以。作为调用者,如果你连返回类型是什么都不知道,你怎么能使用函数呢?您可以使用boost::function&lt;void *&gt; 并将返回类型转换为您想要的类型。如果你想要现代 C++ 替代品,你可以使用boost::function&lt;boost::any&gt; 来玩一些类型擦除。
    【解决方案2】:

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

    【讨论】:

    • void(*)(void) 是比void* 更好的“通用”函数指针类型,因为如果您将void(*)(void) 类型的值转换回正确的函数指针类型,则可以保证原始函数指针值将被保留。如果您将函数指针值转换为 void* 并返回,则无法保证这一点。
    • @DanielTrebbien: 5.2.10 [expr.reinterpret.cast] / 6.
    【解决方案3】:

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

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

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

    【讨论】:

    • +1,再次阅读问题后,我不太确定参数的数量和类型是未知的,如果这些是已知的,这比我的更简单更好的解决方案。
    • 这不是我实际问的。看起来大卫的答案更有用。
    【解决方案4】:

    就现代 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);
    }
    

    实际上,您希望“触发并忘记”计时器,因此如果您使用此方案,您可能还需要一种方法让计时器在所有触发完成后释放 userdata 指针。

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

    【讨论】:

      【解决方案5】:

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

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-09-25
        • 2020-10-03
        相关资源
        最近更新 更多