有人可以帮助我创建一个变量容器使用Boost::MPL
Could someone help me create a variable container using 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();
...
...
}
我想通过add函数推导出容器,以便使用系统自动更新类型列表。我想我知道如何用模板函数生成类型列表,但不知道如何在我需要它的地方得到它,或者在编译的什么时候它是完整的。
如果不是这样,则某些系统使用两个类型列表,然后在内部编写更新函数来遍历所有列表,将它们相互配对。
我读了一些boost MPL的书,也读了Andrei的书好几遍。但是,我似乎陷入了它是如何工作的东西,并没有真正将其转化为我如何使用它。我希望他们在MPL的书中有一个关于真实世界的例子的章节。
我已经能够让游戏引擎的所有部分与渲染,物理,碰撞(我将检测与反应分开),输入,网络,声音等进行交互。都是一般的方式。现在我只需要用一般的方式来表示所有的东西。在所有泛型工作之后,仅仅为了在容器中保存一些东西而要求继承是愚蠢的,我不想手工编写每个集合的可能性,因为这是泛型编程的巨大好处之一。
我看到jhalf已经表明他/她使用MPL做了类似的事情,但没有深入到足够的细节让我弄清楚。如果有人知道一个实际使用的例子,或者在哪里我可以得到更多关于使用MPL的信息,我将不胜感激。
再次感谢!
boost MPL和boost Fusion似乎都做了我想要的,但似乎很少有好的现实生活中的例子。MPL的文档只不过是这个模板做了这些,希望你能理解其中的含义。"这是一个例子,但它只是冰山一角!"
一个典型的boost MPL示例是has_xxx。他们在示例中使用XXX和XXX,这使得很难看出XXX(所需文本)和Test或CheckType或任何其他可区分的用户类型可以用来代替XXX的区别。此外,没有提到这些都不在名称空间中。现在我知道为什么斯科特·迈耶斯把这个和《惊魂记》里的淋浴戏相比了。
这真的很遗憾,因为我所得到的编译和理解的东西确实很有用,但很难理解,如果我是在一个发布产品上,我绝不会花这么多精力。
如果有人知道真实世界的例子或更好的参考,解释,或教程,我将不胜感激。
下面是更多代码:
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);
那么我所需要做的就是调用collision与所有活动的可碰撞对象碰撞所有活动的和被动的对象
我没有使用std::function,因为函数名的添加使代码更清晰。但也许这只是传统思维。
如果我理解正确的话,你的问题是:
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
的契约。这里的契约是类型擦除的编译时方面。
我不打算演示如何实现类型擦除,因为在2010年的Boostcon上有一个很好的关于这个主题的演讲。(你可以观看演示和/或通过链接获取幻灯片)。或者我(或其他人)可以在评论中做。
最后需要说明的是,类型擦除的实现(通常)使用动态多态性。我提到这一点是因为我注意到您考虑将类型列表用作存储为manager
成员的运行时对象。这闻起来像穷人的反射,真的,穷人的动态多态性。所以请不要这样做。如果您指的是MPL计算结果中的类型列表,则忽略节点。
这是不完整的,我没有得到我想要的一切,但现在已经足够好了。我正在输入整个解决方案,以防它对其他人有所帮助。
#include <boostmplvector.hpp>
#include <boostmplfold.hpp>
#include <boostmplfor_each.hpp>
#include <boostmplinherit.hpp>
#include <boostmplinherit_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容器,它包含了应该保存的类型。
我认为这为扩展提供了一个很好的起点,但涵盖了元编程的大部分内容。
- 如何创建一个CMake变量,除非显式重写,否则使用默认值
- 用C++中的一个变量定义一个常量
- 有没有什么方法可以使用一个函数中定义的常量变量,也可以由c++中同一程序中的其他函数使用
- 为什么我不能在一个类的不同行中声明和定义成员变量?
- 如何从另一个文件继承私有成员变量和公共函数
- 在 Windows 上,是否可以让 dll 在不使用 PATH 环境变量的情况下在另一个文件夹中查找依赖项?
- 全局变量 多读取器 一个写入器多线程安全?
- 如何声明一个可以在整个程序中使用的全局 2d 3d 4d .. 数组(堆版本)变量?
- 我可以创建一个包含两个变量的 for 循环,但时间复杂度仍然为 O(n) 吗?
- 一个变量的输入值也会保存到另一个变量中
- 从另一个 cpp 文件更改结构内、映射键内的变量
- 在另一个函数 (c++) 中调用变量
- 将双精度变量设置为另一个变量的值
- C++线程安全:如果只有一个线程可以写入非原子变量,但多个线程从中读取. 会遇到问题吗?
- 为什么从另一个构造函数内部调用C++构造函数不修改类变量?
- 如果我注释掉换行符,为什么'string'会成为一个不合格的变量
- 在 C++ 中声明 const 对象需要用户定义的默认构造函数.如果我有一个可变成员变量,为什么不呢?
- 我有一个类,它创建了另一个类的实例.如何将变量通过第一个类传递到第二个类的实例化中?
- C++使用cin给变量一个非整数的值
- 怎么可能有两个同名的变量——一个是全局变量,另一个是局部变量