compile time loops

Yes. Possible using compile time recursion.

I was trying with your code but since it was not compilable here is a modified and compiling exmaple:

template<class C, int T=10>
class CountSketch
{
  template<int N>
  void Init ()
  {
    Init<N-1>();
    hashfuncs[N] = &CountSketch<C>::template hash<N>;
    cout<<"Initializing "<<N<<"th element\n";
  }

public:
    CountSketch()
    {
      Init<T>();
    }
private:
   template<int offset>
   size_t hash(C &c)
   {
     return 0;
   }
   size_t (CountSketch::*hashfuncs[T])(C &c);
};

template<>
template<>
void CountSketch<int,10>::Init<0> ()
{
  hashfuncs[0] = &CountSketch<int,10>::hash<0>;
  cout<<"Initializing "<<0<<"th element\n";
}

Demo. The only constraint of this solution is that you have to provide the final specialized version as, CountSketch<int,10>::Init<0> for whatever type and size.


You need a combination of boost::mpl::for_each and boost::mpl::range_c.

Note: This will result in run-time code and this is what you actually need. Because there is no way to know the result of operator& at compile time. At least none that I'm aware of.

The actual difficulty with this is to build a struct that is templated on an int parameter (mpl::int_ in our case) and that does the assignment when operator() is called and we also need a functor to actually capture the this pointer.

This is somewhat more complicated than I anticipated but it's fun.

#include <boost/mpl/range_c.hpp>
#include <boost/mpl/vector.hpp>
#include <boost/mpl/for_each.hpp>
#include <boost/mpl/transform.hpp>
#include <boost/mpl/copy.hpp>

// aforementioned struct
template<class C, class I>
struct assign_hash;

// this actually evaluates the functor and captures the this pointer
// T is the argument for the functor U
template<typename T>
struct my_apply {
  T* t;
  template<typename U>
  void operator()(U u) {
    u(t);
  }
};

template<class C, int T=10, int B=10>
class CountSketch
{
public:
  CountSketch()
    {   
      using namespace boost::mpl;

      // we need to do this because range_c is not an ExtensibleSequence
      typedef typename copy< range_c<int, 0, T>,
                             back_inserter< vector<> > >::type r;
      // fiddle together a vector of the correct types
      typedef typename transform<r, typename lambda< assign_hash<C, _1 > >::type >
        ::type assignees;

      // now we need to unfold the type list into a run-time construct
      // capture this
      my_apply< CountSketch<C, T, B> > apply = { this };
      // this is a compile-time loop which actually does something at run-time
      for_each<assignees>(apply);
    };

  // no way around
  template<typename TT, typename I>
  friend struct assign_hash;

private:
  template<int offset>
  size_t hash(C& c)
    {
      return c;
      // return (reinterpret_cast<int>(&c)+offset)%B;
    }
  size_t (CountSketch::*hashfuncs[T])(C &c);
};

// mpl uses int_ so we don't use a non-type template parameter 
// but get a compile time value through the value member
template<class C, class I>
struct assign_hash {
  template<typename T>
  void operator()(T* t) {
    t->hashfuncs[I::value] = &CountSketch<C>::template hash<I::value>;
  }
};

int main() 
{
  CountSketch<int> a;
}

Nope, it's not directly possible. Template metaprogramming is a pure functional language. Every value or type defined through it are immutable. A loop inherently requires mutable variables (Repeatedly test some condition until X happens, then exit the loop).

Instead, you would typically rely on recursion. (Instantiate this template with a different template parameter each time, until you reach some terminating condition).

However, that can solve all the same problems as a loop could.

Edit: Here's a quick example, computing the factorial of N using recursion at compile-time:

template <int N>
struct fac {
  enum { value = N * fac<N-1>::value };
};

template <>
struct fac<0> {
  enum { value = 1 };
};

int main() {
  assert(fac<4>::value == 24);
}

Template metaprogramming in C++ is a Turing-complete language, so as long as you don't run into various internal compiler limits, you can solve basically any problem with it.

However, for practical purposes, it may be worth investigating libraries like Boost.MPL, which contains a large number of data structures and algorithms which simplify a lot of metaprogramming tasks.

Tags:

C++

Templates