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);
}
Template substantiation
Cast your eyes over the following.
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]
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?
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)
Option 2: Manual instantiation
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 :-).
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://#include "tmemory.impl"#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 :-).
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.
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.
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.
Just as a friendly addendum, you'll probably want to avoid code of the sort that you wrote below:
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.
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.
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.
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.
Quote:Original post by Polymorphic OOPQuote: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). :)
This topic is closed to new replies.
Advertisement
Popular Topics
Advertisement