Jump to content
  • Advertisement
Sign in to follow this  
supagu

[C++] calling functions on unknown objects

This topic is 4450 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

so im writing a game (this question isnt game specific), and i have all classes extending from 'Entity', currently im using a message system to handle all communication between Entity's for example (rough pesudoish code):
class Message
{
 int type;
}

class Fire : public Message
{
  type = 5;
}

class Entity
{
 bool HandleMsg(Message* msg)
 {
   switch (msg->type)
   {
   case Fire::Type:
     Fire* pFire = static_cast<Fire*>(msg);
     ...
   }
 }
}
problem with this method is i need to declare a class for each function call, this results in lots of messages. So i'm investigating other methods I'm looking at the COM interface IUnknown and IDispatch and thinking these could be use use. I Like idispatch's Invoke method so something like: bool hasFunction = myEntity->Invoke("Fire", list of parameters, return value); if the entity has a fire method it gets called, if not well invoke returns false. any ideas on how this can be done? or maybe some other ideas i should investigate?

Share this post


Link to post
Share on other sites
Advertisement
Use member function pointers. Create a virtual function in the base class for each possible message (you have to define them somewhere) that returns false, implement it wherever needed. To send a message, store the (base class) member function pointer and the required arguments.

Edit: short unverified example:


class Message {
public:
virtual bool Send(Entity &);
virtual ~Message { }
};

template<typename Arg>
class UnaryMessage : public Message{
public:
typedef void (Entity::*Function)(Arg);
private:
Function f;
Arg a;
public:
UnaryMessage(Function f, Arg a) : f(f), a(a) {}
virtual bool Send(Entity & e) {
(e.*f)(a);
}
};

class Entity {
public:
virtual bool Fire { return false; }
virtual ~Entity() {}
};

void ShootAt(Entity* shooter, Entity* target) {

Message * fire = new UnaryMessage(&Entity::Fire, target);
fire->Send(*shooter);
delete fire;
}


Share this post


Link to post
Share on other sites
yeah well i dont want to do it that way, makes my base class really ugly and bloated, and not very plugin friendly

Share this post


Link to post
Share on other sites
Maybe a delegate is what you are looking for.

Read this article:
http://www.codeproject.com/cpp/ImpossiblyFastCppDelegate.asp

A delegate is the combination of a function pointer and a data pointer, so you can store a call to a member function of an object. Since all delegates of the same type (i.e. they can be called with the same number and type of paramters) look the same, they can be stored in a list or an array.

Instead of creating messages you would create delegates and put them into a list. Then each delegate can be executed later on calling the method that it was created for.

If you compare the delegate solution to virtual functions, you have only one level of indirection for the delegate (stub_ptr in the article) versus two levels of indirection for virtual functions (lookup vtable ptr and indirect call through vtable).

Share this post


Link to post
Share on other sites
Your intended approach would require you to emulate reflection. I could suggest an ugly (and approximative) way of doing this:


template<class Receiver,typename Arg1> class UnaryReflector {
typedef void (Receiver::*Function)(Arg1);
std::map<std::string,Function> functions;
static Reflector instance;
Reflector() { }
public:
static Reflector GetInstance() {
return instance;
}
void Register(std::string name, Function function) {
functions[name] = function;
}
Function Get(std::string name) {
return functions[name];
}
};

template<class Receiver,typename Arg1> Register(std::string name, void (Receiver::*function)(Arg1) {
UnaryReflector<Receiver,Arg1>::GetInstance().Register(name,function);
}

template<class Receiver,typename Arg1> bool Call(std::string name, Receiver r, Arg1 arg) {
UnaryReflector<Receiver,Arg1>::Function f = UnaryReflector<Receiver,Arg1>::GetInstance().Get(name);

if (f is null) return false;

(r.*f)(arg);
return true;
}




Then, place a virtual function in each entity-derived class to forward the received messages to the Call function above.

Share this post


Link to post
Share on other sites
okay delegates is probably what i want

ToohrVyk, can you explain what your code snipet is intended to do? or a sample snipet on how its used

[Edited by - supagu on August 11, 2006 10:36:44 AM]

Share this post


Link to post
Share on other sites
In your original post, when you created the message you already knew which type it is and what action should be performed when it is handled.

What you do when using the delegate is to bind the function call when the message is generated. So the delegate IS the message in some sense.

Consider the case of the Fire-Message. You may move the part of the switch-statement that handles the Fire-Message into a burn() function. Then when you would create the Fire-Message you create the delegate instead:

class Fire {
void burn(int howMuch) { ... }
};

// create the delegate, then it may be put into a queue with other delegates
// which are not necessarily fires
// creation of the delegate requires to specify the type (Fire) and the
// member function to call (burn) and an instance on which the function
// will be called later on
delegate d = delegate::from_member<Fire, &Fire::burn>(new Fire());

...

// later on execute the delegate
d(10);



To avoid memory leaks, the burn() function should call "delete this;" before returning or the delegate should be modified to automatically take care of this. Another option is to not dynamically create the instance (i.e. replace "new Fire()" by &someFire as was done in the article). This really depends on your needs.

Share this post


Link to post
Share on other sites
After some thinking, I reworked my initial proposition to an even uglier one (besides, macros are evil):


#define REGISTER_MESSAGE(name,args) class name ## Receiver { public: virtual void name args = 0; typedef void (name ## Receiver::*Type) args; }

#define MESSAGE(name) &name ## Receiver::name
#define MESSAGE_T(name) name ## Receiver::Type
#define USER(name) name ## Receiver
#define AS_USER(name,obj) dynamic_cast<USER(name)*>(obj)



Usage:


// Messages.hpp

REGISTER_MESSAGE(Fire,(Entity*));

// Soldier.hpp

#include "messages.hpp"

class Soldier : public Entity, public USER(Fire) {
virtual void Fire(Entity* at) { /* Do things */ }
};

// Orders.cpp

void Frobnicate(Entity* shooter, Entity* target) {
USER(Fire)* f = AS_USER(Fire,shooter);
if (f != null) { f->Fire(otherEntity); }
}



This can be used to create delegates, which in turn can be bound to strings:



// Definition of delegates, still requires to REGISTER_MESSAGE(name,args)
// and inherit from USER(name) before using a message.

class Delegate {
public:
virtual bool operator()(Entity*) const = 0;
};

template<class Basic, class Arg> class UnaryDelegate : public Delegate {
void (Basic::*msg)(Arg);
Arg a;
public:
UnaryDelegate(void (Basic::*msg)(Arg), Arg a) : msg(msg), a(a) {}
virtual bool operator()(Entity* e) const {
Basic* b = dynamic_cast<Basic*>(e);
if (!b) return false;
(b->*msg)(a);
return true;
}
};

#define DELEGATE1(name,arg) UnaryDelegate(MESSAGE(name),arg)

// Added to class Entity:

bool Invoke(const Delegate& d) { return d(this); }

// Usage:

void Frobnicate(Entity* shooter, Entity* target) {
Delegate* fire = new DELEGATE1(Fire,target);
shooter->Invoke(*fire);
delete fire;

shooter->Invoke(DELEGATE1(Fire,target)); // Equivalent
}



A priori, this should work (although untested).

The key idea behind this is to use dynamic_cast to determine if a given object can use a given message (one can be declared as an user by inheriting from the correct message-receiver, and new messages can be created by plugins without bloating the main class) and then call the correct member function.

Delegates push this further by allowing you to save for later a given message (instead of having to call it directly) by having specialized templates inherit from a base "Delegate" class.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!