在编译时生成函数指针

时间:2018-11-11 17:10:06

标签: c++

我有一个接受回调类型的库

double *(double)

现在,我想传递几个在另一个参数上参数化的回调。更准确地说,我有一个带有签名的功能

double f(double, int)

现在我想写些类似的东西

for(int parameter: {...some values(known at compile time)...})
{
    //register callback x mapsto f(x,parameter)
}

我无法将回调包装在lambda中,因为它将被捕获,因此它不能转换为函数指针。同样,据我了解,绑定对象无法转换为函数指针(尽管我不确定为什么),因此无法绑定参数。

有人知道该怎么做吗?

编辑:我想要一个使用编译器模板引擎为我生成这些功能的解决方案。 另外,如果这对您有帮助,我会使用c ++ 17,如果需要的话,我也愿意使用诸如boost hana之类的库。

1 个答案:

答案 0 :(得分:0)

您需要一些存储空间来保持绑定值,除非您在编译时使用宏创建函数,否则原始函数指针无法做到这一点。 (或者在不久的将来使用反射+元类)在运行时无法执行。

您可以使用std::bind,然后例如存储std::function实例。

#include <vector>
#include <functional>

double function(double, int);

void register_all() {
    using namespace std::placeholders;

    // values to bind
    std::vector<int> v = { 1, 4, 6, 8 };
    // our callback registry
    std::vector<std::function<double(double)>> funcs;

    // registration
    for (auto i : v) {
        funcs.emplace_back(std::bind(function, _1, i));
    }

    //call
    for (auto &f : funcs) {
        f(1.0);
    }
}

如果您不能将回调类型更改为std::function或类似的方法,则唯一的方法是注册一个回调以进行进一步的调用。即

double my_callback(double d) {
  for (int i : whatever) {
    f(d, i);
  }
}

// ... somewhere else
register_callback(my_callback);

旁注: 一个好的C回调API将允许传递void*,可以通过它获得额外的特定于回调的信息,这些信息可以用于携带额外的数据。但是,简单函数指针对于其他任何东西都太受限制了。

编辑:

如果在编译时知道所有(潜在)值,则可以使用模板进行包装:

#include <vector>

double f(double d, int i);

template<int i>
double f_wrapper(double d) {
    return f(d, i);
}

int main() {    
    std::vector<double(*)(double)> funcs;

    funcs.push_back(&f_wrapper<0>);
    funcs.push_back(&f_wrapper<1>);
    funcs.push_back(&f_wrapper<2>);

    //call
    double r = .0;
    for (auto &f : funcs) {
        r += f(1.0);
    }
}

具有更多可变参数模板魔术:

#include <vector>

double f(double d, int i);

template<int i>
double f_wrapper(double d) {
    return f(d, i);
}

template<int... id>
void register_cbs(std::vector<double(*)(double)>& funcs) {
    (funcs.push_back(&f_wrapper<id>), ...);
}

int main() {
    // register
    std::vector<double(*)(double)> funcs;
    register_cbs<1,2,5,10>(funcs);

    //call
    double r = .0;
    for (auto &f : funcs) {
        r += f(1.0);
    }
}

但是再次必须在编译时知道这些值。