Compile time evaluation of a C++ loop

2019-06-05 18:10发布

I am trying to write a C++ loop that I want evaluated at compile-time. This is because I need to use the looping variable as a template argument to initialize a class. In the very simple case it would look something like:

for (unsigned int i = 1; i < 8; i++) {
  Vector<i> foo;
  // do something with foo
}

After going through some similar StackOverflow questions, I found a way to write a static for loop recursively. Now my code looks something like this:

template <unsigned int i, unsigned int end>
struct static_for {
  template <typename Lambda>
  void operator()(const Lambda& function) const {
    if (i < end) {
      function(i);
      static_for<start + 1, end>(function);
    }
  }
};

template <int N>
struct static_for<N, N> {
  template <typename Lambda>
  void operator()(const Lambda& function) const {
    // This is just to avoid the unused variable warning - does nothing.
    (void)function;
  }
};

This works perfectly and as expected. In order to call this static_for loop I can simply write:

static_for<0, 8>()([](int size) {
      // some code here.
    });

The problem, however, is that I can still not evaluate i at compile time. As in I cannot create a templated object within the lambda expression:

static_for<0, 8>()([](int size) {
      Vector<size> var; // does not compile as size is not a constexpr.
    });

How can I make this work? My static_for loop has the i variable as a template argument which is available to it at compile time. I would like my lambda expression to receive this argument (size) also at compile time rather than being passed in as a runtime variable. How would this work? Conceptually this seems simple and useful as a more general language feature as well.

2条回答
老娘就宠你
2楼-- · 2019-06-05 18:33

If you can use C++14, you could pass an std::integral_constant to a generic lambda instead of an int. gcc.godbolt.org example

template <unsigned int i, unsigned int end>
struct static_for {
  template <typename Lambda>
  void operator()(const Lambda& function) const {
    if (i < end) {
      function(std::integral_constant<int, i>{});
      static_for<start + 1, end>(function);
    }
  }
};

static_for<0, 8>()([](auto size) {
  Vector<decltype(size)::value> var; // does not compile as size is not a constexpr.
});
查看更多
姐就是有狂的资本
3楼-- · 2019-06-05 18:41

I've made a bit different solution based on @Vittorio Romero's answer which should work with c++11 and g++ 4.9. You can run it here. In @Vittorio's solution you have to define Lambda function outside block scope due to integral_constant (see example code in comments) use. In my solution you have more lambda-like behaviour. You can define your structure within other function and pass to it any number of arguments.

[EDIT] Ok, it was a bit too late for my mind work ;) Unfortunately I can't come out with a type of lambda-like solution to work in compile-time. So you have to define your lambda structures outside block scope. You can run modified code here.

#include <type_traits>
#include <iostream>

template <int COUNT, int MAX, typename Lambda>
struct StaticFor 
{
    template <typename... Args>
    static void impl(Args&&... args) 
    {
        Lambda::impl(std::integral_constant<int, COUNT>(), std::forward<Args>(args)...);
        StaticFor<COUNT + 1, MAX, Lambda>::impl(std::forward<Args>(args)...);
    }
};

template <int N, typename Lambda>
struct StaticFor<N, N, Lambda> 
{
    template <typename... Args>
    static void impl(Args&&... args) 
    {
    }
};

static const int DIM = 3;
static const int POINTS = 6;

template <int m, typename T>
T foo(T value)
{ 
    return value * m;
}

struct IterateDim
{
  template <typename D, typename P>    
  static void impl(D dIdx, P pIdx, float (&smpl)[POINTS][DIM])
  {
    smpl[P::value][D::value] = foo<D::value>(P::value);
  }
};

struct IteratePoints
{
  template <typename P>  
  static void impl(P pIdx, float (&smpl)[POINTS][DIM])
  {
    StaticFor<0, DIM, IterateDim>::impl(pIdx, smpl);
  }
};

int main(void)
{
  float table[POINTS][DIM];

 StaticFor<0, POINTS, IteratePoints>::impl(table);

  for (int p = 0; p < POINTS; ++p)
  {
    for (int d = 0; d < DIM; ++d)
    {
      std::cout << table[p][d] << ", ";
    }
  }
}
查看更多
登录 后发表回答