Skip to main content
GameDev.net gamedev.net
🔒 Locked

template class using std::map

Started by Rhiakath Jun 17, 2010 at 7:44 AM 7 replies 1.5k views
Original Post
Rhiakath
Rhiakath
Hi there.
I want to create a template class CDatabase , which inside has an std::map .

So, for example, creating a CDatabase with a string as key, it would be
std::map something;

so far, i got this:

template <typename Type> class CKeyDatabase	{	public:		typedef std::map <Type, void*> TDBMap;

Alas, it does not work. It gives out:

/home/rhiakath/Programming/Database/Source/KeyDatabase.h:14: error: type 'std::map, std::allocator > >' is not derived from type 'Database::CKeyDatabase'


What's wrong here?
Thanks

[Edited by - ApochPiQ on June 17, 2010 3:57:08 PM]
alvaro
alvaro
The code you posted doesn't result in any such errors. Try to put together some code that I can try to compile myself and see the error.
Rhiakath
Rhiakath
Nevermind the whole bunch of error in this code. I'm just trying to adapt from an existing class, and so i'm trying to fix this issue first, before anything else.

keydatabase.h:


#ifndef KEYDATABASE_INCLUDED
#define KEYDATABASE_INCLUDED

#include "map"

namespace Database
{

template <typename Type> class CKeyDatabase
{
public:
typedef std::map <Type, void*> TDBMap;
typedef TDBMap::iterator TIterator;

protected:
TDBMap Entries;
unsigned ChangeCount;

public:
CDatabase ( void );
~CDatabase ( void );
bool AddEntry ( Type In_Key, void *In_Entry );
bool DeleteEntry ( Type In_Key );
bool GetEntry ( Type In_Key , void **Out_Entry );
unsigned GetEntryCount ( void ) const;
void Clear ( void );
TIterator Begin ( void );
TIterator End ( void );
void * operator () ( const Type In_Key );
};

}

#endif


keydatabase.cpp

#include "KeyDatabase.h"
#include "stdio.h"

namespace Database
{

/**
*
* @param
*/
template <typename Type> CKeyDatabase::CKeyDatabase ( void )
{
ChangeCount = 0;
}

/**
*
* @param
*/
template <typename Type> CKeyDatabase::~CKeyDatabase ( void )
{
Clear();
}

/**
*
* @param In_Entry
* @return
*/
template <typename Type> bool CKeyDatabase::AddEntry ( Type In_Key, void *In_Entry )
{
std::pair <TIterator, bool> Result;
Result = Entries.insert ( std::make_pair ( In_Key, In_Entry ) );
if ( Result.second == false )
{
printf ( "DATABASE! UNABLE TO INSERT RECORD" );
return false;
}

++ChangeCount;
return true;
}

/**
*
* @param In_Index
* @return
*/
template <typename Type> bool CKeyDatabase::DeleteEntry ( Type In_Key )
{
TIterator Iterator;
Iterator = Entries.find ( In_Key );
if ( Iterator == Entries.end() )
return false;

Entries.erase ( Iterator );
++ChangeCount;
return true;
}

/**
*
* @param In_Index
* @param Out_Entry
* @return
*/
template <typename Type> bool CKeyDatabase::GetEntry ( Type In_Key, void **Out_Entry )
{
TIterator Iterator;
Iterator = Entries.find ( In_Key );
if ( Iterator == Entries.end() )
return false;

*Out_Entry = Iterator->second;
return true;
}

/**
*
* @param
* @return
*/
template <typename Type> unsigned CKeyDatabase::GetEntryCount ( void ) const
{
return Entries.size();
}

/**
*
* @param
*/
template <typename Type> void CKeyDatabase::Clear ( void )
{
Entries.clear();
++ChangeCount;
}

/**
*
* @param
* @return
*/
template <typename Type> CKeyDatabase::TIterator CKeyDatabase::Begin ( void )
{
return Entries.begin();
}

/**
*
* @param
* @return
*/
template <typename Type> CKeyDatabase::TIterator CKeyDatabase::End ( void )
{
return Entries.end();
}

/**
*
* @param
* @return
*/
/*template <typename Type> CDBIterator CKeyDatabase::GetIterator ( void )
{
return CDBIterator ( &Entries );
}
*/
/**
*
* @param In_Index
* @return
*/
template <typename Type> void * CKeyDatabase::operator () ( const Type In_Key )
{
TIterator Iterator;
Iterator=Entries.find ( In_Key );

if ( Iterator == Entries.end() )
{
printf ( "TDatabase - Index unavailable\n" );
return NULL;
}
else
return Iterator->second;
}

}


summaky
summaky
TDBMap::iterator is a dependent type and the compiler has no way to know whether you are referring to a type or a variable. Unless told otherwise, it assumes is a variable.

What you need to do is to tell the compiler that is a type, like so:

typedef typename TDBMap::iterator TIterator;


I won't say anything about the other errors, as requested :-)
Rhiakath
Rhiakath
Ok, great! That, along with some other bunch of changes, cleared most of the error.

Now, the next one...

#include "KeyDatabase.h"#include "stdio.h"namespace Database	{template <typename Type>CKeyDatabase <Type>::CKeyDatabase ( void )	{	ChangeCount = 0;	}template <typename Type>CKeyDatabase <Type>::~CKeyDatabase ( void )	{	Entries.clear();	}template <typename Type>bool CKeyDatabase <Type>::AddEntry ( Type In_Key, void *In_Entry )	{	std::pair <TIterator, bool> Result;	Result = Entries.insert ( std::make_pair ( In_Key, In_Entry ) );	if ( Result.second == false )		{		printf ( "DATABASE! UNABLE TO INSERT RECORD" );		return false;		}	++ChangeCount;	return true;	}template <typename Type>bool CKeyDatabase <Type>::DeleteEntry ( Type In_Key )	{	TIterator Iterator;	Iterator = Entries.find ( In_Key );	if ( Iterator == Entries.end() )		return false;	Entries.erase ( Iterator );	++ChangeCount;	return true;	}template <typename Type>bool CKeyDatabase <Type>::GetEntry ( Type In_Key, void **Out_Entry )	{	TIterator Iterator;	Iterator = Entries.find ( In_Key );	if ( Iterator == Entries.end() )		return false;	*Out_Entry = Iterator->second;	return true;	}template <typename Type>unsigned CKeyDatabase <Type>::GetEntryCount ( void ) const	{	return Entries.size();	}template <typename Type>void CKeyDatabase <Type>::Clear ( void )	{	Entries.clear();	++ChangeCount;	}template <typename Type>CKeyDatabase<Type>::TIterator CKeyDatabase <Type>::Begin ( void )	{	return Entries.begin();	}template <typename Type>CKeyDatabase <Type>::TIterator CKeyDatabase <Type>::End ( void )	{	return Entries.end();	}template <typename Type>CDBIterator CKeyDatabase <Type>::GetIterator ( void )	{	return CDBIterator ( &Entries );	}template <typename Type>void * CKeyDatabase <Type>::operator () ( const Type In_Key )	{	TIterator Iterator;	Iterator=Entries.find ( In_Key );	if ( Iterator == Entries.end() )		{		printf ( "TDatabase - Index unavailable\n" );		return NULL;		}	else		return Iterator->second;	}	}


declared with

#ifndef KEYDATABASE_INCLUDED#define KEYDATABASE_INCLUDED#include "map"namespace Database	{template <typename Type> class CKeyDatabase	{	public:		typedef std::map <Type, void*> TDBMap;		typedef typename TDBMap::iterator TIterator;	protected:		TDBMap						Entries;		unsigned					ChangeCount;	public:		CKeyDatabase ( void );		~CKeyDatabase ( void );		bool						AddEntry ( Type In_Key, void *In_Entry );		bool						DeleteEntry ( Type In_Key );		bool						GetEntry ( Type In_Key , void **Out_Entry );		unsigned					GetEntryCount ( void ) const;		void						Clear ( void );		TIterator					Begin ( void );		TIterator					End ( void );		void *						operator () ( const Type In_Key );	};	}#endif

This gives out:
[ 25%] Building CXX object CMakeFiles/lib.dir/Source/KeyDatabase.cpp.o
/home/rhiakath/Programming/Database/Source/KeyDatabase.cpp:73: error: expected constructor, destructor, or type conversion before 'CKeyDatabase'

Any ideas? (I really don't get along with templates)
meisawesome
meisawesome
Don't forget, you need to put typename when declaring something with types declared inside a template class.

template <typename Type>typename CKeyDatabase<Type>::TIterator CKeyDatabase <Type>::Begin ( void )	{	return Entries.begin();	}template <typename Type>typename CKeyDatabase<Type>::TIterator CKeyDatabase<Type>::End ( void )	{	return Entries.end();	}
Rhiakath
Rhiakath
ok, almost there.
(this is the reason why i don't get along with templates)

Now i have a linker error. Undefined reference. I guess I should put the code inside the header file, but i REALLY hate that.

#include "../Source/KeyDatabase.h"#include "string.h"#include "stdio.h"#include <string>int main ( int argc, char *argv[] )	{	printf("KeyDatabaseTest");	Database::CKeyDatabase<std::string> DB2;	DB2.AddEntry("val1",(void *)"teste1");	DB2.AddEntry("val2",(void *)"teste2");	DB2.AddEntry("val3",(void *)"teste3");	DB2.AddEntry("val4",(void *)"teste4");	DB2.AddEntry("val5",(void *)"teste5");	printf ( "teste iterador da bd\n" );	for ( Database::CKeyDatabase <std::string> ::TIterator Iterator = DB2.Begin();Iterator != DB2.End();++Iterator )		{		printf ( "%s - %s\n", (static_cast <std::string> (Iterator->first)).c_str(), static_cast <char *> ( Iterator->second ) );		}		return 0;	}


undefined reference to `Database::CKeyDatabase, std::allocator > >::CKeyDatabase()'

And so on, so on... For every line of code...
SiCrane
SiCrane
Short answer: when dealing with template functions you pretty much should put the function definitions in the header.
Rhiakath
Rhiakath
Sniff... Sniff....
But I have some HUGE classes i want to convert to templates.... sniff sniff...

Is there no way to keep cpp and h files separate?


Ok. I think i managed a "less-ugly" solution.

I include the cpp file within the header, and wrap the cpp file with #ifdef KEYDATABASE_INCLUDED

So, it only compiles the code IF it's being included thru the header.

So... I keep hating templates

But thanks. You have been most helpful:D

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.