5
\$\begingroup\$

I have implemented some meta functions to create sequence for exponents of 2. I would love to know if there is any way to make the code more optimized.

Note: int main() should not be modified. From the requirements of my school:

This program must implement three definitions: sequence<Ts...>, print() and printing_type().

Hence, the name of functions should not be modified.

#include<iostream>

template<size_t... Ts>
struct int_seq{}; 

template<size_t TCount, size_t...Ts>
struct sequence
{
    using type = typename sequence<TCount - 1,  1, (2 * Ts)...>::type;
};

template<size_t...Ts>
struct sequence<0, Ts...>
{
    using type = int_seq<Ts...>;
};

template<size_t TCount>
using create_int_seq  = typename sequence<TCount + 1>::type;

template <typename T>
void print(T val)
{
    std::cout << val << " ";
}

template <typename T, typename... Ts>
void print(T val, Ts... t)
{
    std::cout << val << " ";
    print(t...);
}

template <
    template <size_t...> class T,
    size_t... S
>
void printing_type(T<S...>)
{
    print(S...);
}

int main()
{
    constexpr size_t N = 63;
    create_int_seq<N> dum{};
    printing_type(dum);
}
\$\endgroup\$

1 Answer 1

7
\$\begingroup\$

Certainly your definitions of printing_type and print could be shortened. You're currently doing

print(t...)  // print(a,b,c,d)

but what you actually want to happen is

print(t)...  // print(a), print(b), print(c), print(d)

So then you have to do a bunch of extra work to transform the former into the latter. Instead of all that extra work, you could just write

void print(size_t val) {
    std::cout << val << " ";
}

template<template<size_t...> class T, size_t... S>
void printing_type(T<S...>) {
    (print(S) , ...);
}

Notice that the single-argument print doesn't have to be a template, because val is always a size_t.

In fact, we can inline print into printing_type if we want:

template<template<size_t...> class TT, size_t... Values>
void printing_type(TT<Values...>) {
    ((std::cout << Values << " ") , ...);
}

The (ts , ...) syntax is a C++17 fold-expression. If you aren't on C++17, then you can use an initializer list to accomplish the same thing:

template<template<size_t...> class TT, size_t... Values>
void printing_type(TT<Values...>) {
    int dummy[] = {
        ((std::cout << Values << " "), 0) ...
    };
}

Your definition for sequence is pretty confusing. Your code could benefit from some unit tests. Compile-time unit tests are easy:

static_assert(std::is_same<create_int_seq<0>, int_seq<1>>::value, "");
static_assert(std::is_same<create_int_seq<1>, int_seq<1,2>>::value, "");
static_assert(std::is_same<create_int_seq<2>, int_seq<1,2,4>>::value, "");
static_assert(std::is_same<create_int_seq<3>, int_seq<1,2,4,8>>::value, "");
static_assert(std::is_same<create_int_seq<4>, int_seq<1,2,4,8,16>>::value, "");

As a bonus, these five lines serve as excellent documentation about what this code does... to the extent that I no longer mind the lack of code comments explaining how it might work.

FWIW, if I wanted to make these test cases pass, I'd implement create_int_seq like this:

template<class> struct powers_of_two;
template<size_t... Is>
struct powers_of_two<std::index_sequence<Is...>> {
    using type = int_seq<(size_t(1) << Is)...>;
};

template<size_t N>
using create_int_seq =
    typename powers_of_two<std::make_index_sequence<N+1>>::type;

Relevant blog post of mine: "Template metaprogramming: Iteration is better than recursion" (July 2018).

\$\endgroup\$
3
  • \$\begingroup\$ Man, you blow me away with those changes! Thank you so so much. Still trying to take it in. And yes, I am using c++14 currently for this. \$\endgroup\$
    – starrk
    Commented Aug 2, 2020 at 16:45
  • \$\begingroup\$ Hi @Quuxplusone, I need to retain both print() and printing_type meta functions, and since I am using c++14, is there another optimize way instead of using the initializer list method as you have mentioned for c++14, such that I am able to use both print() and printing_type functions at the same time. And, FYI, the compiler has issued a warning being treated as error that the "dummy" variable isn't used. Hope to hear more from you. Thank you. \$\endgroup\$
    – starrk
    Commented Aug 2, 2020 at 17:26
  • 1
    \$\begingroup\$ Re unused variable dummy: Add (void)dummy; in the body of that function. Re needing to keep some function named print: Add void print() {} :) \$\endgroup\$ Commented Aug 3, 2020 at 1:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.