関数ポインタを無理矢理取得して、別の関数ポインタに入れる方法

__event/__hook 関数を使う理由のひとつとして、クラスの関数ポインタを別のクラスの関数ポインタには容易に入れられない、というのがあって。

class Alice {
	string _name;
public:
	void (*Say)(string name);	// ここで関数ポインタを定義
};

というようにポインタを定義しておいて、これを設定したいわけですが、これに設定できるのは Alice::func(string) という関数だけなんですね。これが比較関数だったりすると、他のクラスから設定できないし(内部のメソッドでないと駄目だし)という具合で困るわけです。
STLの比較関数(for_each とか remove_if など)の algorithm 系はグローバルの関数なので大丈夫なんですが…

まぁ、そんな訳で __event と __hook の組み合わせにするのが普通(?)なのですが、やっぱり関数ポインタまま使いたい、というのを実装したのが以下です。

// 関数ポインタを無理矢理取得
#include <string>
#include <iostream>
#include <stdarg.h>

using namespace std;

class Alice {
	string _name;
public:
//	__event void SayEvent(string name);
	void (*Say)(string name);
};

class Lewis {
public:
	void Say( string name )
	{
		cout << "in Lewis: " << name << endl;
	}
};

void *funcp( int dummy, ... )
{
	va_list ap;
    va_start(ap, dummy);
    int ret = va_arg(ap, int);
    va_end(ap);
    return (void*)ret;
}
void hook( int dummy, ... )
{
	int *p = &dummy;
	int src  = (int)p[1];
	int *dest = (int*)p[2];
	*dest = src;
}

int main( void )
{

	Alice alice;
	Lewis lewis;

//	__hook( &Alice::SayEvent, &alice, &Lewis::Say, &lewis );
// 	alice.SayEvent( "Alice" );

	void *p = funcp(0,&Lewis::Say);
	void *p2 = funcp(0,&alice.Say);

	printf("say:   %08X\n", &Lewis::Say );
	printf("funcp: %08X\n", p );
	printf("say:   %08X\n", &alice.Say);
	printf("funcp: %08X\n", p2 );

	hook(0, &Lewis::Say, &alice.Say );
	alice.Say( "Alice X" );

	return 0;
}

hook 関数がそれですね。試しに使った funcp 関数を観て貰うと分かりますが、可変引数のスタックを使って取り出すという方法です。
これがトリッキーなのは、可変引数の場合は型のチェックがされないことです。C++ の場合、型変換が厳しくて、関数ポインタと void * の相互変換ができません。なので無理矢理ポインタで解決しています。

これで、__event/__hook が消えて、gcc でもコンパイルできそうで、先の model と view の関係にも使えるね…と思っていたのですが、よくよく考え直してみると、model-view の関係には Observable パターンを使えば即解決というのが分かりました orz __hook/__event を使う必要がないってことで「車輪の再発明」。きれいな Observable パターンは後程。それだけじゃつまらないので、C++ で ObservableCollection を実装してみます。

カテゴリー: C++ パーマリンク

関数ポインタを無理矢理取得して、別の関数ポインタに入れる方法 への2件のフィードバック

  1. masuda のコメント:

    いやいや、for_each などは mem_fun を使うべき > 自分
    あとで直しますか。

  2. masuda のコメント:

    lamda 式の関数ポインタに関しては std::function を使えば良いことが分かった。
    これも後で修正しよう。

コメントは停止中です。