Q: Использование смещения vtable определенной виртуальной функции в операторе switch? - PullRequest
0 голосов
/ 19 октября 2018

После прочтения (и, надеюсь, изучения) большого количества указателей на функции-члены, vtables, constexpr и его ограничений, объединений и т. Д. Я пришел к точке, когда мне нужна помощь от экспертов C ++:

Я должен использовать библиотеку с множеством похожих виртуальных методов.Методы могут иметь разные подписи, так что, по крайней мере, мне нужно предоставить разный набор параметров вызова для каждого метода.Как правило, каждому методу нужна отдельная обработка.

Я пытаюсь использовать оператор switch для выбора между различными вариантами обработки.Условием переключения должен быть метод смещения в виртуальной таблице, который, как я предполагаю,

  • уникален для каждого метода библиотеки и
  • доступен во время компиляции.

Из других потоков, где, например, строки используются как case-выражения , я узнал, что для использования смещения vtable в качестве case-выражения это должно быть 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 приводит к ошибке компилятора.

Может кто-нибудь посмотреть и дать мне несколько советов?

Добавление: Вот некоторый псевдокод, который, надеюсь, прояснит мои намерения:

// 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);
...