Лучший подход для приведения указателя на метод из производного в базовый класс - PullRequest
2 голосов
/ 23 мая 2019

У нас есть базовый класс ByteCode, который должен быть универсальным.Предполагается, что потомки ByteCode будут писать методы вида:

void m();

Класс ByteCode должен иметь определение метода:

typedef void (ByteCode::*Method)();

Чтобы выполнить байт-код, мы имеем:

void exec() {
  while (true) {
    uint16_t opcode = getOpcode();
    Method m = opcodes[opcode];
    this->*m();
  }
}

Выполнение этого в одном классе не будет проблемой.Но у нас есть общий код в базовом классе, а у производного есть массив:

class MyByteCodeEngine : public ByteCode {
private:
  static Method opcodes[65536];

  void m1() {}
  void m2() {}
  void m3() {}
};

Method MyByteCodeEngine ::opcodes[65536] = {
  MyByteCodeEngine::m1,
  MyByteCodeEngine::m2,
  MyByteCodeEngine::m3
}

Проблема в том, что эти методы не являются базовым классом, они являются производными.Но единственный экземпляр, который у нас есть, является производным. Мы не хотим нести виртуальные издержки, мы просто хотим привести и выполнить эту работу, но компилятор ловит каждую хитрость.Если бы нам просто доверяли:

Method MyByteCodeEngine ::opcodes[65536] = {
  (Method)MyByteCodeEngine::m1,
  (Method)MyByteCodeEngine::m2,
  (Method)MyByteCodeEngine::m3
}

Мы можем решить эту проблему, исключив класс ByteCode, но это вынуждает нас повторять код всякий раз, когда у нас есть интерпретатор байт-кода.Любые предложения о том, как обмануть C ++, приняв это, чисто?

1 Ответ

3 голосов
/ 23 мая 2019

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

template<class T>
struct ByteCode {
  typedef void (T::* Method)();

  void exec() {
    while (true) {
      uint16_t opcode = getOpcode();
      Method m = T::opcodes[opcode];
      static_cast<T*>(this)->*m();
    }
  }
};

class MyByteCodeEngine : public ByteCode<MyByteCodeEngine > {
  private:
    static Method opcodes[65536];

    void m1() {}
    void m2() {}
    void m3() {}
};

MyByteCodeEngine::Method MyByteCodeEngine ::opcodes[65536] = {
  &MyByteCodeEngine::m1,
  &MyByteCodeEngine::m2,
  &MyByteCodeEngine::m3
}
...