Reputation: 13886
I would like to create a Vertex class, and would like to genericise it by being able to create a 32 bit float and 64 bit double version, and maybe int version. I would like to do this:
template <typename P>
struct Vertex
{
if (typeid(P) == typeid(float))
{
vec3 position;
vec3 normal;
vec2 texcoords;
}
else if (typeid(P) == typeid(double))
{
dvec3 position; // This is a double vector
dvec3 normal;
dvec2 texcoords;
}
else if (typeid(P) == typeid(int))
{
ivec3 position; // This is an integer vector
ivec3 normal;
ivec2 texcoords;
}
};
I don't think if statements aren't evaluated at compile-time, so this is just an illustration of what I would like to do. Is there any way to do this? Or do I have to specialise each type, or just rewrite all the different versions?
Upvotes: 11
Views: 2409
Reputation: 96013
OP has mentioned in comments that they're using GML.
GLM vectors are actually templates, so there is no need for intricate solutions:
template <typename P>
struct Vertex
{
tvec3<P> position;
tvec3<P> normal;
tvec2<P> texcoords;
};
Upvotes: 2
Reputation: 506837
Here's an alternative
template<typename T>
struct Type { typedef T type; };
template<typename T>
inline constexpr Type<T> type{};
template <typename P>
struct Vertex
{
static constexpr auto D3 = []{
if constexpr(std::is_same_v<P,float>)
return type<vec3>;
else if constexpr(std::is_same_v<P,double>)
return type<dvec3>;
else if constexpr(std::is_same_v<P,int>)
return type<ivec3>;
}();
static constexpr auto D2 = []{
if constexpr(std::is_same_v<P,float>)
return type<vec2>;
else if constexpr(std::is_same_v<P,double>)
return type<dvec2>;
else if constexpr(std::is_same_v<P,int>)
return type<ivec2>;
}();
typename decltype(D3)::type position;
typename decltype(D3)::type normal;
typename decltype(D2)::type texcoords;
};
With a little bit of more effort on the Type
template, you can improve the code of the lambdas quite a bit (perhaps you've seen boost hana, which follows this idea aswell)
template<typename T>
struct Type {
typedef T type;
friend constexpr bool operator==(Type, Type) {
return true;
}
};
template<typename T1, typename T2>
constexpr bool operator==(Type<T1>, Type<T2>) {
return false;
}
template<typename T>
inline constexpr Type<T> type{};
Now it will not need std::is_same_v
anymore
template <typename P>
struct Vertex
{
static constexpr auto D3 = [](auto t) {
if constexpr(t == type<float>)
return type<vec3>;
else if constexpr(t == type<double>)
return type<dvec3>;
else if constexpr(t == type<int>)
return type<ivec3>;
}(type<P>);
static constexpr auto D2 = [](auto t) {
if constexpr(t == type<float>)
return type<vec2>;
else if constexpr(t == type<double>)
return type<dvec2>;
else if constexpr(t == type<int>)
return type<ivec2>;
}(type<P>);
typename decltype(D3)::type position;
typename decltype(D3)::type normal;
typename decltype(D2)::type texcoords;
};
The ugly decltype
writing could be fixed by using auto
aswell
template<auto &t>
using type_of = typename std::remove_reference_t<decltype(t)>::type;
So you can write
type_of<D3> position;
type_of<D3> normal;
type_of<D2> texcoords;
Upvotes: 6
Reputation: 13886
This is just an addition to Justin's solution. It's actually his anyway. The idea to use std::conditional was François Andrieux's idea given in the comments. Something along these lines:
template <typename P>
struct vertex
{
using vec3_t = std::conditional_t <std::is_same_v<float, P>,
/*IF FLOAT*/ vec3,
/*OTHERWISE*/ std::conditional_t <is_same_v<double, P>,
/*IF DOUBLE*/ dvec3,
/*IF INT*/ ivec3>>;
vec3_t position;
vec3_t normal;
//vec2_t texcoords; WILL HAVE TO TYPEDEF THIS AS WELL.
};
Johannes Schaub has given two different solutions in the comments based on constexpr and decltype also.
Upvotes: 0
Reputation: 25277
You may want to have some sort of vec3
and vec2
selector type. If there are already templated versions of vec3
and vec2
, just use them. Otherwise, you can use template specialization:
template <typename T>
struct vec_selector {};
template <>
struct vec_selector<float> {
using vec3_type = vec3;
using vec2_type = vec2;
};
template <>
struct vec_selector<double> {
using vec3_type = dvec3;
using vec2_type = dvec2;
};
template <>
struct vec_selector<int> {
using vec3_type = ivec3;
using vec2_type = ivec2;
};
template <typename P>
using vec3_select_t = typename vec_selector<P>::vec3_type;
template <typename P>
using vec2_select_t = typename vec_selector<P>::vec2_type;
Then you can simply write:
template <typename P>
struct Vertex
{
vec3_select_t<P> position;
vec3_select_t<P> normal;
vec2_select_t<P> texcoords;
};
You could also just specialize the Vertex
template, but it seems likely that it would be useful to have vec3_select_t
elsewhere, and you'd have to repeat any member functions on Vertex
(or else make the code more complicated)
Upvotes: 21