Skip to main content
GameDev.net gamedev.net
🔒 Locked

Automatic calling of C++ code...

Started by Lethe Aug 5, 2002 at 5:00 PM 6 replies 1k views
Original Post
Lethe
Lethe
People arn''t being very helpfull with my other thread - possibly because it''s too specific or possibly because they don''t have enough information. So I''m going to try another approach - all the information:-) My aim is to call a global function once for every class in existance that inherits from a base class - BSmart - this function takes two parameters - a text string representing that class, ie if you have GTurnip : public BSmart you would give it "GTurnip". The other parameter is a function pointer to a function that returns a BSmart * which has been declared new type() - new GTurnip() in the example case. Thats all thats required - its for an object factory incase your wondering. I can''t see any way of doing it completly automatically - so I''ve decided that an extra line of code per class is ok. Currently a macro that uses a templated singlton to do it is in use - this works except for templated classes. What happens is the singlton is declared by the macro in a different namespace - using the class token appended to a token as the identifier for the singlton - fine for normal classes but the angular braces in a templated name are not parts of a legal identifier name, so it dosn''t work for them. The annoying thing is I don''t even need an identifier for it as I never touch it and only want it for the code in the constructor:-( A solution for this problem at any level would be greatly appreciated. -Lethe
Shannon Barber
Shannon Barber
I''m pretty sure XParam
does precisely what you want... you can try using that or you need to write something like this:


  
#include <map>
#include <string>

class PluggableFactory
{
private:
typedef std::map<std::string, PluggableFactory*> map_t;
static map_t registry;

public:
static void Register(std::string& name, PluggableFactory* factory)
{
PluggableFactory::registry[name] = factory;
}
virtual PluggableFactory* Create() =0;
virtual const std::string& Name() const =0;

static PluggableFactory* Create(const std::string& name)
{
map_t::iterator it = PluggableFactory::registry.find(name);
if(it!=PluggableFactory::registry.end())
return it->second->Create();
else
{//not found

return 0; //assert, throw an exception, or just return 0

}
}
};
PluggableFactory::map_t PluggableFactory::registry;

#define PLUGGABLE_FACTORY(classname)\
const char* str_##classname = #classname;\
struct classname : SerialFactoryImpl<classname, str_##classname>\


#pragma warning(push)
#pragma warning(disable: 4355) //''this'': used in base member initialize list

//It''s safe - for now...


template<class this_t, const char* class_name>
struct SerialFactoryImpl : PluggableFactory
{
static std::string name;
SerialFactoryImpl()
{
PluggableFactory::Register(std::string(class_name), this);
}
virtual PluggableFactory* Create()
{
return new this_t;
}
virtual const std::string& Name() const
{
return name;
}
};
template<class this_t, const char* class_name>
std::string SerialFactoryImpl<this_t, class_name>::name = class_name;

PLUGGABLE_FACTORY(TestCase)
{
} testcase_;

#pragma warning(pop)

int main()
{
PluggableFactory* p = PluggableFactory::Create(TestCase::name);
}
The trade-off between price and quality does not exist in Japan. Rather, the idea that high quality brings on cost reduction is widely accepted.-- Tajima & Matsubara
Kippesoep
Kippesoep
One way of accomplishing this is to put pointers to each object into a list and then use for_each. Sort of like this:


    
#include <list>


class CBase;

typedef std::list <CBase *> BASELIST;
BASELIST g_BaseClasses;

class CBase
{
public :
//Constructor

CBase ()
{
g_BaseClasses.push_back (this);
}
//Destructor

virtual ~CBase ()
{
BASELIST::iterator it = g_BaseClasses.begin ();
while (it != g_BaseClasses.end ())
{
if (*it == this) it = g_BaseClasses.erase (it);
else ++it;
}
}
};


Derive all your classes from CBase and they'll be registered automatically. To do your function-call:


  
void Function (CBase *obj)
{
...
}

for_each (g_BaseClasses.begin (), g_BaseClasses.end (), Function);


It's some extra code, but it will only be present in the base class' constructor and destructor.



[edited by - Kippesoep on August 6, 2002 4:42:16 AM]
Kippesoep
Kippesoep
Kippesoep
BTW, you specified the function as having another parameter, being the name of the class. If this is for some kind of RTTI, then use that instead of the horrible way you suggest. At least do something like:


  
class CBase
{
...
virtual const char *GetType () const { return "CBase"; }
...
};

class GTurnip : public CBase
{
const char *GetType () const { return "GTurnip"; }
};


Although I''d fire you for using that in production code to determine the behaviour of your global function.

Kippesoep
JonStelly
JonStelly
Well, you can create another macro that registers a template class differently than a normal class. You''ll need to pass 2 parameters to the template macros.

That said, I think there''s probably a better way to do what you''re trying to do.
Lethe
Lethe
Thanks for all the responses - unfortunatly none do what I'm after.

I don't want to use a libary like XParm - it dosn't have all the features I want. Same again for the code. I want my object factory to have objects registered without calling code in a code block and in many seperate object files - basically I don't want to have to go back to the same place(s) in the code and edit in every object I create - which what you have specified requires. I want to be able to add an object at any point in the code - external to the 'real' code. Straight after I've finished defining each class being the intended use.

For the second one, I wouldn't want to be getting pointers - I want new objects - that what an object factory is! However I could adjust your idea to work as I want - instead of pointers to objects give it function pointers to static methods and have an appropriate method in each class. However there is a rather major problem - it requires one of the class to have been created before the object factory knows about it. And as the factory's purpose is mainly saving & loading, anyone who runs the program and does a load operation straight away is going to have problems with unregistered classes.

For #3 - I am using rtti. The conversion in a macro of the type to a string is because if I used rtti I would have to copy the data returned by typeid - this way I can just use that pointer confident it will allways have the right data on the other end - it's simply coded like that to make the code neater and easier to understand & save on calls to malloc!

And to the last one - yes, thats an alternative, but a last resort one. And maby there is a better way - but I would like to see you think of it:-)

Thanks to all,
-Lethe

[edited by - Lethe on August 6, 2002 4:28:50 PM]
JonStelly
JonStelly
Ok, this is my final answer. You''re getting into some really strange stuff here and you''re getting a little too picky about what you want it to look like without worrying about how it works. Not a slam, just my opinion and I''m running out of ideas.

You can use a typedef to define a type for a specialized template for each type you want to use.

Assuming you''re using something close to the code I gave on your other thread, this would work:


  
template<class T>
class t
{
public:
void PrintType()
{
printf("t\n");
}
};
typedef t<int> t_int;
DEFINE_FACTORY(t_int);


If that doesn''t give you enough flexibility, I don''t know of any way to do what you''re asking. Good luck.
Lethe
Lethe
That acheives the same level of abstraction as what I've currently got, and slightly more elegantly:-) Though it would look a lot worse than what you have typed once you add in all the other features:-(

I've gone for a less elegent solution that works - and is basically what I had before I tried the forum, with a few modifications based on what you all said.

I code for the challange - part of that is setting high and possibly unattainable targets for myself:-D This one appears unattainable without writting my own dirivative of the C++ language or adding an extra pre-proccesor step - both rather excessive for such a problem;-)

Well, thanks all for trying - I'm just going to have to go for second best:-(
-Lethe

[edited by - Lethe on August 6, 2002 6:27:38 PM]

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.