Boost.MPL и генерация списка типов

Фон

Это для менеджера памяти в игровом движке. У меня есть freelist, и я хотел бы иметь список времени компиляции, если они. (например, вектор MPL или Fusion). 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

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_if это не степень 2 и т. д., но, конечно, это не лучший вариант. Может быть, вместо этого что-то рекурсивное, которое каждый раз удваивается, подталкивая мой результирующий вектор? Я не уверен, как это сделать.


person GManNickG    schedule 13.01.2010    source источник
comment
Рассматривали ли вы возможность использования библиотеки Boost Pool Library для такого управления памятью? ;)   -  person e.tadeu    schedule 14.01.2010
comment
Кстати, не злоупотребляйте метапрограммированием шаблонов... с TMP все происходит за кулисами, и ваш код вскоре может стать неуправляемым, трудно компилируемым и полным черной магии. Предпочитайте использовать явную генерацию кода для метапрограммирования, это намного понятнее и отлаживаемее.   -  person e.tadeu    schedule 14.01.2010
comment
По первому вопросу да. У меня есть этот вопрос для работы, а затем собственно пулы. Я сравниваю их (мой фрилист и пулы Boost) и склоняюсь к пулам. Моя реализация была на удивление похожа, но, так сказать, не поставляется с бесплатными обновлениями. :) (и было проверено более тщательно) Что касается вашего второго утверждения, я согласен, что использование неправильного инструмента для работы создает беспорядок, но я создал небольшое почти трехстрочное решение проблемы, и оно работает довольно хорошо. Я опубликую это через несколько часов, так как мне нужно сначала посетить урок.   -  person GManNickG    schedule 14.01.2010
comment
В целях обучения я бы также предложил проверить Распределитель малых объектов Александреску. Использование простое (просто наследуйте от Loki::SmallObject, вы можете уточнить некоторые параметры шаблона) и использует аналогичную систему из нескольких списков (по размеру). Проверьте заголовок в loki- lib.cvs.sourceforge.net/loki-lib/loki/include/loki/ и попробуйте прочитать объяснения в книге :)   -  person Matthieu M.    schedule 17.01.2010
comment
@e.tadeu, хотя в целом вы правы, если вы посмотрите на его ответ, вы увидите, что он достиг хорошего равновесия. конечно не прошел точку неремонтопригодности.   -  person v.oddou    schedule 19.12.2014


Ответы (1)


Это лучшее решение, которое я придумал, и оно довольно простое. Для этого требуется меташаблон 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 и две typedef. Трюк с log значительно уменьшил размер диапазона, а pow, конечно же, просто отменяет log. Работает именно так, как я хотел бы, и я не вижу способа сделать его проще.

Тем не менее, я решил использовать Boost.Pool, поэтому мне не понадобится мое решение (поскольку размеры их пулов являются динамическими, а не во время компиляции). Но это было весело.

person GManNickG    schedule 14.01.2010
comment
Привет GMan, спасибо, что поделились кодом. Вы открываете исходный код, написанный для глобального распределителя? Было бы здорово, если бы вы могли. Я пытаюсь создать полудинамический распределитель и создал его раннюю версию: github.com/vietlq/ галлок. Надеюсь, вы тоже сможете открыть свой код. Спасибо. - person Viet; 08.04.2010
comment
@Viet: я мог бы предоставить конкретную функциональность, но я не могу дать вам весь код. :\ - person GManNickG; 09.04.2010
comment
Тогда это должно быть хорошо. По крайней мере, у меня есть идея. Спасибо. - person Viet; 09.04.2010