if constexpr with recursive parameter packs
The trick is to use index_sequence
.
Here is a C++14 working solution, improved using @MartinMorterol suggestion.
// -*- compile-command: "g++ -Wall -std=c++14 poub.cpp; ./a.out"; -*-
#include <iostream>
#include <string>
#include <tuple>
#include <type_traits>
template <typename... Ts>
struct temp
{
using TypeList = std::tuple<Ts...>;
constexpr static std::size_t _N = std::tuple_size<TypeList>::value;
void print_this() { _inner_print(std::make_index_sequence<_N>()); }
template <std::size_t... IDX>
void _inner_print(std::index_sequence<IDX...>)
{
auto dummy = {0, (_inner_print<IDX>(),0)...};
(void)dummy;
}
template <std::size_t IDX>
void _inner_print()
{
std::cout << "\nCall #" << IDX
<< " sizeof " << sizeof(std::get<IDX>(_mem));
}
TypeList _mem;
};
int main()
{
std::string name;
temp<int, double, char> t1;
t1.print_this();
}
which prints:
g++ -Wall -std=c++14 poub.cpp; ./a.out
Call #0 sizeof 4
Call #1 sizeof 8
Call #2 sizeof 1
My initial answer (using recursion)
// -*- compile-command: "g++ -std=c++14 poub.cpp; ./a.out"; -*-
#include <iostream>
#include <string>
#include <tuple>
#include <type_traits>
template <typename... Ts>
struct temp
{
using TypeList = std::tuple<Ts...>;
constexpr static std::size_t _N = std::tuple_size<TypeList>::value;
void print_this() { _inner_print(std::make_index_sequence<_N>()); }
template <std::size_t... IDX>
void _inner_print(std::index_sequence<IDX...>)
{
_inner_print(std::integral_constant<std::size_t, IDX>()...);
}
template <std::size_t HEAD_IDX, typename... TAIL>
void _inner_print(std::integral_constant<std::size_t, HEAD_IDX>, TAIL... tail)
{
std::cout << "\nCall #" << HEAD_IDX
<< " sizeof " << sizeof(std::get<HEAD_IDX>(_mem));
// whatever you want HERE ...
_inner_print(tail...);
}
void _inner_print(){};
TypeList _mem;
};
int main()
{
std::string name;
temp<int, double, char> t1;
t1.print_this();
}
If you can change your _inner_print
function to a class, you can make use of a partial specialization to end the recursion:
template <std::size_t N, std::size_t MAX>
struct _inner_print
{
_inner_print()
{
std::cout << "Call #"<<MAX-N<<std::endl;
////////////////////////
/* other dynamic code */
////////////////////////
_inner_print<N-1, MAX>();
}
};
template <std::size_t MAX> struct _inner_print<0, MAX> { };
Rather than calling _inner_print()
as a function, it becomes a declaration for an unnamed temporary, invoking the constructor that performs your output.
You should use partial specialization. But you can't do this with a function.
You should use a struct
to do the trick.
#include <iostream>
#include <string>
#include <tuple>
template <std::size_t N, std::size_t MAX, class T>
struct inner_print_impl{
static void run(const T& caller)
{
std::cout << "Call #"<<MAX-N<< " " << caller.a << std::endl;
////////////////////////
/* other dynamic code */
////////////////////////
inner_print_impl<N-1, MAX , T>::run(caller);
}
};
template < std::size_t MAX, class T>
struct inner_print_impl<0, MAX , T>{
static void run(const T& caller)
{
std::cout << "Call #"<<MAX<< " " << caller.a << std::endl;
////////////////////////
/* other dynamic code */
////////////////////////
// no recursion
}
};
template <typename ...Ts>
struct temp{
using TypeList = std::tuple<Ts...>;
constexpr static std::size_t N_ = std::tuple_size<TypeList>::value;
template <std::size_t N, std::size_t MAX, class T>
friend struct inner_print_impl;
void print_this()
{
inner_print_impl<N_,N_, temp<Ts...> >::run(*this);
}
TypeList _mem;
private :
int a ; // test acces
};
int main()
{
std::string name;
temp<int, int, int> t1;
t1.print_this();
}
Note :
- If you need to acces to a private membre, you will need to pass
*this
to the call and add the newstruct
as friend of your class - In my code, I have a duplication on the
/* other dynamic code */
part. You may :- Use a function
- make your template parameter a
int
instead ofsize_t
and stop at-1
rather than0
PS :
I don't get the part
in a tuple in a function in a struct (not in a function in the struct)
I hope I didn't miss something
Edit :
My code do one more iteration than your, you may just empty this :
template < std::size_t MAX, class T>
struct inner_print_impl<0, MAX , T>{
static void run(const T& caller)
{
}
};
And you don't display for the 0
case.