Sign in to follow this  
ZedFx

Template substantiation

Recommended Posts

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]

Share this post


Link to post
Share on other sites
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?

Share this post


Link to post
Share on other sites
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_TMEMORY

template < typename T > struct tMemory {
...
};

#include "tmemory.impl"
#endif //ndef IG_TMEMORY

//tmemory.impl
template < typename T > void tMemory< T >::Allocate(T * & Memory , unsigned int Count ) {
...
}
...


Option 2: Manual instantiation
//tmemory.hpp
#ifndef IG_TMEMORY
#define IG_TMEMORY

template < typename T > struct tMemory {
...
};

//Not including their actual implementation:
//#include "tmemory.impl"
#endif //ndef IG_TMEMORY

//tmemory.impl
template < 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 :-).

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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). :)

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

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