C++ の vtbl に、関数以外の型のデータを入れる

C++ でコードを書いていて、vtbl に関数以外のデータを入れたくなったことはありませんか? 僕はあります。

型によって異なる定数を高速に知りたい(たとえばあるフィールドが何バイト目にあるか)けど、仮想関数呼出だと遅いし、かといって、インスタンス変数にしちゃうと、オブジェクトサイズが大きくなるので避けたい*1とか。

実は、GCC の -Wno-pmf-conversions *2をオンにすると、こんな感じで書けます。

#include <cstdio>

struct Base {
  virtual ~Base() {}
  virtual void _className() = 0; // is const char*
  const char* className() const {
    void (Base::*f)() = &Base::_className;
    return *(const char**)(void(*)(Base*))(this->*f);
  }
};

struct Derived1 : public Base {
  virtual void _className();
};

void Derived1::_className() {} // fake
extern const char* _ZN8Derived110_classNameEv = "Derived1";

struct Derived2 : public Base {
  virtual void _className();
};

void Derived2::_className() {} // fake
extern const char* _ZN8Derived210_classNameEv = "Derived2";

int main(int argc, char** argv)
{
  Base* o = new Derived1;
  printf("class: %s\n", o->className()); // "Derived1"
  o = new Derived2;
  printf("class: %s\n", o->className()); // "Derived2"
  return 0;
}

これは便利! だと思ったあなた、Clang ですらコンパイルできないし name mangling rule に依存してる*3ので、真面目にやるなら vtbl 機構を自作しましょう。

*1:ポインタ1つ分でも削りたいことって多いですよね

*2:参照: Bound member functions - Using the GNU Compiler Collection (GCC)

*3:あとthunkが絡むと動かない気がする