Home > Software engineering >  How can you redefine a POD type such that it is considered a distinct type while maintaining the sem
How can you redefine a POD type such that it is considered a distinct type while maintaining the sem

Time:02-13

Imagine the following scenario where we have a pod container type that applies to many different scenarios:

struct vec2 {
  float x, y;
};

Some of these scenarios might be storing a velocity, a position, an acceleration, etc. Now suppose we want to be able to handle all these scenarios independently while putting the type system to work and also maintaining the semantics of vec2, i.e. velocity.x, acceleration.y, etc.:

struct Simulation {
    template<typename T>
    static void handle() { fprintf( stdout, "Generic handler\n" ); }
};

template<>
void Simulation::handle<vec2>() { fprintf( stdout, "generic vec2 handler\n" ); }

template<>
void Simulation::handle<position>() { fprintf( stdout, "position handler\n" ); }

template<>
void Simulation::handle<velocity>() { fprintf( stdout, "velocity handler\n" ); }

template<>
void Simulation::handle<acceleration>() { fprintf( stdout, "acceleration handler\n" ); }

In the above case using either typedef or using will not do the job as they do not affect the typeid of the alias as per the standard, so one cannot do:

using position = vec2;

The next attempt might be to try and inherit from the pod, i.e.:

struct position : public vec2 {}

however this has the disadvantage that new and delete are now broken as there is no virtual destructor on the pod type, and also the qualification of the position being a pod has been lost due to the inheritance.

Then it could be argued that simply copying and pasting the code for vec2 and renaming it to the desired type would satisfy all the requirement, but there are also a couple of issues with this:

  1. Copy-Pasting code is bad*
  2. In the example above vec2 has no associated operators or anything like that, but any useful vec2 class would most certainly have this and copying and pasting while renaming the data structure in this case would be pretty error prone especially if a fix is required in one of the operators

The vec2 could also be encapsulated in the new type:

struct position {
  vec2 pos;
};

but this solution breaks the requirement for maintaining the semantics.

Another thing to note is that we might not have control over the implementation of vec2 (i.e. in the case where a library like GLM is used).

I feel that templating should provide the necessary solution, but can't work out how it would work. So the question is what is the correct way of going about redefining a type, while maintaining the semantics of the type, but ending up with a different type id?

*-not in all scenarios, but this one definitely

CodePudding user response:

Something along these lines, perhaps.

template <typename Tag>
struct vec2 {
  float x, y;
};

using position = vec2<struct PositionTag>;
using velocity = vec2<struct VelocityTag>;

position and velocity would have similar behavior, but are distinct types.

CodePudding user response:

You can define vec2 as a template class and use an enum flag to represent its storage type.

enum class data_type {
  position, velocity, acceleration
};

template<data_type>
struct vec2 {
  float x, y;
};

vec2<data_type::position> p;
vec2<data_type::velocity> v;
vec2<data_type::acceleration> a;
  • Related