Search code examples
c++templatesmetaprogramming

Print template parameters


I am trying to create a template vector of arbitrary number of int parameters and create a function to print those parameters:

namespace ex {

    template <int I, class... Ts>
    constexpr decltype(auto) get(Ts&& ... ts) {
        return std::get<I>(std::forward_as_tuple(ts...));
    }

    template<int... N>
    struct vec
    {
        static const int size = sizeof...(N);
        void print() {
            print(size-1);
        }

        void print(const int n) {
            if (n >= 0) {
                print(n - 1);
                std::cout << ex::get<n>(N...) << std::endl;
            }
        }
    };
}

int main()
{
    ex::vec<1, 2, 3> v;
    v.print();
}

And I got the following errors:

error C2672: 'ex::get': no matching overloaded function found
error C2975: 'I': invalid template argument for 'ex::get', expected compile-time constant expression

Can anyone please explain that am I missing and how to achieve the behaviour I expect?
Thanks in advance.


Solution

  • If you have access to C++17, you can benefit from fold expressions:

    template<int... N>
    struct vec {
        void print() {
            ((std::cout << N << std::endl), ...);
        }
    };
    

    Before C++17, you can use dummy array initialization trick:

    template<int... N>
    struct vec {
        void print() {
            int dummy[]{0, ((std::cout << N << std::endl), 0)...};
            (void)dummy;
        }
    };
    

    The first zero is there to handle empty N... packs. The second zero is a part of a comma operator expression. The order of evaluation of arguments in a braced list is fixed by their order in that list [dcl.init.list/4].


    But what if I want to operate on the individual parameter, for instance, I want to add operator+ which adds 2 vectors. For instance adding vec<1,2,3> v1 and vec<4,5> v2 will result a vec<5,7,3> v3?

    If packs can have different sizes, some tricks are needed. For example:

    template<std::size_t... S, int... N, int... M>
    auto add_impl(vec<N...>, vec<M...>, std::index_sequence<S...>)
    {
        constexpr int is1[sizeof...(S)]{N...};
        constexpr int is2[sizeof...(S)]{M...};
        return vec<(is1[S] + is2[S])...>{};
    }
    
    template<int... N, int... M>
    auto operator+(vec<N...> v1, vec<M...> v2) {
        constexpr auto size = std::max(sizeof...(N), sizeof...(M));
        return add_impl(v1, v2, std::make_index_sequence<size>{});
    }