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

您可以尝试该解决方案(有点hacky,但我认为它应该适合您):

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 函数)。

此解决方案仅适用于 fortran 函数的单个参数加上回调和回调函数本身中的单个参数,但您应该能够轻松扩展它。这也是为什么我称它为“call1_1”(带 1 个参数的 fortran 函数,带 1 个参数的回调函数)。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 回答