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でコンパイルしようとしましたが、すべて同じエラーが発生しました。これについては何も行われていないか、何か間違ったことをしました/ EDIT

どうもありがとうございました!よくわからない場合は、喜んで詳しく説明します。

PS:myIntegrator.cppのどこかで定義された関数への関数ポインターを使用して、tr1を使わずにこれを回避できますか?

最終更新:わかりました。TR1がこれに対して1行または2行のソリューションを提供していると誤解しました。残念。クラスを名前空間に「変換」し、関数宣言をコピーして貼り付けています。必要なのは、インターフェイスを再実装した1つの基本クラスと1つのサブクラスだけです。C関数ポインタ+C++クラス=私にとって悪いニュース。とにかくすべての答えをありがとう、あなたは私にC ++のいくつかの暗いコーナーを見せてくれました;)

4

7 に答える 7

3

3つの問題があります...最初はが必要ですがstd::tr1::function<R (Args..)>、要約すると、 std::tr1::function<R (*)(Args...)>2つの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 に答える
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;
};
于 2010-07-16T14:29:33.247 に答える
2

(非静的!)クラスメンバー関数を被積分関数として渡したい...

できません。コールバックとしてメンバー関数を使用するためにSOを検索すると、実行しようとしていること、とにかく直接的なアプローチが不可能であるという事実など、有用な情報を見つけることができます。

編集:ところで、コードの問題の1つ(もちろん、実行しようとしていることは単純に不可能なので、さらに多くの問題があります)は、関数ポインター型を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 に答える
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;
};
于 2010-07-15T22:34:09.917 に答える
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関数です。このようにして、std :: tr1 :: bind式を含め、関数呼び出し構文をサポートするものをcontext_type::funcに割り当てることができます。また、(これについて言及することは道徳的に義務付けられていると思いますが)厳密に言えば、C関数とC ++関数の呼び出し規約が同じであると標準で定義されていませんが、実際には、context_typeをクラステンプレートにし、callback_relayを関数テンプレートにすることができます。 context_type :: dataをより柔軟にし、この方法で好きなものを渡すため。

于 2010-07-16T03:10:58.643 に答える
0

このエラーメッセージにより、関連するタイプの1つにインクルードがないように聞こえます。少なくともあなたのintegrandとインクルードを再確認してみてくださいtr1

于 2010-07-15T20:49:52.507 に答える
0

bindあなたが思っているのとは少し違った働きをします。すべての引数に値またはプレースホルダーを指定する必要があります。

あなたの例では、これは(プレースホルダーを使用して)に要約されます

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

メンバー関数をバインドしているので、追加の(暗黙の)引数、つまりメンバー関数を呼び出すオブジェクトがあり、6つあります。

最初にオブジェクトをバインドthisし、他の引数にはプレースホルダーを渡すだけです。

ちなみに、メンバー関数はdoubleを返しますが、関数宣言はvoidを返します。

(記録のために、私はまだtr1をほとんどサポートしていない古いコンパイラを使用しているので、boostを使用した経験bindしかなく、tr1では状況が少し変わったかもしれません...)function

于 2010-07-15T21:43:48.370 に答える