4

我在 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 维积分。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 进行编译,都导致相同的错误。要么没有做任何工作,要么我做错了什么/编辑

非常感谢!如果我不够清楚,我会很乐意详细说明。

PS:我可以通过使用指向 myIntegrator.cpp 中某处定义的函数的函数指针来解决这个问题吗?

最终更新:好的,我错误地认为 TR1 为此提供了一个/两行解决方案。真可惜。我正在将我的类“转换”为命名空间并复制粘贴函数声明。我只需要一个基类和一个重新实现接口的子类。C 函数指针 + C++ 类 = 对我来说是个坏消息。无论如何感谢所有答案,你已经向我展示了 C++ 的一些黑暗角落;)

4

7 回答 7

3

如果您只是想将成员函数传递给 c 风格的回调,您可以不使用std::t1::bindor来做到这一点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;
};
于 2010-07-16T14:29:33.247 回答
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, ...);
于 2010-07-15T22:22:54.873 回答
2

由于std::tr1::bind和 c 风格的函数指针不能相处,所以试试这个。它会起作用,但它myIntegrator::getValue不再是线程安全的。如果calcTripleIntegral从界面中删除,这将更简单,不需要使用std::tr1::bindor 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;
};
于 2010-07-15T22:34:09.917 回答
2

我想传递一个(非静态!)类成员函数作为被积函数......

你不能。如果您搜索 SO 以使用成员函数作为回调,您一定会找到有用的信息,包括您正在尝试做的事情,无论如何直接方法是不可能的。

编辑:顺便说一句,您的代码中的一个问题(当然还有更多,因为您尝试做的事情根本不可能)是您已经将函数指针类型传递给 function<> 当它期望的是签名时. 函数模板的实现如下:

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 版本将遇到您当前面临的相同问题。

于 2010-07-15T20:30:12.007 回答
1

假设 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 更加灵活并以这种方式传递您喜欢的任何内容。

于 2010-07-16T03:10:58.643 回答
0

bind工作方式与你想象的有点不同。您需要为每个参数提供一个值或占位符。

对于您的示例,这归结为(使用占位符)

std::tr1::function<integrand> func(std::tr1::bind(&myIntegrator::integrandF2, *this, _1, _2, _3, _4, _5));

因为你绑定了一个成员函数,你有一个额外的(隐式)参数,即你调用成员函数的对象,所以你有六个。

首先,您绑定this对象,对于其他参数,您只需传递占位符。

附带说明一下,您的成员函数返回 double,而函数声明返回 void。

(作为记录,我仍在使用旧的编译器,几乎没有 tr1 支持,所以我只有使用 boostbindfunction经验,也许 tr1 的情况有所改变......)

于 2010-07-15T21:43:48.370 回答
0

该错误消息听起来像是您缺少所涉及类型之一的包含。至少尝试仔细检查您的integrandtr1包含?

于 2010-07-15T20:49:52.507 回答