1

クラスメンバー関数のコールバックに関するこの問題を解決したいと思います。次のような外部ライブラリ(変更できない!)の関数があるとします。

void fortranFunction(int n, void udf(double*) );

既存のクラスの関数メンバーの上にudf関数として渡したいと思います。次のコードを見てください。

// External function (tipically from a fortran library) 
void fortranFunction(int n, void udf(double*) ) 
{ 
     // do something 
} 

// User Defined Function (UDF) 
void myUDF(double* a) 
{ 
      // do something 
} 

// Class containing the User Defined Function (UDF) 
class myClass 
{ 
public: 
    void classUDF(double* a) 
    { 
        // do something... 
    }; 
}; 

int main() 
{ 
    int     n=1; 

    // The UDF to be supplied is myUDF 
    fortranFunction(n, myUDF); 

    // The UDF is the classUDF member function of a myClass object 
    myClass myClassObj; 
    fortranFunction(n, myClassObj.classUDF);   // ERROR!! 
} 

上記のコードの最後の行では、classUDFメンバー関数を静的関数として宣言できないため、コンパイルエラーが発生します。この問題を解決できるかどうか知っていますか?おそらくBoostライブラリが役立つかもしれませんが、方法がわかりません(fortranFunctionは外部ライブラリからのものであるため変更できないことを考慮してください)。どうもありがとう!アルベルト

4

2 に答える 2

0

あなたはその解決策を試すかもしれません(少しハッキーですが、私はそれがあなたのために働くはずだと思います):

void fortranFunction(int n, void udf(double*))
{
    double d = static_cast<double>(n);
    udf(&d);
}

class myClass {
public:
    void classUDF(double* a) {
    }
};

#ifdef _MSC_VER
#define THREADLOCALSTATIC __declspec(thread) static
#define THREADLOCAL
#else
#define THREADLOCALSTATIC static ___thread
#define THREADLOCAL ___thread
#endif

struct _trampolinebase {
    THREADLOCALSTATIC _trampolinebase* current_trampoline;
};
THREADLOCAL _trampolinebase* _trampolinebase::current_trampoline = 0;

#undef THREADLOCAL
#undef THREADLOCALSTATIC

template<class CBRET, class CBARG1, class T>
struct _trampoline1 : _trampolinebase
{
    typedef CBRET (T::*CALLBACKFN)(CBARG1);
    _trampoline1(T& target, CALLBACKFN& callback)
    : callback_(callback)
    , target_(target)
    {
        assert(current_trampoline == 0);
        current_trampoline = this;
    }
    static CBRET callback(CBARG1 a1) {
        _trampoline1* this_ = static_cast<_trampoline1*>(current_trampoline);
        current_trampoline = 0;
        return this_->trampoline(a1);
    }
private:
    CBRET trampoline(CBARG1 a1) {
        return (target_.*callback_)(a1);
    }

    CALLBACKFN& callback_;
    T& target_;
};

template<class FRET, class FARG1, class CBRET, class CBARG1, class T, class F>
FRET call1_1(T& target, CBRET (T::*callback)(CBARG1), F& fortranfunction, FARG1 a)
{
    typedef typename _trampoline1<CBRET, CBARG1, T> trampoline;
    trampoline t(target, callback);
    return fortranFunction(a, trampoline::callback);
}

int main() 
{ 
    int n=1; 
    myClass myClassObj; 
    call1_1<void,int,void,double*>(myClassObj, &myClass::classUDF, fortranFunction, 1);
} 

'threadlocal'のものを使用すると、これはマルチスレッド呼び出しでも機能します。マルチスレッド環境を使用しない場合は、これを省略できます。また、再帰呼び出しでも機能します(たとえば、コールバックが別のFortran関数を呼び出す場合)。

このソリューションは、1つの引数とfortran関数のコールバック、およびコールバック関数自体の1つの引数に対してのみ機能しますが、簡単に拡張できるはずです。これが、私がそれを「call1_1」(1つの引数を持つfortran関数、1つの引数を持つcallbackfunction)と呼んだ理由でもあります。FRETはFortran関数の戻り型であり、FARG1は最初の引数(この場合はint)の型です。CBRETとCBARGは、コールバック関数で同じです。

fortran関数が実際に呼び出される前に、ターゲットオブジェクトはグローバル(スレッドローカル)変数内に格納されます。fortran関数は静的コールバック関数を呼び出し、最後にメンバー関数を呼び出します。

静的メンバーをインスタンス化するためにトランポリンベースを発明しました。そのためにグローバル変数を使用することもできました(ただし、何らかの理由で、グローバル変数はあまり好きではありません);-)

于 2012-09-26T08:43:28.260 に答える
0

わからない、なぜこのようclassUDFに宣言できないのかstatic

class myClass {
public:
  static void classUDF(double *a) {
  ...
  }
};

そしてそれを次のように渡します

fortranFunction(n, myClass::classUDF);
于 2012-09-25T07:34:36.980 に答える