Archived

This topic is now archived and is closed to further replies.

Freegor

Templates and Inheritance

Recommended Posts

The goal of this example was to prototype a message system for the engine I''m working on. (I may not even go with a message system, but this was a good exercise in intermediate - advanced C++ which I''ve discovered I still need a lot of work in) Includes and Objects
	
#include <iostream.h>

#include <stdlib.h>
#include <string.h>

#include <map>
using namespace std;

class Object {
public:
    int x;    
    Object() {}
    Object(int in) {  this->x = in;   }
};

class AddObject : public Object {
public:
    AddObject(int t) { this->x = t; }
    void add(char * arg) { this->x += atoi(arg); }
};

class SubObject : public Object {
public:
    SubObject(int t) { this->x = t; }
    void sub(char * arg) { this->x -= atoi(arg); }
};
	
Then the command / functor class and a class to store commands.
template <class T>
class Command {
public:    
    Command() {}
    Command( void(T::*f)(char * arg) ) {
        this->fpt = f;
    }
    void Execute(T * obj, char * arg) {
        (*obj.*fpt)(arg);
    }
    void operator()(T * obj, char * arg) {
        (*obj.*fpt)(arg);
    }
private:
    void (T::*fpt)(char * arg);
};

template <class T>
class CommandRegistry {
public:    
    static void RegisterCommand(char * name, Command<T> * com) {
        commandList[name] = com;
    }    
    static void ExecuteCommand(char * name, T * obj, char * arg) {
        commandList[name]->Execute(obj, arg);
    }
private:
    static map<char *, Command<T> * > commandList;
};

template <class T>
std::map<char*, Command<T>*> CommandRegistry<T>::commandList;
And a main function to give a simple example of what I''d like to do..

void main() {

    Object * adder = new AddObject(10);
    Object * suber = new SubObject(10);

    Command<AddObject> * add = new Command<AddObject>(&AddObject::add);
    Command<SubObject> * sub = new Command<SubObject>(&SubObject::sub);

    char * addName = "add";
    char * subName = "sub";

    CommandRegistry<AddObject>::RegisterCommand(addName, add);
    CommandRegistry<SubObject>::RegisterCommand(subName, sub);
   
    cout << "---ORIGINALLY---" << endl;
    cout << "ADDER : " << adder->x << endl;
    cout << "SUBER : " << suber->x << endl;
    cout << "----------------" << endl;

    CommandRegistry<AddObject>::ExecuteCommand(addName, (AddObject*)adder, "5");
    CommandRegistry<SubObject>::ExecuteCommand(subName, (SubObject*)suber, "5");

    cout << "--- AFTER COM---" << endl;    
    cout << "ADDER : " << adder->x << endl;
    cout << "SUBER : " << suber->x << endl;
    cout << "----------------" << endl;
}
Basically, I want to execute any registered function, on the object(same type as created with the templated command) I pass to the ExecuteCommand function with the given argument. While this code works, there are several things I dislike about it and would wish to change but don''t know how.. Main Problem: The templated CommandRegistry.. If possible I would like to remove the template from this class, so that only one is initialized so I could use it as a Singleton. With this code a new registry is created for each type of Command I register with it. Basically, can you store a templated class in a class that is not templated? Secondary Problem: Casting the Object I pass to Execute Not as important to me as the main problem (not a big deal if this isn''t solved), but is there any way to get around casting adder and suber when calling Execute? Some things I''ve tried and failed: 1. Instead of templating Command, I just used it to hold base Object pointers. However, I couldn''t figure out how to pass function pointers with this method. add() was a function of AddObject, but not Object. I couldn''t just have, say a general Operation function in Object though, because I expect the derived classes of Object to be extending it with many different functions.. Is there a way I''m missing to do this? 2. I''ve tried just having pointers to Command in CommandRegistry, but the compiler doesn''t like that because different typed Commands are being made. In this respect I''ve tried to have a non-templated BaseCommand class which the templated Command class derived from, and then just had pointers to BaseCommand in the registry, but I had casting problems.. Thats about it, let me know if I have to clarify anything further.. I appreciate any help.

Share this post


Link to post
Share on other sites
The .h header files are deprecatd, use #include<iostream> and #include<cstdio> No .h''s (not on string either!)

Read Modern C++, and look at boost''s function, bind, lamba, and lexical_cast libraries.

I wrote code similar to this where you give it a string name for the function, and then the function, method, or functor and it figured out all the parameters and how to extract them from a string input to execute the function/method/functor.
It was exetremly complicated code and never works as good as I''d hope it would; that is it takes a VM and a compiler to do the whole job. It was quite usefull none-the-less.

Share this post


Link to post
Share on other sites
I think my Event class does what you want. It uses a few files, so look it over.

Event.hpp:

// OpenIM - a lightweight, object-oriented instant messaging system.

// by the OpenIM team

//

// This program is free software; you can redistribute it and/or

// modify it under the terms of the GNU General Public License

// as published by the Free Software Foundation; either version 2

// of the License, or (at your option) any later version.

//

// This program is distributed in the hope that it will be useful,

// but WITHOUT ANY WARRANTY; without even the implied warranty of

// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the

// GNU General Public License for more details.

//

// You should have received a copy of the GNU General Public License

// along with this program; if not, write to the Free Software

// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.


#ifndef CORE_EVENTS_EVENT_HPP
#define CORE_EVENTS_EVENT_HPP

#include <memory>

#include "../Export.hpp"

#include "../EventManager.hpp"
#include "../Handler.hpp"

namespace OpenIM
{
namespace Events
{
/// Templated base class for Events

///

/// Uses Coplien''s Curiously Recurring Template idiom to automatically generate

/// stub code to connect client code to the EventManager. Supports both pointer to

/// function and pointer to member functions. The necessary code is injected

/// into user-level components by virtue of templates.

///

/// Member function template support is necessary to use member function connections. I have

/// no interest in supporting poor attempts at compilers. (MSVC 6) If you still use

/// MSVC6, use free functions that then invoke the appropriate method on the component.

template<class DerivedEventT>
class Event
{
public:
/// Member function Handler

template<typename ClassT>
class Handler :
public OpenIM::Events::Handler
{
public:
typedef void (ClassT::* MemberFn)(DerivedEventT&);

Handler(ClassT* inst, MemberFn fn) :
instance(inst),
function(fn)
{
m_isMemberFunction = true;
}

void operator()(void* arg)
{
(instance->*function)(*static_cast<DerivedEventT*>(arg));
}

bool operator==(const OpenIM::Events::Handler* rhs)
{

if(rhs->m_isMemberFunction)
{
const Event<DerivedEventT>::Handler<ClassT>* h =
static_cast<const Event<DerivedEventT>::Handler<ClassT>*>(rhs);

return this->instance == h->instance && this->function == h->function;
}
else
return false;
}

private:
ClassT* instance;
MemberFn function;
};

class HandlerFunction :
public OpenIM::Events::Handler
{
public:
typedef void (*HandlerFn)(DerivedEventT&);

explicit HandlerFunction(HandlerFn fn) :
m_fn(fn)
{
m_isMemberFunction = false;
}

void operator()(void* arg)
{
m_fn(*static_cast<DerivedEventT*>(arg));
}

bool operator==(const OpenIM::Events::Handler* rhs)
{
if(! rhs->m_isMemberFunction)
{
const Event<DerivedEventT>::HandlerFunction* h =
static_cast<const Event<DerivedEventT>::HandlerFunction*>(rhs);

return this->m_fn == h->m_fn;
}
else
return false;
}

private:
HandlerFn m_fn;
};

static void Attach(void (*function)(DerivedEventT&))
{
OpenIM::Core::EventManager::Attach(DerivedEventT::GetName(),
new Event<DerivedEventT>::HandlerFunction(function));
}

static void Detach(void (*function)(DerivedEventT&))
{
std::auto_ptr<Event<DerivedEventT>::HandlerFunction> handler (
new Event<DerivedEventT>::HandlerFunction(function));
OpenIM::Core::EventManager::Detach(DerivedEventT::GetName(), handler.get());
}

template<class ClassT>
static void Attach(ClassT* instance, void(ClassT::* fn)(DerivedEventT&))
{
OpenIM::Core::EventManager::Attach(DerivedEventT::GetName(),
new Event<DerivedEventT>::Handler<ClassT>(instance, fn));
}

template<class ClassT>
static void Detach(ClassT* instance, void(ClassT::* fn)(DerivedEventT&))
{
std::auto_ptr<Event<DerivedEventT>::Handler<ClassT> > handler(
new Event<DerivedEventT>::Handler<ClassT>(instance, fn));
OpenIM::Core::EventManager::Detach(DerivedEventT::GetName(), handler.get());
}

void Raise()
{
OpenIM::Core::EventManager::Raise(DerivedEventT::GetName(),
static_cast<void*>(static_cast<DerivedEventT*>(this)));
}
};
}
}

#endif


EventManager.cpp

// OpenIM - a lightweight, object-oriented instant messaging system.

// by the OpenIM team

//

// This program is free software; you can redistribute it and/or

// modify it under the terms of the GNU General Public License

// as published by the Free Software Foundation; either version 2

// of the License, or (at your option) any later version.

//

// This program is distributed in the hope that it will be useful,

// but WITHOUT ANY WARRANTY; without even the implied warranty of

// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the

// GNU General Public License for more details.

//

// You should have received a copy of the GNU General Public License

// along with this program; if not, write to the Free Software

// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.


#include "EventManager.hpp"

#include <cassert>
#include <deque>
#include <map>

#include "Handler.hpp"
#include "Log.hpp"
#include "String.hpp"

#include "Events/AccountEvents.hpp"
#include "Events/ConversationEvents.hpp"
#include "Events/ContactEvents.hpp"
#include "Events/GroupEvents.hpp"
#include "Events/MessageEvents.hpp"
#include "Events/SystemEvents.hpp"

namespace
{
typedef std::deque<OpenIM::Events::Handler*> Handlers;
typedef std::map<OpenIM::String, Handlers*> HandlerTable;

HandlerTable handlers;
}

namespace OpenIM
{
namespace Core
{
void EventManager::Startup()
{
Log::Write(L"EventManager: Starting up\n");
}

void EventManager::Shutdown()
{
Log::Write(L"EventManager: Shutting down\n");

for(HandlerTable::iterator i = handlers.begin(); i != handlers.end(); ++i)
{
for(Handlers::iterator j = i->second->begin(); j != i->second->end(); ++j)
delete *j;

delete i->second;
}

handlers.clear();

}

void EventManager::Attach(const wchar_t* name, OpenIM::Events::Handler* handler)
{
String eventName = name;
HandlerTable::iterator i = handlers.find(eventName);
if(i == handlers.end())
{
// TODO: sucky

handlers[eventName] = new Handlers();
i = handlers.find(eventName);
}

i->second->push_back(handler);

}

void EventManager::Detach(const wchar_t* name, OpenIM::Events::Handler* handler)
{
String eventName = name;
HandlerTable::iterator i = handlers.find(eventName);
if(i != handlers.end())
{
for(Handlers::iterator j = i->second->begin(); j != i->second->end(); ++j)
{
if((**j) == handler)
{
i->second->erase(j);
delete *j;

break;
}
}
}
}

void EventManager::Raise(const wchar_t* name, void* data)
{
assert(name);
assert(data);

Log::Write(L"%s\n", name);

String eventName = name;
HandlerTable::iterator i = handlers.find(eventName);
if(i != handlers.end())
{
for(Handlers::iterator j = i->second->begin(); j != i->second->end(); ++j)
(*j)->operator()(data);
}
}
}
}


// OpenIM - a lightweight, object-oriented instant messaging system.

// by the OpenIM team

//

// This program is free software; you can redistribute it and/or

// modify it under the terms of the GNU General Public License

// as published by the Free Software Foundation; either version 2

// of the License, or (at your option) any later version.

//

// This program is distributed in the hope that it will be useful,

// but WITHOUT ANY WARRANTY; without even the implied warranty of

// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the

// GNU General Public License for more details.

//

// You should have received a copy of the GNU General Public License

// along with this program; if not, write to the Free Software

// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.


#ifndef CORE_EVENTS_HANDLER_HPP
#define CORE_EVENTS_HANDLER_HPP

#include "Export.hpp"
#include "Heap.h"

namespace OpenIM
{
namespace Events
{
class CORE_API Handler
{
public:
virtual ~Handler()
{
}

virtual bool operator==(const Handler*) = 0;
virtual void operator()(void*) = 0;

static void* operator new(std::size_t size)
{
return openim_malloc(size);
}

static void operator delete(void* instance)
{
openim_free(instance);
}

bool m_isMemberFunction;
};
}
}

#endif


ContactEvents.hpp - shows example events

// OpenIM - a lightweight, object-oriented instant messaging system.

// by the OpenIM team

//

// This program is free software; you can redistribute it and/or

// modify it under the terms of the GNU General Public License

// as published by the Free Software Foundation; either version 2

// of the License, or (at your option) any later version.

//

// This program is distributed in the hope that it will be useful,

// but WITHOUT ANY WARRANTY; without even the implied warranty of

// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the

// GNU General Public License for more details.

//

// You should have received a copy of the GNU General Public License

// along with this program; if not, write to the Free Software

// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.


#ifndef CORE_EVENTS_CONTACTEVENTS_HPP
#define CORE_EVENTS_CONTACTEVENTS_HPP

#include "Event.hpp"

#include "../Contact.hpp"
#include "../Group.hpp"

namespace OpenIM
{
namespace Events
{
/// A contact has been added to a specific group. The group may be null.

struct ContactCreateEvent :
public Event<ContactCreateEvent>
{
static const wchar_t* GetName() { return L"ContactCreateEvent"; }

OpenIM::Contact* Contact;
OpenIM::Group* Group;
int Position;
};

/// A contact has been removed from a group. The group may be null.

struct ContactDestroyEvent :
public Event<ContactDestroyEvent>
{
static const wchar_t* GetName() { return L"ContactDestroyEvent"; }

OpenIM::Contact* Contact;
OpenIM::Group* Group;
};

/// A contact has updated their details (such as nickname formatting or status).

struct ContactUpdateEvent :
public Event<ContactUpdateEvent>
{
static const wchar_t* GetName() { return L"ContactUpdateEvent"; }

OpenIM::Contact* Contact;
};
}
}

#endif


Attaching to events

ContactCreateEvent::Attach(this, ContactListComponent::OnContactCreate);
ContactUpdateEvent::Attach(this, ContactListComponent::OnContactUpdate);
ContactDestroyEvent::Attach(this, ContactListComponent::OnContactDestroy);


Invoking events

ContactCreateEvent e;
e.Contact = contact;
e.Group = g;
e.Position = position;
e.Raise();


It uses type erasure (like boost::function) to avoid vtable bloat, which, by the way, is very noticeable when you''re doing stuff like this. I had to write my own functors because I wanted to be able to use operator== on functors.

Share this post


Link to post
Share on other sites
Thanks to both of you for helping me out.. I know its been a while since the op, but I haven''t had much free time the past couple of days..

Magmai Kai Holmlor : Thanks for letting me know about the .h thing, haven''t heard that before. I also haven''t been anywhere near C++ in a while. Thanks for letting me know about those resources. I have ordered Modern C++, and I started looking over the Boost library. I''ve always thought Boost was mainly used for Python, didn''t know it had all those other nice goodies.

antareus: Thanks for taking the time to post some code. I was able to solve my second problem by using the void* parameter and static casting it to the templated type like your code indicates.. This produced some pretty surprising(at least surprising to me) results. I was able to pass an AddObject to the sub command and the AddObject "gained" the subtract function. It kind of makes sense, but I half expected it to crash. Is there a name for this I could look up? This could definitly open up some interesting possibilities...

Still haven''t solved the first problem, but hopefully after a little more research into these resources you provided, looking over your code more, and the arrival of the book, I will reach a workable solution of some sorts.

Share this post


Link to post
Share on other sites