После прочтения (и, надеюсь, изучения) большого количества указателей на функции-члены, 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);