RubyのC拡張で配置newを使うためのマクロ考えた
RixILっていうRixmap公開用っていう位置づけのRuby C拡張書いてるわけなんですが、C++で書いてるせいで後からnewでインスタンス作る実装が必要になったしまったわけです。
その場合メモリが足りない場合にRubyのNoMemoryErrorを出したいという欲求が出まして。
ruby_xmalloc使うとそれができるんですが、これを使うと配置newという方法を使わないとC++クラスのコンストラクタを呼び出せなくなるんですよ(゚д゚` )ネー。
以前は全部構造体扱いにしてC++クラスのコンストラクタを使わないようにしてたんですが、それはどうよってことで配置newを使うことを決意しました。
とはいえ、配置newのために毎回
RixIL::ModeInfo* info = new(ruby_xmalloc(sizeof(RixIL::ModeInfo))) RixIL::ModeInfo();
とか書きたくないわけです。
そこでぐーぐるさんに聞いてみたらC99拡張で __VA_ARGS__ というマクロ見つかりました。これを使って配置new用のメモリ確保と、ついでに定義したデストラクタを呼び出しつつruby_xfreeでメモリを解放する関数も定義してみた。一応動いてるっぽい。
/** * placement new用new実装. * new(ruby_xmalloc(sizeof(TYPE))) TYPE(); をマクロでやります. * * 後に定義してある _xdelete で開放してあげてください. * * 使用例: * ChannelInfo* info = _xnew(ChannelInfo, Channel::PALETTE, (value & 0x0000001F)) */ #define _xnew(TYPE, ...) (new(ruby_xmalloc(sizeof(TYPE))) TYPE(__VA_ARGS__)) /** * _xnewのコンストラクタ引数なし版. * __VA_ARGS__ マクロが可変引数がない場合に不定となるらしいので、 * デフォルトコンストラクタを使う場合は特別に処理する */ #define _xnew0(TYPE) (new(ruby_xmalloc(sizeof(TYPE))) TYPE()) /** * placement new用delete実装. * デストラクタを呼び出した後にruby_xfreeを使ってメモリ領域を解放します. * * 想定されているケースは、new(ruby_xmalloc(sizeof(T))) T(); で作られたオブジェクトの * 後始末処理です. * * via http://stackoverflow.com/questions/13210757/how-to-call-destructor-of-type-in-template */ template<typename T> inline void _xdelete(T* p) { p->~T(); ruby_xfree(p); }