また BREW の話です。
あんまり役に立つことは無い気がしますが、組み込みではもしかしたらという淡い期待を抱きつつ書こうと思います。
自己書き換えでグローバル変数を使えるようにしてしまおうというお話です。
BREW では、その構造上、グローバル変数・スタティック変数を使うことが出来ません。
確かにメインクラスにグローバルとして使いたいオブジェクトを集積させれば、通常はグローバル・スタティック変数を使用する必要など無いのですが、利便性や保守性から、グローバル・スタティック変数を使いたいということが発生することはあります。
まあそれを叶わぬ夢だと言って諦めたり、こんなのやってられっかとディスプレイをぶち破ったり、毎夜神社に通って五寸釘を打ち付けたりするのも一つの手段ではありますが、ここはプログラマらしく、何とかグローバル・スタティック変数を使う方法を探してみようと思います。
とにかく、どこからでも値を取得することが出来る領域があれば良いわけです。
char g_pointer[ 4 ] = { 0 };
こうやって書くと、実行時に0で初期化する必要があるので、そういった初期化の処理が出来ない BREW では使用することが出来ません。
というか、ARM コンパイラに食わすとエラーを吐き出します。
そこで、
const char g_pointer[ 4 ] = { 0 };
こんな風に書いてみることにします。
この場合、g_pointer は const な領域であるため、バイナリに直接埋め込まれます。
これで、4バイトのグローバルな領域が確保出来たことになります。
const だから書き換えちゃいけないとか、そんなの知ったこっちゃありません。とにかくグローバルに領域が確保出来たので、これを使って、何らかの値を詰めていきます。
const char g_pointer[ 4 ] = { 0 };
void SetGlobalPointer( void* p ){
*((void**)&g_pointer[ 0 ]) = p;
}
void* GetGlobalPointer(){
return *((void**)&g_pointer[ 0 ]);
}
これで、無理矢理ですがグローバル変数が使用出来るようになります。
スタティック変数が欲しい場合も、必要なだけグローバルに領域を取って、それを使用するだけでいいのですが、template を使用したクラスでスタティック変数が欲しい場合、グローバルな領域を書き換えて実現することは、使用する数が(コーディング時に)一意に定まらないため難しいです。
そこで、次のような形でスタティック変数を作ってやることにします。
template< class T >
class Hoge{
public:
static void*& StaticPointer(){
return *(void**)&_dummy;
}
private:
static void _dummy(){}
}
変態コードです。
ARM では空の関数は BX 命令(x86 の RET 命令みたいなもの)で4バイト使用するので、この領域を書き換えて、4バイトのスタティック変数として使用しています。
もっと領域が欲しいなら、NOP で埋めていけばいいかと。
こうすることで、template を使用している場合でも、スタティック変数を使用することが出来るようになります。
こういった実行バイナリの一部を書き換えるのを、自己書き換えというらしいです。
WindowsNT 系列の場合、メモリ保護という機能があって、メモリ上にロードされた実行バイナリへの書き込みは禁止されているため、ちゃんとした手続きを踏まない限りはこれを使用することは出来ません。
しかし、携帯の場合、そんな負荷の掛かる処理が出来るはずもなく、余裕で自分のバイナリを書き換えることが出来るのです。
通常、こういうのを書くのは、いつか ARM がメモリ保護を付けたときに困るのであまりよろしくないです。
自分もそう思ってこういうのは使用しないようにしていた訳ですが、最近の携帯では OpenGL/ES が使えて、OpenGL/ES はグローバルな変数を持っています。
この領域はどこから確保してるんだと思って調べてみると、
#if defined(__ARMCC_VERSION) && !defined(AEE_STATIC)
IGL *const gpIGL;
#define GPIGL (*(IGL**)&gpIGL)
IEGL *const gpIEGL;
#define GPIEGL (*(IEGL**)&gpIEGL)
#else
IGL *gpIGL;
#define GPIGL (gpIGL)
IEGL *gpIEGL;
#define GPIEGL (gpIEGL)
#endif
ちょっと待てコラ。普通に自己書き換え使用してるやないかと。
ということで、公式なプログラムが自己書き換えを使用している以上、しばらくはメモリ保護が掛かることは無さそうですし、メモリ保護が掛かる場合はグローバル変数が使えるようになっているだろうということで、どんどん自己書き換えしても良さそうです。
というかやっぱりこんなの知ったところで全く役に立ちそうにないですね(;´Д`)