/// //////////////////////////////////////////////////////////////////////// ///
/// //////////////////////////////////////////////////////////////////////// ///
/// //////////////////////////////////////////////////////////////////////// ///
#include <cxxabi.h>
#include <string>
#include <typeinfo>
#include <cassert>
#include <cstdlib>
#include <iostream>

const std::string demangle(const char* const mangledTypeName) {
   int resultStatus = -4;
   const char* const result = abi::__cxa_demangle( mangledTypeName, 0, 0, &resultStatus );
   std::string demangledTypeName;
   const bool noError = (0 == resultStatus);
   const bool demangledSuccess = (noError && result != 0);
   assert(demangledSuccess);
   demangledTypeName = (demangledSuccess ? result : "Unknown type.");
   free(const_cast<char*>(result));
   return demangledTypeName;
}

const std::string demangle(const std::string& mangledTypeName) {
   return demangle(mangledTypeName.c_str());
}

const std::string demangledNameOfType(const std::type_info& type_id) {
   return demangle(type_id.name());
}

#define DEMANGLED_NAME_OF_TYPE(TypeOrValue) (demangledNameOfType(typeid(TypeOrValue)))
#define PRINT_NAME_OF_TYPE(TypeOrValue) {std::cout<<DEMANGLED_NAME_OF_TYPE(TypeOrValue)<<std::endl;}
/// //////////////////////////////////////////////////////////////////////// ///
/// //////////////////////////////////////////////////////////////////////// ///
/// //////////////////////////////////////////////////////////////////////// ///

#include <string>
#include <vector>
#include <iostream>

// Store parameter pack
template<typename... T>
struct pack
{
    static const unsigned int size = sizeof...(T);
};

// Get i-th element of parameter pack
template<int n, typename F, typename... T>
struct element_at : public element_at<n-1, T...>
{
};

template<typename F, typename... T>
struct element_at<0, F, T...>
{
    typedef F type;
};

// Get i-th element of pack
template<int n, typename P>
struct element
{
};

template<int n, typename... T>
struct element<n, pack<T...>>
{
    typedef typename element_at<n, T...>::type type;
};

// Concat at left
template<typename a, typename b>
struct tuple_concat_left
{
};

template<typename a, typename... b>
struct tuple_concat_left<a, pack<b...>>
{
    typedef pack<a, b...> type;
};

// Concat 2 tuples
template<typename a, typename b, int n = 0, bool ok = (n < a::size)>
struct tuple_concat : public tuple_concat<a, b, n+1>
{
    typedef typename tuple_concat_left<
        typename element<n, a>::type,
        typename tuple_concat<a, b, n+1>::type
    >::type type;
};

template<typename a, typename b, int n>
struct tuple_concat<a, b, n, false>
{
    typedef b type;
};

// Test
typedef pack<bool, int, char, float, std::string> MyPack;
typedef pack<double, std::vector<std::string>> PackToAppend;

int main()
{
    typedef tuple_concat_left<void, MyPack>::type concat1;
    typedef element<2, concat1>::type elem_at_2;
    typedef tuple_concat<MyPack, PackToAppend>::type concat2;
    typedef tuple_concat<PackToAppend, MyPack>::type concat3;
    
    
    std::cout << "MyPack: "; PRINT_NAME_OF_TYPE(MyPack);
    std::cout << "concat1: "; PRINT_NAME_OF_TYPE(concat1);
    std::cout << "elem_at_2: "; PRINT_NAME_OF_TYPE(elem_at_2);
    std::cout << "concat2: "; PRINT_NAME_OF_TYPE(concat2);
    std::cout << "concat3: "; PRINT_NAME_OF_TYPE(concat3);
}


















