Альтернатива C++ статические виртуальные методы

В 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
}
36
задан Georg Fritzsche 27 April 2010 в 17:09
поделиться

9 ответов

Вы можете сделать 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();
}

Осторожно: Статические функции-члены не обязательно совместимы с обычными указателями функций . По моему опыту, , если компилятор принимает операторы присваивания, показанные выше, то вы, по крайней мере, можете быть уверены, что они совместимы для этого компилятора . Этот код нельзя переносить, но если он работает на всех поддерживаемых вами платформах, вы можете считать его «достаточно переносимым».

23
ответ дан 27 November 2019 в 05:37
поделиться

Предполагая, что 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 для работы с участием.

3
ответ дан 27 November 2019 в 05:37
поделиться

Если C SDK хочет, чтобы вы выполняли операции без предоставления пользовательских данных, то объектная ориентация, скорее всего, не нужна, и вам следует просто написать несколько функций. В противном случае пора найти новый SDK.

2
ответ дан 27 November 2019 в 05:37
поделиться

Очевидный способ выглядит так: 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(...) {...};
};

Однако вы, вероятно, можете избежать этого, используя магию шаблонов ...

2
ответ дан 27 November 2019 в 05:37
поделиться

Вы можете просто передать функции прямо в конструктор базового класса:

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
}
6
ответ дан 27 November 2019 в 05:37
поделиться

Распространенный шаблон при передаче указателя функции (обратного вызова) в C SDK использует тот факт, что многие такие функции допускают параметр void *, который это «пользовательские данные». Вы можете определить свои обратные вызовы как простые глобальные функции или статические функции-члены класса. Затем каждый обратный вызов может привести параметр «пользовательские данные» к указателю базового класса, чтобы вы могли вызвать функцию-член, которая выполняет работу обратного вызова.

6
ответ дан 27 November 2019 в 05:37
поделиться

Виртуальные функции по сути являются внутренними указателями функций. Они просто указывают на разные функции для разных классов. Чтобы смоделировать поведение виртуальной функции, храните где-нибудь указатель на функцию, а затем, чтобы «переопределить» его, просто переназначьте его какой-либо другой функции.

В качестве альтернативы вы можете захотеть проверить это, но я думаю, что интерфейсы имеют довольно хорошую двоичную совместимость. Вы можете обойтись без демонстрации интерфейса C ++, полностью состоящего из чистых виртуальных функций, при условии, что все параметры и возвращаемые типы имеют согласованный двоичный формат (например, типы C). Это не стандарт, но может быть достаточно портативным.

2
ответ дан 27 November 2019 в 05:37
поделиться

Я думаю, вам просто нужно использовать обычную виртуальную функцию. Статическая виртуальная функция не имеет смысла, потому что виртуальная функция разрешается во время выполнения. Что там нужно решать, когда компилятор точно знает, что такое статическая функция?

В любом случае, я бы предложил оставить существующее решение с указателями функций на месте, если это возможно. В противном случае, рассмотрите возможность использования обычной виртуальной функции.

16
ответ дан 27 November 2019 в 05:37
поделиться
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++?

2
ответ дан 27 November 2019 в 05:37
поделиться
Другие вопросы по тегам:

Похожие вопросы: