Archived

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

C++ Problem (Advanced)

This topic is 5618 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

I have a ''simple'' problem - but I can''t find an elegent answer. The problem is that I want to convert a class''s name - as a null terminated string - into a pointer to a newly declared version of this class. (The constructor never takes parameters.) So for example I want to get to get from "DList" to void * ptr = new DList(). And then theres the nasty bit - the list of classes that it suports has to be extensible at any point in the code - a switch statement is not acceptable. Calling a macro with the class name after each class you want in the list it will convert or similer is what I''m after. So far I have one solution - it involves templates, closures, namespaces and a long macro, with a singlton to keep it all company. I''m not even entirelly sure it would work, not to mention how compiler dependent it would be. Any nice friendly solutions coders? -Lethe

Share this post


Link to post
Share on other sites
Could you post an example usage of the code? Are you trying to get something so that the user can enter a class name into a null terminated string and then create an instance of the class that the user named?

Share this post


Link to post
Share on other sites
Object Factories. If you don''t have Modern C++ Design, now is the time to make a smart purchase!

Otherwise, I''m sure somebody here can explain it to you (or you can Google it), but I don''t have the time. Sorry.

Share this post


Link to post
Share on other sites
This is the (Abstract) Factory pattern. Basically, you have
a "factory" class that can produce one of many classes that
are "registered" to its database. Do more research on
it as it is non-trivial. And yes, the factory is usually
a Singleton.

Some books that may help are Design Pattern and Modern C++ Design.



Kami no Itte ga ore ni zettai naru!

Share this post


Link to post
Share on other sites
That singlton I mentioned in my list of insane features is effectivly an object factory - a sort of dynamic one - in reality it would be a dictionary that converts strings to new versions of an associated object. The problem is not maintaining that - I've used & created them before - the problem is how to add objects to it.

I want to be able to declare at *any point* in my code a class, then add it to my 'object factory' there and then - no going back to edit the object factory or adding the object to some list in some peice of code. The factory won't be in the same object file afterall.

Then at any point in the code - before or after - I would want to write

    
void * ptr = MakeClass("BClass");


Thanks all for your input so far.
-Lethe

[edited by - Lethe on July 26, 2002 5:03:04 PM]

Share this post


Link to post
Share on other sites
You need an object that does the registration for you.

Read this. There is a full implementation :

http://www.cuj.com/experts/1906/hyslop.htm

Share this post


Link to post
Share on other sites
Just read that link - that solution happens to be rather close to what I was planning to do - and if you read it you might see why I don''t want to do it! Especially as I have even more functionality required so it will get even friendlier still;-) I was hoping for a more elegent solution - I suspect by the time someone thinks of one I could have implimented the not so friendly one myself. Supose I better start...

Useful link however - it gave me some improvments over my original idea - thanks!
-Lethe

Share this post


Link to post
Share on other sites
Not sure if this will be much use to you but here is one way to do it (this is roughly how the Half-Life engine handles it)


          
//This should be used outside of any functions preferably in the

// same file (note there will need to be a slight difference

//between the class name and the string for this to work (for

// example remove the C from the front of it

#define REGISTER_CLASS(classname,stringname) __declspec(dllexport) void* stringname(){void *c = new classname; return c;
}

typedef void *(*GET_CLASS)();

void *CreateClass(LPSTR Name)
{
//There should be a variable hInstance (containing the instance of the program you are running now)

GET_CLASS *Function = (GET_CLASS*)GetProcAddress(hInstance,Name);
if(Function == NULL)
{
return NULL;
//error

}
void *rv = Function();
return rv;
}

//example

class CClass
{
public:
void hello() {};
};

//then in a .cpp file

REGISTER_CLASS(CCLass,Class)

//to create this class you would call

something = CreateClass("Class");


like i said dunno if this will be useful. I just wrote that code there, so there will probably be bugs in it (and it's very messy) and even the entire context might not work. anyway just a quickly put together suggestion so if you don't like the look of it feel free to ignore it

EDIT : Fixed a few mistakes in the code above
[edited by - Grambo on July 26, 2002 5:57:03 PM]

[edited by - Grambo on July 26, 2002 6:00:00 PM]

[edited by - Grambo on July 26, 2002 6:01:16 PM]

[edited by - Grambo on July 26, 2002 6:44:53 PM]

Share this post


Link to post
Share on other sites
Just a quick idea: if what you're worrying about is writing a new function for each object, you could use templated functors to 'automate' that job, and store functors instead of pointers to functions in the factory:

  template<class T>
struct CreateObject{
string name;
CreateObject(string newname):name(newname){}
T *Create(){return new T;}
};

template<class T>
void Factory::RegisterClass(string name){
ObjectCreatorList.push_back(CreateObject<T>(name));
}


You get the idea... It simplifies the client code somewhat.

Cédric

EDIT: Glanced at the CUJ article (or novel); it might be the same idea, but I'm too lazy to check it out.

[edited by - cedricl on July 26, 2002 6:04:07 PM]

[edited by - cedricl on July 26, 2002 6:04:40 PM]

Share this post


Link to post
Share on other sites
Here ya go... the macro and implementation are ugly, but the use in your code looks nice. You could also wrap up the creation of an instance of a class into another macro, but that's up to you.


    
#include "stdafx.h"
#include <map>

class BaseFactory
{
public:
BaseFactory()
{
m_mapDb.insert(std::pair<char*, BaseFactory*>("BaseFactory", this));
}

static void *CreateInstance(char *pClassName)
{
std::map<char*, BaseFactory*>::iterator it = m_mapDb.find(pClassName);
BaseFactory *pFact = it->second;
if(pFact == NULL)
return NULL;

return pFact->CreateNew();
}

protected:
virtual void *CreateNew()
{
return NULL;
}

static std::map<char *, BaseFactory *> m_mapDb;
};
std::map<char*, BaseFactory*> BaseFactory::m_mapDb = std::map<char*, BaseFactory*>();
BaseFactory _Factory;

#define DEFINE_FACTORY( c )
class c##Factory : public BaseFactory { public: c##Factory() { m_mapDb.insert(std::pair<char*, BaseFactory*>(#c, this)); } protected: void *CreateNew() { return new c(); } }; c##Factory _Factory##c;

#define CREATE_INSTANCE( c )(c##*)BaseFactory::CreateInstance(#c);


class a
{
public:
void PrintType()
{
printf("a\n");
}
};
DEFINE_FACTORY(a);

class b
{
public:
void PrintType()
{
printf("b\n");
}
};
DEFINE_FACTORY(b);

int _tmain(int argc, _TCHAR* argv[])
{
a *pA = (a *)BaseFactory::CreateInstance("a");
void *pV = BaseFactory::CreateInstance("b");

pA->PrintType();
((b*)pV)->PrintType();
return 0;
}




[edited by - jonstelly on July 26, 2002 6:39:36 PM]

Share this post


Link to post
Share on other sites
I''m afraid your all iterating over stuff I allready know & have thought off, except for maby the half-life one - I''m not entirely sure whats going on there:-) I''m simply going to write it the way I originally thought of - and you have all sugested some part off! (With a few little bits I didn''t think off - thanks for those.)

I was just hoping for a solution with a shade more elegance.
Thanks to all.

-Lethe

Share this post


Link to post
Share on other sites
How does the solution lack elegance? You add a single call to a macro after you declare a class and you''re done. To create a new instance, you call a single function, passing the parameter you specified.

I don''t see what''s so ugly about it.

Share this post


Link to post
Share on other sites
quote:
Original post by gumby
Umm, the fact that it is a macro implies that it lacks elegance.


Hardly, the macro lets you turn the parameter into a string literal in a deterministic manor - exactly what he needs to do. C++ is lacking in reflective features, so this isn''t an easy to do as it could be.

The other alternative is to use RTTI, which is ground to trend lightly on, as the resultant names are not deterministic (will be different on different compilers).

Share this post


Link to post
Share on other sites
"Umm, the fact that it is a macro implies that it lacks elegance.
Go back to flipcode."

Will do sir. It''s obvious that your solution is much better. Oh wait, you didn''t post anything helpful here. I can only assume that the ''Go back to flipcode'' comment is some sort of intended insult.

And macros don''t imply that something isn''t elegant. There are plenty of legit uses for macros. There is no other way to create a factory for each class in a single simple function. He could get rid of the macro-ing all together and write out every factory by hand with some cut and pasting, but that kind of thing is exactly what macros are there for.

Share this post


Link to post
Share on other sites