In a nutshell I am trying to write a program that will connect to a database, query several tables for various sets of data that will then be transformed into XML that is stored into a file on the local hard disk. My first thought process to solve this problem was to create classes that would be used to contain one row of data returned for each result set. I would create a base class that would define the common behavior between each derived class, while the derived classes would handle anything that is specific to the type of result set. I then decided that I wanted to use a template container to collect each row so that I could easily iterate, sort, transform into XML each of the rows returned.

Now, I am using basic polymorphism for the base and derived classes. For the template container, I am declaring the template and then defining my specialized function definitions. The program compiles fine, but I get undefined symbols when the program enters the linking stage.

Here is a simplified example of what I am trying to do:

#include <iostream>

using namespace std;

// A common base class that will be branched into multiple derived classes
class Base {
protected:
    Base() {
    }

    virtual ~Base() {
    }
    int _foo;
};

// One example deriviation of Base
class Derived : public Base {
public:
    Derived();
    virtual ~Derived();
    int getFoo();
};

Derived::Derived() {
}

Derived::~Derived() {
}

int Derived::getFoo() {
    return _foo;
}

// A template container that will be specialized to only accept
// classes derived from Base (that's the hope, anyway)
template <typename T>
class Container {
public:
    Container();
    ~Container();
};

template <>
Container<Base>::Container() {
}

template <>
Container<Base>::~Container() {
}

int main() {
    // GCC (4.2)/VSE08 compiles the code just fine, but the linker complains
    // as if Derived doesn't inherit from Base.
    Container<Derived> dc;

    cout << &dc << endl;
    return 0;
}

I have tried this in both Visual Studio Express 2008 on Windows XP SP2, and with GCC 4.2 on Mac OS X 10.6.4, and both compilers report the same problem(s):

------ Build started: Project: Sandbox, Configuration: Debug Win32 ------
Linking...
LINK : warning LNK4076: invalid incremental status file 'C:\Documents and Settings\kstanley\My Documents\Visual Studio 2008\Projects\Strayer - CIS326\Debug\Sandbox.ilk'; linking nonincrementally
main.obj : error LNK2019: unresolved external symbol "public: __thiscall Container<class Derived>::~Container<class Derived>(void)" (??1?$Container@VDerived@@@@QAE@XZ) referenced in function _main
main.obj : error LNK2019: unresolved external symbol "public: __thiscall Container<class Derived>::Container<class Derived>(void)" (??0?$Container@VDerived@@@@QAE@XZ) referenced in function _main
C:\Documents and Settings\kstanley\My Documents\Visual Studio 2008\Projects\Strayer - CIS326\Debug\Sandbox.exe : fatal error LNK1120: 2 unresolved externals
Build log was saved at "file://c:\Documents and Settings\kstanley\My Documents\Visual Studio 2008\Projects\Strayer - CIS326\Sandbox\Debug\BuildLog.htm"
Sandbox - 3 error(s), 1 warning(s)
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

and

h223:solr_import kstanley$ g++ c.cpp
Undefined symbols:
"Container<Derived>::~Container()", referenced from:
_main in cc1iB1E2.o
_main in cc1iB1E2.o
"Container<Derived>::Container()", referenced from:
_main in cc1iB1E2.o
ld: symbol(s) not found
collect2: ld returned 1 exit status

I believe that I understand the Why of the problem: after the compiler does its "magic" with the specialized template, the linker only sees the call to "Container<Derived>", but doesn't know that Derived is also Base (I know that there is a lot that goes into template specialization, and that I am surely over simplifying everything). My question to the community is how can I achieve something like what I am trying to do above so that a) I can have separate classes to work with the different result sets, b) use templates (mostly for the practice), and c) use specialization so the template only accepts specific types?

Thank you in advance,

Ken

I can't see where to edit my original post right now, but I wanted to point out that in the Base class definition, I meant to put public for the scope, and not protected. My apologies. :)

Andrei Alexandrescu came up with a way to check convertability between types at compile time as part of his Loki library
http://loki-lib.sourceforge.net/

- you could use a similar trick to enforce the types which are allowed to use your container

class base {};
class derived : public base {};


template<typename T, typename Base>
class relationship 
{
    typedef char Small;
    class Big { char dummy[2]; };
    static Small Test(const Base&);
    static Big Test(...);
    static T MakeT();
public:
    enum { exists = 
        sizeof(Test(MakeT())) == sizeof(Small) };
};

template<bool> struct check;
template<> struct check<true> {};


template<typename T>
class container : public check<relationship<T, base>::exists>
{
};

int main()
{
    container<derived> cd;
    container<base> cb;
}

Edited 6 Years Ago by Bench: n/a

Template parameters are not polymorphic that way. One way to do what you want is to put identifying information in the base class and require that information in the container class.

class Base {
public:
    enum { BASE_TYPE }; // Identifying information
    virtual ~Base() {}
};

class Derived : public Base {
public:
    virtual ~Derived() {}
};

template <typename T>
class Container {
    enum { SafeType = T::BASE_TYPE }; // Require identifying info to exist
};

int main()
{
    Container<Base> c1;
    Container<Derived> c2;
    Container<int> c3; // Error, yay!
}
class base {};
...
    enum { exists = 
        sizeof(Test(MakeT())) == sizeof(Small) };
...

I am curious, but wouldn't this allow *any* class that happens to allocate the same amount of memory as Small? i.e., I would think that this would introduce a very subtle bug where someone could use a type that was not intended for use with the container. Wouldn't this break the concept of inheritance and type enforcement?

Don't get me wrong, I appreciate your insight. I was just hoping for something that doesn't circumvent the language constructs. :)

Edited 6 Years Ago by dohpaz42: Fixing my broken quote.

I am curious, but wouldn't this allow *any* class that happens to allocate the same amount of memory as Small?

No, That's not how it works - there are two overloaded functions which are used to determine the convertability.

small test(base&);
big test(...);

the class checks the sizeof the return types of these functions. the sizeof the return type will only be 'small' when the class is convertable to base&, otherwise, the type will fall into the ellipsis.


This isn't in any way circumventing the language constructs - its completely portable, and works on well-defined behaviour of the ellipsis operator ...

Edited 6 Years Ago by Bench: n/a

No, That's not how it works - there are two overloaded functions which are used to determine the convertability.

... the class checks the sizeof the return types of these functions. the sizeof the return type will only be 'small' when the class is convertable to base&, otherwise, the type will fall into the ellipsis.


This isn't in any way circumventing the language constructs - its completely portable, and works on well-defined behaviour of the ellipsis operator ...

Oh okay. I didn't recognize what you were doing. That makes more sense now, and I see where I had the incorrect assumption. However, I think that I would prefer Radical Edward's solution, since it is less involved to implement. But again, I really do appreciate your insights. :)

This question has already been answered. Start a new discussion instead.