std :: tr1 :: function和std :: tr1 :: bind

时间:2010-07-15 20:20:56

标签: c++ c++11 tr1

我在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 ++的一些黑暗角落;)

7 个答案:

答案 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::bindstd::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::bindstd::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)

该错误消息使您听起来像是缺少其中一种类型的包含。至少尝试仔细检查integrandtr1包括哪些内容?

答案 6 :(得分:0)

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

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

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

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

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

在旁注中,您的成员函数返回double,而函数声明返回void。

(为了记录,我仍然使用较旧的编译器,支持很少的tr1,所以我只有bindfunction使用boost的经验,也许tr1的情况有所改变。 。)

相关问题