Skip to main content
gamedev.net
🔒 Locked

Template substantiation

Started by ZedFx 10 replies 1.5k views
Original Post
ZedFx
ZedFx
Cast your eyes over the following.

template < typename T > struct tMemory
{
	static void Allocate(T * & Memory, unsigned int Count);
	static void Free(T * & Memory);

};

template < typename T > void tMemory< T >::Allocate(T * & Memory, unsigned int Count)
{

	try
	{
		Memory = new T[Count];

	}

	catch (...)
	{
		Err_OutOfMemory.Raise(); // this is defined elsewhere (do not worry about it)

		goto error;

	}

	return;

error:

	// display error message

	return;

}

template < typename T > void tMemory< T >::Free(T * & Memory)
{
	delete [] Memory;

}

typedef int (* cFunc)(void *);

class cTest
{
public:

	// rest of class

	void CreateFunc(unsigned int Count);

	// rest of class

private:

	tMemory< cFunc > m_Memory;
	cFunc * m_MemoryPtr;

	// other member variables

	void DestroyFunc();

};

void cTest::CreateFunc(unsigned int Count)
{
	m_Memory.Allocate(m_MemoryPtr, Count);

}


void cTest::DestroyFunc()
{
	m_Memory.Free(m_MemoryPtr);

}



I am getting linker errors where static void tMemory< int (*)(void *) >::Allocate(int (** &)(void *), unsigned int); static void tMemory< int (*)(void *) >::Free(int (** &)(void *)); are both unresolved.... I have no idea why, anyone else have any ideas ? EDIT: sloppy coding.. (fixed) [Edited by - ZedFx on August 31, 2005 5:18:42 PM]
SiCrane
SiCrane
Once I change the function prototype of DestroyFunc so that it matches with the definition, it compiles and links fine for me on MSVC 2003. The template functions are all defined in headers or are otherwise available right?
MaulingMonkey
MaulingMonkey
To toss out some random possibilities:

1) Try placing the static keyword with the function declerations as well. I don't think it's needed (it may not even be valid) but it's worth a shot.
Given SiCrane's response, I apparently was correct in thinking it wasn't needed.

2) Are the functions declared within a source file? Currently, most compilers require the functions be declared where they're "visible" for proper instantiation, or manually instantiated. E.g.:

Option 1: Visible decleration for automatic instantiation (multiple files not required)
//tmemory.hpp#ifndef IG_TMEMORY#define IG_TMEMORYtemplate < typename T > struct tMemory {    ...};#include "tmemory.impl"#endif //ndef IG_TMEMORY//tmemory.impltemplate < typename T > void tMemory< T >::Allocate(T * & Memory , unsigned int Count ) {    ...}...


Option 2: Manual instantiation
//tmemory.hpp#ifndef IG_TMEMORY#define IG_TMEMORYtemplate < typename T > struct tMemory {    ...};//Not including their actual implementation://<strike>#include "tmemory.impl"</strike>#endif //ndef IG_TMEMORY//tmemory.impltemplate < typename T > void tMemory< T >::Allocate(T * & Memory , unsigned int Count ) {    ...}...//tmemory.cc#include "tmemory.impl"template tMemory<int>;template tMemory<char>;template tMemory<int (*)(void *)>;...


Option 3: Use the export template feature
This isn't available on most compilers. However, see Comeau C++ Export Overview documentaiton for that, for the, like, one compiler that does that. Comeau, in case you couldn't guess :-).
ZedFx
ZedFx
template < typename T > struct tMemory
is defined in tMemory.h

template < typename T > void tMemory< T >::Allocate(T * & Memory, unsigned int Count)
and
template < typename T > void tMemory< T >::Free(T * & Memory)
are defined in tMemory.cpp which includes tMemory.h

typedef int (* cFunc)(void *);
is defined in cFunc.h

class cTest
is defined in cTest.h which includes tMemory.h

void cTest::CreateFunc(unsigned int Count)
and
void cTest::DestroyFunc()
are defined in cTest.cpp which include cTest.h (and implicitly tMemory.h)

That is how I have it structured essentially.
SiCrane
SiCrane
Quote:
Original post by ZedFx
template < typename T > void tMemory< T >::Allocate(T * & Memory, unsigned int Count)
and
template < typename T > void tMemory< T >::Free(T * & Memory)
are defined in tMemory.cpp which includes tMemory.h


Here's the problem. Unless your compiler supports the export keyword, and I'm willing to bet that yours doesn't, then you can't put the definition of a template in a separate source file without explicit instantiation for specific types. Without explicit instantiation, the complete definition of the template needs to be available at point of instantiation, which means, in effect, that the definition needs to go into the header. (Or an inline file of some sort, etc.)

For more details see these articles: "Export" Restrictions, Part 1 and "Export" Restrictions, Part 2.
ZedFx
ZedFx
Thanks. Damn C++ is finicky... I need a new language ;-)
kSquared
kSquared
Just as a friendly addendum, you'll probably want to avoid code of the sort that you wrote below:

	catch (...)	{		Err_OutOfMemory.Raise(); // this is defined elsewhere (do not worry about it)		goto error;	}	return;error:	// display error message	return;


Using goto statements is in general frowned upon, but escaping from a catch block is definitely evil. Also evil is using the dreaded ellipsis construct in a catch expression. It's pretty clear that you really wanted to catch some kind of out of memory exception ("Err_OutOfMemory.Raise()") in that catch block, so that's what you should be catching instead.
- k2 "Choose a job you love, and you'll never have to work a day in your life." — Confucius"Logic will get you from A to B. Imagination will get you everywhere." — Albert Einstein"Money is the most egalitarian force in society. It confers power on whoever holds it." — Roger Starr{General Programming Forum FAQ<
Polymorphic OOP
Polymorphic OOP
Quote:
Original post by kSquared
Also evil is using the dreaded ellipsis construct in a catch expression.

That much I definately would not call evil, even though in this case, considering the body of the catch, it is.

Often times, especially in templates, you may need to peform some type of memory deallocation, other cleanup, or something else when any exception is thrown, usually prior to a rethrow (since you often can't handle an exception when you don't even know what it is). You may not know what type of exceptions can be thrown, for instance, because you are in a template and the exception is thrown by something dependent on one or more template arguments, such as in this case. Note that here, there is the dynamic allocation of template argument T in the try expression. His catch assumes that only an out of memory exception can be thrown (since he is always calling Err_OutOfMemory.Raise()). Realistically, however, any type of exception can be thrown. This is because the constructor call on one of the array elements may throw an exception for any reason. You don't know what types are possible (and generally shouldn't care) since that information is dependent on T. Such an error should probably still be caught, partially handled (maybe for logging, releasing things not accounted for by RAII, etc.), and then probably rethrown. Using ... in a catch statement is really the best way to handle such a situation.
Deyja
Deyja
The best solution here would be to have two catch blocks, of course. Catch the out-of-memory condition explicitly (Though it's actually fairly difficult to get new to throw. It calls the new handler and this path usually leads to abort().) and catch every other kind of exception with ... I usually prefer to catch exceptions derived from std::exception in another seperate block, as I know these have a loggable what() function. All exceptions should be derived from this anyway.
kSquared
kSquared
Quote:
Original post by Polymorphic OOP
Quote:
Original post by kSquared
Also evil is using the dreaded ellipsis construct in a catch expression.

That much I definately would not call evil, even though in this case, considering the body of the catch, it is.

That's what I really meant to say (see the following sentence of my previous post). :)
- k2 "Choose a job you love, and you'll never have to work a day in your life." — Confucius"Logic will get you from A to B. Imagination will get you everywhere." — Albert Einstein"Money is the most egalitarian force in society. It confers power on whoever holds it." — Roger Starr{General Programming Forum FAQ<
ZedFx
ZedFx
Yes essentially I am trying to catch any error from new and rethrow a user-defined error class.

Although specifically documentation says new will throw a bad_alloc when an out of memory condition is reached, yet what I am doing is a generalisation for when memory allocation fails.

As for the goto keyword, here it is *really* un-needed, it was just a pattern I was following in the program I was writing.
antareus
antareus
Quote:
Original post by Polymorphic OOP
Using ... in a catch statement is really the best way to handle such a situation.

On Win32, this is complicated by the fact that exceptions are almost always implemented in terms of Win32 structured exceptions. The fallout from this is that the catch(...) construct will squelch *all* exceptions, even those you'd want to consider horribly fatal (such as an access violation). If you aren't running under a debugger, you'd never know that code within the try block generated an AV.

I don't know of a way to filter out structured exceptions from C++ ones other than using a common base exception class, and dictating its use system-wide. From what I understand, this is a Win32 oddity. I believe *NIX responds by sending a signal to the process.
--God has paid us the intolerable compliment of loving us, in the deepest, most tragic, most inexorable sense.- C.S. Lewis

Topic Locked

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

Sign in to reply to this topic.