使用模板减少班级规模的合理方法?
给定:(代码减少到合理的最小值)
// MemberTypes
template
<
typename SPEEDTYPE = float,
typename SIZETYPE = float,
typename ACCELERATIONTYPE = float
>
struct ParticleMemberTypes
{
typedef typename SPEEDTYPE SpeedType;
typedef typename SIZETYPE SizeType;
typedef typename ACCELERATIONTYPE AccelerationType;
};
// Properties
template <class T>
class PSpeed
{
public:
inline const typename T::SpeedType& GetSpeed() const { return v; }
inline void SetSpeed(const typename T::SpeedType& V) { v = V; }
const static bool hasSpeed = true;
private:
typename T::SpeedType v;
};
template <class T>
class PSize
{
public:
inline const typename T::SizeType& GetSize() const { return v; }
inline void SetSize(const typename T::SizeType& V) { v = V; }
const static bool hasSize = true;
private:
typename T::SizeType v;
};
template <class T>
class PAcceleration
{
public:
inline const typename T::AccelerationType& GetAcceleration() const { return v; }
inline void SetAcceleration(const typename T::AccelerationType& V) { v = V; }
const static bool hasAcceleration = true;
private:
typename T::AccelerationType v;
};
// 空基和特化
(每个 EmptyBase 必须是不同的类型,以避免多次从同一基类继承)
template <typename P, typename T> struct EmptyBase {};
template <typename T> struct EmptyBase<PSpeed<T>, T>
{
const static bool hasSpeed = false;
};
template <typename T> struct EmptyBase<PSize<T>, T>
{
const static bool hasSize = false;
};
template <typename T> struct EmptyBase<PAcceleration<T>, T>
{
const static bool hasAcceleration = false;
};
// 基选择template
template <bool ENABLE, typename P, typename T> struct EnableBase;
template <typename P, typename T> struct EnableBase<true, P, T>
{
typedef P Type;
};
template <typename P, typename T> struct EnableBase<false, P, T>
{
typedef EmptyBase<P, T> Type;
};
// 粒子模板类
template
<
bool USE_SPEED = false,
bool USE_SIZE = false,
bool USE_ACCELERATION = false,
typename T = ParticleMemberTypes<>
>
struct Particle :
public EnableBase<USE_SPEED, PSpeed<T>, T>::Type,
public EnableBase<USE_SIZE, PSize<T>, T>::Type,
public EnableBase<USE_ACCELERATION, PAcceleration<T>, T>::Type
{
};
我们现在可以这样做:
using namespace std;
Particle<> p1;
Particle<true, true, true, ParticleMemberTypes<Vector3<double> > > p2;
cout << "p1: " << sizeof(p1) << endl;
cout << "p2: " << sizeof(p2) << endl;
输出:
p1: 2
p1: 32
所以这是我的问题:
- 这是自动减小类大小的合理方法吗?
- 如果我只继承两个属性,则 Particle 的大小为 1,除此之外,每增加一个 EmptyBase,大小就会增加 1,这是为什么呢?
- 有什么模式、习语等在这里有用吗?
计划是编写模板以根据存在的属性自动处理粒子。
我可能应该提到,我正在开发的这个粒子系统不是“实时”的,将处理大量的粒子,并且我将使用 C++ 配置每个渲染。 另外,这几乎是我第一次尝试使用模板。
编辑: 我选择模板方法基本上有两个原因:一是好奇心 - 只是为了了解模板并探索它们的用途。 第二个原因是速度。 鉴于我不需要在运行时更改任何内容,我想我可以使用模板来消除虚拟函数和未使用的类成员等的开销。
预期用途是创建无数个粒子,所有粒子都完全相同尽可能快地输入、处理和渲染它们,让代码运行起来。 :)
这个想法是拥有一个高度可配置的系统,我可以在其中插入自定义函子来处理粒子。 理想情况下,粒子的属性只有在实际使用时才会启用,但我还没有弄清楚这是否以及如何可能。
Given: (Code reduced to a sensible minimum)
// MemberTypes
template
<
typename SPEEDTYPE = float,
typename SIZETYPE = float,
typename ACCELERATIONTYPE = float
>
struct ParticleMemberTypes
{
typedef typename SPEEDTYPE SpeedType;
typedef typename SIZETYPE SizeType;
typedef typename ACCELERATIONTYPE AccelerationType;
};
// Properties
template <class T>
class PSpeed
{
public:
inline const typename T::SpeedType& GetSpeed() const { return v; }
inline void SetSpeed(const typename T::SpeedType& V) { v = V; }
const static bool hasSpeed = true;
private:
typename T::SpeedType v;
};
template <class T>
class PSize
{
public:
inline const typename T::SizeType& GetSize() const { return v; }
inline void SetSize(const typename T::SizeType& V) { v = V; }
const static bool hasSize = true;
private:
typename T::SizeType v;
};
template <class T>
class PAcceleration
{
public:
inline const typename T::AccelerationType& GetAcceleration() const { return v; }
inline void SetAcceleration(const typename T::AccelerationType& V) { v = V; }
const static bool hasAcceleration = true;
private:
typename T::AccelerationType v;
};
// Empty base and specializations
(It is necessary that each EmptyBase is a distinct type, to avoid inheriting from the same base class more than once)
template <typename P, typename T> struct EmptyBase {};
template <typename T> struct EmptyBase<PSpeed<T>, T>
{
const static bool hasSpeed = false;
};
template <typename T> struct EmptyBase<PSize<T>, T>
{
const static bool hasSize = false;
};
template <typename T> struct EmptyBase<PAcceleration<T>, T>
{
const static bool hasAcceleration = false;
};
// Base selection template
template <bool ENABLE, typename P, typename T> struct EnableBase;
template <typename P, typename T> struct EnableBase<true, P, T>
{
typedef P Type;
};
template <typename P, typename T> struct EnableBase<false, P, T>
{
typedef EmptyBase<P, T> Type;
};
// Particle template class
template
<
bool USE_SPEED = false,
bool USE_SIZE = false,
bool USE_ACCELERATION = false,
typename T = ParticleMemberTypes<>
>
struct Particle :
public EnableBase<USE_SPEED, PSpeed<T>, T>::Type,
public EnableBase<USE_SIZE, PSize<T>, T>::Type,
public EnableBase<USE_ACCELERATION, PAcceleration<T>, T>::Type
{
};
We can now do:
using namespace std;
Particle<> p1;
Particle<true, true, true, ParticleMemberTypes<Vector3<double> > > p2;
cout << "p1: " << sizeof(p1) << endl;
cout << "p2: " << sizeof(p2) << endl;
Output:
p1: 2
p1: 32
So here are my questions:
- Is this a reasonable approach to automatically reducing the size of a class?
- If I only inherit from two Properties the size of Particle is 1, beyond that the size increases by one for each additional EmptyBase, why is that?
- Are there any patterns, idioms, etc. that would be useful here?
The plan is to write templates to automate the processing of particles based on what properties are present.
I should probably mention that this particle system i'm working on is not "realtime", will be dealing with massive amounts of particles, and that I will be configuring each rendering from C++. Also, this is pretty much my first go at using templates.
EDIT:
There are basically two reasons why I've chosen the template approach: one is curiosity - simply to learn about templates and explore their use. The second reason is speed. Seing as I won't need to change anything at run-time, I figured I could use templates to remove the overhead of virtual functions and unused class members, etc.
The intended use is to create a bazillion particles, all of the exact same type, and process and render them, as fast as I can possibly make the code go. :)
The idea is to have a highly configurable system, where I can plug in custom functors to process the particles. Ideally the properties of a particle would be enabled only if they're actually used, but I haven't figured out if and how that's possible.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
嗯,首先,你似乎在滚动很多你自己的东西。 我会研究 Boost::MPL 替换,例如基本选择模板,并使用继承继承向量。
其次,您使用的是“const static bool hasSpeed = true;” 很多; 在我的编程中,我通常更喜欢 typedefed 特征,类似于 Boost::type_traits。 您可以使用它们来选择要在编译时运行的函数。 你可以避免这样做“EmptyBase”。
第三,您在这里所做的很多事情取决于您希望最终用途是什么; 您是否希望粒子是单独的、不能相互转换的类型? 如果你走这条路,几乎每个函数都将是一个模板,你可以使用 boost::enable_if 来禁用不适用于给定粒子的代码。 它可能会非常快(因为很多工作是在编译时发生的),但是您会遇到巨大的、难以阅读的错误语句(对于刚接触模板的人来说不太容易理解)。
另一种非模板路线是继承; 您可以定义粒子所需的一组虚函数,然后将它们分解为您继承的类。 从您的代码中尚不清楚您期望能够对粒子执行什么操作。 错误会更容易理解,但代码可能会更慢(更多工作转移到运行时,并且会使您的对象更大)
下面是一个代码示例来说明差异:
Hmm, first, you seem to be rolling a lot of your own stuff. I'd look into Boost::MPL to replace, say Base selection template, and to inherit over a vector with inherit.
Second, you are using "const static bool hasSpeed = true;" a lot; In my programing, I usually prefer a typedefed trait, similar to Boost::type_traits. You can use them to select functions to run at compile time. You could avoid doing that "EmptyBase".
Third, a lot of what you are doing here depends on what you want your end usage to be; do you want the particles to be separate, types that can't be converted to each other? If you go this route, pretty much every function will be a template, and you could use boost::enable_if to disable code that doesn't apply to a given particle. It will be potentially very fast (since a lot of work happens at compile time), but you'll have gigantic, hard to read error statements (not very accessible for someone new to templates).
Another, non template route would be inheritance; you would define a set of virtual functions that a particle needs, and then break those out into classes you inherit from. It's not quite clear from your code what you expect to be able to do with the Particle. The errors would be easier to understand, but the code potentially slower (more work shifted to runtime, and would make your objects bigger)
Here's a code sample to illustrate the differences:
您知道不同参数的模板类型是不同的吗? 也就是说,在您的代码中,p1 和 p2 具有不同的类型,因此不能存储在同一集合中、相互分配等。当然,您的应用程序可能不需要这种等价性。
You are aware that the types of templates with different parameters are different? That is, in your code p1 and p2 are of different types and so cannot be stored in the same collection, assigned to each other etc. Of course, your application may not require such equivalence.
一件事是,您在粒子的模板定义中使用了两次 USE_SIZE 。
您还可以考虑使用享元模式,具体取决于数据的重复性。
http://www.boost.org/ doc/libs/1_39_0/libs/flyweight/doc/tutorial/index.html
One thing, you use USE_SIZE twice in your template definition for Particle.
You might also look into using the flyweight pattern, depending on repetitiveness of the data.
http://www.boost.org/doc/libs/1_39_0/libs/flyweight/doc/tutorial/index.html