2

My code uses a library (FastLED) which uses a templated function:

#define NUM_WIDGETS 4

Library.function<1>();
Library.function<2>();
Library.function<3>();
Library.function<4>();

I can't put this in a normal for loop since the template argument needs to be computable at compile-time. Can I do this in the preprocessor? Any other suggestion? I'd like to be able to change NUM_WIDGETS conveniently without copy-pasting these lines.

Joel Spolsky
  • 33,372
  • 17
  • 89
  • 105
Anna
  • 2,645
  • 5
  • 25
  • 34
  • 1
    look at https://en.cppreference.com/w/cpp/utility/integer_sequence – xaxxon Jul 17 '18 at 00:30
  • 2
    Not preprocessor. Recursive template. `template void invoke() {Library.function(); invoke(i-1);};` with a specialisation to end the recursion `template<> void invoke<0U>() {};` – Peter Jul 17 '18 at 00:31

4 Answers4

5

You can do this using templates with the help of std::index_sequence:

constexpr size_t NUM_WIDGETS = 4;

template <size_t N>
void foo() {
    std::cout << N << '\n';
}

template <size_t... Ns>
void call_foo_helper(std::index_sequence<Ns...>) {
    (foo<Ns>(), ...);
}

template <size_t N>
void call_foo() {
    call_foo_helper(std::make_index_sequence<N>());
}

int main() {
    call_foo<NUM_WIDGETS>();
}

This uses C++17 fold expressions. If you don't have C++17 available, you could use a recursive template instead:

constexpr size_t NUM_WIDGETS = 4;

template <size_t N>
void foo() {
    std::cout << N << '\n';
}

template <size_t N>
void call_foo() {
    foo<N>();
    call_foo<N - 1>();
}

template <>
void call_foo<0>() { }

int main() {
    call_foo<NUM_WIDGETS>();
}
Miles Budnek
  • 28,216
  • 2
  • 35
  • 52
1

The fold expression based or recursive template solution is superior. But, if you wanted a preprocessor solution, you can see if your compiler provides the inclusion depth as a predefined macro. For example, GCC provides __INCLUDE_LEVEL__ (and MSVC provides __COUNTER__). You can then use recursive inclusion up to a limit to generate your template function calls.

$ gcc -E r.cc | grep -v '^#' | sed '/^ *$/d'
int main()
{
   Library.function<1>();
   Library.function<2>();
   Library.function<3>();
   Library.function<4>();
}
$ cat r.cc
int main()
{
    #define NUM_WIDGETS 4
    #include "r.i"
}
$ cat r.i
#ifndef NUM_WIDGETS
# error "NUM_WIDGETS needs to be defined!"
#else
# if (__INCLUDE_LEVEL__) < ((NUM_WIDGETS) + 1)
   Library.function<__INCLUDE_LEVEL__>();
#  include __FILE__
# endif
#endif

This technique is limited to the nested inclusion limits of your compiler.

jxh
  • 69,070
  • 8
  • 110
  • 193
1

The template solutions in Miles' answer are definitely the way to go.

But just for kicks, you can in fact also do it with the preprocessor:

#include <iostream>
#include <boost/preprocessor/repetition/repeat.hpp>

template <int N> void function() { std::cout << N << "\n"; }

#define NUM_WIDGETS 4

int main()
{
#define CALL_FUNC(z,n,d) function<n+1>();
    BOOST_PP_REPEAT(NUM_WIDGETS, CALL_FUNC, ~)
#undef CALL_FUNC
}

This isn't as good, because the count NUM_WIDGETS needs to be known to the preprocessor as a plain string of digits. Not (4), not 4U, not a constexpr variable, and so on. And Boost.Preprocessor does have a limit of 256 repetitions (or slightly less if using MSVC).

aschepler
  • 70,891
  • 9
  • 107
  • 161
1

As I noted earlier in comment, it is easier to do this with templates than with preprocessor.

First define a template

 template<unsigned n> void invoke() {Library.function<n>();  invoke<n-1>();};

Then specialise an end condition that does nothing

 template<> void invoke<0U>() {};

and call it

 int main()
 {
      invoke<NUM_WIDGETS>();
 }

This will call the function in reverse order than specified. That can be fixed by changing the template function to

 template<unsigned n> void invoke() {Library.function<NUM_WIDGETS + 1 - n>(); invoke<n-1>();};

and keeping the same specialisation to end the recursion.

Of course, if you need to do tricks like this (whether with the preprocessor or templates) that is suggestive of a broken design and you should be asking WHY Library.function() is a template in the first place.

Peter
  • 35,646
  • 4
  • 32
  • 74