std::vector と例外を使ってる状態でコンパイルを通す方法

まだ実機で動かせないので、とりあえずコンパイルを通すだけ。


まず当然のごとく new と delete は再定義するべき。

void* operator new(std::size_t size) throw(std::bad_alloc)
{
    return MALLOC(size);
}
void* operator new[](std::size_t size) throw(std::bad_alloc)
{
    return MALLOC(size);
}
void operator delete(void* p) throw()
{
    if (p != nullptr) FREE(p);
}
void operator delete[](void* p) throw()
{
    if (p != nullptr) FREE(p);
}

void* operator new(std::size_t size, const std::nothrow_t&) throw()
{
    return MALLOC(size);
}
void* operator new[](std::size_t size, const std::nothrow_t&) throw()
{
    return MALLOC(size);
}
// この辺はいらないかも
void operator delete(void* p, const std::nothrow_t&) throw()
{
    if (p != nullptr) FREE(p);
}
void operator delete[](void* p, const std::nothrow_t&) throw()
{
    if (p != nullptr) FREE(p);
}

ほんとは size が 0 の場合の対応とか NULL だったら例外を投げたりとかそういう実装にする必要があるけど、とりあえずの実装ということで。


で、例外機構が malloc と free を使ってるので、これを再定義する。

extern "C" void* malloc(unsigned int size) { return MALLOC(size); }
extern "C" void free(void* p) { FREE(p); }

この辺で NULL なんか返したりすると多分あぼーんしちゃうので、これはメモリプールか何かから渡してやるのが吉な感じ?どうせシステムからしか呼ばれないので数KBあれば大丈夫でしょ。
でも例外で使われるだけなら↓の緊急用バッファがあるだろうから、わざわざプールから確保する必要もないかも?


あと、これはドキュメントにもあるけど、std::bad_alloc が投げられて例外用のメモリが足りないときのための緊急時バッファを取得するための関数がある。
__ARM_exceptions_buffer_init, __ARM_exceptions_buffer_allocate, __ARM_exceptions_buffer_free がそう。
とりあえずメモリが足りなくなることは無いだろうということで NULL を返しておく。

extern "C" void *__ARM_exceptions_buffer_init() { return 0; }
extern "C" void *__ARM_exceptions_buffer_allocate(void *buffer, unsigned int size) { return 0; }
extern "C" void *__ARM_exceptions_buffer_free(void *buffer, void *addr) { return 0; }

多分、malloc で例外用の領域を確保しようとして、NULL が返された場合にこの緊急用のバッファを使うのだと思うので、__ARM_exceptions_buffer_init() あたりで sizeof(std::bad_alloc) 分だけ確保しておいたほうがよさそう。


あとは で使ってるデフォルトのアロケータが __rw::__rw_allocate と __rw::__rw_deallocate を使ってるので、これを実装する。
これは std::allocator の実装にあわせればいいはずなので、::operator new と ::operator delete にルーティング。

#include <rw/_defs.h>
namespace __rw
{
    void* __rw_throw(int, ...) { return 0; }
    void* __rw_allocate(_RWSTD_C::size_t size, int = 0) { return ::operator new(size); }
    void  __rw_deallocate(void* p, _RWSTD_C::size_t, int = 0) { ::operator delete(p); }
}

__rw_throw という関数があるんだけど、これ、バイナリを作った際のコールグラフにはどこからも呼ばれていないのに、定義しておかないと怒られる。
多分どこかで使うんだろうけど、とりあえず今は使ってないので何も考えず return する。


自分の場合、とりあえずこれでコンパイルが通った。あとは実機で動くかどうか……多分動かないだろうなぁ。