Reputation: 103
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:
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.
Upvotes: 0
Views: 419
Reputation: 13521
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".
template <typename T>
struct has_size;
template <bool speed, bool accel, typename T>
struct has_size< Particle<true, speed, accel, T> > : public true_type
{ };
template <bool speed, bool accel, typename T>
struct has_size< Particle<false, speed, accel, T> > : public false_type
{ };
// given a particle typedef SomeParticle_t
has_size<SomeParticle_T>::value
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:
// Using templates
template <typename particle>
typename boost::enable_if< has_accel<particle>, typename particle::speed_type >::type
calculate_future_speed(const particle& t)
{ /* do your acceleration calculation */ }
template <typename particle>
typename boost::enable_if< boost::and_< has_speed<particle>,
boost::not_< has_accel<particle> >,
typename particle::speed_type >::type
calculate_future_speed(const particle& t)
{ /* no acceleration, so no calculation! just return the speed*/ }
template <typename particle>
typename boost::enable_if< boost::not_< has_speed<particle>,
typename particle::speed_type >::type
calculate_future_speed(const particle& t)
{ return 0; /* Has no speed, and no acceleration */ }
Upvotes: 3
Reputation:
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.
Upvotes: 2
Reputation: 1658
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
Upvotes: 1