【问题标题】:C++ class member callback and external libraryC++ 类成员回调和外部库
【发布时间】:2012-09-25 07:16:08
【问题描述】:

我想解决这个关于类成员函数回调的问题。 想象一下,您有一个来自外部库的函数(无法修改!),如下所示:

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 无法修改,因为它来自外部库)。 非常感谢! 阿尔贝托

【问题讨论】:

    标签: boost callback function-pointers static-members


    【解决方案1】:

    我不明白,你为什么不能像这样将classUDF声明为static

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

    然后像这样传递它

    fortranFunction(n, myClass::classUDF);
    

    【讨论】:

    • @Mark 好吧,这并不容易,因为非静态函数有一个隐式 this 指针传递给它,所以签名不会匹配。
    【解决方案2】:

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

    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 函数调用一个静态回调函数,最终调用你的成员函数。

    我发明了 trampolinebase 来实例化静态成员,我也可以为此使用全局变量(但出于某种原因,我不太喜欢全局变量);-)

    【讨论】:

    • 请注意,这只有在从 fortran 函数本身调用回调函数时才有效。如果回调存储在 fortran 代码中并在 fortran 调用返回之后调用,则它将不起作用。
    猜你喜欢
    • 1970-01-01
    • 2011-08-23
    • 1970-01-01
    • 2012-12-20
    • 2012-03-04
    • 2020-09-04
    • 1970-01-01
    相关资源
    最近更新 更多