これはrealloc()
を拡張したい、というトピックですが、その中でBoost.Container作者のIonさんが「realloc()
に相当する機能はBoost.Containerの1.56.0向けにすでに実装したよ」とおっしゃっていました。
std::vector
では、push_back()
で要素をどんどん追加していき、キャパシティが不足したら全要素が収まる新しい領域を確保します。realloc()
相当の機能がアロケータにあれば、このような状況では領域を伸長すれば効率がよくなります。
realloc
相当の機能に関する提案
アロケータにrealloc()
相当の機能を追加する提案は現在、2つあります。
今回、Boost.Containerが実装したのは、N2045の方です。この方式では、アロケータクラスに、以下の関数を追加します。
enum allocation_type { //Bitwise OR (|) combinable values allocate_new = ..., expand_fwd = ..., expand_bwd = ..., shrink_in_place = ..., nothrow_allocation = ... }; template <class T> struct allocator { std::pair<pointer, bool> allocation_command(std::allocation_type command, size_type limit_size, size_type preferred_size, size_type& received_size, pointer reuse = 0); };
コマンドによってメモリアロケーションの動作が代わります。expand_fwd
やexpand_bwd
がrealloc()
相当になります。(allocate_new
は今までのメモリアロケート)
実装方法
realloc()
相当の機能と言っていますが、realloc()
をそのままは使えません。realloc()
は、領域の伸長に失敗したら新たな領域を作る、というところまでやってしまうので、細かい制御ができません。
std::vector
の内部実装として使うには、realloc()
の前半部分である「領域の伸長を行う。(できなかったらヌルポインタか何かを返す。)」というところだけがほしいです。
そこで、Boost.Containerでは、dlmallocのソースを持ってきてBoost.Containerのリポジトリに入れてしまい、その上でdlmallocを拡張し、realloc()
の前半部分を抜き出しています。dlmallocはCC0ライセンスなので、Boostのリポジトリに入れてしまってもライセンス上問題ありません。
使い方
realloc()
相当の機能であるallocation_command()
関数は、Boost.Containerのallocator
クラスに入っていますが、この機能は、アロケータにバージョンとして2を与えると有効になります。いままでは1で、デフォルトも1です。
#include <iostream> #include <boost/container/allocator.hpp> #include <boost/container/vector.hpp> namespace cont = boost::container; int main() { cont::vector<int, cont::allocator<int, 2>> v; v.push_back(3); v.push_back(1); v.push_back(4); for (int x : v) { std::cout << x << std::endl; } }
出力:
3 1 4
バージョン2のアロケータを使用する場合、Boost.Containerのソース(dlmalloc)をビルドする必要があります。
結び
Boost.Containerは、標準コンテナの最新仕様を使用できることをひとつの目的としています。今回の機能はまさに、realloc()
相当の機能に関する実装経験、使用経験を積む、いいケースだと感じます。