Console design problem

Get answers to all your basic programming questions. No Ogre questions, please!
maxiwill
Halfling
Posts: 45
Joined: Sat May 22, 2010 8:43 am

Console design problem

Post by maxiwill »

I'm using a console thats very cleanly designed as shown here :

Code: Select all

http://www.ogre3d.org/tikiwiki/tiki-index.php?page=ConsoleCode&structure=Cookbook
but i run into a problem :

specifically this part

Code: Select all

enum ConsoleItemType
{
	CTYPE_UCHAR,	CTYPE_CHAR,
	CTYPE_UINT,		CTYPE_INT,
	CTYPE_FLOAT,	CTYPE_STRING,
	CTYPE_FUNCTION,	CTYPE_APPSTATEFUNCTION
};

typedef void (*ConsoleFunction)(const std::vector<std::string> &);

typedef struct {
	std::string name;
	ConsoleItemType type;
	union 
	{
		ConsoleFunction function;
		void *variablePtr;
	};
} ConsoleItem;
and how function gets called :

Code: Select all

case CTYPE_FUNCTION:
			(*iter).function(arguments);
			return true;
			break;
the design is clean all in all, but its only for static functions.

what if I wanted to use a non-static member function pointer?

like accessing a various interface-purposed public member functions that modifies private data ?

for example, if i designed the framework with different states : menu, pause, action, each with different scenemanager and UI elements

and I want to be able to modify settings at run time ?

I tried using "friend" on static functions, but it requires function to be declared to take object references, which wont work with the

Code: Select all

typedef void (*ConsoleFunction)(const std::vector<std::string> &);
design

and I tried making a custom class function pointer, which wouldn't work, because I used a framework which encapsulated states manager quite well. (there's no interface to getsingleton() that can be used by static function)

Hope i dont make it frustratingly painful to understand :)


Any thoughts ?
User avatar
saejox
Goblin
Posts: 260
Joined: Tue Oct 25, 2011 1:07 am
x 36

Re: Console design problem

Post by saejox »

this is how you define member pointers:

Code: Select all

typedef void (ClassName::*ConsoleFunction)(const std::vector<std::string> &);
this is how you create member functions:

Code: Select all

ConsoleFunction func = &ClassName::DoStuff;
this is how you call them.

Code: Select all

std::vector<std::string> MyVector;
MyVector.push_back("I dont like the look of this");
(yourObject->*func )(MyVector);

i use this like 2 times a year :)
possibly wrong syntax.
Nimet - Advanced Ogre3D Mesh/dotScene Viewer
asPEEK - Remote Angelscript debugger with html interface
ogreHTML - HTML5 user interfaces in Ogre
User avatar
Herb
Orc
Posts: 412
Joined: Thu Jun 04, 2009 3:21 am
Location: Kalamazoo,MI
x 38

Re: Console design problem

Post by Herb »

I have a code snippet that should help. I snagged it from somewhere and tweaked it and have used it in a few projects. Basically, it's a observer pattern for subscribing to events using member function pointers of an object. You could adapt it easily for events on the console if you want, or just use the logic on storing member function pointers.

Code: Select all

#ifndef _MSG_EVENT_H_
#define _MSG_EVENT_H_

#include <map>

typedef int MsgEventHandler;

template <typename ParamT>
class EventHandlerBase
{
public:
	virtual void notify(ParamT param) = 0;
};

template <typename ParamT, typename ParamT2>
class EventHandlerBase2
{
public:
	virtual void notify(ParamT param, ParamT2 param1) = 0;
};

template <typename ListenerT,typename ParamT>
class EventHandler : public EventHandlerBase<ParamT>
{
	typedef void (ListenerT::*PtrMember)(ParamT);
	ListenerT* m_object;
	PtrMember m_member;

public:

	EventHandler(ListenerT* object, PtrMember member)
		: m_object(object), m_member(member)
	{}

	void notify(ParamT param)
	{
		return (m_object->*m_member)(param);		
	}	
};


/**
 *  This is class is used to provide a way for other objects to "subscribe" to notify events with 
 *  specified callback methods (mainly used for subscribing to network message types)
 */
template <typename ParamT>
class MsgEvent
{
	typedef std::map<int,EventHandlerBase<ParamT> *> HandlersMap;
	HandlersMap m_handlers;
	int m_count;

public:

	MsgEvent()
		: m_count(0) {}

	template <typename ListenerT>
	MsgEventHandler subscribe(ListenerT* object,void (ListenerT::*member)(ParamT))
	{
		typedef void (ListenerT::*PtrMember)(ParamT);	
		m_handlers[m_count] = (new EventHandler<ListenerT,ParamT>(object,member));
		m_count++;	
		return m_count-1;
	}

	bool unsubscribe(MsgEventHandler id)
	{
		typename HandlersMap::iterator it = m_handlers.find(id);

		if(it == m_handlers.end())
			return false;

		delete it->second;
		m_handlers.erase(it);				
		return true;
	}

	void notify(ParamT param)
	{
		if (m_handlers.size() > 0)
		{
			typename HandlersMap::iterator it = m_handlers.begin();
			for(; it != m_handlers.end(); it++)
			{
				it->second->notify(param);
			}
		}
	}
};

#endif
Here would be the usage of it:

Code: Select all


// class that would trigger the event
class Foo
{
public:
    MsgEvent<T> MyEvent;  // where T is whatever type you want passed with the event, int, short, custom object, etc...
};

// class that would subscribe to the event
class Foo2
{
public:
   // showing subscribing to one event
   void subscribeToEvents(Foo* foo)
   {
         foo->MyEvent.subscribe(this, &Foo2::onEventTrigger);
   }

   void onEventTrigger(T)   // where T is whatever type you made up above for the event
   {
        // do whatever
   }

protected:
    MsgEventHandler myEventId;  // id of the event subscribed to, use that id to unsubscribe on destructor or whatever...
};



I hope that helps.
User avatar
volca
Gnome
Posts: 393
Joined: Thu Dec 08, 2005 9:57 pm
x 1

Re: Console design problem

Post by volca »

Personally I'd rework the console to use std::function instead and then use std::bind.
Image
User avatar
Klaim
Old One
Posts: 2565
Joined: Sun Sep 11, 2005 1:04 am
Location: Paris, France
x 56

Re: Console design problem

Post by Klaim »

I would too but only if I don't need to add other data, like the name of the function, or others properties...
User avatar
volca
Gnome
Posts: 393
Joined: Thu Dec 08, 2005 9:57 pm
x 1

Re: Console design problem

Post by volca »

Well you can still package that and forward as the first parameter to the std::function.

Code: Select all

struct Callback {
    std::function<void(Callback*, /*other params*/)> callback;
    std::string name;
    // etc.
};
Image
User avatar
Klaim
Old One
Posts: 2565
Joined: Sun Sep 11, 2005 1:04 am
Location: Paris, France
x 56

Re: Console design problem

Post by Klaim »

Yes something like that.

That being said, sometime you want all the code related to a command to be together.

So really it depends. Actually I've implemented such system in different ways and it's a bit like event systems: the right way to do it is always dependent on how you will use it (in code). It will be the same view from outside anyway.