Может ли кто-нибудь помочь мне создать контейнер переменных, используя Boost::MPL?

Я создал физическую систему, которая обрабатывает любой объект столкновения с любым объектом столкновения следующим образом:

namespace Collision
{
    template <typename T, typename U>
    inline void Check(T& t, U& u)
    {
        if(u.CheckCollision(t.GetCollider()))
        {
            u.HitBy(t);
            t.Hit(u);
        }
    }
}

и есть несколько других вспомогательных объектов, чтобы упростить их использование, но суть в том, что есть динамические объекты, которые необходимо проверить на статические объекты и другие динамические объекты, но статические объекты проверять не нужно.

Я хотел бы что-то вроде этого:

void func()
{
    PhysicsWorld world;
    shared_ptr<CSphere> ballPhysics(new CSphere(0,0,ballSprite->Width()));
    BallCommand ballBehavior;
    CBounds bounds(0, 0, 640, 480);
    CBox obstacle(200, 150, 10, 10);

    Collision::Collidable<CBounds> boundC(bounds);
    Collision::Collidable<std::shared_ptr<CSphere>, BallCommand&> ballC(ballPhysics, ballBehavior);
    Collision::Collidable<CBox> obstC(obstacle);

    world.addStatic(boundC);
    world.addDynamic(ballC);
    world.addStatic(obstC);
    ...
    ...
    world.Update();
    ...
    ...
}

Я бы хотел выводить контейнеры с помощью функций добавления, поэтому использование системы автоматически обновляет списки типов. Я думаю, что понимаю, как создать список типов с помощью функции шаблона, но не то, как получить его там, где он мне нужен, или на каком этапе компиляции он завершен.

Если нет, то какая-то система использует два списка типов, а затем внутренне записывает функцию обновления для перебора всех списков, объединяя их друг с другом.

Я прочитал несколько книг по ускоренному MPL и несколько раз читал книгу Андрея. Но я, кажется, увлекся тем, как все это работает, и не перевожу это на вопрос, как мне это использовать. Я бы хотел, чтобы у них был еще один раздел о реальных примерах в книге MPL.

Мне удалось заставить все части игрового движка взаимодействовать с рендерингом, физикой, столкновениями (я отделяю обнаружение от реакции), вводом, сетью, звуком и т. Д. Все в общих чертах. Теперь мне просто нужно обобщить все вещи. После всей этой общей работы было бы глупо требовать наследования, чтобы я мог что-то хранить в контейнере, и я не хочу передавать код каждой возможности сбора, поскольку это является одним из больших преимуществ универсального программирования.

Я видел, что Джальф указал, что он / она использовал MPL для того, чтобы сделать нечто подобное, но не вдавался в подробности, чтобы я мог это понять. Если кто-нибудь знает пример практического использования или где я могу получить больше информации об использовании MPL, я был бы благодарен.

Еще раз спасибо!

Обновить

Boost MPL и Boost Fusion, кажется, делают то, что я хочу, но, похоже, очень мало хороших примеров из реальной жизни обеих библиотек. Документация по MPL немного больше, чем этот шаблон, и удачи в понимании последствий этого. Fusion немного лучше с "Вот пример, но это только верхушка айсберга!"

Типичным примером повышения MPL является has_xxx. В этом примере они используют XXX и xxx, что затрудняет понимание разницы, где вместо xxx можно использовать XXX(требуемый текст) и Test или CheckType или любой более различимый тип пользователя. Плюс нет упоминания о том, что ничего из этого нет в пространстве имен. Теперь я знаю, почему Скотт Мейерс сравнил это с душевой в Психо.

Это действительно позор, потому что то, что я получил, чтобы скомпилировать и понять, действительно полезно, но так сложно понять, что я никогда не потратил бы столько усилий, если бы работал над доставкой продукта.

Если кто-то знает примеры из реального мира или лучшие ссылки, объяснения или учебник, я был бы благодарен.

Обновить

Вот еще код:

template <typename T, typename V = VictimEffect, typename M = MenaceEffect>
class Collidable
{
    T m_Collider;
    V m_HitBy;
    M m_Hit;

public:
    Collidable(T collide, V victim, M menace) : m_Collider(collide), m_HitBy(victim),         m_Hit(menace) {;}
    Collidable(T collide) : m_Collider(collide) {;}
    Collidable(T collide, V victim) : m_Collider(collide), m_HitBy(victim) {;}

    T& GetCollider()
    {
        return m_Collider;
    }

    template <typename V>
    void HitBy(V& menace)
    {
        m_HitBy.HitBy(menace.GetCollider());
    }

    template <typename V>
    void Hit(V& victim)
    {
        m_Hit.Hit(victim.GetCollider());
    }

    template <typename V>
    bool CheckCollision(V& menace)
    {
        return m_Collider.CheckCollision(menace);
    }
};

Затем, чтобы использовать это, я делаю это

    Collidable<Boundary, BallCommand> boundC(boundary, ballBehavior);
    Collidable<CollisionBox> ballC(circle);

Тогда все, что мне нужно, это вызвать столкновение со всеми моими активными объектами, способными к столкновению со всеми моими активными и пассивными объектами.

Я не использую std::function, потому что добавление имен функций делает код более понятным для меня. Но, возможно, это просто устаревшее мышление.

2 ответа

Решение

Это не полный И я не получил все, что я хочу, но это достаточно хорошо на данный момент. Я ввожу все решение на случай, если оно поможет другим.

#include <boost\mpl\vector.hpp>
#include <boost\mpl\fold.hpp>
#include <boost\mpl\for_each.hpp>
#include <boost\mpl\inherit.hpp>
#include <boost\mpl\inherit_linearly.hpp>
#include <iostream>
#include <vector>

using namespace boost::mpl::placeholders;

typedef boost::mpl::vector<short, long, char, int> member_types;

template <typename T>
struct wrap
{
    std::vector<T> value;
};

typedef boost::mpl::inherit_linearly<member_types, boost::mpl::inherit<wrap<_2>, _1> >::type Generate;

class print
{
    Generate generated;

public:
    template <typename T>
    void operator()(T)
    {
        std::cout << *static_cast<wrap<T>&>(generated).value.begin() << std::endl;
    }

    template <typename T>
    void Add(T const& t)
    {
        static_cast<wrap<T>&>(generated).value.push_back(t);
    }
};

void main()
{
    print p;

    short s = 5;
    p.Add(s);
    long l = 555;
    p.Add(l);
    char c = 'c';
    p.Add(c);
    int i = 55;
    p.Add(i);

    boost::mpl::for_each<member_types>(p);
}

Это не последний объект, который мне нужен, но теперь у меня есть все, чтобы сделать то, что я хочу.

Обновить

И наконец я понял это.

template <typename TL>
class print
{
    template <typename T>
    struct wrap
    {
        std::vector<T> value;
    };

    typedef typename boost::mpl::inherit_linearly<TL, boost::mpl::inherit<wrap<_2>, _1> >::type Generate;
    Generate generated;

public:
    void Print()
    {
        boost::mpl::for_each<TL>(*this);
    }

    template <typename T>
    void operator()(T)
    {
        std::cout << *static_cast<wrap<T>&>(generated).value.begin() << std::endl;
    }

    template <typename T>
    void Add(T const& t)
    {
        static_cast<wrap<T>&>(generated).value.push_back(t);
    }
};

Здесь TL является контейнером boost::mpl того, какие типы должны храниться.

Я думаю, что это хорошая отправная точка для расширения, но охватывает большую часть частей метапрограммирования.

Я надеюсь, что это помогает другим.

Если я правильно понимаю, ваша проблема:

class manager {
public:
    template<typename T>
    void add(T t);

private:
    /* ??? */ data;
    /* other members? */
};

manager m;
some_type1 s1;
some_type2 s2;
m.add(s1);
m.add(s2);
/* m should hold its copies of s1 and s2 */

где some_type1 и some_type2 не связаны, и вы не хотите изменять их, чтобы использовать динамический полиморфизм.

Я не думаю, что MPL или Fusion будут делать то, что вы хотите с этой формой. Если ваша проблема в том, какой контейнер использовать в качестве члена PhysicsWorldтогда никакое количество вычислений времени компиляции не поможет: тип члена определяется во время создания экземпляра, т.е. manager m;,

Вы могли бы переписать менеджер несколько метапрограммирующим способом, чтобы использовать его следующим образом:

typedef manager<> m0_type;
typedef typename result_of::add<m0_type, some_type1>::type m1_type;
typedef typename result_of::add<m1_type, some_type2>::type final_type;
/* compile-time computations are over: time to instantiate */
final_type m;
/* final_type::data could be a tuple<some_type1, some_type2> for instance */
m.add(s1); m.add(s2);

Это действительно то, с чем может помочь MPL+Fusion. Тем не менее, это все еще остается довольно привязанным в мире времени компиляции: можете ли вы представить template<typename Iter> void insert(Iter first, Iter last) просто так вы можете скопировать содержимое контейнера в менеджер?

Позвольте мне предположить, что ваши требования таковы, что на самом деле manager должен быть использован гораздо более динамично, как в моей первоначальной формулировке вашего вопроса. (Я не думаю, что это довольно большая часть воображения для PhysicsWorld). Существует альтернатива, которая, на мой взгляд, является более подходящей, гораздо менее многословной и более подходящей: стирание типов. (Название техники может быть немного неудачным и может вводить в заблуждение в первый раз.)

Хорошим примером стирания типа является std::function:

std::function<void()> func;
func = &some_func; /* this just looks like that internally std::function stores a void(*)() */
func = some_type(); /* but here we're storing a some_type! */

Стирание типов - это метод, позволяющий связать время компиляции со временем выполнения: в обоих приведенных выше назначениях аргументы являются несвязанными типами (один из которых не является классом, поэтому даже не является полиморфным во время выполнения в удаленном режиме), но std:: function обрабатывает оба, если они выполнить договор, что они могут быть использованы как f() (где f является экземпляром соответствующего типа) и что выражение имеет тип (преобразуемый в) void, Контракт здесь - это аспект компиляции типа во время компиляции.

Я не собираюсь демонстрировать, как реализовать стирание типов, потому что на эту тему есть отличная презентация Boostcon 2010. (Вы можете посмотреть презентацию и / или получить слайды по ссылке). Или я (или кто-то еще) могу сделать это в комментариях.

В заключение отметим, что реализация стирания типов (как правило) использует динамический полиморфизм. Я упоминаю об этом, потому что заметил, что вы рассматривали использование списков типов в качестве объекта времени выполнения, хранящегося как manager член. Это пахнет как отражение бедного человека, и действительно, динамический полиморфизм бедного человека. Так что не делай этого, пожалуйста. Если вы имели в виду списки типов как результат вычисления MPL, игнорируйте узел.

Другие вопросы по тегам