问:在switch语句中使用特定虚拟函数的vtable偏移量?

时间:2018-10-19 13:26:08

标签: c++ switch-statement constexpr member-function-pointers vtable

在阅读(并希望学习)有关成员函数指针,vtables,constexpr及其限制,并集等内容之后,我已经到了需要C ++专家帮助的地步:

我必须使用带有许多类似虚拟方法的库。这些方法可能具有不同的签名,因此至少我必须为每种方法提供一组不同的调用参数。通常,每种方法都需要不同的处理方式。

我尝试使用switch语句在不同的处理情况之间进行选择。切换条件应该是vtable中的方法offset,我认为是

  • 对于库的每种方法都是唯一的
  • 在编译时可用。

从其他线程,例如strings are used as case-expressions,我了解到,要将vtable偏移量用作大小写表达式,它必须是 constexpr 。由于 constexpr 的许多限制,我创建了一个联合以转换成员函数指针,并提供了一个 constexpr 运算符以将联合值返回为整数。

所有这些都导致了以下代码,该代码几乎但不完全不同于我的意图:

// Compiled with g++ (Ubuntu 4.8.5-4ubuntu8~14.04.2) 4.8.5
// -std=c++03 and -std=c++1y give the same results !
#include <iostream>

// method library:
struct funcLib
{
    virtual void a() {}
    virtual void b(const double &_dVal) {}
    virtual void c(const double &_dVal, int &iVal) {}
};

// typedef to handle member function pointers with different signatures:
typedef void (funcLib::*T_Function)();

// union for type-conversion:
union funcID {
    T_Function m_funcPtr;
    uintptr_t  m_funcID;

    explicit constexpr funcID( T_Function _funcPtr ) : m_funcPtr(_funcPtr) {}
    constexpr operator uintptr_t() const { return m_funcID; }
};


int main()
{
    uintptr_t i = funcID((T_Function)&funcLib::a);
    uintptr_t j = funcID((T_Function)&funcLib::c);
    uintptr_t k = funcID((T_Function)&funcLib::b);

    std::cout << i << " " << j << " " << k << std::endl; // results "1 9 5" which seems to be OK!!

// Uncommenting the switch statement gives the error:
//  ../src/Test.cpp:37:44:   in constexpr expansion of 'funcID(&funcLib::a).funcID::operator uintptr_t()'
//  ../src/Test.cpp:37:44: error: accessing 'funcID::m_funcID' member instead of initialized 'funcID::m_funcPtr' member in constant expression
//
//    switch (i) {
//        case funcID((T_Function)&funcLib::a) : /* some handling */ break;
//        case funcID((T_Function)&funcLib::b) : /* some handling */ break;
//        case funcID((T_Function)&funcLib::c) : /* some handling */ break;
//    }

    return 0;
}

前3行确实为我提供了方法的vtable偏移量,这证明了类型转换是合理的,但是在case-expressions中使用相同的调用会导致编译器错误。

有人可以看看并给我一些提示吗?

添加: 这是一些伪代码,希望可以阐明我的意图:

// Own method to centralize the calls into the library
double T_MyClass::callLibraryMethod(T_FunctionID _funcID, T_CallParams _callParams) 
{
  double result;
  switch (_funcID) {
    case &Lib::methodA: result = Lib::methodA(_callParams); break;
    case &Lib::methodB: result = Lib::methodB(_callParams); break; 
    case &Lib::methodC: result = Lib::methodC(_callParams); break;
    ...
  }
  return result;
}

...

// Usage:

T_CallParams ParamsForMethodA, ParamsForMethodB;
double dResult;

...
dResult = callLibraryMethod(&Lib::methodA, ParamsForMethodA);
... 
dResult = callLibraryMethod(&Lib::methodB, ParamsForMethodB);

0 个答案:

没有答案