views:

133

answers:

3

This is what I would like to do using templates:

struct op1
{
   virtual void Method1() = 0;
}

...

struct opN
{
   virtual void MethodN() = 0;
}

struct test : op1, op2, op3, op4
{
    virtual void Method1(){/*do work1*/};
    virtual void Method2(){/*do work2*/};
    virtual void Method3(){/*do work3*/};
    virtual void Method4(){/*do work4*/};
}

I would like to have a class that simply derives from a template class that provides these method declarations while at the same time making them virtual. This is what I've managed to come up with:

#include <iostream>

template< size_t N >
struct ops : ops< N - 1 >
{
protected:
    virtual void DoStuff(){ std::cout<<N<<std::endl; };
public:
    template< size_t i >
    void Method()
    { if( i < N ) ops<i>::DoStuff(); } 
    //leaving out compile time asserts for brevity
};

template<>
struct ops<0>
{
};

struct test : ops<6>
{
};

int main( int argc, char ** argv )
{
  test obj;
  obj.Method<3>(); //prints 3
  return 0;
}

However, as you've probably guessed, I am unable to override any of the 6 methods I have inherited. I'm obviously missing something here. What is my error? No, this isn't homework. This is curiosity.

+1  A: 
template< size_t N >
struct ops : ops< N - 1 >

This codes an endless loop. The recursion doesn't stop when N reaches 0. Add a specialization for the end case, immediately after the primary template:

template<>
struct ops<0> {}

Also, what does this do? Why not just call ops<i>::DoStuff() directly?

template< size_t i >
void Method()
{ if( i < N ) ops<i>::DoStuff(); } 
Potatoswatter
Its a runtime version of boost::enable_if. If i is too big, Method just does nothing.
Dennis Zickefoose
So we're taking a template which might do anything, and add the possibility it could quietly do nothing if the argument is out of range. Fun.
Potatoswatter
True. It does. I should have added in the specialization to terminate the hierarchy. UPDATE: Just did.
carleeto
@Potatoswatter: Like I said, I left out the out of range compile time assert for brevity.
carleeto
+2  A: 

Tested with GCC 4.3. Don't even know why I spent time on this :-/

#include <iostream>

template <std::size_t N>
struct mark
{ };

template <std::size_t N>
struct op : op <N - 1>
{
  virtual  void  do_method (const mark <N>&) = 0;
};

template <>
struct op <1>
{
  virtual  void  do_method (const mark <1>&) = 0;
};

struct test : op <2>
{
  template <std::size_t K>
  void
  method ()
  {  do_method (mark <K> ());  }

  virtual  void do_method (const mark <1>&)
  {  std::cout << "1\n";  }

  virtual  void do_method (const mark <2>&)
  {  std::cout << "2\n";  }
};

int
main ()
{
  test  x;

  x.method <1> ();
  x.method <2> ();
}

I don't know how to move the "prettifier" method() template function out of test.

doublep
You can use one `template<size_t N> op` for the methods and terminate with an empty specialization `template<> struct op<0> {};`.
Georg Fritzsche
@gf: Good point.
doublep
Thank you. I knew the answer had something to do with converting an int to a type and using that to differentiate the functions called.
carleeto
+1  A: 

To mimic your original desire:

#define MAKE_OPS(N) template<> struct Ops<N> : Ops<N-1> { virtual void Method##N() = 0; }

template<int N>
struct Ops;

template<>
struct Ops<0> { };

MAKE_OPS(1);
MAKE_OPS(2);
template<> struct Ops<3> : Ops<2> { virtual void Method3() { std::cout << "3" << std::endl; } };
MAKE_OPS(4);
MAKE_OPS(5);
MAKE_OPS(6);

struct Test : Ops<3> {
    virtual void Method1() { std::cout << 1 << std::endl; }
    virtual void Method2() { std::cout << 2 << std::endl; }
};
Dennis Zickefoose
Yes, that would work, but the whole point is that I don't want to have to write a new method declaration for every additional method. Call me lazy, but to me this is exactly the type of stuff that should be able to be auto generated, which is the reason for the this excercise.
carleeto
@carleeto: I know it isn't quite what you were looking for, but it has the advantage of being able to provide default implementations if whatever obscure use you have in mind calls for it. I also thought there'd be a problem of name-hiding in the alternative if you derived from `test`, but on further reflection there wouldn't be.
Dennis Zickefoose
Lol. Nice! Totally forgot about layering the pre-processor on top. Thank you for reminding me! :)
carleeto