В C++ не возможно объявить статическую виртуальную функцию, никакой бросок, нестатическая функция к C разрабатывает указатель функции.
Теперь, у меня есть простой SDK C, который использует указатели функции в большой степени.
Я должен заполнить структуру несколькими указателями функции. Я планировал использовать абстрактный класс с набором статических чистых виртуальных методов, и переопределить их в производных классах и заполнить структуру ими. Только в затем, я понял, что статичный виртуальный не позволяются в C++.
Также этот C SDKs функциональная подпись не имеет userData param.
Действительно ли там польза альтернативна? Лучшее, о котором я могу думать, определяет некоторые чистые виртуальные методы GetFuncA (), GetFuncB ()... и некоторые статические участники FuncA ()/FuncB () в каждом производном классе, который был бы возвращен GetFuncX (). Затем функция в абстрактном классе вызвала бы те функции, чтобы получить указатели и заполнить структуру.
Редактирование, Отвечающее John Dibling, было бы замечательно смочь сделать это:
class Base
{
FillPointers() { myStruct.funA = myFunA; myStruct.funB = myFunB; ...}
private:
CStruct myStruct;
static virtual myFunA(...) = 0;
static virtual myFunB(...) = 0;
};
class Derived1 : public Base
{
Derived1() { FillPointers(); }
static virtual myFunA(...) {...};
static virtual myFunB(...) {...};
};
class Derived2 : public Base
{
Derived2() { FillPointers(); }
static virtual myFunA(...) {...};
static virtual myFunB(...) {...};
};
int main()
{
Derived1 d1;
Derived2 d2;
// Now I have two objects with different functionality
}
Вы можете сделать Base
шаблоном класса, который берет указатели на функции из аргумента шаблона:
extern "C" {
struct CStruct
{
void (*funA)(int, char const*);
int (*funB)(void);
};
}
template <typename T>
class Base
{
public:
CStruct myStruct;
void FillPointers() {
myStruct.funA = &T::myFunA;
myStruct.funB = &T::myFunB;
}
Base() {
FillPointers();
}
};
Then определите производные классы, которые будут производными от экземпляра Base
, используя каждый производный класс в качестве аргумента шаблона:
class Derived1: public Base<Derived1>
{
public:
static void myFunA(int, char const*) { }
static int myFunB() { return 0; }
};
class Derived2: public Base<Derived2>
{
public:
static void myFunA(int, char const*) { }
static int myFunB() { return 1; }
};
int main() {
Derived1 d1;
d1.myStruct.funA(0, 0);
d1.myStruct.funB();
Derived2 d2;
d2.myStruct.funA(0, 0);
d2.myStruct.funB();
}
Этот метод известен как необычно повторяющийся шаблон шаблона . Если вы не реализуете одну из функций в производном классе или измените сигнатуру функции, вы получите ошибку компиляции, которую вы и ожидаете получить, если не реализовали одну из чисто виртуальных функции из вашего первоначального плана.
Однако следствием этого метода является то, что Derived1
и Derived2
не имеют общего базового класса. Два экземпляра Base <>
никак не связаны с системой типов. Если вам нужно, чтобы они были связаны, вы можете ввести другой класс, который будет служить базой для шаблона, а затем поместить туда общие вещи:
class RealBase
{
public:
CStruct myStruct;
};
template <typename T>
class Base: public RealBase
{
// ...
};
int main()
RealBase* b;
Derived1 d1;
b = &d1;
b->myStruct.funA(0, 0);
b->myStruct.funB();
Derived2 d2;
b = &d2;
b->myStruct.funA(0, 0);
b->myStruct.funB();
}
Осторожно: Статические функции-члены не обязательно совместимы с обычными указателями функций . По моему опыту, , если компилятор принимает операторы присваивания, показанные выше, то вы, по крайней мере, можете быть уверены, что они совместимы для этого компилятора . Этот код нельзя переносить, но если он работает на всех поддерживаемых вами платформах, вы можете считать его «достаточно переносимым».
Предполагая, что C SDK позволяет вам передать ему void * в ваши данные (и вы должны передать ему свой this указатель для производный класс :)
class Base {
public:
void Initialize() { /* Pass /this/ and a pointer to myFuncAGate to your C SDK */ }
virtual myFuncA()=0;
// This is the method you pass to the C SDK:
static myFuncAGate(void *user_data) {
((Base*)user_data)->myFuncA();
}
};
class Derived1: public Base {
public:
virtual myFuncA() { ... } // This gets called by myFuncAGate()
};
Если C SDK не позволяет вам передавать указатель на ваши данные, который затем возвращается вам через обратные вызовы, тогда вам будет очень сложно это сделать. Поскольку вы указали в одном из своих комментариев, что это действительно так, вам в значительной степени не повезло. Я бы предложил использовать простые функции в качестве обратных вызовов или перегрузить конструктор и определить несколько статических методов. Вам все равно будет сложно определить, с каким именно объектом должны работать ваши методы, когда ваши обратные вызовы вызываются кодом C.
Если вы опубликуете более подробную информацию о SDK, возможно, вы сможете дать вам более подходящие предложения, но в общем случае, даже со статическими методами, вам понадобится какой-то способ получения указателя this для работы с участием.
Если C SDK хочет, чтобы вы выполняли операции без предоставления пользовательских данных, то объектная ориентация, скорее всего, не нужна, и вам следует просто написать несколько функций. В противном случае пора найти новый SDK.
Очевидный способ выглядит так: FillPointers
реализованы в каждом производном классе.
class Base
{
private:
CStruct myStruct;
};
class Derived1 : public Base
{
private:
static FillPointers() { myStruct.funA = myFunA; myStruct.funB = myFunB; ...}
Derived1() { FillPointers(); }
static myFunA(...) {...};
static myFunB(...) {...};
};
Однако вы, вероятно, можете избежать этого, используя магию шаблонов ...
Вы можете просто передать функции прямо в конструктор базового класса:
class Base
{
Base()(int (*myFunA)(...), int (*myFunB)(...))
{ myStruct.funA = funA; myStruct.funB = myFunB; ...}
private:
CStruct myStruct;
};
class Derived1 : public Base
{
Derived1() : Base (myFunA, myFunB) {}
static myFunA(...) {...};
static myFunB(...) {...};
};
class Derived2 : public Base
{
Derived2() : Base (myFunA, myFunB) {}
static myFunA(...) {...};
static myFunB(...) {...};
};
int main()
{
Derived1 d1;
Derived2 d2;
// Now I have two objects with different functionality
}
Распространенный шаблон при передаче указателя функции (обратного вызова) в C SDK использует тот факт, что многие такие функции допускают параметр void *, который это «пользовательские данные». Вы можете определить свои обратные вызовы как простые глобальные функции или статические функции-члены класса. Затем каждый обратный вызов может привести параметр «пользовательские данные» к указателю базового класса, чтобы вы могли вызвать функцию-член, которая выполняет работу обратного вызова.
Виртуальные функции по сути являются внутренними указателями функций. Они просто указывают на разные функции для разных классов. Чтобы смоделировать поведение виртуальной функции, храните где-нибудь указатель на функцию, а затем, чтобы «переопределить» его, просто переназначьте его какой-либо другой функции.
В качестве альтернативы вы можете захотеть проверить это, но я думаю, что интерфейсы имеют довольно хорошую двоичную совместимость. Вы можете обойтись без демонстрации интерфейса C ++, полностью состоящего из чистых виртуальных функций, при условии, что все параметры и возвращаемые типы имеют согласованный двоичный формат (например, типы C). Это не стандарт, но может быть достаточно портативным.
Я думаю, вам просто нужно использовать обычную виртуальную функцию. Статическая виртуальная функция не имеет смысла, потому что виртуальная функция разрешается во время выполнения. Что там нужно решать, когда компилятор точно знает, что такое статическая функция?
В любом случае, я бы предложил оставить существующее решение с указателями функций на месте, если это возможно. В противном случае, рассмотрите возможность использования обычной виртуальной функции.
class Base
{
template<class T>
FillPointers(T* dummy) { myStruct.funA = T::myFunA; myStruct.funB = T::myFunB; ...}
private:
CStruct myStruct;
};
class Derived1 : public Base
{
Derived1() { FillPointers(this); }
static myFunA(...) {...};
static myFunB(...) {...};
};
class Derived2 : public Base
{
Derived2() { FillPointers(this); }
static myFunA(...) {...};
static myFunB(...) {...};
};
int main()
{
Derived1 d1;
Derived2 d2;
// Now I have two objects with different functionality
}
см. также Статические виртуальные члены C++?