[英]std::tr1::function and std::tr1::bind
我使用C ++类非常复杂的C函数问题(重写C函数不是一个选项)。 C功能:
typedef void (*integrand) (unsigned ndim, const double* x, void* fdata,
unsigned fdim, double* fval);
// This one:
int adapt_integrate(unsigned fdim, integrand f, void* fdata,
unsigned dim, const double* xmin, const double* xmax,
unsigned maxEval, double reqAbsError, double reqRelError,
double* val, double* err);
我需要提供类型的void函数integrand
自己,adapt_integrate将计算n维积分。 如果func
是独立函数,则calcTripleIntegral
(下面)中的代码作为独立函数工作。 我想传递一个(非静态!)类成员函数作为被积函数,因为这可以很容易地重载等...
class myIntegrator
{
public:
double calcTripleIntegral( double x, double Q2, std::tr1::function<integrand> &func ) const
{
//...declare val, err, xMin, xMax and input(x,Q2) ...//
adapt_integrate( 1, func, input,
3, xMin, xMax,
0, 0, 1e-4,
&val, &err);
return val;
}
double integrandF2( unsigned ndim, const double *x, void *, // no matter what's inside
unsigned fdim, double *fval) const; // this qualifies as an integrand if it were not a class member
double getValue( double x, double Q2 ) const
{
std::tr1::function<integrand> func(std::tr1::bind(&myIntegrator::integrandF2, *this);
return calcTripleIntegral(x,Q2,func);
}
}
在GCC 4.4.5(预发布)上,这给了我:
错误:变量'std :: tr1 :: function func'具有初始化程序但不完整的类型
编辑 :我的代码中的错误是什么? 我现在尝试使用GCC 4.4,4.5和4.6进行编译,所有这些都导致了同样的错误。 对此没有做过任何工作,或者我做错了什么/编辑
非常感谢! 如果我不够清楚,我很乐意详细说明。
PS:我可以通过使用指向myIntegrator.cpp中某处定义的函数的函数指针来解决这个问题吗?
最后更新 :好吧,我错误地认为TR1为此提供了一个/两线解决方案。 游民。 我正在“转换”我的类到命名空间和copypasting函数声明。 我只需要一个基类和一个子类来重新实现接口。 C函数指针+ C ++类对我来说是坏消息。 非常感谢所有答案,你已经向我展示了C ++的一些黑暗角落;)
你有三个问题...首先你想要一个std::tr1::function<R (Args..)>
,但你的归结为std::tr1::function<R (*)(Args...)>
- 所以你需要两个typedef:
typedef void (integrand) (unsigned ndim, const double *x, void *,
unsigned fdim, double *fval);
typedef integrand* integrand_ptr;
...所以第一个允许你编译function<integrand>
。 adapt_integrate
必须相应地修改:
int adapt_integrate(unsigned fdim, integrand_ptr f, ...);
接下来你的bind
语法是关闭的,它应该是:
std::tr1::bind(&myIntegrator::integrandF2, *this, _1, _2, _3, _4, _5);
剩下的问题是tr1::function<T>
不能转换为函数指针,因此您必须通过包装函数,使用void* fdata
参数传递上下文。 例如:
extern "C" void integrand_helper (unsigned ndim, const double *x, void* data,
unsigned fdim, double *fval)
{
typedef std::tr1::function<integrand> Functor;
Functor& f = *static_cast<Functor*>(data);
f(ndim, x, data, fdim, fval);
}
// ...
adapt_integrate(1, &integrand_helper, &func, ...);
这当然是假设void*
参数传递给函数,如果不是,它会变得丑陋。
另一方面,如果void* fdata
允许传递上下文,那么所有tr1::function
东西都是不必要的,你可以直接通过trampoline函数 - 只需将this
作为context参数传递:
extern "C" void integrand_helper (unsigned ndim, const double *x, void* data,
unsigned fdim, double *fval)
{
static_cast<myIntegrator*>(data)->integrandF2(ndim, ...);
}
// ...
adapt_integrate(1, &integrand_helper, this, ...);
如果您只是尝试将成员函数传递给c样式的回调,则可以使用std::t1::bind
或std::tr1::function
。
class myIntegrator
{
public:
// getValue is no longer const. but integrandF2 wasn't changed
double getValue( double x, double Q2 )
{
m_x = x;
m_Q2 = Q2;
// these could be members if they need to change
const double xMin[3] = {0.0};
const double xMax[3] = {1.0,1.0,1.0};
const unsigned maxEval = 0;
double reqAbsError = 0.0;
double reqRelError = 1e-4;
double val;
adapt_integrate( 1, &myIntegrator::fancy_integrand,
reinterpret_cast<void*>(this),
3, xMin, xMax,
maxEval, reqAbsError, reqRelError,
&val, &m_err);
return val;
}
double get_error()
{ return m_error; }
private:
// use m_x and m_Q2 internally
// I removed the unused void* parameter
double integrandF2( unsigned ndim, const double *x,
unsigned fdim, double *fval) const;
static double fancy_integrand( unsigned ndim, const double* x, void* this_ptr,
unsigned fdim, double* fval)
{
myIntegrator& self = reinterpret_cast<myIntegrator*>(this_ptr);
self.integrateF2(ndim,x,fdim,fval);
}
double m_x
double m_Q2;
double m_err;
};
你不能。 如果你搜索SO使用成员函数作为回调,你将必然会找到有用的信息,包括你要做的事情,无论如何直接的方法是不可能的。
编辑:顺便说一句,你的代码中的一个问题(当然,因为你要做的事情根本不可能),你已经将函数指针类型传递给函数<>,当它期望的是签名时。 函数模板实现如下:
template < typename Signature >
struct function;
// for each possible number of arguments:
template < typename R, typename Arg1, typename Arg2 >
struct function<R(Arg1,Arg2)>
{
... body ...
};
正如您所看到的,将函数指针传递给此类事物根本不会被编译器理解。 它将尝试实例化前向声明而无处可去。 这当然是你得到的编译器错误的意思,但它没有解决你的根本问题,那就是你正在做的事永远不会工作。
在一个完全C ++ 0x编译器中,这可以用不同的方式完成,但boost :: function和MSVC必须是这样的。 此外,C ++ 0x版本将遇到您当前面临的相同问题。
由于std::tr1::bind
和c风格的函数指针不相处,所以请尝试这样做。 它会工作,除了myIntegrator::getValue
不再是线程安全的。 如果从界面中删除了calcTripleIntegral
,这将更简单,并且不需要使用std::tr1::bind
或std::tr1::function
。
class myIntegrator
{
public:
double getValue( double x, double Q2 ) const
{
return calcTripleIntegral(x,Q2,std::tr1::bind(&Integrator::integrandF2,this));
}
double calcTripleIntegral( double x, double Q2, const std::tr1::function<integrand>& func ) const
{
assert( s_integrator == NULL );
s_integrator = this;
m_integrand = func;
//...declare val, err, xMin, xMax and input(x,Q2) ...//
adapt_integrate( 1, &myIntegrator::fancy_integrand, input,
3, xMin, xMax,
0, 0, 1e-4,
&val, &err);
assert( s_integrator == this);
s_integrator = NULL;
return val;
}
private:
double integrandF2( unsigned ndim, const double *x, void *,
unsigned fdim, double *fval) const;
static double fancy_integrand( unsigned ndim, const double* x, void* input,
unsigned fdim, double* fval)
{
s_integrator->integrateF2(ndim,x,input,fdim,fval);
}
std::tr1::function<integrand> m_integrand;
static const myIntegrator* s_integrator;
};
假设C-API允许传递类型不可知(在某种意义上,C-API函数不必知道它的类型但依赖于回调函数来知道它需要什么)上下文参数(这通常是回调函数的情况;在这种情况下,我怀疑fdata参数是沿着这些行的东西),将函数对象作为此上下文参数的一部分传递。
它应该看起来像这样:
#include <iostream>
#include <tr1/functional>
typedef void (*callback_function_t)(void *input, int arg);
struct data_type {
int x;
};
struct context_type {
std::tr1::function<void(data_type const &, int)> func;
data_type data;
};
void callback(data_type const&data, int x) {
std::cout << data.x << ", " << x << std::endl;
}
void callback_relay(void *context, int x) {
context_type const *ctxt = reinterpret_cast<context_type const*>(context);
ctxt->func(ctxt->data, x);
}
void call_callback(callback_function_t func, void *context, int x) {
func(context, x);
}
int main() {
context_type ctxt = { callback, { 1 } };
call_callback(callback_relay, &ctxt, 2);
}
其中call_callback是C-API函数。 这样,您可以将任何支持函数调用语法的内容分配给context_type :: func,包括std :: tr1 :: bind表达式。 此外,即使(我觉得在道德上有义务提到这一点)严格来说,严格来说,在标准中没有定义C和C ++函数的调用约定是相同的,实际上你可以使context_type成为类模板而callback_relay是一个函数模板使context_type :: data更加灵活,并以这种方式传递任何你喜欢的东西。
该错误消息使您听起来像是缺少其中一种类型的包含。 至少尝试tr1
检查你的integrand
和tr1
包含?
bind
工作有点不同你认为我认为。 您需要为每个参数提供值或占位符。
对于您的示例,这归结为(使用占位符)
std::tr1::function<integrand> func(std::tr1::bind(&myIntegrator::integrandF2, *this, _1, _2, _3, _4, _5));
由于您绑定了一个成员函数,因此您有一个额外的(隐式)参数,即您调用成员函数的对象,因此您有六个。
对于第一个绑定this
对象,对于其他参数,您只需传递占位符。
另外,您的成员函数返回double,而函数声明返回void。
(为了记录,我仍然使用一个较小的tr1支持的旧编译器,所以我只有使用boost的bind
和function
经验,也许tr1的东西改变了一点......)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.