【问题标题】:Is a pointer to a function which have unknown number of parameters possible?指向具有未知数量参数的函数的指针是否可能?
【发布时间】:2011-02-12 02:38:53
【问题描述】:

我正在编写一个简单的类来衡量函数在时间方面的性能。用户应该能够发送一个指向他的函数的指针,函数的参数,调用函数的时间,我将调用函数,返回经过的时间。这里我的问题是我不知道用户函数需要多少参数!我想使用可变参数函数来获取未知数量的参数,但是我仍然有声明用户作为参数传递的函数指针的问题(因为它没有常量变量号)并且不知道我收到的变量类型使用一个可变参数函数。

我想这应该不难:) 我想要做的就是使用函数指针调用我未定义的函数。

有什么办法可以解决这些问题吗?

【问题讨论】:

  • 删除了 C 标签,因为 OP 指定他正在编写一个类。

标签: c++ variadic-functions


【解决方案1】:

将函数指针指向具有未知参数的函数实际上没有任何意义。如果您不知道有多少参数(更不用说它们的类型),您将如何在运行时填写参数?

您能做的最好的事情是要求用户的函数都具有相同的原型,即以va_list 作为参数,并要求您的库为您的库提供相同的va_list(另请参阅@987654321 @)。

例如:

// Function-pointer type
typedef void (*func_t)(int, va_list);

// Your timer library function
void timer(func_t *p_func, ...)
{   
    va_list arg;
    va_start(arg, fmt);
    p_func(0, arg);
    va_end(arg);
}


// User's function
void user_function(int first_arg, va_list args)
{
   ...
};

// Invoke the timer library function
timer(&user_function, arg1, arg2, arg3);

【讨论】:

  • 确实有道理。您确切知道要使用多少个参数调用函数,只是对于您要测量的不同函数,它是不同的数字(和不同的类型)。
  • @sbi:是的,但没有任何有意义的方式可以让您以编程方式做到这一点。您唯一的选择是透明地转发va_list
  • @Oli:有办法。看我的回答。
  • 啊,这似乎是我需要的 :) 非常感谢大家 ^^ 顺便说一句,您发布的链接看起来很熟悉...我相信我今天在寻找答案时看过它:)
  • @sbi:是的,我想你的方法有效(尽管我从未尝试在 C++ 中使用 lambdas),并且它完全避免了“传递可变数量的参数”(我仍然认为不是一个有意义的概念!)。我想唯一的问题是 OP 的编译器是否支持它!
【解决方案2】:

我认为 lambda 函数 可以用来做到这一点:

template< typename Func >
unsigned int measure(Func f)
{
  // take time
  f();
  // take time
  return result;
}

void test_func_1(int i)            { std::cout << i; }
void test_func_2(std::ostream& os) { os << 42; }

int main()
{
  auto lambda_func_1 = [](){ test_func_1(42); };
  const unsigned int time_1 = measure( lambda_func_1 );
  std::cout << "calling test_func_1(42) took " << time_1 << " <unit>\n";

  auto lambda_func_2 = [](){ test_func_2(std::cerr); };
  const unsigned int time_2 = measure( lambda_func_2 );
  std::cout << "calling test_func_2(std::cout) took " << time_2 << " <unit>\n";

  return 0;
}

当然,lambda 函数只有在下一个标准发布(希望今年)之后才会成为 C++ 的一部分,但是相当多的编译器(包括 GCC 和 VC)已经实现了它们,所以你有机会去做这边走。


还可以使用可变参数模板参数和完美转发来制作函数模板,将要测量的函数的参数传递给测量函数,然后传递它们。但是我没有玩过这个,所以我不能写这个。

【讨论】:

  • 我也会试试这个。测量( [](){test_func(42);] );函数调用似乎有点混乱,但可能是因为我还不知道 lambda 函数 :) 我现在正在研究它们 ^^ 非常感谢
  • @Gökay: [](){...} 定义了一个不带参数的匿名类型函数对象,在调用时执行...。结尾的] 是我后来修正的错字。为了减少嵌套括号、方括号和大括号的数量,我更改了答案,将 lambda 函数的创建与对计时器函数的调用分开。 :)
【解决方案3】:

如果您想要一个不需要可变参数模板或 lambda 的解决方案(您将不得不等待即将发布的标准版本),您可以使用 boost::bind 将函数变为不带参数的函数将所有参数预绑定到函数中:

#include <boost/bind.hpp>
#include <iostream>

template <typename Func>
int time_call(Func f) {
  int start_time = some_get_current_time(); //record the start time
  f(); // call the function with no parameters
  return some_get_current_time() - start_time; //return the time difference.
};

void my_algorithm(int x, float w, std::string s) {
  std::cout << x << w << s << std::endl;
};

int main() {
  int time_taken = time_call(boost::bind(&my_algorithm, 42, 0.3, "Hello World!"));
  std::cout << "The function took " << time_taken << " time-units to execute!" << std::endl;
  return 0;
};

以上内容适用于所有编译器,不需要 C++0x 的任何功能。另请注意,在对boost::bind 的调用中,您可以放置​​任何变量(它们不必是文字常量)。而且,boost::bind 也可以使用指向成员函数的指针。

编辑:如果您想知道 boost::bind 如何在没有可变参数模板的情况下完成此任务,嗯,这很简单,他们只是为所有可能的参数数量制作了一个函数模板重载(我认为默认限制是 10 个参数,但它可以延长)。

【讨论】:

  • 啊,是的,我倾向于忘记boost::bind! Lambda 函数更易于使用和理解,但如果您还不能使用它们,那么bind 来救援! +1来自我。
【解决方案4】:

不幸的是,当前的 C++ 要求您编写一堆具有不同长度的模板,每个可能的参数计数一个。原则上,C++0x 将允许您像这样使用可变参数模板:

template<typename Rv, typename Wrapper, typename... Args>
struct impl_wrapper {
    std::function<Rv (Args...)> func;

    Rv operator()(Args... args) const {
        Wrapper w;
        return func(args...);
    }

    impl_wrapper(const  std::function<Rv (Args...)> f)
        : func(f)
    {
    }
};

template<typename Wrapper>
struct wrap_func_ {
    template<typename Rv, typename... Args>
        impl_wrapper<Rv, Args...> operator()(const std::function<Rv (Args...)> &f)
        {
            return impl_wrapper<Rv, Wrapper, Args...>(f);
        }
};

template<typename Wrapper>
static wrap_func_<Wrapper> wrap_func;



struct test_wrapper {
    test_wrapper() {
        std::cout << "Begin call!\n";
    }

    ~test_wrapper() {
        std::cout << "End call!\n";
    }
};

int test_call(int x, char *y) {
    std::cout << y << x << std::endl;
    return x + 1;
}

int main() {
    std::function<int (int, char *)> f = test_call;
    f = wrap_func<test_wrapper>(f);

    std::cout << "Returned: " << f(42, "Prior to increment: ") << std::endl;
    return 0;
}

但是,这需要支持尚未在 G++ 中实现的功能,而且很可能在任何其他现存的 C++ 编译器中也没有实现:

test.cpp:21: sorry, unimplemented: cannot expand ‘Args ...’ into a fixed-length argument list

因此,您必须改为对每个可能的参数计数使用模板重载,直至达到某个合理的最大值。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-06-17
    • 1970-01-01
    • 1970-01-01
    • 2011-11-12
    • 2017-03-26
    • 1970-01-01
    • 1970-01-01
    • 2014-04-20
    相关资源
    最近更新 更多