Thread: What I've got so far, creating a menu system:

  1. #1
    Absent Minded Programmer
    Join Date
    May 2005
    Posts
    968

    What I've got so far, creating a menu system:

    So, I have 5 header files with some stuff and an empty cpp file, I have various classes that work together with some missing pieces:

    So far I have:

    Interpreter: Takes input from the user via string, and sends the command to the right engine part for processing, well, perhaps more ideally the proper game component, which aren't in existence yet .

    Object: Basic object class, courtesy goes to Bubba for the suggestion
    Object Manager: container for objects
    Database: handles the initialization of object managers and the creation of objects, as well as population of the managers..

    Menu Object: Subclass to Object, the Menu objects are designed to be a sort of linked list menu system, it should provide easy creation of menu's and lots of on the fly menu tweaking, such as options that aren't available at a certain time.

    And the code:

    Interpreter:
    Code:
    #ifndef INTERPRETER_H
    #define INTERPRETER_H
    
    #include <string>
    #include <map>
    #include "Database.h"
    #include <iostream>
    
    class Interpreter
    {
    public:
    
    	typedef std::map< std::string, std::string > Command_List;
    
    	virtual void Navigate_Menu(std::string Command)
    	{	
    		Command_List::iterator it = Commands.find(Command);
    
    		if(it == Commands.end())
    		{
    			std::cout << "Fail" << std::endl;
    		}
    			
    
    		//if it is a recognized command, we delete
    		//the current list of valid commands
    
    		//then
    
    		//We make a new list of valid coimmands by
    		//inserting command strings into valid commands
    		//by looping through the selected menu's
    		//children and getting their Selection string
    		
    	}
    
    private:
    	
    	Command_List Commands;
    };
    #endif
    Object/Object Manager:
    Code:
    #ifndef OBJECT_H
    #define OBJECT_H
    
    #pragma warning(disable: 4786)
    #pragma warning(disable: 64)
    #include <vector>
    #include <map>
    #include <string>
    #include <boost\shared_ptr.hpp>
    #include <boost\weak_ptr.hpp>
    #include <boost\smart_ptr.hpp>
    #include <iostream>
    
    class CObject
    {
    
    public:
    
    	virtual void Create()
    	{}
    
    	virtual void Access()
    	{}
    
    	virtual void Delete()
    	{delete this;}
    private:
    	virtual void Save_to_Disk(std::string Filename)
    	{}
    	
    	virtual void Read_File(std::string Filename)
    	{}
    };
    
    class Object_Manager
    {  
    public:
    	Object_Manager() {};
    	~Object_Manager() {};
    
    	typedef boost::shared_ptr<CObject> Object_Ptr;
    	typedef boost::weak_ptr<CObject> Object_Observer;
    	typedef std::map< std::string, Object_Ptr > Object_Map;
    
    	void Add_Object(const std::string & name, CObject * pObject)
    	{
    		Object_Ptr Raw_Object(pObject);
    		mObjects.insert(std::make_pair(name, Raw_Object));
    	}
    
    	Object_Observer Request_Object(const std::string & name)
    	{
    		Object_Map::iterator  it = mObjects.find(name);
    
    		if (it == mObjects.end())
    		{
    			std::cout << "Internal program error, " << name << " does not exist!" << std::endl;
    		}
    		else
    		{
    			return Object_Observer(it->second);
    		}
    	}
    
    	void Request_Object_Removal(const std::string & name)
    	{
    		Object_Map::iterator it = mObjects.find(name);
    
    		if (it != mObjects.end())
    		{
    			mObjects.erase(it);
    		}
    	}
    private:
    	Object_Map  mObjects;
    };
    #endif
    Database:
    Code:
    #ifndef DATABASE_H
    #define DATABASE_H
    
    #include "Menu_Object.h"
    
    class Database
    {
    public:
    
    };
    
    #endif
    Most of the code is largely incomplete, but it shows you the basic concept of the menu system at least. I want the process of events to move like so:

    Input->Interpreter->Database->Menu_Manager->put game component here->Output

    Constructive criticism encouraged

    I'm currently working on object serialization so I can save my objects to files and load them later, this will be perfect for a menu system I think. I'll post updates of my progress in this area, because it is crucial to creating menu objects on a massive scale with all the right stuff, as well as other objects I may have.
    Last edited by Shamino; 06-12-2007 at 08:50 AM.
    Sometimes I forget what I am doing when I enter a room, actually, quite often.

  2. #2
    Absent Minded Programmer
    Join Date
    May 2005
    Posts
    968
    This is my first attempt at making the Object base class writable to disk:
    Code:
    class CObject
    {
    
    public:
    
    	virtual void Create()
    	{}
    
    	virtual void Access()
    	{}
    
    	virtual void Delete()
    	{delete this;}
    
    private:
    	virtual void Save_to_Disk(std::string Filename)
    	{}
    	
    	virtual void Read_File(std::string Filename)
    	{}
    	friend class boost::serialization::access;
    	template<class Archive>
    	virtual void boost::serialization::serialize(Archive & ar, const unsigned int version){};
    };
    The problem is, I get an error of this nature:
    Code:
    object.h(39) : error C2838: 'serialize' : illegal qualified name in member declaration
    Any clues on this one anyone? It sucks because this is how boost says you should do it
    Sometimes I forget what I am doing when I enter a room, actually, quite often.

  3. #3
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    Have you included the right header file for boost::serialization? The only boost header files that you've included (in a different file, however) are
    Code:
    #include <boost\shared_ptr.hpp>
    #include <boost\weak_ptr.hpp>
    #include <boost\smart_ptr.hpp>
    which don't look like the right ones.

    It looks like the sort of error you might get when you did something like this:
    Code:
    std::something invalid;
    But I'm not familiar with that compiler's (MSVC?) error messages so I can't be more specific.
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  4. #4
    Absent Minded Programmer
    Join Date
    May 2005
    Posts
    968
    I fixed the problem, it had to do with the template callback method serialize, I didn't fully understand what it was doing so I made a silly syntax error.

    Heres the new code:

    Code:
    #include <vector>
    #include <map>
    #include <string>
    #include <boost\shared_ptr.hpp>
    #include <boost\weak_ptr.hpp>
    #include <boost\smart_ptr.hpp>
    #include <iostream>
    #include <fstream>
    #include <boost/archive/text_iarchive.hpp>
    #include <boost/archive/text_oarchive.hpp>
    #include <boost/serialization/base_object.hpp>
    
    Here is the root Object class:
    class CObject
    {
    
    public:
    	CObject(){}
    
    	virtual void Create()
    	{}
    
    	virtual void Access()
    	{}
    
    	virtual void Delete()
    	{delete this;}
    
    private:
    	friend class boost::serialization::access;
    	template<class Archive>
    	void serialize(Archive & ar, const unsigned int version){};
    };
    Here is derived subclass serialization in action:
    Code:
    #include "Object.h"
    #include <string>
    #include <list>
    #include <boost/serialization/string.hpp>
    
    class Menu : public CObject
    {
    public:
    	Menu(){};
    	Menu(std::string mSelection) 
    	{
    		mSelection = Selection;
    	}
    	virtual ~Menu() { Destroy(); }
    	// deletes self
    	void Release() { delete this; }
    	// call update on all children
    	virtual void Update()
    	{
    		for( std::list<Menu*>::iterator i = MenuChoices.begin();
    			i != MenuChoices.end(); i++ )
    		{
    			(*i)->Update();
    		}
    	}
    	// recursively destroy all children and self
    	void Destroy()
    	{
    		for( std::list<Menu*>::iterator i = MenuChoices.begin();
    			i != MenuChoices.end(); i++ )
    		(*i)->Release();
      
    		MenuChoices.clear();
    	}
    	// add a child
    	void MenuSelection( Menu* MenuSelection )
    	{
    		MenuSelection->SetRootMenu(this);
    	    MenuChoices.push_back(MenuSelection);
    	}
    	// Set the parent of the child
    	void SetRootMenu(Menu* Root)
    	{
    		RootMenu = Root;
    	}
    private:
    	friend class boost::serialization::access;
        template<class Archive>
        void serialize(Archive & ar, const unsigned int version)
        {
            // serialize base class information
            ar & boost::serialization::base_object<CObject>(*this);
    		ar & Selection;
    		ar & RootMenu;
        }
    	// list of children
    	std::list<Menu*> MenuChoices;
    	// pointer to parent
    	Menu * RootMenu;
    
    	std::string Selection;
    };
    And here is the code for saving and loading my objects, the loading code is incomplete, something about saving pointers and recreating the objects via pointer, gotta figure that one out to continue now, any help in this area would be appreciated

    Code:
    #include "Menu_Object.h"
    #include <vector>
    class Database
    {
    	friend class boost::serialization::access;
    	void Save_to_File(std::string Filename, const CObject Data)
    	{
    		std::ofstream ofs(Filename.c_str());
    		boost::archive::text_oarchive oa(ofs);
            // write class instance to archive
            oa << Data;
        	// archive and stream closed when destructors are called
    	}
    
    	void Read_File(std::string Filename, CObject Data)
    	{
            // create and open an archive for input
            std::ifstream ifs(Filename.c_str(), std::ios::binary);
            boost::archive::text_iarchive ia(ifs);
            // read class state from archive
            ia >> Data;
            // archive and stream closed when destructors are called
    	}
    private:
    	Object_Manager Menu_Objects;
    };
    Sometimes I forget what I am doing when I enter a room, actually, quite often.

  5. #5
    Absent Minded Programmer
    Join Date
    May 2005
    Posts
    968
    Okie dokey, I've made a bit more progress in my attempt to build a text menu system, new source code is on its way. I decided that there were some minor design flaws in making it so that I would serialize each menu object individually, instead I decided that it was a good idea to instead serialize the manager classes themselves, in all their entirety. This simplifies the saving and loading functions ALOT. I had a couple discussions with some pretty smart people and have come to a few thoughts.

    First off, I realized that in using an Object base class for all my game objects, I have alot of type safety issues. While I don't see this as really a problem, because all I need to do is make sure I don't use the types in an incorrect manner. The way I see it, it's like asking a yes or no question and expecting a different answer. If you don't put the different answer there it will never happen, it's all coding style I guess.

    I've been beginning to question the actual need for a manager object because in reality it's just a wrapper for a map, when the map already has it's own functions for accessing it and whatnot. Then after that thought, I thought about where I would put the serialization function for the maps that contain my objects, to be honest I left at a blank with this question. Thus I think I will keep the manager classes for aesthetic purposes. I can save a manager object, but I don't think I can just save a map that is floating in free space, it has to be in an object, Either that or I'm just not sure how to go about doing that.

    I was also told it might be better to use a map of strings and vectors of shared pointers of type T_, instead of using the generic Object base class, this would clean up the type safety issue but I'm not really certain about the necessity of this.

    Heres the current code:

    Object.h
    Code:
    #ifndef OBJECT_H
    #define OBJECT_H
    
    #pragma warning(disable: 4786)
    #pragma warning(disable: 64)
    #include <vector>
    #include <map>
    #include <string>
    #include <boost\shared_ptr.hpp>
    #include <boost\weak_ptr.hpp>
    #include <boost\smart_ptr.hpp>
    #include <iostream>
    #include <fstream>
    #include <boost/archive/text_iarchive.hpp>
    #include <boost/archive/text_oarchive.hpp>
    #include <boost/serialization/base_object.hpp>
    #include <boost/serialization/map.hpp>
    #include <boost/serialization/shared_ptr.hpp>
    
    
    class Object
    {
    
    public:
    	Object(){}
    
    	virtual void Create()
    	{}
    
    	virtual void Access()
    	{}
    
    	virtual void Delete()
    	{delete this;}
    
    private:
    	friend class boost::serialization::access;
    	template<class Archive>
    	void serialize(Archive & ar, const unsigned int version){};
    };
    
    class Object_Manager
    {  
    public:
    	Object_Manager() {};
    	~Object_Manager() {};
    
    	typedef boost::shared_ptr<Object> Object_Ptr;
    	typedef boost::weak_ptr<Object> Object_Observer;
    	typedef std::map< std::string, Object_Ptr > Object_Map;
    
    	void Add_Object(const std::string & name, Object * pObject)
    	{
    		Object_Ptr Raw_Object(pObject);
    		mObjects.insert(std::make_pair(name, Raw_Object));
    	}
    
    	Object_Observer Request_Object(const std::string & name)
    	{
    		Object_Map::iterator  it = mObjects.find(name);
    
    		if (it == mObjects.end())
    		{
    			std::cout << "Internal program error, " << name << " does not exist!" << std::endl;
    		}
    		else
    		{
    			return Object_Observer(it->second);
    		}
    	}
    
    	void Request_Object_Removal(const std::string & name)
    	{
    		Object_Map::iterator it = mObjects.find(name);
    
    		if (it != mObjects.end())
    		{
    			mObjects.erase(it);
    		}
    	}
    private:
    	friend class boost::serialization::access;
    	template<class Archive>
    	void serialize(Archive & ar, const unsigned int version)
    	{
    		ar & mObjects;
    	}
    	Object_Map  mObjects;
    };
    #endif
    Database.h
    Code:
    #ifndef DATABASE_H
    #define DATABASE_H
    
    #include "Object.h"
    #include <map>
    #include <iostream>
    class Database
    {
    public:	
    
    	typedef boost::shared_ptr<Object_Manager> Manager_Ptr;
    	typedef boost::weak_ptr<Object_Manager> Manager_Observer;
    	typedef std::map< std::string, Manager_Ptr > Manager_Map;
    
    	// Function to access a specific manager within the map
    	Manager_Observer Access_Manager(std::string Filename)
    	{
    		Manager_Map::iterator it = Database_Manager.find(Filename);
    		// check if the manager exists in the map
    		if (it == Database_Manager.end())
    		{
    			// if not throw an error
    			std::cout << "Manager does not exist!" << std::endl;
    		}
    		else
    		{
    			// if so return a pointer to the manager
    			return Manager_Observer(it->second);
    		}
    	}
    
    	//Save a manager completely to a file
    	void Save_to_File(std::string Filename, const Object_Manager Data)
    	{
    		std::ofstream ofs(Filename.c_str());
    		boost::archive::text_oarchive oa(ofs);
            // write class instance to archive
            oa << Data;
    		Data.~Object_Manager();
        	// archive and stream closed when destructors are called
    	}
    
    	// Load a manager from a file and add it to the list of managers
    	void Read_File(std::string Filename, Object_Manager * Data)
    	{
            // create and open an archive for input
            std::ifstream ifs(Filename.c_str(), std::ios::binary);
            boost::archive::text_iarchive ia(ifs);
            // read class state from archive
            ia >> Data;
    		// create a pointer to the data
    		Manager_Ptr Raw_Manager(Data);
    		// pair the filename with the manager typ for easy access
    		Database_Manager.insert(std::make_pair(Filename, Raw_Manager));
            // archive and stream closed when destructors are called
    	}
    private:
    	Manager_Map Database_Manager;
    };
    
    #endif
    Menu_Object.h
    Code:
    #ifndef MENU_H
    #define MENU_H
    
    #include "Object.h"
    #include <string>
    #include <list>
    #include <boost/serialization/string.hpp>
    #include <boost/serialization/vector.hpp>
    
    class Menu : public Object
    {
    public:
    	Menu(){};
    	Menu(std::string mSelection) 
    	{
    		mSelection = Selection;
    	}
    	virtual ~Menu() { Destroy(); }
    	// deletes self
    	void Release() { delete this; }
    	// call update on all children
    	virtual void Update()
    	{
    		for( std::list<Menu*>::iterator i = MenuChoices.begin();
    			i != MenuChoices.end(); i++ )
    		{
    			(*i)->Update();
    		}
    	}
    	// recursively destroy all children and self
    	void Destroy()
    	{
    		for( std::list<Menu*>::iterator i = MenuChoices.begin();
    			i != MenuChoices.end(); i++ )
    		(*i)->Release();
      
    		MenuChoices.clear();
    	}
    	// add a child
    	void MenuSelection( Menu* MenuSelection )
    	{
    		MenuSelection->SetRootMenu(this);
    	    MenuChoices.push_back(MenuSelection);
    	}
    	// Set the parent of the child
    	void SetRootMenu(Menu* Root)
    	{
    		RootMenu = Root;
    	}
    private:
    	friend class boost::serialization::access;
        template<class Archive>
        void serialize(Archive & ar, const unsigned int version)
        {
            // serialize base class information
            ar & boost::serialization::base_object<CObject>(*this);
    		ar & Selection;
    		ar & RootMenu;
    		ar & MenuChoices;
        }
    	// list of children
    	std::list<Menu*> MenuChoices;
    	// pointer to parent
    	Menu * RootMenu;
    
    	std::string Selection;
    };
    #endif

    Thats it so far, to test out everything all I need to do is make a utility class to build and save menu trees to an object manager. Anyone wanna help me do that? I'm not sure on the process of events I need in a function to create a massive tree object in one function.
    Sometimes I forget what I am doing when I enter a room, actually, quite often.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Creating dynamic menu items
    By abachler in forum Windows Programming
    Replies: 2
    Last Post: 10-09-2008, 12:26 PM
  2. Creating a window through menu
    By Homunculus in forum Windows Programming
    Replies: 17
    Last Post: 02-20-2006, 06:56 PM
  3. creating a menu using C
    By whitey82 in forum C Programming
    Replies: 11
    Last Post: 12-01-2005, 12:43 PM
  4. quick question about C Dos text menu pgm i was doing
    By Shadow in forum C Programming
    Replies: 2
    Last Post: 09-16-2001, 10:26 AM
  5. Creating an Instant Messenging system...
    By Fool in forum C++ Programming
    Replies: 8
    Last Post: 08-13-2001, 06:41 PM