Sign in to follow this  
Endar

static initialisation in source files, VC++

Recommended Posts

When you have static data members in a class, you have to add a declaration in a source file that is compiled, right?
#ifndef _IMBASE_CPP_
#define _IMBASE_CPP_

#include "list.h"
#include "IMBase.h"


util::list<IMBase*> IMBase::activeList;		///< The list of active objects (with ref_count > 0)
util::list<IMBase*> IMBase::deadList;		///< The list of dead objects (with ref_count == 0)


#endif



#ifndef _IMBASE_H_
#define _IMBASE_H_

#include "list.h"

/**
 * Provides the basic menmory managment for objects.
 */
class IMBase
{
private:

	static util::list<IMBase*> activeList;		///< The list of active objects (with ref_count > 0)
	static util::list<IMBase*> deadList;		///< The list of dead objects (with ref_count == 0)

	long ref_count;			///< Reference variable

public:

	// functions
};



So, shouldn't this work? I've added the IMBase source file to the project, and the IMBase header is included where it needs to be. Also the list header file is included and the list source file is added to the project. So, theoretically, shouldn't this be working? I'm still getting:
IMBase.obj : error LNK2001: unresolved external symbol 
"public: __thiscall util::list<class IMBase *>::list<class IMBase *>(void)" (??0?$list@PAVIMBase@@@util@@QAE@XZ)
IMBase.obj : error LNK2001: unresolved external symbol 
"public: __thiscall util::list<class IMBase *>::~list<class IMBase *>(void)" (??1?$list@PAVIMBase@@@util@@QAE@XZ)
Debug/test.exe : fatal error LNK1120: 2 unresolved externals


Share this post


Link to post
Share on other sites
I will have to question your use of those include guards on your .CPP file. Try taking those off and recompiling (clean then rebuild). Right now, other than that, your method of declaring them in the .CPP file looks correct to me.

Share this post


Link to post
Share on other sites
The include guards in the source file was habit from a time I didn't use project files properly. [smile]

Nah, it didn't work. Anything else that could be the problem?

Share this post


Link to post
Share on other sites
Well then it seems that the .cpp file is not being compiled for some reason. Try moving the two statements:

util::list<IMBase*> IMBase::activeList; ///< The list of active objects (with ref_count > 0)
util::list<IMBase*> IMBase::deadList; ///< The list of dead objects (with ref_count == 0)


to your Main.cpp file (make sure it includes the IMBase.h) and rebuild to see if that works.

The only other thing I can think of is that if the util::list is your own user defined data type that represents a list (as in you are not using the stl's list class) then it may be a template problem then. But try the first part of this post first, if it fails still, can we see the util class's definition.

Share this post


Link to post
Share on other sites
Still didn't work.


#ifndef _UTIL_LIST_H_
#define _UTIL_LIST_H_

#include <stddef.h>

namespace util{


/**
* A templated doubly linked list class
*/

template <class T>
class list
{
private:

/**
* A node in a doubly linked list.
*/

struct Node{
T obj; ///< An object of type T
Node* next; ///< A pointer to the next node
Node* prev; ///< A pointer to the previous node

/**
* Constructor
* \param o A const reference to an object of type T
* \param n A pointer to the next node in the list
* \param p A pointer to the previous node in the list
*/

Node(const T& o, Node* n = NULL, Node* p = NULL)
: obj(o), next(n), prev(p)
{ }
};


Node* head; ///< The first linked list element in the list
unsigned int size; ///< The number of elements in the list


public:

/**
* An iterator that is used to step through the linked list
*/

struct iterator{
private:
friend class list<T>; ///< Declare this a friend to list<T> so, list<T> can access its members
Node* current; ///< The current list element being pointed to

public:
/**
* Constructor
* \param n A pointer to an element in the list
*/

iterator(Node* n = NULL)
: current(n)
{ }

/**
* Overloaded operator* (for de-referencing)
* \return A reference of type T to the object element of the
*/

T& operator*() { return current->obj; }

/**
* Overloaded prefix operator++ (but will be used as postfix default function for iterator)
* Moves the iterator forward in the list
*/

void operator++(int j)
{
if( current == NULL )
return;
else
current = current->next;
}

/**
* Overloaded prefix operator-- (but will be used as postfix default function for iterator)
* Moves the iterator backward in the list
*/

void operator--(int j)
{
if( current == NULL )
return;
else
current = current->prev;
}

/**
* Overloaded operator==
* \param i A const reference to an iterator object
* \return true if current pointer is equal to i.current pointer, else false
*/

bool operator==(const iterator& i) { return current == i.current; }

/**
* Overloaded operator!=
* \param i A const reference to an iterator object
* \return true if current pointer is not equal to i.current pointer, else false
*/

bool operator!=(const iterator& i) { return current != i.current; }

/**
* Overloaded assignment operator
* \param i A const refernce to an iterator object
* \return A reference to 'this' iterator object
*/

iterator& operator=(const iterator& i)
{
if( &i != this )
current = i.current;

return *this;
}

}; // iterator


/**
* Constructor
*/

list();

/**
* Copy constructor
* \param l A const reference to a list object
*/

list(const list& l);

/**
* Destructor
*/

~list();

/**
* Delete all elements in the list.
*/

void clear();

/**
* Return an iterator to the first element in the list
* \return An iterator object to the first element in the list.
*/

iterator begin() const;

/**
* Return an iterator to the point after the last element in the list,
* which effectively is a NULL pointer.
*/

iterator end() const;

/**
* Add an element to the back of the list.
* \param o A const reference to an object of type T
*/

void push_back(const T& o);

/**
* Add a list element to the front of the list
* \param o A const reference to an object of type T
*/

void push_front(const T& o);

/**
* Insert an item in the list before the item pointed to by the specified iterator.
* \param i An iterator pointing to the item to insert before
* \param o The item to insert before iterator i
*/

void insertBefore(iterator& i, const T& o);

/**
* Insert an item in the list after the item pointed to by the specified iterator.
* \param i An iterator pointing to the item to insert after
* \param o The item to insert after iterator i
*/

void insertAfter(iterator& i, const T& o);

/**
* Erase the element at this iterator.
* \param i A const reference to an iterator object
*/

void erase(iterator& i);

/**
* Erase a list element.
* \param pos The position of the element to erase
*/

void erase(unsigned int pos);

};

} // namespace util

#endif






Share this post


Link to post
Share on other sites
Quote:
Still didn't work.

Since they're static members, you need to declare them immediately after your class definition in the header file. Try this instead :

class IMBase
{
// class definition ^^
};
util::list<IMBase*> IMBase::activeList; ///< The list of active objects (with ref_count > 0)
util::list<IMBase*> IMBase::deadList; ///< The list of dead objects (with ref_count == 0)

Share this post


Link to post
Share on other sites
Okay, its a problem with the list, and not the IMBase, becuase when I declared a util::list<int> object, I got the same 2 errors, just with an int template.

I just noticed that whenever I compile this, the list header file doesn't show up on the list of dependencies, like all the other header files. It looks like I'm compiling the source, but there's no class definition, even if I add "#include "util\list.h" " to the main source file.

Share this post


Link to post
Share on other sites
Well, now I'm thinking it has to do with the way that I'm compiling the templated files, because I have a dynamic array "array<T>", and I've added that the way it should be, and I get problems with that as well. One different thing is that "array.h" actually comes up in the external dependencies, with all the other header files.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Static member variables don't have to be declared in header file, the source file works fine.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Have you made a default constructor for the list class? The linker errors tell that the constructor hasn't been found.

Share this post


Link to post
Share on other sites
list.cpp source


#ifndef _UTIL_LIST_CPP_
#define _UTIL_LIST_CPP_

#include "list.h"

namespace util{

/**
* Constructor
*/

template <class T>
list<T>::list()
: head(NULL), size(0)
{ }

/**
* Destructor
*/

template <class T>
list<T>::~list()
{
clear(); // erase all elements in the list
}

/**
* Copy constructor
* \param l A const reference to a list object
*/

template <class T>
list<T>::list(const list& l)
{
list<T>::iterator i = l.begin(); // iterator for list l

for( ; i != l.end(); i++ )
push_back(*i); // add element int list l to 'this' list
}


/**
* Return an iterator to the first element in the list
* \return An iterator object to the first element in the list.
*/

template <class T>
list<T>::iterator list<T>::begin() const
{
return list<T>::iterator(head);
}

/**
* Return an iterator to the point after the last element in the list,
* which effectively is a NULL pointer.
*/

template <class T>
list<T>::iterator list<T>::end() const
{
return list<T>::iterator(NULL);
}

/**
* Add an element to the back of the list.
* \param o A const reference to an object of type T
*/

template <class T>
void list<T>::push_back(const T& o)
{
list<T>::iterator i = begin();
list<T>::iterator j;

if( i.current == NULL ){
head = new Node(o);
size++;
return;
}

for( ; i != end(); i++)
j = i;

// if size > 1, then at this point in the function
// i == i-1, i == NULL

j.current->next = new Node(o);
(j.current->next)->prev = j.current; // element (j+1)->prev = &j
size++;
}// push_back

/**
* Add a list element to the front of the list
* \param o A const reference to an object of type T
*/

template <class T>
void list<T>::push_front(const T& o)
{
list<T>::iterator i = begin();

if( head == NULL ){
head = new Node(o);
size++;
return;
}

head = new Node(o);
head->next = i.current; // head->next is equal to address of previous first element
i.current->prev = head; // previous first element->prev pointer is equal to head
size++;
}// push_front

/**
* Erase the element at this iterator.
* \param i A const reference to an iterator object
*/

template <class T>
void list<T>::erase(iterator& i)
{
list<T>::iterator j;

if( i.current == NULL )
return;

if( i.current == head && head != NULL ){
i++;
delete head;
head = i.current;
size--;
return;
}

j = i.current->prev;
j.current->next = i.current->next;
i.current->prev = j.current;
size--;
delete i.current;
}// erase

/**
* Erase a list element.
* \param pos The position of the element to erase
*/

template <class T>
void list<T>::erase(unsigned int pos)
{
list<T>::iterator j(head);

if( pos > size - 1) // if position is out of bounds
return;

for(int i=0; i < pos; i++, j++);

erase(j);
}

/**
* Delete all elements in the list.
*/

template <class T>
void list<T>::clear()
{
while( begin() != end() )
erase( begin() ); // erase the first element

size = 0; // set size of list to 0
}


/**
* Insert an item in the list before the item pointed to by the specified iterator.
* \param i An iterator pointing to the item to insert before
* \param o The item to insert before iterator i
*/

template <class T>
void list<T>::insertBefore(iterator& i, const T& o)
{
Node* n;
iterator j(i);

// if i == head
if( i.current->prev == NULL ){
push_front(o);
return;
}

j--; // move iterator j back 1 position

n = new Node(o); // create a new list element

j.current->next = n; // New list element pointed to by 'n' is now after element pointed to by
i.current->prev = n; // iterator j and before element pointed to by iterator i.

n->next = i.current; // Element pointed to by iterator i is now after 'n'
n->prev = j.current; // and element pointed to by iterator j is now before 'n'

size++; // increase number of elements in list
}

/**
* Insert an item in the list after the item pointed to by the specified iterator.
* \param i An iterator pointing to the item to insert after
* \param o The item to insert after iterator i
*/

template <class T>
void list<T>::insertAfter(iterator& i, const T& o)
{
Node* n;
iterator j(i);

// if i == last element in list
if( i.current->next == NULL ){
push_back(o);
return;
}

j++; // move iterator j forward 1 position

n = new Node(o); // create a new list element

i.current->next = n; // New list element pointed to by 'n' is now after element pointed to by
j.current->prev = n; // iterator i and before element pointed to by iterator j.

n->next = j.current; // Element pointed to by iterator j is now after 'n'
n->prev = i.current; // and element pointed to by iterator i is now before 'n'

size++; // increase number of elements in list
}
}//namespace util
#endif

Share this post


Link to post
Share on other sites
Classic template problem. Templates are not like normal functions. Either the template function definitions need to be visible at the point of instantiation or else the compiler must support the export keyword (Comeau compiler only) or else the template must be explicitly instantiated for all used types. In your case the best solution would probably be to rename your list.cpp file to list.tmpl or equivalent, remove the #include "list.h" from the start of list.cpp/tmpl and add #include "list.tmpl" or whatever you called it to the end of list.h.

Enigma

Share this post


Link to post
Share on other sites
This is what I've changed it to:


#ifndef _UTIL_LIST_H_
#define _UTIL_LIST_H_

#include <stddef.h>

namespace util{


/**
* A templated doubly linked list class
*/

template <class T>
class list
{
private:

/**
* A node in a doubly linked list.
*/

struct Node{
T obj; ///< An object of type T
Node* next; ///< A pointer to the next node
Node* prev; ///< A pointer to the previous node

/**
* Constructor
* \param o A const reference to an object of type T
* \param n A pointer to the next node in the list
* \param p A pointer to the previous node in the list
*/

Node(const T& o, Node* n = NULL, Node* p = NULL)
: obj(o), next(n), prev(p)
{ }
};


Node* head; ///< The first linked list element in the list
unsigned int size; ///< The number of elements in the list


public:

/**
* An iterator that is used to step through the linked list
*/

struct iterator{
private:
friend class list<T>; ///< Declare this a friend to list<T> so, list<T> can access its members
Node* current; ///< The current list element being pointed to

public:
/**
* Constructor
* \param n A pointer to an element in the list
*/

iterator(Node* n = NULL)
: current(n)
{ }

/**
* Overloaded operator* (for de-referencing)
* \return A reference of type T to the object element of the
*/

T& operator*() { return current->obj; }

/**
* Overloaded prefix operator++ (but will be used as postfix default function for iterator)
* Moves the iterator forward in the list
*/

void operator++(int j)
{
if( current == NULL )
return;
else
current = current->next;
}

/**
* Overloaded prefix operator-- (but will be used as postfix default function for iterator)
* Moves the iterator backward in the list
*/

void operator--(int j)
{
if( current == NULL )
return;
else
current = current->prev;
}

/**
* Overloaded operator==
* \param i A const reference to an iterator object
* \return true if current pointer is equal to i.current pointer, else false
*/

bool operator==(const iterator& i) { return current == i.current; }

/**
* Overloaded operator!=
* \param i A const reference to an iterator object
* \return true if current pointer is not equal to i.current pointer, else false
*/

bool operator!=(const iterator& i) { return current != i.current; }

/**
* Overloaded assignment operator
* \param i A const refernce to an iterator object
* \return A reference to 'this' iterator object
*/

iterator& operator=(const iterator& i)
{
if( &i != this )
current = i.current;

return *this;
}

}; // iterator


/**
* Constructor
*/

list();

/**
* Copy constructor
* \param l A const reference to a list object
*/

list(const list& l);

/**
* Destructor
*/

~list();

/**
* Delete all elements in the list.
*/

void clear();

/**
* Return an iterator to the first element in the list
* \return An iterator object to the first element in the list.
*/

iterator begin() const;

/**
* Return an iterator to the point after the last element in the list,
* which effectively is a NULL pointer.
*/

iterator end() const;

/**
* Add an element to the back of the list.
* \param o A const reference to an object of type T
*/

void push_back(const T& o);

/**
* Add a list element to the front of the list
* \param o A const reference to an object of type T
*/

void push_front(const T& o);

/**
* Insert an item in the list before the item pointed to by the specified iterator.
* \param i An iterator pointing to the item to insert before
* \param o The item to insert before iterator i
*/

void insertBefore(iterator& i, const T& o);

/**
* Insert an item in the list after the item pointed to by the specified iterator.
* \param i An iterator pointing to the item to insert after
* \param o The item to insert after iterator i
*/

void insertAfter(iterator& i, const T& o);

/**
* Erase the element at this iterator.
* \param i A const reference to an iterator object
*/

void erase(iterator& i);

/**
* Erase a list element.
* \param pos The position of the element to erase
*/

void erase(unsigned int pos);

};


} // namespace util

#include "list.cpp"

#endif




With the only change to the list.cpp file is I removed the
#include "list.h"
, and it doens't work.

It gives me all the errors you'd expect when it doesnt' recognize list as a template class.


c:\documents and settings\naim\my documents\programming\everything\engine\util\list.cpp(26) : error C2143: syntax error : missing ';' before '<'
c:\documents and settings\naim\my documents\programming\everything\engine\util\list.cpp(26) : error C2501: 'list' : missing storage-class or type specifiers
c:\documents and settings\naim\my documents\programming\everything\engine\util\list.cpp(26) : error C2059: syntax error : ';'
c:\documents and settings\naim\my documents\programming\everything\engine\util\list.cpp(26) : error C2059: syntax error : '<'
c:\documents and settings\naim\my documents\programming\everything\engine\util\list.cpp(26) : error C2039: 'list' : is not a member of '`global namespace''
c:\documents and settings\naim\my documents\programming\everything\engine\util\list.cpp(34) : error C2588: '::~list' : illegal global destructor
c:\documents and settings\naim\my documents\programming\everything\engine\util\list.cpp(34) : fatal error C1903: unable to recover from previous error(s); stopping compilation
test.cpp

Share this post


Link to post
Share on other sites
I didn't see an #endif to close _IMBASE_H_.

However, according to Vandevoorde and Josuttis 'C++ Templates', most people use an inclusion model for solving template-related linker problems. This can be accomplished, for example, by doing away with IMBase.cpp and instead including the code for IMBase.cpp in the IMBase.hpp file. Then the compiler and linker will find everything they need for your template in one .hpp file. The same could be done for your list template, and other templates that you develop. When you include the .hpp files, everything will be in one place so that the compiler and linker should be happy.

The main drawback, according to them is that the compile time will be extended.

--random_thinker


Share this post


Link to post
Share on other sites
Sorry, you also need to remove the list.cpp file from your compiler command line (however you do that in your IDE), since it now gets compiled when included and doesn't need to be compiled as a separate unit. So your IDE should not be building list.cpp, just (for example) imbase.cpp, which includes list.cpp.

Enigma

Share this post


Link to post
Share on other sites
I fell I may not be explaining things very well here, so here's a brief example of what your code should look like:
/* list.h */
#if !defined(my_unique_list_inclusion_guard)
#define (my_unique_list_inclusion_guard)

namespace util
{

template < typename TYPE >
class list
{

list();

// rest of list definition

};

}

#include "list.tmpl"

#endif

/* list.tmpl */
namespace util
{

template < typename TYPE >
list< TYPE >::list()
{
// constructor
}

// rest of list member definitions

}

/* imbase.h */
#if !defined(my_unique_imbase_inclusion_guard)
#define my_unique_imbase_inclusion_guard

#include "list.h"

// imbase definition

#endif

/* imbase.cpp */
#include "imbase.h"

// imbase member definitions

/* main.cpp */
#include "imbase.h"

int main()
{
// some stuff that uses class imbase
}

Then you would build files main.cpp and imbase.cpp, i.e. cl /EHsc main.cpp imbase.cpp.

Enigma

Share this post


Link to post
Share on other sites
I went with moving all the functions for the templated classes into the header file, and it worked fine.

I used to do this all the time, and would you believe that this was the project on which I said "No, do it properly, enough with the dodgy programming practices.", and it turns out that one of these is the solution. [smile]

Thanks for all your help, guys.

Share this post


Link to post
Share on other sites
Thats good news. I don't think that the inclusion model is dodgy programming practice, on the contrary, it has proved to be a simple and robust practice on many projects.

Templates are a sort of 'meta-code' really. Many programmers forget this. For this reason is can often be problematic to separate the definition from the implementation, especially for the linker.

--random_thinker

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this