将泛型函数及其参数传递给元函数

时间:2018-06-14 15:10:35

标签: c++

首先,我觉得必须已经在某个地方提出这个要求,但我所有的搜索都没有结果。如果这确实是某个地方的重复,我会事先道歉。

我正在尝试从OpenCV中对一堆函数进行大规模基准测试,并且这样做我想编写一个小的元函数,它使函数运行,其参数(根据传递的函数而变化)并且基本上设置计时并在循环中运行函数。

由于我计划稍后将lambdas传递给元函数(以基准函数组合),我想到了使用std::function

这是我在阅读the parameter pack description后提出的代码:

template<typename ...Ts> 
void run_test(std::string test_name, int num_repeats, std::function<void(Ts...)> f, Ts... fargs)
{
    std::cout << std::endl << "Starting " << test_name << std::endl;
    start_timing(test_name);
    for(int i=0; i<num_repeats; i++)
    {
        f(fargs...);
    }
    end_timing(num_repeats);
}

如您所见,功能降至最低。 start_timingend_timing是超出此问题范围的简单辅助函数。

在我的主要内容中,我打电话:

// im_in defined and loaded elsewhere
cv::Mat out(im_in.size(), CV_8U);
run_test(
    "erode 3x3",
    100,
    cv::erode,
    im_in, out, cv::Mat::ones(3,3,CV_8U)
);

现在,如果我尝试编译它,我得到:

error: no matching function for call to 'run_test(const char [10], const int&, void (&)(cv::InputArray, cv::OutputArray, cv::InputArray, cv::Point, int, int, const Scalar&), cv::Mat&, cv::Mat&, cv::MatExpr)'
  );
  ^
note: candidate: template<class ... Ts> void run_test(std::__cxx11::string, int, std::function<void(Ts ...)>, Ts ...)
 void run_test(std::string test_name, int num_repeats, std::function<void(Ts...)> f, Ts... fargs)
      ^~~~~~~~
note:   template argument deduction/substitution failed:
note:   mismatched types 'std::function<void(Ts ...)>' and 'void (*)(cv::InputArray, cv::OutputArray, cv::InputArray, cv::Point, int, int, const Scalar&) {aka void (*)(const cv::_InputArray&, const cv::_OutputArray&, const cv::_InputArray&, cv::Point_<int>, int, int, const cv::Scalar_<double>&)}'
  );

那么......我做错了什么?为什么它的类型不匹配而不是像我预期的那样从参数列表中推导Ts...中的类型?

更新

在写完上面的上一个问题之后,我意识到可能在为Ts...推导std::function时遇到问题,因为参数在实际参数列表之前扩展到Ts...。所以,我改变了我的代码如下(实际上,我在最后移动了f参数):

void run_test(std::string test_name, int num_repeats, Ts... fargs, std::function<void(Ts...)> f)
{ ... }

当然,我也相应调整了函数调用:

cv::Mat out(im_in.size(), CV_8U);
run_test(
    "erode 3x3",
    NUM_REPEATS,
    im_in, out, cv::Mat::ones(3,3,CV_8U),
    cv::erode, // <<<<<<<<<<<<<<<<<<<<<<<
);

现在,如果我编译,我会得到一个不同的错误:

error: expected primary-expression before ')' token );

错误的变化让我觉得参数的顺序确实很重要。但是,这是正确的吗?如果是这样,我做错了什么?

UPDATE2:

编写第一个更新时,让我感到震惊的是,我可能认为f可以接受一个函数并将其转换为std::function可能是错误的。经过快速研究后,似乎就是这样。

我尝试用std::function<void(Ts...)> fauto f替换auto& f(并在启用C ++ 14的情况下进行编译),但expected primary-expression错误仍然存​​在。

对于我可以研究的所有内容,我无法找到一种方法,只允许我依靠编译器传递函数来找出类型。 我正在考虑调整C ++ 17 std::apply函数实现显示hereinvoke调用周围添加我的时序循环,但我不明白那段代码,所以出错的可能性很高。

2 个答案:

答案 0 :(得分:4)

你可能会摆脱std::function(增加BTW的开销),并使用f的泛型:

template<typename F, typename ...Ts> 
void run_test(std::string test_name, int num_repeats, F f, const Ts&... fargs)
{
    std::cout << std::endl << "Starting " << test_name << std::endl;
    start_timing(test_name);
    for(int i=0; i<num_repeats; i++)
    {
        f(fargs...);
    }
    end_timing(num_repeats);
}
  

我意识到在std :: function

中推断Ts可能会有问题

由于cv::erode不是std::functionTs...无法从中推断,但会来自额外的参数。

您的问题是cv::erode有额外(默认)参数。 因此,您无法使用参数推导出std::function<Ts...>来创建Ts...

要绕过该问题,您可以改用lambda:

run_test(
    "erode 3x3",
    100,
    [&](){ cv::erode(im_in, out, cv::Mat::ones(3,3,CV_8U)); }
);

答案 1 :(得分:3)

错误expected primary-expression before ')'是因为您在最后一个参数

之后留下了,

参数包必须始终是函数的最后一个参数

你应该试试这个

template<typename Callable, typename ...Ts> 
void run_test(std::string test_name, int num_repeats, Callable func, Ts&& ... fargs)
{
    std::cout << std::endl << "Starting " << test_name << std::endl;
    start_timing(test_name);
    for(int i=0; i<num_repeats; i++)
    {
        func(std::forward<Ts>(fargs)...);
    }
    end_timing(num_repeats);
}