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 :

  1. If you need to acces to a private membre, you will need to pass *this to the call and add the new struct as friend of your class
  2. 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 of size_t and stop at -1 rather than 0

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.

Tags:

C++