chikuchikugonzalezの雑記帳

趣味とか日記とかメモとか(∩゚д゚)

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);
}