从作为参数传递的函数中提取参数

Extracting a parameter from a function passed as parameter

我有以下功能:

void Class1::MainThreadFunction(const __int64 param) {
    if(GetCurrentThreadId() != System::MainThreadID) {
        RunInMainThread(MainThreadFunction, param);
        return;
    }
    //...
}

void Class2::RunInMainThread(void(__closure* FuncToCall)(const __int64 ParToExtract),
                             const __int64 fP1) {
    struct {
        __int64 P1;
        void(__closure* F)(const __int64);

        void __fastcall FTC() { F(P1); }
    } Args = {fP1, FuncToCall};

    TThread::Synchronize(NULL, &Args.FTC);
}

所以我想做的是提取 FuncToCall 中的第一个参数,上面名为 ParToExtract 用于初始化 Args 结构。换句话说,结构中的 P1 应该从名为 ParToExtract.

的传递函数接收 const __int64

上面的方法有效,但我目前作为一种解决方法将参数作为 fP1 传递,我用它来初始化 P1 但肯定有更好的方法来做到这一点。

额外的好处是在 RunInMainThread 中有可变数量的函数参数(但我暂时要避免使用 C++11 <functional>)。

请不要使用基于 lambda 的(或 C++11 功能)- 这是我暂时还不能使用的另一件事。

TThread::Synchronize() 调用的方法的签名必须匹配 TThreadMethod 类型:

void __fastcall (__closure *TThreadMethod)(void);

所以不能直接通过它传递参数。不要通过代理函数,而是使用 lambda:

void MainThreadFunction(int64_t param) {
    if(GetCurrentThreadId() != System::MainThreadID)
        TThread::Synchronize(nullptr, [&param]{ MainThreadFunction(param); } );
    //...
}

为了拥有可变数量的参数,您可以将其设为函数模板:

template< class... Args >
void MainThreadFunction(Args&&... args) {
    if(GetCurrentThreadId() != System::MainThreadID)
        TThread::Synchronize(nullptr, [&args...] {
                MainThreadFunction(std::forward<Args>(args)...); 
            }
        );
    //...
}

使用 classic(C++11 之前)编译器时,您通常会使用 class 私有变量来携带信息。

您已经拥有的是在 C++Builder 的 "classic" (pre-C++11) 编译器中处理这种情况的正确(也是唯一)方法。

为了支持可变数量的参数,您将不得不使用多个重载,没有其他选择(无需深入研究 low-level 内联汇编来手动设置调用堆栈,但即便如此,它也可能无法跨线程边界正常工作),例如:

void Class1::MainThreadFunction()
{
    if (GetCurrentThreadId() != System::MainThreadID)
    {
        RunInMainThread(MainThreadFunction);
        return;
    }
    //...
}

void Class1::MainThreadFunction(const __int64 param)
{
    if(GetCurrentThreadId() != System::MainThreadID)
    {
        RunInMainThread(MainThreadFunction, param);
        return;
    }
    //...
}

// and so on as needed ...
template<typename FuncType>
void Class2::RunInMainThread(FuncType FuncToCall)
{
    struct {
        FuncType F;
        void __fastcall FTC() { F(); }
    } Args = {FuncToCall};

    TThread::Synchronize(NULL, &Args.FTC);
}

template<typename FuncType, typename ParamType>
void Class2::RunInMainThread(FuncType FuncToCall, const ParamType param)
{
    struct {
        const ParamType &P;
        FuncType F;
        void __fastcall FTC() { F(P); }
    } Args = {param, FuncToCall};

    TThread::Synchronize(NULL, &Args.FTC);
}

template<typename FuncType, typename ParamType1, typename ParamType2>
void Class2::RunInMainThread(FuncType FuncToCall, const ParamType1 param1, const ParamType2 param2)
{
    struct {
        const ParamType1 &P1;
        const ParamType2 &P2;
        FuncType F;
        void __fastcall FTC() { F(P1, P2); }
    } Args = {param1, param2, FuncToCall};

    TThread::Synchronize(NULL, &Args.FTC);
}

// and so on as needed...

如果您查看各种 RTL 头文件,例如 sysvari.hutilcls.h,使用重载是 Borland 本身在其自己的几个 API 中解决参数数量可变问题的方式,有时超过 30 个参数,足以处理大多数用户代码。