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

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

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, я был бы благодарен.

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

Обновление

повышение MPL и повышение 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);

Затем всеМне нужно вызвать callide со всеми моими активными объектами, которые могут быть сопоставлены со всеми моими активными и пассивными объектами.

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

Ответы [ 2 ]

3 голосов
/ 27 апреля 2011

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

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, игнорируйте узел.

1 голос
/ 02 мая 2011

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

#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 того, какие типы должны храниться.

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

Надеюсь, это поможет другим.

...