繁体   English   中英

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

[英]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::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;
};

你不能。 如果你搜索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::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;
};

假设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检查你的integrandtr1包含?

bind工作有点不同你认为我认为。 您需要为每个参数提供值或占位符。

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

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

由于您绑定了一个成员函数,因此您有一个额外的(隐式)参数,即您调用成员函数的对象,因此您有六个。

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

另外,您的成员函数返回double,而函数声明返回void。

(为了记录,我仍然使用一个较小的tr1支持的旧编译器,所以我只有使用boost的bindfunction经验,也许tr1的东西改变了一点......)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM