Is there a way to store argument's type from templated method at compile time?

Viewed 45

I got a weird one. I am trying to find a way to store every templated method's argument type to make it avaiable to class' users. A simple example may be:

template <typename... Ts>
struct Types {};

template <typename... Lhs, typename... Rhs>
constexpr auto operator+(Types<Lhs...>, Types<Rhs...>) {
    return Types<Lhs..., Rhs...>{};
}

class User {
public:

    template <typename Event, typename... New, typename... Old>
    void run(const Event& event) {
        // Something along the lines of:
        types_<New...> = types_<Old...> + Types<Event>{};

        /* Do stuff.. */
    }


private:

    template <typename... T>
    static constexpr Types<T...> types_{};

};

I'm trying to store every Event type from each generated run() method, basically I'm looking for the variadic template's arguments <T...> form the types_ attribute. Is this even allowed by the language?

P.S. The Types struct is just a failed attempt to hopefully makes the intent of the question clear, any other solution would be more than welcome.

1 Answers

This reminded me of TypeList template structure which has Head and Tail elements

#include <iostream>
#include <typeinfo>    

// List declaration
template <typename... Types>
struct TypeList;

//default specialization
template <typename H, typename... T>
struct TypeList<H, T...>
{
    using Head = H;
    using Tail = TypeList<T...>;
    static const int Length = 1 + sizeof...(T);   
};

//empty list specialization
template<>
struct TypeList<> 
{
    static const int Length = 0;
};

// add element to the top
template<typename H, typename TL>
struct Cons;

template<typename H, typename... Types>
struct Cons<H, TypeList<Types...>>
{
    using type = TypeList<H, Types...>;
};

// concat two TypeLists
template<typename TL1, typename TL2>
struct Concat;

template<typename... Ts1, typename... Ts2>
struct Concat<TypeList<Ts1...>, TypeList<Ts2...>>
{
    using type = TypeList<Ts1..., Ts2...>;
};

// print TypeList
template<typename TL>
void printTypeList(std::ostream& os)
{
    os << typeid(typename TL::Head).name() << '\n';
    printTypeList<typename TL::Tail>(os);
}

template<>
void printTypeList<TypeList<>>(std::ostream& os) {}

int main()
{
    using TL1 = TypeList<double, float, int, char>;
    using TL2 = TypeList<bool, int, bool>;
    using TL3 = Cons<int, TL2>::type;
    using TL4 = Concat<TL1, TL3>::type;

    printTypeList<TL4>(std::cout);
    return 0;
}

UPD I tried to impliment TypeList strategy to your problem but the best I can do to put all types at run-time into std::string...

#include <iostream>
#include <typeinfo>
#include <string>

// List declaration
template <typename... Types>
struct TypeList;

//default specialization
template <typename H, typename... T>
struct TypeList<H, T...>
{
    using Head = H;
    using Tail = TypeList<T...>;
};

//empty list specialization
template<>
struct TypeList<> {};

template<typename TL>
void save_type_to_string(std::string& s)
{
    s.append(typeid(typename TL::Head).name()).append("\n");
    save_type_to_string<typename TL::Tail>(s);
}

template<>
void save_type_to_string<TypeList<>>(std::string& s) {}

class User {
public:

    template <typename... Event>
    void run(const Event... event) {  
        save_type_to_string<TypeList<Event...>>(types);
    }
    void print()
    {
        std::cout << types;
    }
private:     
    std::string types;
};



int main()
{
    User u;
    u.run((int) 10, (long) 0.1, (bool) true);

    u.run((int)10, (bool)true);
    u.print();
    return 0;
}
Related