7

I am creating a templated class D<N>, with a method (operator(), in this case) that returns different types, depending on the value of N.

I could only make this work by creating two separate class declarations, but this came at the cost of a lot of code duplication. I also tried to create a common base class to throw the common stuff into, but I couldn't get the constructor to inherit right and don't know how idiomatic that would be as well...

#include <cstdio>

template <int N>
struct D{
    int s;
    D(int x):s(x){}

    int yell(int x){
        printf("N=%d, %d\n", N, s+x);
        return s+x;
    }

    D<N-1> operator()(int x){
        D<N-1> d(yell(x));
        return d;
    }
};

template <>
struct D<1>{
    int s;
    D(int x): s(x){}

    int yell(int x){
        printf("N=%d, %d\n", 1, s+x);
        return s+x;
    }

    int operator()(int x){
        return yell(x);
    }
};


int main()
{
    D<2> f(42);
    printf("%d\n", f(1)(2));
    return 0;
}

How can I make my code better looking?

hugomg
  • 68,213
  • 24
  • 160
  • 246
  • You need to rethink the utility of the return type being dependent on the value of the parameters. Either make it a consistent polymorphic return type, or figure out a sensible way to always return a basic type. In situations like this I usually find it helpful to return to basic needs of the application. – wallyk Jun 02 '11 at 21:05
  • The problem is that there isn't a good type to encode lists of a certain length. Doing the template black magic would allow for my little DSL to be nice to use and type-safe as well. – hugomg Jun 02 '11 at 22:09
  • So you need some "compile time length" linked-list ? Isn't std::array/boost::array suitable for your case ? The downside is that you cannot take a "subarray" object from it but you can use the "runtime variable length interface" as well (iterators, [], pointer …). – ysdx Jun 03 '11 at 06:51

2 Answers2

8

You can use the Curiously Recurring Template Pattern.

template<int N, template<int> typename D> struct d_inner {
    D<N-1> operator()(int x) {
        return D<N-1>(static_cast<D<N>*>(this)->yell(x));
    }
};
template<template<int> typename D> struct d_inner<1, D> {
    int operator()(int x) {
        return static_cast<D<1>*>(this)->yell(x);
    }
};

template <int N> struct D : public d_inner<N, D> {
    int s;
    D(int x):s(x){}

    int yell(int x){
        printf("N=%d, %d\n", N, s+x);
        return s+x;
    }
};

Not that I see the utility- or purpose- of this particular object being templated, it could easily not be.

Puppy
  • 144,682
  • 38
  • 256
  • 465
  • +1 for "Not that I see the utility- or purpose- of this particular object being templated, it could easily not be" – fedvasu Sep 18 '13 at 17:22
4

I'm not sure it is better looking: but it avoids source code duplication :

 // Find a name for this ...
template<int N, template<int M> class X>
struct foo {
  typedef X<N> type;
};
template< template<int M> class X >
struct foo<0,X> {
  typedef int type;
};

template <int N>
struct D{
  int s;
  D(int x):s(x){}

  int yell(int x){
    printf("N=%d, %d\n", N, s+x);
        return s+x;
  }

  typename foo<N-1,D>::type
  operator()(int x){
    return typename foo<N-1,D>::type(yell(x));
  }
};
ysdx
  • 8,889
  • 1
  • 38
  • 51