Repeat of destructor/constructor C++ discussion.  TFC uses them.  HL
doesn't.  Read why and how below.

-----Original Message-----
From: Ken Birdwell 
Sent: Thursday, September 13, 2001 2:31 AM
To: '[EMAIL PROTECTED]'
Subject: RE: [HLCoders] CBaseEntity Memory Mgmnt Q.


I guess an important point to remember is that there is NO C++ in the
engine.  It's 100% C and assembly*.  The only C++ portions are the game
(server) and client dll.  Their interface to the engine is 100% C, that's
what all the Dispatch<blah> functions are for in cbase.cpp; they resolve the
C interface to their C++ counterparts.

During initialization, the engine calls the game dll's GetEntityAPI()
function.  This returns a structure filled in with function pointers to all
the game dll's dispatch functions, functions that the engine can then call
at the appropriate time.  Half-life implemented its game DLL in C++, though
you don't have to. At an early stage of the project the game dll was in fact
written completely in C - it was quicker to convert the original Quake 1 QC
code to C first instead of directly to C++ - your game dll could also be
written completely in C, but I can't think of why you'd ever want to.

Besides calling GetEntityAPI() during initialization, the engine also calls
the function GetEntityAPI2(), in which the game dll can return back another
list of dispatch functions, most notably containing the function
OnFreeEntPrivateData.  This function is called by the engine whenever it is
going to free up an edict, it's basically giving the game dll an opportunity
to do something - such as calling a local destructor on the private data -
before that memory becomes invalid.

As to constructors and destructors, we never used them.  The reasons are
mostly historical, there's no fundamental reason why we didn't, it's just
that the during the conversion from C to C++ they were never needed and we
just got into the habit of not using them.  Well, that's not totally true,
you may also notice that there's not a single** call to "new" in the entire
game dll, it (was) 100% written to just use the engine's memory management
system.  The driving force behind that coding standard was that the game
needed to run on a 16MB console machine with no virtual memory, and you
should also note that there's no (okay, hardly any) explicit CBaseEntity
pointers in any of the base classes since in some configurations (that you
don't have to worry about) the engines garbage collection memory system can
(could) move the addresses of everything on you between calls to
DispatchThink().  Not really something that's very clean to do on a real
free form C++ system, so avoiding calls to new and calls to destructors and
constructors made it a lot simpler to avoided bugs when that happened,
though I don't think the code works perfectly in its current configuration
(ie the GearBox guys had to beat on it some to make in run on a machine like
the PS2 again).

So who cares, call new all you want, make your mod run on a system that
needs 128MB of ram and go for it, you don't need to make sure it runs on all
the computers that people bought in 1995 like we had to (boo hoo, now I'm
just whining).  If you want your destructors called, do this:

NEW_DLL_FUNCTIONS gNewDLLFunctions =
{
        OnFreeEntPrivateData,           //pfnOnFreeEntPrivateData
        NULL,                                   //pfnGameShutdown
        NULL,                                   //pfnShouldCollide
};

int GetNewDLLFunctions(NEW_DLL_FUNCTIONS *pFunctionTable, int
*interfaceVersion)
{
        if(!pFunctionTable || *interfaceVersion !=
NEW_DLL_FUNCTIONS_VERSION)
        {
                *interfaceVersion = NEW_DLL_FUNCTIONS_VERSION;
                return FALSE;
        }

        memcpy(pFunctionTable, &gNewDLLFunctions, sizeof(gNewDLLFunctions));
        return TRUE;
}

void OnFreeEntPrivateData(edict_s *pEdict)
{
        if(pEdict && pEdict->pvPrivateData)
        {
                ((CBaseEntity*)pEdict->pvPrivateData)->~CBaseEntity();
        }
}

*okay, technically there's C++ in the engine for VGUI and audio stuff, but
it's very small and only went in the last year or so and it wasn't there
last time I worked on it.
**not sure if that's true anymore, it was true last time I checked.

-----Original Message-----
From: Soul Sounds [mailto:[EMAIL PROTECTED]]
Sent: Wednesday, September 12, 2001 10:10 AM
To: [EMAIL PROTECTED]
Subject: [HLCoders] CBaseEntity Memory Mgmnt Q.


============================================================
Should You Check Your Credit Report? Of course! We all check
our credit card statements for inaccuracies and we should do
the same for our credit history. Click here now to check
yours FOR FREE at ConsumerInfo.Com!
http://click.topica.com/caaadcTclvXIra2kxuha/ConsumerInfo
============================================================

Hello,
 
I tried adding a destructor to a class I derived from CBaseEntity and 
got the following error:
 
"C:\SIERRA\Half-Life\SDK\SourceCode\dlls\CBigThinker.cpp(51) : error 
C2573: 'CBigThinker' : cannot delete pointers to objects of this type; 
the class has no non-placement overload for 'operator delete'."
 
During my explorations I found that CBaseEntity has it's own "placement" 
overload of the new() operator and, if the MSVC++ compiler version is 
6.0 or higher, it has its own "placement" overload for the delete() 
operator.  I noticed that the delete() operator simply sets the 
FL_KILLME flag in the pev->flags variable, and does nothing else.  
 
So it appears that the engine is responsible for allocating memory for 
all classes in the CBaseEntity chain, and cleaning up such allocations 
in a Java-like fashion (that is, whenever it needs to or decides to), by 
looking for FL_KILLME in the containing class's "pev" variable.  
 
To test my assumptions a I created a bogus class called 
_DestructorTest() which output's a simple debug message, using 
OutputDebugString(), in its constructor() and destructor().  I then put 
an instance of the variable in my CBaseEntity's derived class 
definition, in the private section.  I ran my HL mod, and then blew up 
the map's single instance of my CBaseEntity derived class with a 
grenade, and then exited HL normally.  I did see the debug message from 
the _DestructorTest class's constructor().  However, there was 
absolutely no corresponding message from the destructor().
 
This brings up an important question.  If this is true, then it is a 
dangerous idea to allocate any memory dynamically in a CBaseEntity 
derived class or even put any memory consuming variables that do 
essential cleanup work in their destructor, since their destructor will 
_never_ be called, as shown in my test.  Is this true?  I would like 
definite confirmation on this, and any auxiliary useful information you 
might have.
 
Thanks.
 
 
 

============================================================
TopOffers: the Smart Shoppers Savings Secret!
Cool savings. Hot deals. Free stuff. Right in your inbox.
And, get a FREE BONUS when you sign up now!
http://click.topica.com/caaadfIclvXIra2kxuhf/Topica
============================================================


An archive exists at: http://www.topica.com/lists/hlcoders

==^================================================================
EASY UNSUBSCRIBE click here: http://topica.com/u/?clvXIr.a2kxuh
Or send an email To: [EMAIL PROTECTED]
This email was sent to: [EMAIL PROTECTED]

T O P I C A -- Register now to manage your mail!
http://www.topica.com/partner/tag02/register
==^================================================================
_______________________________________________
To unsubscribe, edit your list preferences, or view the list archives, please visit:
http://list.valvesoftware.com/mailman/listinfo/hlcoders

Reply via email to