7

バックグラウンド

これは、ゲーム エンジンのメモリ マネージャー用です。私はfreelist実装しており、これらの場合はコンパイル時のリストが必要です。(たとえば、MPL または融合ベクター)。はfreelist割り当てサイズに対応し、定数より小さいサイズのオブジェクトを割り当て/割り当て解除すると、対応する に移動しfreelistます。

結局のところ、これは小さなオブジェクトが一定時間の割り当てと一定時間の割り当て解除をグローバルに償却したことを意味します。(わーい。)

問題

問題は必要な型を生成することなので、最終的には Fusion を使用してそれらの型をインスタンス化する可能性があります。使用されているタイプは次のとおりです (短縮など):

template <size_t N>
struct data_block
{
    size_t mSize; // = N
    char mData[N];
};

template <typename T, size_t ElementsPerPage,
    template <typename> class Allocator = std::allocator >
class freelist { /* ... */ };

template <typename T>
class callocator; // allocator that uses malloc/free

は、最小値から最大値までの 2 のべき乗サイズのfreelistを管理します。data_blockだから私が欲しいのは:

static const size_t MinimumSmallSize = 4; // anything smaller gets rounded up
static const size_t MaximumSmallSize = 512; // anything bigger goes to the large allocator
static const size_t ElementsPerPage = 4096;

// mpl magic

これを生成するには:

typedef boost::mpl::vector<
    freelist<data_block<4>, ElementsPerPage, callocator>,
    freelist<data_block<8>, ElementsPerPage, callocator>
    // ...
    freelist<data_block<256>, ElementsPerPage, callocator>
    freelist<data_block<512>, ElementsPerPage, callocator>
    > free_list_collection;

明らかに、これを手動で行うこともできますが、より一般的で微調整可能なインターフェイスのために、それは避けたいと思います。コードでの Fusion ベクターの使用も、ハードコードされたメンバーよりも簡単です。

質問

これについて最善の方法がわかりません。これまで MPL を広範囲に使用したことはありません。何か案は?範囲を作ったり、remove_if2 の累乗ではないなど、いくつかの悪いアイデアがありましたが、確かにそれは最善ではありません。代わりに、毎回倍増して結果ベクトルにプッシュする再帰的なものでしょうか? どうすればいいのかわかりません。

4

1 に答える 1

4

これは私が思いついた最良の解決策であり、かなり簡単です。それには、メタテンプレートが必要です。これは、logプレイpowしたり試したりしたい人のために含まれています。

#include <boost/mpl/for_each.hpp>
#include <boost/mpl/range_c.hpp>
#include <boost/mpl/transform.hpp>
#include <boost/mpl/vector.hpp>
#include <iostream>

namespace bmpl = boost::mpl;

//// helpers
template <size_t N, size_t Base>
struct log
{
    static const size_t value = 1 + log<N / Base, Base>::value;
};

template <size_t Base>
struct log<1, Base>
{
    static const size_t value = 0;
};

template <size_t Base>
struct log<0, Base>
{
    static const size_t value = 0;
};

template <size_t N, size_t Power>
struct pow
{
    static const size_t value = N * pow<N, Power - 1>::value;
};

template <size_t N>
struct pow<N, 0>
{
    static const size_t value = 1;
};

//// types and constants
template <size_t N>
struct data_block
{
    size_t mSize; // = N
    char mData[N];
};

template <typename T, size_t ElementsPerPage,
    template <typename> class Allocator = std::allocator >
class freelist { /* ... */ };

template <typename T>
class callocator; // allocator that uses malloc/free

static const size_t MinimumSmallSize = 4;
static const size_t MaximumSmallSize = 512;
static const size_t ElementsPerPage = 4096;

//// type generation
// turn a power into a freelist
template <typename T>
struct make_freelist
{
    static const size_t DataSize = pow<2, T::value>::value;
    typedef data_block<DataSize> data_type;

    typedef freelist<data_type, ElementsPerPage, callocator> type;
};

// list of powers
typedef bmpl::range_c<size_t, log<MinimumSmallSize, 2>::value,
                        log<MaximumSmallSize, 2>::value + 1> size_range_powers;

// transform that list into freelists, into a vector
typedef bmpl::transform<size_range_powers, make_freelist<bmpl::_1>,
                            bmpl::back_inserter<bmpl::vector<> > >::type size_range;

//// testing
struct print_type
{
    template <typename T>
    void operator()(const T&) const
    {
        std::cout << typeid(T).name() << "\n";
    }
};

int main(void)
{
    bmpl::for_each<size_range>(print_type());
    std::cout << std::endl;
}

その核となるのはstruct、1つと2つだけtypedefです。このlogトリックは範囲のサイズを大幅に縮小し、powもちろん元に戻すだけlogです。私が望むように正確に動作し、それを簡単にする方法はありません。

そうは言っても、Boost.Poolを使用することにしたので、ソリューションは必要ありません(プールのサイズはコンパイル時ではなく動的であるため)。しかし、これはとても楽しかったです。

于 2010-01-14T20:26:44.393 に答える