可以使用预处理器完成此for循环吗?

时间:2018-07-17 00:16:03

标签: c++ templates arduino c-preprocessor arduino-due

我的代码使用库(FastLED),该库使用模板化函数:

#define NUM_WIDGETS 4

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

我不能将其放在普通的for循环中,因为template参数需要在编译时可计算。我可以在预处理器中执行此操作吗?还有其他建议吗?我希望能够方便地更改NUM_WIDGETS,而无需复制粘贴这些行。

4 个答案:

答案 0 :(得分:5)

您可以在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>();
}

这使用C ++ 17折叠表达式。如果您没有可用的C ++ 17,则可以改用递归模板:

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>();
}

答案 1 :(得分:1)

基于折叠表达式或递归模板的解决方案更为出色。但是,如果需要预处理器解决方案,则可以查看编译器是否提供包含深度作为预定义的宏。例如,GCC提供__INCLUDE_LEVEL__(而MSVC提供__COUNTER__)。然后,您可以使用递归包含(上限)来生成模板函数调用。

$ 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

此技术仅限于编译器的嵌套包含限制。

答案 2 :(得分:1)

Miles' answer中的模板解决方案绝对是可行的方法。

但是,只是为了踢球,实际上您也可以使用预处理器:

#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
}

这不是很好,因为预处理器需要将计数NUM_WIDGETS看作是纯数字串。不是(4),不是4U,不是constexpr变量,依此类推。而且Boost.Preprocessor的重复次数确实限制为256次(如果使用MSVC,则更少)。

答案 3 :(得分:1)

正如我之前在评论中指出的那样,使用模板比使用预处理器更容易做到这一点。

首先定义一个模板

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

然后专门说明什么都不做的结束条件

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

并调用它

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

这将以与指定相反的顺序调用该函数。可以通过将模板功能更改为

来解决此问题。
 template<unsigned n> void invoke() {Library.function<NUM_WIDGETS + 1 - n>(); invoke<n-1>();};

并保留相同的专业知识以结束递归。

当然,如果您需要做一些这样的技巧(无论是与预处理程序还是模板一起使用),可能表明设计已损坏,那么您应该首先问为什么Library.function()是模板。