我在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);
我需要自己提供类型为integrand
的void函数,而adapt_integrate将计算n维积分。如果calcTripleIntegral
是独立函数,func
(下面)中的代码将作为独立函数运行。
我想传递一个(非静态!)类成员函数作为被积函数,因为这可以很容易地重载等...
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进行编译,所有这些都导致了同样的错误。要么没有做过这方面的工作,要么我做错了 / EDIT
非常感谢非常!如果我不够清楚,我很乐意详细说明。
PS:我可以通过使用指向myIntegrator.cpp中某处定义的函数的函数指针来解决这个问题吗?
最终更新:好吧,我错误地认为TR1为此提供了单线/双线解决方案。游民。我正在“转换”我的类到命名空间和copypasting函数声明。我只需要一个基类和一个子类来重新实现接口。 C函数指针+ C ++类对我来说是坏消息。 非常感谢所有答案,你已经向我展示了C ++的一些黑暗角落;)
答案 0 :(得分:3)
你有三个问题......首先你想要一个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
内容都是不必要的,您可以直接通过蹦床函数 - 只需将this
传递给上下文参数:
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, ...);
答案 1 :(得分:3)
如果您只是尝试将成员函数传递给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;
};
答案 2 :(得分:2)
我想传递一个(非静态!)类成员函数作为被积函数...
你做不到。如果您搜索SO以使用成员函数作为回调,您将必然会找到有用的信息,包括您尝试做的事情,无论如何都是直接的方法。
编辑:顺便说一下,代码中的一个问题(当然,因为你要做的事情根本不可能),你已经将函数指针类型传递给函数&lt;&gt;什么时候它是一个签名。功能模板实现如下:
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版本将遇到您目前面临的相同问题。
答案 3 :(得分:2)
由于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;
};
答案 4 :(得分:1)
假设C-API允许传递类型不可知(在某种意义上,C-API函数不必知道它的类型,但依赖于回调函数来知道它需要什么)context parameter(这通常是回调函数的情况;在这种情况下,我怀疑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更加灵活,并以这种方式传递任何你喜欢的东西。
答案 5 :(得分:0)
该错误消息使您听起来像是缺少其中一种类型的包含。至少尝试仔细检查integrand
和tr1
包括哪些内容?
答案 6 :(得分:0)
bind
的工作方式与我想的有点不同。您需要为每个参数提供值或占位符。
对于您的示例,这归结为(使用占位符)
std::tr1::function<integrand> func(std::tr1::bind(&myIntegrator::integrandF2, *this, _1, _2, _3, _4, _5));
由于你绑定了一个成员函数,你得到了一个额外的(隐式)参数,即你调用成员函数的对象,所以你有六个。
首先绑定this
对象,对于其他参数,只需传递占位符。
在旁注中,您的成员函数返回double,而函数声明返回void。
(为了记录,我仍然使用较旧的编译器,支持很少的tr1,所以我只有bind
和function
使用boost的经验,也许tr1的情况有所改变。 。)