Category Archives: 雑記

C++ メンバー関数ポインタの話

C++メンバー関数ポインタで仮想関数だとどうなるんだろ?と思って調べてみた。

class C {
public:
  virtual void func() { ... }
  typedef void (C::*funcptr)();
  funcptr fp;

  C() {
    fp = &C::func();
  }
};

このとき、fpはC::func関数のメモリアドレスと同じ値かと思っていたのですが、ぜんぜん違った。

実態はvtbl内のaddress offsetのような数値(実際はオフセット+1の数値)です。

vtblのオフセットなので、class Cを継承したDというクラスでfuncをoverrideするとメンバー関数ポインタfpを使った呼び出しはきちんと、overrideされたD::funcを呼び出します(コンストラクタの中ではvtblの関係でだめですけど)。

このソースコードで、C::func()が呼び出されないという不思議。

funcがvirtualではない場合、普通に関数アドレスが戻ってるので、呼び出し時にどちらか判断する必要があります。

ここに、なんらかの仕組みが必要なのでasmソース出して調べてみた。

	movl	-12(%ebp), %eax
	movl	4(%eax), %eax
	andl	$1, %eax ← 仮想関数テーブルオフセットかの判断
	testl	%eax, %eax
	jne	L4 ← 仮想関数テーブルだったらL4に飛ぶ
	movl	-12(%ebp), %eax
	movl	4(%eax), %eax
	jmp	L5
L4:
	movl	-12(%ebp), %eax ← 仮想関数のアドレスを解決
	movl	8(%eax), %eax
	movl	%eax, %edx
	movl	-12(%ebp), %eax
	addl	%edx, %eax
	movl	(%eax), %edx
	movl	-12(%ebp), %eax
	movl	4(%eax), %eax
	decl	%eax ← 仮想関数オフセットのフラグを落とす
	addl	%edx, %eax
	movl	(%eax), %eax
L5:
	movl	-12(%ebp), %edx
	movl	8(%edx), %edx
	movl	%edx, %ecx
	movl	-12(%ebp), %edx
	addl	%ecx, %edx
	movl	%edx, %ecx
	call	*%eax

この通り(?)、通常の関数アドレスやvtblオフセットが32bit alignなのを利用して、関数アドレスの場合はそのまま、vtblオフセットの場合は+1しておくことで1ビット目で判断してました。

メンバー関数ポインタの呼び出しがオブジェクト内部からでも(this->*fp)();とする必要がある理由は、この処理にあるんすね。

それにしても仮想関数テーブルのlookupコードが長いので-O2してみた。

	movl	4(%ebx), %eax
	movl	8(%ebx), %ecx
	testb	$1, %al
	je	L6
	movl	(%ebx,%ecx), %edx
	movl	-1(%edx,%eax), %eax
L6:
	addl	%ebx, %ecx
	call	*%eax

こんなもんか。ふむ。

Cocos2d-x CCControlExtensionをLuaから使う

ってな事をちょっとやってみたのですが、、、。最終的にちょっと嫌な仕様になっちゃったのでやめておいたというお話。

CCControlはtarget object(CCObjectを継承したクラス)とselectorとイベントのマスクでイベントのハンドラを登録します。
内部では、それらをCCInvocationオブジェクトとして保持していますが、CCInvocationの中で対象となるObjectをretainしません。

GUIのイベントハンドラは自分自身に向ける事も多いので循環参照になりますし、
CCInvocationはtarget objectのownerであるとは限らないのでretainしないのはこれは正しい設計に思います。

ここまではNativeの話。ここからLuaで使う場合のはなしです。

Luaでイベントハンドラを実装したい場合、CCControlはスクリプトのハンドラを設定する事ができないため、
スクリプトを呼び出すHelper Objectを実装してHelper ObjectをLuaとBind、
イベントの発生を一旦このHelper Objectで受け、そこからスクリプトを呼び出す様にしてやりました。

ここで一つ問題が。Helper ObjectをCCControlの寿命と一致させる事が出来れば
スクリプトハンドラの削除をHelper Objectの削除タイミングで行えます。
しかしながら、Helper Object側でCCControlが削除された事を知る事ができません。

このためHelper ObjectのOwnerは別に用意して管理する必要が出てきます。うーん。面倒。

CCControlExtension側を書き換えちゃえばいいんですけどねー。
というわけでLuaからはCCControlExtensionは使わない方向で行こうと決めた、今日このごろなのでした。

Sigil – カーソルが消える

Sigilを使ってるとカーソルが良く消えるのでパッチを作ってみたんですが、治るものの実装方法の正当性について確証が持てないのでフィードバックできてなかったり。
個人的に使うならこれでいいかなと思うので 検証終わったらパッチを開発側に送ってみようかな。

電子出版系の本や記事を眺めると結構Sigilが紹介されていますが、この問題について触れている記事が殆ど見つからないのは何故なんだろう?
Sigilの古いバージョンからある問題だし、入力作業がかなりやりにくいのですけども。