0
votes

I have a problem where I want to specialize a template member function of a template class in the code below. The answer to this question explicit specialization of template class member function seems to suggest that it can't be done. Is that correct, and if so, is there any work around I can use so that by inline inc functions get expanded at compile time?

Many thanks!

#include <iostream>
#include <cstdio>

template <class IT, unsigned int N>
struct IdxIterator {
private:
  int startIdx[N], endIdx[N];  
  int curIdx[N];
  IT iter;

public:
  IdxIterator(IT it, int cur[], int start[], int end[]): iter(it) {
    for (int i = 0; i < N; i++) {
      curIdx[i] = cur[i];
      startIdx[i] = start[i];
      endIdx[i] = end[i];
    }
  }

  template <int dim>
  inline void inc() {
    curIdx[dim]++;
    if (curIdx[dim] > endIdx[dim]) {
      if (dim > 0) {
        curIdx[dim] = startIdx[dim];
        inc<dim-1>();
      }
    }
  }

  // how to declare this specialization?
  template <> template <>
  inline void inc<-1>() {
    std::cerr << "IdxIterator::inc(" << -1 << ") dim out of bounds!\n";
    throw 1;
  }

  inline IdxIterator<IT, N> operator++() {
    iter++;
    inc<N-1>();
    return *this;
  }

};

int main(int argc, char** argv) {

  int *buf = new int[100];
  int start[1], end[1];
  start[0] = 0; end[0] = 99;
  IdxIterator<int*, 1> it(buf, start, start, end);
  ++it;

  return 0;

}

G++ spits out:

test2.cpp:32:13: error: explicit specialisation in non-namespace scope ‘struct IdxIterator’ test2.cpp:32:25: error: explicit specialisation in non-namespace scope ‘struct IdxIterator’ test2.cpp:33:23: error: template-id ‘inc<-0x00000000000000001>’ in declaration of primary template test2.cpp: In member function ‘void IdxIterator::inc() [with int dim = -0x000000000000003fe, IT = int*, unsigned int N = 1u]’: test2.cpp:27:9: error: template instantiation depth exceeds maximum of 1024 (use -ftemplate-depth= to increase the maximum) instantiating ‘void IdxIterator::inc() [with int dim = -0x000000000000003ff, IT = int*, unsigned int N = 1u]’ test2.cpp:27:9: recursively instantiated from ‘void IdxIterator::inc() [with int dim = -0x00000000000000001, IT = int*, unsigned int N = 1u]’ test2.cpp:27:9: instantiated from ‘void IdxIterator::inc() [with int dim = 0, IT = int*, unsigned int N = 1u]’ test2.cpp:41:5: instantiated from ‘IdxIterator IdxIterator::operator++() [with IT = int*, unsigned int N = 1u]’ test2.cpp:53:5: instantiated from here

test2.cpp: At global scope: test2.cpp:22:15: warning: inline function ‘void IdxIterator::inc() [with int dim = -0x000000000000003ff, IT = int*, unsigned int N = 1u]’ used but never defined [enabled by default]

3
If your compiler supports C++11, you can use a static_assert. Else, you could invoke instantiation of a second template (independent of IdxIterator) inside inc that does this check.dyp
Just try to minimize the code you put into your questions!!! Most of the stuff here is not relevant to the problem at all.iavr
This seems pretty trivial if your only issue is ensuring a positive value. template<unsigned var> void foo() { /*impl*/ }Joseph Franciscus

3 Answers

2
votes

There may be a better way in C++11, but you can always go via overloading instead of specialization:

template <int N>
struct num { };

class A
{
    template <int N>
    void f(num <N>) { };

    void f(num <-1>) { };

public:
    template <int N>
    void f() { f(num <N>()); };
};
0
votes

You can do what the compiler error message suggests:

template <class IT, unsigned int N>
struct IdxIterator {
private:
  template <int dim>
  inline void inc() {
    curIdx[dim]++;
    if (curIdx[dim] > endIdx[dim]) {
      if (dim > 0) {
        curIdx[dim] = startIdx[dim];
        inc<dim-1>();
      }
    }
  }
};

template <> template <>
inline void IdxIterator::inc<-1>() {
  std::cerr << "IdxIterator::inc(" << -1 << ") dim out of bounds!\n";
  throw 1;
}

ie move the definition to namespace-scope.

0
votes

Create a helper struct outside of class

template<dim>
struct inc {
template<class cur, end>
      inline static void foo(cur curIdx, end endIdx) {
        curIdx[dim]++;
    if (curIdx[dim] > endIdx[dim]) {
        inc<dim-1>::foo(curIdx, endIdx);
      }
    }    
};

template<>
struct inc<0> {
    template<class cur, end>
      inline static void foo(cur, end) {
       //terminate
    }    
};

class IdxIterator {
       template<int i>
       void inc() {
        static_assert(i > 0, "error out of bounds");
         int<i>::foo(/*params*/); 
    }

};

Note if you are using the GCC you may __attribute__((always_inline))to force inlining.