Auto-Add/Remove Object From Hash On new/delete?


DevX Home    Today's Headlines   Articles Archive   Tip Bank   Forums   

Results 1 to 11 of 11

Thread: Auto-Add/Remove Object From Hash On new/delete?

Hybrid View

  1. #1
    Join Date
    Apr 2005
    Posts
    5

    Auto-Add/Remove Object From Hash On new/delete?

    First off, before I say anything, I just wanted to let you know that you might want to check out these two links for background info on what I'd like to talk about.

    Link 1 (note the line that says: "C::f() is not called from B's constructor"):
    http://www.devx.com/tips/Tip/13651

    Link 2 (this is a question that is similar to myown but doesn't have any solid answer other than "Read the Source Code")
    http://www.devx.com/tips/Tip/23751

    If you looked into those two links, and you're still with me, you already have my thanks.

    Okay... here's what I'm trying to do. On creation of an object, I'd like it to automatically put itself into a hash/manager so the user can never forget or mess up that step... later, on delete, I'd like the object to auto-remove itself from the hash/manager for the same reasons.

    Now the twist, the objects are Children and GrandChildren of a Base class.


    here's a super minimalist version of what I would like to see happening:
    Code:
    class Base
    {...};
    
    class Child : public Base
    {...};
    
    class GrandChild : public Child
    {...};
    
    void main( void )
    {
      Child *pChild = new Child(); //auto instert self into a hash
      GrandChild *pGrandChild = new GrandChild(); //auto instert self into a hash
    
      delete pGrandChild; //auto remove self from hash
      delete pChild; //auto remove self from hash
    }
    naturally I created a virtual function "GetTypeID()" that was define in all three classes and tells the hash where to store the object.

    so this worked:
    Code:
    class Base
    {
      //...
      virtual int GetTypeID( void ) { return 1; }
    };
    
    class Child : public Base
    {
      //...
      virtual int GetTypeID( void ) { return 2; }
    };
    
    
    class GrandChild : public Child
    {
      //...
      virtual int GetTypeID( void ) { return 3; }
    };
    
    
    void main( void )
    {
      Child *pChild = new Child();
      GrandChild *pGrandChild = new GrandChild(); 
    
      AddToManager( pChild, pChild->GetTypeID() );
      AddToManager( pGrandChild, pGrandChild->GetTypeID() );
    
    
      RemoveFromManager( pGrandChild, pGrandChild->GetTypeID() );
      RemoveFromManager( pChild, pChild->GetTypeID() );
    
      delete pGrandChild; 
      delete pChild; 
    }

    however, the moment I moved those "AddToManager()" and "RemoveFromManager()" calls into Base's constructor and deconstructor, it stopped working for reasons that were explained in those to links I gave at the top of this post.

    I understand why it didn't work. I'm cool with that. This post is not to ask someone to explain it to me. This post is to ask "knowing all this, where do I go from here?"

    again, I'd just like Child and GrandChild to add and remove themselves from a hash/manager automatically. But I'm stuck because every solution I do get to work ends up being very messy and involves Every Inherited Class Redefining a New and Delete function.

    It just seems to me that Objects that handle themselves in this manner must occur all the time since C++ is centered on Object Orented coding. I feel like, for an OO Langauge, the code should be way easier than what I've been doing, therefore I figure I must be doing something wrong.

    For those who are still with me (and I thank you), here is my working code. Is there a way to make it more elegent?

    Thanks Everyone,
    ~Danny B.

    My Messy Working Code, Followed by the output that it generates:
    Code:
    #include "stdio.h"
    
    
    class Base
    {
    protected:
    	Base(){ printf( "Base()\n" ); }
    	~Base(){ printf( "~Base()\n" ); }
    public:
    
    	static Base* New( void )
    	{
    		Base *pB = new Base();
    		printf( "AddToSuperCoolManager_ByTypeID( %i );\n", pB->GetTypeID() );
    		return pB;
    	}
    	
    	virtual void DeleteSelf( void )
    	{
    		printf( "RemoveFromSuperCoolManager_ByTypeID( %i );\n", this->GetTypeID() );
    		delete this;
    	}
    
    	virtual int GetTypeID( void ) { return 1; }
    
    }; // end class Base -----------------
    
    
    class Child : public Base
    {
    protected:
    	Child(){ printf( "Child()\n" ); }
    	~Child(){ printf( "~Child()\n" ); }
    public:
    
    	static Child* New( void )
    	{
    		Child *pC = new Child();
    		printf( "AddToSuperCoolManager_ByTypeID( %i );\n", pC->GetTypeID() );
    		return pC;
    	}
    
    	virtual void DeleteSelf( void )
    	{
    		printf( "RemoveFromSuperCoolManager_ByTypeID( %i );\n", this->GetTypeID() );
    		delete this;
    	}
    
    	virtual int GetTypeID( void ) { return 2; }
    
    }; // end class Child -----------------
    
    
    class GrandChild : public Child
    {
    protected:
    	GrandChild(){ printf( "GrandChild()\n" ); }
    	~GrandChild(){ printf( "~GrandChild()\n" ); }
    public:
    
    	static GrandChild* New( void )
    	{
    		GrandChild *pGC = new GrandChild();
    		printf( "AddToSuperCoolManager_ByTypeID( %i );\n", pGC->GetTypeID() );
    		return pGC;
    	}
    
    	virtual void DeleteSelf( void )
    	{
    		printf( "RemoveFromSuperCoolManager_ByTypeID( %i );\n", this->GetTypeID() );
    		delete this;
    	}
    
    	virtual int GetTypeID( void ) { return 3; }
    
    }; // end class GrandChild -----------------
    
    
    void main( void )
    {
    	Child *pChild = Child::New();
    	GrandChild *pGrandChild = GrandChild::New();
    
    	pGrandChild->DeleteSelf();
    	pChild->DeleteSelf();
    }

    Output from above code:
    Code:
    Base()
    Child()
    AddToSuperCoolManager_ByTypeID( 2 );
    Base()
    Child()
    GrandChild()
    AddToSuperCoolManager_ByTypeID( 3 );
    RemoveFromSuperCoolManager_ByTypeID( 3 );
    ~GrandChild()
    ~Child()
    ~Base()
    RemoveFromSuperCoolManager_ByTypeID( 2 );
    ~Child()
    ~Base()

  2. #2
    Join Date
    Dec 2003
    Posts
    3,366
    a different approach, you could require a pointer to a hash structure in all the constructors, to bypass the whole problem. Then its a simple put in on create, pull off on destroy, and the hash is now external to everything so you can manage it apart from all this. Is that helpful or not good for your situation?

  3. #3
    Join Date
    Apr 2005
    Posts
    5
    thanks for the reply jonnin.

    That will definetly work. I was just hoping to put together something that would really cut the end user out of the loop so they wouldn't have to worry about how different objects work.

    Basically, if I wrote an API with a thousand files (each with it's own object class) and told you it was the coolest thing since sliced bread... would you use it, or would it seem overwhelming? Then what if I told you, all you need to know is "New()" and "DeleteSelf()" and all memory managing, loading of assets, UI, etc, would be garrenteed to be create and destroy without any knowledge or interaction on your part.

    for example:

    Code:
    //would you like to use code that some one else wrote
    //that looked something like this:
    {
      AppWindow *pAppWin = new AppWindow();
      SoundManager *pSndMan = new SoundManager( AppWindow );
      SoundFX *pSndFX = new SoundFX( pSndMan );
      Sound3D *pSnd3D = new Sound3D( pSndMan );
    
      // blah blah blah
    
      pSndFX->RemoveFromManager(); //<-- how did the user know to call this?
      delete pSndFX;
      pSnd3D->RemoveFromManager(); // <--*they* never called Add()
      delete pSnd3D;
      pSndMan->RemoveFromManager(); // <-- if they don't call this, will they
      delete pSndMan;
      pAppWin->RemoveFromManager(); // <-- be leaking memory?
      delete pAppWin;
    }
    
    //or
    //would you like to use code that some one else wrote
    //that looked something like this:
    {
      AppWindow *pAppWin = AppWindow::New(); <-- registers itself in hash
      SoundFX *pSndFX = SoundFX::New(); <-- auto create SndMan and reg
      Sound3D *pSnd3D = Sound3D::New(); <-- auto reg w/SndMan
    
      // blah blah blah
    
      pSndFX->DeleteSelf(); // <-- auto remove from SndMan
      pSnd3D->DeleteSelf(); // <-- auto remove from SndMan
      pAppWin->DeleteSelf(); // <-- auto delete SndMan, remove self from hash
    }

    I hope this helps get a clearer picture of what I'd like to do. All of this is just an idea. so there's no right or wrong.... I was just hoping that maybe this level of Object Encapsilation was more common since C++ is always marketed as such an Object Orented language. Being such a big OO Language, I'm suprised I have to make all these New() and DeleteSelf() funcitons myself rather than have the language help handle it for me when new and delete are called.... but.. the language was build in a way such that virtual functions can't be used inside constructors and deconstructors. Hence, I'm finding myself doing a lot of backend work in order to present the use with a clean API.

    I was hoping that I wasn't just plowing off in the wrong direction and there's a really simple answer to setting up objects that handle themselves that I just don't know about...

    thanks again for the replay, and thanks to those who took the time to read all this.

    Open to Suggestions,
    ~Danny
    Last edited by AFool; 04-05-2005 at 02:58 PM.

  4. #4
    Join Date
    Nov 2003
    Posts
    4,118
    To me it looks like you're either trying to reinvent the wheel or missing something crucial about C++. Answering your question: no, I wouldn't be ahppy to use such code that forces me to keep track of every individual object's creation and destruction. Why not use an STL container for example? I know that these containers aren't very good at handling class hierarchies but you can store pointers to polymorphic objects within a container and then call delete vector[i] in a loop. If the destructor of these classes is virtual, delete should do the right thing.
    You also want to consider some sort of smart pointers. The standard defines only one at the moment auto_ptr, which has a few peculiar aspects but for certain tasks, it's pretty good. There is also shared_ptr (available from www.boost.org and soon -- in standard C++ as well).
    Danny Kalev

  5. #5
    Join Date
    Dec 2003
    Posts
    3,366
    What he said...

    But along my idea, all the user has to know is the constructors need a pointer to a hash map or something, which is self documenting... it tells them!
    type foo(); //error: no default constructor
    type foo(phash); //ok

    You get to handle all the internals, pushing to the hash and deleting from it, etc.
    You can even be nice and check... if the incoming pointer is no good, make a new, empty one... It could work, might be a way out, esp if you use the stl hash structure? Its just a thought, not knowing what you *really* want to accomplish. A redesign without the excessive inheritence could also be use to make a self hashing class. There are a couple of options that are 'user free', probably several other ways beyond a redesign or depending on a passed in pointer.

    As far as API's -- take a look at LAPACK. Its the absolute worst set of conventions ever, with happy little routines like 'dorgbr' which sound like the sweedish chef on a bad day. yet it has jillions of users! If you do something unique that is important to many people, they will deal with a bad api. But try to make a good api of course... why would you not make it as nice as you can? (and not picking on lapack too hard -- its age and language are the problem, not the coders)

  6. #6
    Join Date
    Apr 2005
    Posts
    5
    okay, cool. I'll do some more API research before moving on.

    thanks for your input guys!

  7. #7
    Join Date
    Nov 2003
    Posts
    4,118
    you also want to read about the Template Method design pattern (not to be confused with C++ templates). It has a way of differentianting between classes in a hierarchy. You also want to read about virtual base classes. The main davantage is that a virtual base class is conctructed before any other non-virtual base class in a hierarchy, so even it a virtual base is located low in a hierarchy, it's constructed earlier.
    That said, without knowing exactly what you're trying to accomplish, it's hard to come up with the right prescription. My feeling is that you simply need a container of smart pointers to polymorphic objects.
    Danny Kalev

  8. #8
    Join Date
    Apr 2005
    Posts
    5
    I'm making a 3d Game in my spare time. I've been working in the game industry for 5 years now, the industry is just starting to make the switch to C++. It was shunned because it was believed to be slower than C and take up more memory than C. These are the top two concerns for a game. Number 1, never write anything that will slow down the game (it MUST run at 60 frames per second or no one will play it). Number 2, If you have 32mb of RAM, use it down to the last byte (which means, if you have more RAM left, you should be using it to make the game look/feel better ei textures, images, sound --but if you've blown your budget you better optimize your assets and code ASAP, concoles are not PCs, there is no VRAM)

    Recently I worked on a title that was written in C++. I did learn a lot and I think C++ is a very sensible solution for games now that the concoles are getting to be so powerful and the project codebase is getting to be so large.

    so since you wondered exactally what I'm trying to accomplish, here it is: I'd like to write my own 3d game using OpenGL for Rendering and DirectX for input and sound. The topic at hand is the SceneGraph.

    I'm doing this as a learning experience since working in the industry for the last 5 years doesn't mean I've worked on every single part of a game and doesn't mean I've had the chance to plan a game from square one (it's usually "we have a working engine that we're bending to fit our needs for the next game since we don't have much time to start from scratch" aka Legacy Code)

    So here I am, I've written a sound module, and input module, an asset module, an errorHandling module, and now I'm putting together my SceneGraph.

    This is where the need for my questions on elegant encapsilation started. The SceneGraph is one of the more complex parts of a game, it stores the data, directs messages, and allows for gathering groups of specific objects based on type, name, world location, orientation, or assosiation.

    For example, if the Scene was a city, then the SceneGraph would be full of objects that make up that city, all the cars, the people in the cars, the trash bins, traffic lights, pedestrians, etc. Not only does the SceneGraph need to message these objects every frame to say "hey you need to draw", the objects will need to interpret input (user input or AI input), and react to other objects in the scene (Did this car hit anything?)

    So in the end, my game engine was written in C, and writing the code for a new object was so complex that I actually made a seperate command line program that would ask a few questions and then dump out the starting code for a new object's .c and .h files. Then, after working on a C++ project at work for a year, I decided what I was doing was silly, why fight C++ when my C code was getting more and more complex as I tried to recreate and Object Oriented aproach on my own (a silly thing to do).

    Why fight C++ when you could embrace it? So I took a weekend and converted my little project to C++. I actually was able to cut that amount of code in the project by a third.

    So here I am, I just converted my project to C++ and did a couple of passes trying to clean and optimize things when I realized my SceneGraph was leaking memory. Me being a "Fix it, don't Hack it" type of person, I set out to rewrite my SceneGraph, but before I do, I'm trying to do as much research as I can in an atempted to only rewrite it once.

    The most fundimental thing about a SceneGraph is that the user really doesn't care about it. If I create a car, I don't want to think about the traction between the tires and the road or attaching the wheels to the car or creating the FX of smoke out of the tail pipe. Further more, I don't want to be in charage of telling the car to test for collision with other cars or telling it to draw everyframe or updating the position of the engine's 3dSoundObject. However, the car also needs access to things like traffic light states so the AI can tell it not to run red lights, and the location of other objects and cars so the AI can drive around them.

    as a user I just want to say: CreateCar( AtThisLocation ); and know that a car will be put in the world at that location, it will be affected by gravity, it will detect collision with the ground (and will not fall out of the world), it will react if hit, it will draw whenever it is on screen, and it will be release when the level changes.

    There are so many objects in a game, and they share so many of the same qualities that they are perfect for a nice Object Oriented style of programming. However I find myself coming across simple problems while trying to encapsilate the objects (like add/remove from various managers) that I had hoped C++ would have handled more elegantly. So my first thought was "I must be doing something wrong," --and I was. After putting together a working solution my second throught was "There must be a better way, I can't be the first person to try and encapsilate an object to register itself with managers."

    So I'm trying to figure out where I should go, what I should read and how I should restructure my SceneGraph. Someone (on another site) used the word "Factory" as what I wanted to do, but didn't elaborate. It have a general idea that comes to mind. I think I know what a factory is, and I think I've written code that would be classified as a factory but I'm not taking anything for granted. I'd rather assume I don't know what a factory is and go reseach to be sure.

    It may sound silly, but I feel like I just want C++ to work as advertised :) When I have to reproduce a bunch of code in every child class (like the New() and DeleteSelf() functions in my posted code) I wonder if I'm headed in the wrong direction, or if C++ really doesn't encapilate as much as I thought (meaning I am heading in the correct direction)

    I'm the kind of person who assumes the are wrong until they can prove they are right... so I started posting on forums and asking (rather length) questions.

    I'm just going to stop right here. I have a habit of going on and on and just loosing people with too many words.

    Bottom Line:
    --Did all that make sense or confuse you (the reader)?
    --Is my approarch of using New() and DeleteSelf() functions a step in the right direction?
    --Are there any good books/sites I could read on this subject (aka what word should I google)?

    Again, thank you all for taking the time to read my lengthy posts.

    ~Danny B.

    PS I'm about to look up Template Method design pattern right now. Thanks Danny.

  9. #9
    Join Date
    Nov 2003
    Posts
    4,118
    Bottom Line:
    --Did all that make sense or confuse you (the reader)?
    --Is my approarch of using New() and DeleteSelf() functions a step in the right direction?
    --Are there any good books/sites I could read on this subject (aka what word should I google)?
    I think I'm beginning to see what your goal is.
    As for the second question: I think that using explicit member functions for something that C++ an do implicitly (say by contructors and destructors, smart pointers etc.) isn't the ideal chocie, although there are frameworks that choose this path too. I think I have to disappoint you a bit by saying that state of the art C++ hasn't been using classic OO principles for about a decade; things like inheritace, virtual functions and dynamic typing are still being used of course but modern C++ code uses generic programming principles. So, instead of capturing similarities by means of inheritance, C++ libraries use generic class templates and generic algorithms that operate on these types. It's possible to combine the two approaches, say by using STL and pointers to polymorphic pointers as I suggested earlier. Also, notice that C++ has two more types of memory: static memory and automatic memory so you don't really have to allocated every object dynamically.
    Finally, I think that creating a huge object ("City") is a design mistake. Instead, you want to represent a city as collection of attributes, say name, coordiantes etc. but the other objects (cars, people) shouldn't be stored as data members. Instead, make them separate objects or better yet, collections of objects that the city object has access to, say by taking a pointer to these elements. After all, cars and people can move from one city to another and it's possible to have different types of cities: ones that have no cars, for example.

    As for your last question: first and foresmost, look for information about STL. There are huge amounts of literature and if you're new to templates it might be overwhelming at first but in the long run, it's a wortwhile investment as it's the only way to write a decent C++ project these days.
    Danny Kalev

  10. #10
    Join Date
    Apr 2005
    Posts
    5
    Okay cool. Thanks, I'll start reseaching these things.

  11. #11
    Join Date
    Dec 2003
    Posts
    3,366
    have you seen openscenegraph (based on opengl) ? Its cool, but suffers a bit of bloat that a uber fast game may not care for. But then, shouldnt direct3d and directdraw be faster still? I like opengl code, but directx blows it away on modern PC systems.
    Last edited by jonnin; 04-06-2005 at 06:34 PM.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
HTML5 Development Center
 
 
FAQ
Latest Articles
Java
.NET
XML
Database
Enterprise
Questions? Contact us.
C++
Web Development
Wireless
Latest Tips
Open Source


   Development Centers

   -- Android Development Center
   -- Cloud Development Project Center
   -- HTML5 Development Center
   -- Windows Mobile Development Center