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