• Advertisement
Sign in to follow this  

C++: Instantiating classes by name

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

In Java, you can instatiate a class at run-time by name. Obviously, this isn't possible in C++. But is there an elegant way to get similar results without having to do a long chain of strcmp's, with one check for every class I know I have?

Share this post


Link to post
Share on other sites
Advertisement
strcmp()s ? You were talking of C++, werent you ? :D

In general, no, there's not much else you can do, apart from using an std::map or std::hash_map in order to optimize search time, eg.


struct Factory {
struct Creatable {
virtual ~Creatable() {}
};

virtual ~Factory() {}
virtual Creatable *createInstance() = 0;
};

template<typename ProductType>
struct FactoryImpl {
// Replace by Factory::Creatable if compile doesn't support
// covariant return types
ProductType *createInstance() { return new ProductType(); }
};

int main() {
std::map<std::string, Factory *> Factories;
Factories["Tank"] = new FactoryImpl<Tank>();
Factories["Ship"] = new FactoryImpl<Ship>();
Factories["Plane"] = new FactoryImpl<Plane>();

Factory::Creatable *p = Factories["Ship"]->second->createInstance();
}



-Markus-

Share this post


Link to post
Share on other sites
Thanks for the replies, guys. Unfortunately, where I work the powers that be are ideologically opposed to templates. So, basically, I foresee a ton of strcmps in my future.

Share this post


Link to post
Share on other sites
Quote:
Original post by smitty1276
Unfortunately, where I work the powers that be are ideologically opposed to templates.

I'm with Polymorphic OOP on this one. Quit.

seriously.

Share this post


Link to post
Share on other sites
Quote:
Original post by ChaosEngine
Quote:
Original post by smitty1276
Unfortunately, where I work the powers that be are ideologically opposed to templates.

I'm with Polymorphic OOP on this one. Quit.

seriously.


That is strange that must mean your company is also opposed to using the complete C++ standard library (excluding the C part) to...

"did i just see them use std::cout?" "ah sorry according to company rules you can't use it as it's an instance of class template...."

That also means CLR or java or any language with generics are out of the question too.... okay i know its not exactly the same but still.

Share this post


Link to post
Share on other sites
Just code an abstract factory using templates and show them how much time and money it'll save. The moment that "time", "money" and "save" are uttered is usually the moment where managers begin to embrace templates.

Or like the people above me said, just leave and find a competent employer.

Share this post


Link to post
Share on other sites
Quote:
Unfortunately, where I work the powers that be are ideologically opposed to templates


When hammering a nail in, do they prefer to hit it with the wooden end of a hammer?

Share this post


Link to post
Share on other sites
Quote:
Original post by smitty1276
Thanks for the replies, guys. Unfortunately, where I work the powers that be are ideologically opposed to templates. So, basically, I foresee a ton of strcmps in my future.


Why would anyone be 'ideologically' opposed to templates? I can't see how anyone who programs in C++ could be opposed to templates, they are one of the most powerful tools for generic programming!

Share this post


Link to post
Share on other sites
They are opposed because someone, somewhere, who doesn't actually code and is barely even attached to the code-producing process, read in some arcane, poorly punctuated, inaccurate document that templates are hard to debug and cause code bloat. These thoughts were mis-interpretted as 'takes more time to debug' and 'ends up with more code to debug'. This, of course, would cost the company money.

What I would do is write a template version and a non-template version, then 'accidentally' show the template version at your next code-review. Make sure you let them get a good look at it before you 'relieze' your 'mistake'.

If you don't have code reviews, just check in the template version.

Share this post


Link to post
Share on other sites
Quote:
Original post by smitty1276
Thanks for the replies, guys. Unfortunately, where I work the powers that be are ideologically opposed to templates. So, basically, I foresee a ton of strcmps in my future.


I'd be really interested to hear more about this.

Here's an interesting article, aimed at embedded programmers but generally useful as well: Why are you still using C? (pdf here)

Also look up how expression templates and things such as the Matrix Template Library are able to generate efficient code, as good as FORTRAN. (link seems to be down at the moment).

Share this post


Link to post
Share on other sites
Alot of places are opposed to using templates in source code. Its not as big a deal as we make it out to be. For the record I'm very pro template.

But most universities don't teach them and if you use them extensivly and then get co-op's in for 4 months' then the use of templates cna really slow them down and it makes the use of co-ops almost impossible.

Sometimes soft factors affect our design as much as hard constraints. Its just a fact of life. Exceptions are another part of the language that falls into this catagory.

I do'nt think its such a big deal to not use templates. Herb Sutter himself has said its often better not to use the more "advanced featuers" of the language, and he's the head of microsoft's C++ dev tools team.

Cheers
Chris

Share this post


Link to post
Share on other sites
you could do it without templates and make it as fast as with 'em. You'll have to require to define factory manually, and that'll be a pain in some body area...

Also, you can even do something like that:

struct Factory {
struct Creatable {
virtual ~Creatable() {}
};

virtual ~Factory() {}
virtual Creatable *createInstance() = 0;
};

std::map<std::string, Factory *> GlobFactories;

template<typename ProductType, char ProductName[]>
struct FactoryImpl:Factory {
// Replace by Factory::Creatable if compile doesn't support
// covariant return types
ProductType *createInstance() { return new ProductType(); }
FactoryImpl(){GlobFactories[ProductName]=*this;}
};
FactoryImpl<Tank, "Tank"> tank_factory;
FactoryImpl<Ship, "Ship"> ship_factory;
FactoryImpl<Plane, "Plane"> plane_factory;




i don't remember exact syntax for putting " into macroses, but i'm pretty sure you could make handy macro
REGISTER_CLASS(ClassName)
that will expand into
FactoryImpl<ClassName, "ClassName"> ClassName_factory;
and that'll look even nicer. Tho, probably they like macroses even less than templates...

Actually, templates and avaliable stuff that uses 'em is one of main reasons why i switched to C++ from Pascal, and why i would hate to switch back.

Also, templates are not very embended-soft-friendly.

Share this post


Link to post
Share on other sites
Quote:
Original post by Dmytry
FactoryImpl<Tank, "Tank"> tank_factory;
FactoryImpl<Ship, "Ship"> ship_factory;
FactoryImpl<Plane, "Plane"> plane_factory;



Unfortunately, string literals are not valid template arguments because they have static linkage. However, extern character arrays *do* work, but are obviously a bit unwieldy:


// assume we are inside a .cpp;
// if in a header we need to declare and define things separately
char tank_str[] = "Tank"; // ok, extern by default
// Or: extern const char tank_str[] = "Tank";
// need explicit extern because constants have internal linkage by default...

FactoryImpl<Tank, tank_str> tank_factory;



BTW, *pointers* to string literals *won't* work; it's not very clear to me why that is so, so if someone in the know liked to elaborate, I'd be thankful.

Share this post


Link to post
Share on other sites
Quote:
Original post by Sharlin
BTW, *pointers* to string literals *won't* work; it's not very clear to me why that is so, so if someone in the know liked to elaborate, I'd be thankful.

Assuming you mean:


template< char const* >
struct your_template {};

char const* const blah = "some string";

your_template< blah > instance; // This should not work

it's because pointer and reference parameters are required by the standard to be directly taken at the pass sight. Otherwise, the compiler wouldn't know if the object being referenced has external or internal linkage, and more subtly, it would be much more difficult for a template to use the same instantiation when refering to the same object in multiple translation units since you are now indirectly referencing the object through a stored variable. Notably in this example, the instance being passed by reference as a template argument has static linkage (the string) so the pointer also can't be used for the same reason why you can't just directly use a string literal. Remember, it's the object being referenced that must have external linkage, not the pointer being passed.

The exact wording in the standard (with the important part in bold):

Quote:
Original post by the C++ Standard 14.3.2[1]
A template-argument for a non-type, non-template template-parameter shall be one of:

-- an integral constant-expression of integral or enumeration type; or

-- the name of a non-type template-parameter; or

-- the name of an object or function with external linkage, including function templates and function template-ids but excluding non-static class members, expressed as id-expression; or

-- the address of an object or function with external linkage, including function templates and function template-ids but excluding non-static class members, expressed as & id-expression where the & is optional if the name refers to a function or array; or

-- a pointer to member expressed as described in 5.3.1.


Edit:

Also noting that since this is the case, you can change the code to:

template< char const* const& >
struct your_template {};

extern char const* const blah = "some string";

your_template< blah > instance; // This will now work


but that would be pretty silly and add an unneeded level of indirection.

[Edited by - Polymorphic OOP on March 15, 2005 12:09:58 PM]

Share this post


Link to post
Share on other sites
Wow! I thought I'd check in and was shocked at how many responses this post received.

Someone was asking about the use of the standard C++ library here at work... in short, there IS NO USE of the C++ libs. All output, for example, is handled with the old C functions from stdio.h.

I *really* don't know why they don't like templates... I never pressed the issue. I'm still learning my way around the place. I also caught on to the fact that they despise the addition of generics to Java. (We don't use much Java around here though).

They only use C++ to the extent that there is a lot of class inheritance and whatnot.

For the record, I am a fan of templates, personally.

Share this post


Link to post
Share on other sites
Quote:
Original post by Polymorphic OOP
it's because pointer and reference parameters are required by the standard to be directly taken at the pass sight. Otherwise, the compiler wouldn't know if the object being referenced has external or internal linkage, and more subtly, it would be much more difficult for a template to use the same instantiation when refering to the same object in multiple translation units since you are now indirectly referencing the object through a stored variable.

Ahh, thanks. That makes sense (kind of, anyway :P)
Quote:
Notably in this example, the instance being passed by reference as a template argument has static linkage (the string) so the pointer also can't be used for the same reason why you can't just directly use a string literal. Remember, it's the object being referenced that must have external linkage, not the pointer being passed.

Right, silly me. Actually, I thought about that, but then became a bit confused about the fact that even pointers to extern arrays won't work (but you explained that above).
Quote:
Also noting that since this is the case, you can change the code to:
<snip>
but that would be pretty silly and add an unneeded level of indirection.

Indeed :)

Thanks again, that was quite helpful.

-Johannes

Share this post


Link to post
Share on other sites
The stone age must not be fun to work in...

Here is a very very simplified example to get you started, it assumed that bare minimum is allowed and uses a linked list to iterate creators (order of N lookup, ideally you can use an rb-tree here).


#include <stdio.h>
#include <string.h>

class MyBase
{
public:
virtual void displayName() = 0;
};

class MyClassA : public MyBase
{
public:
static MyBase *create() { return new MyClassA(); }

virtual void displayName() { printf("MyClassA"); }
};

class MyClassB : public MyBase
{
public:
static MyBase *create() { return new MyClassB(); }

virtual void displayName() { printf("MyClassB"); }
};

class MyClassC : public MyBase
{
public:
static MyBase *create() { return new MyClassC(); }

virtual void displayName() { printf("MyClassC"); }
};

typedef MyBase *(PFN_CREATOR)();

class MyFactory
{
private:
class FactoryNode
{
public:
FactoryNode() : m_pccName(NULL), m_pfn(NULL), m_pNext(NULL) {}
FactoryNode(const char *name, PFN_CREATOR pfn) : m_pccName(name), m_pfn(pfn), m_pNext(NULL) {}
const char *m_pccName;
PFN_CREATOR *m_pfn;
FactoryNode *m_pNext;
};

public:
static void registerCreator(const char *name, PFN_CREATOR pfn)
{
FactoryNode *p = new FactoryNode(name, pfn);

if (!m_pHead)
{
m_pHead = p;
}
else
{
FactoryNode *pStart = m_pHead;
while(pStart->m_pNext)
pStart = pStart->m_pNext;

pStart->m_pNext = p;
}
}

static MyBase* createClass(const char *name)
{
FactoryNode *pStart = m_pHead;
while(pStart && strcmp(pStart->m_pccName, name))
{
pStart = pStart->m_pNext;
}
if (pStart)
return pStart->m_pfn();
else
return NULL;
}

private:
static FactoryNode *m_pHead;
};

MyFactory::FactoryNode * MyFactory::m_pHead = NULL;

int main(int argc, char **argv)
{
MyFactory::registerCreator("red", MyClassA::create);
MyFactory::registerCreator("blue", MyClassB::create);
MyFactory::registerCreator("green", MyClassC::create);

MyBase *pObject = MyFactory::createClass("blue");
if (pObject)
pObject->displayName();
else
printf("No such object.\n");

delete pObject;
return 0;
}



++Alex

Share this post


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

  • Advertisement