views:

178

answers:

4

I have a template class that I've subclassed with a pointer to it (Decorator pattern). I added a getBase() call to return the pointer to the base class for any further subclasses. However, when I use that getBase() and call the base classes only method, I get a linker error that it can't find the symbol for that method in the intervening (Decorator) class?

Like this:

template <typename T> class B {    
  public:
    typedef std::auto_ptr<T> MYFUN( 
      std::istream&, const std::string&, const std::string& );

  public:
    B<T>( MYFUN* p );
    auto_ptr<T> fun( istream& );

  private:
    MYFUN *fptr;
};

template <typename T>
class D : public class B<T>
{
  D( typename B<T>::MYFUN *fPtr, B<T> *providedBase );
  //Looks like B
  B* getBase() { return base_ ; }
  private:
    B* base_;
};

template <typename T>
class Dagain : public class D<T>
{
  //Looks like D
  auto_ptr<T> fun( istream& );
};

auto_ptr<T>
Dagain::fun( istream& is )
{
  this->getBase()->fun( is );
}

Note that there is no definition for fun( istream& ) in D<T>. The intention is for the client to use the pointer to the base to call B<T>::fun( istream& )

When the client constructs a Dagain object, the linker says (basically):

Client.o:
(.data.rel.ro. --stuff--
[vtable for D<T>]:
undefined reference to
'D<T>::fun( basic_istream<char, char_traits<char> >&)'

But, I'm not calling D's definition of fun(istream&)... it doesn't even have one! I'm using the pointer directly to the base class... When I add a definition for D<T>::fun(istream&) things work, but I don't understand why?

A: 

I'm not a C++ expert and not sure if this has anything to do with templates, but if we ignore the templates, I think part of your problem might be that fun() in B is not virtual.

When you call getBase() you get a static type of B*, and then when you invoke fun() on it you're getting the version from B rather than the version in DAgain. I would think that the linker would complain over the fact that you have not defined the version for B (and thus for D). The fact that fun() is overridden in DAgain would not suffice since the binding is static.

Another option is that returning B* rather than B* may be the issue, but I haven't played with C++ enough recently.

Uri
A: 

It looks to me like your getBase is returning the wrong type. It should be returning the correct type of the base class:

template <typename T>
class D : public class B<T>
{
  ...
  //Looks like B
  B<T>* getBase() { return base_ ; }
  private:
    B<T>* base_;
};
1800 INFORMATION
A: 

What you call a "base" actually isn't -- D has a real B base (due to inheritance) and a fake one via that pointer. As to why the compiler is whining about a vtable when you appear to have no virtuals at all, that's weird -- you sure you don't have a virtual somewhere?

Alex Martelli
i think he is trying to do something like javas' Thread <-> Runnable. storing what runnable's run method is actually invoked. "this" or "another runnable"
Johannes Schaub - litb
+1  A: 

You have forgotten to specify the template parameter in the template definition. Furthermore you had some more errors. Here is the working code:

template <typename T> class B {
  public:
    typedef std::auto_ptr<T> MYFUN(
      std::istream&, const std::string&, const std::string& );

  public:
    B<T>( MYFUN* p );
    auto_ptr<T> fun( istream& );

  private:
    MYFUN *fptr;
};

template <typename T>
class D : public B<T>
{
  D( typename B<T>::MYFUN *fPtr, B<T> *providedBase );
  //Looks like B
  B<T>* getBase() { return base_ ; }
  private:
    B<T>* base_;
};

template <typename T>
class Dagain : public D<T>
{
  //Looks like D
  auto_ptr<T> fun( istream& );
};

template <typename T>
auto_ptr<T>
Dagain<T>::fun( istream& is )
{
  this->getBase()->fun( is );
}
ablaeul