Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I use C++ template argument to decide which type of member is in a class

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?

like image 312
Zebrafish Avatar asked Oct 25 '17 18:10

Zebrafish


People also ask

How would you define member function of template class?

Member functions of class templates (C++ only)You may define a template member function outside of its class template definition. The overloaded addition operator has been defined outside of class X . The statement a + 'z' is equivalent to a. operator+('z') .

Which data member can be declared in a class template?

Each class template instantiation has its own copy of any static data members. The static declaration can be of template argument type or of any defined type. The statement template T K::x defines the static member of class K , while the statement in the main() function assigns a value to the data member for K <int> .

Can a template method be a member of a non template class?

It's not the method that is templated, it's the class. You can have a templated method in a non-templated class, a non-templated method in a templated class (your case) and a templated method in a templated class, and of course a non-templated method in a non-templated class. Save this answer.

What are template arguments enlist types of template arguments?

A template argument for a template template parameter is the name of a class template. When the compiler tries to find a template to match the template template argument, it only considers primary class templates. (A primary template is the template that is being specialized.)


2 Answers

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;
like image 71
Johannes Schaub - litb Avatar answered Oct 03 '22 23:10

Johannes Schaub - litb


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;
};
like image 33
HolyBlackCat Avatar answered Oct 03 '22 23:10

HolyBlackCat