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.