Base class static member initialisation

S

Sanatan

Hi,
I am trying to create a factory scheme to create objects with a
certain base class.
The pattern I am using is to have a creator class, from which creators
for each
desired type are derived. I then declare a global object of these
creator types.
The base creator class has a static map (keyed by string) that
registers these
objects. This allows people to add new objects and their creators, and
one
can specify the objects to create by passing the string names in a
command line/
config file.

Here is the code:

// BEGIN: base.hpp
#if !defined(BASE_HPP_INCLUDED)
#define BASE_HPP_INCLUDED
#include <string>
#include <map>
using namespace std;

class base
{
private:
protected:
static map<string, base *> c;
base(string name)
{
c[name] = this;
}
public:
static base *getInstance(string name)
{
map<string, base *>::iterator loc = c.find(name);
return ((loc == c.end()) ? 0 : loc->second);
}
virtual string getName() const = 0;
};
map<string, base *> base::c;
#endif
// END: base.hpp

// BEGIN: a.cpp
#include "base.hpp"
class a : public base
{
public:
a() : base("A") {}
string getName() const
{
return "This is A";
}
} globalA;
// END: a.cpp

// BEGIN: b.cpp
#include "base.hpp"
class b : public base
{
public:
b() : base("B") {}
string getName() const
{
return "This is B";
}
} globalB;
// END: b.cpp

// BEGIN: testStatic.cpp
#include <iostream>
#include "base.hpp"
using namespace std;
main(int, char **)
{
cout << base::getInstance("A")->getName()
<< " "
<< base::getInstance("B")->getName()
<< endl;
return 0;
}
// END: testStatic.cpp

When I compile, I get the duplicate static initialisation complaint:

$ g++ -o testStatic a.cpp b.cpp testStatic.cpp

/tmp/ccBJjSIu.o:(.bss+0x0): multiple definition of `base::c'
/tmp/ccqWdF5o.o:(.bss+0x0): first defined here
/tmp/cc8bDsEr.o:(.bss+0x0): multiple definition of `base::c'
/tmp/ccqWdF5o.o:(.bss+0x0): first defined here
collect2: ld returned 1 exit status

It would seem that the compiler sees a problem with the static map.
Is there a way of fixing this pattern? I have looked at the discussion
on:

http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.15

but unfortunately, these solutions won't work for me. I need to be
able
to create these objects by their string names, and want users to not
have
to do much more beyond deriving their class from base, and declare
a global object.

Any suggestions would be appreciated!

--Sanatan
 
I

Ian Collins

Hi,
I am trying to create a factory scheme to create objects with a
certain base class.
The pattern I am using is to have a creator class, from which creators
for each
desired type are derived. I then declare a global object of these
creator types.
The base creator class has a static map (keyed by string) that
registers these
objects. This allows people to add new objects and their creators, and
one
can specify the objects to create by passing the string names in a
command line/
config file.

Here is the code:

// BEGIN: base.hpp
#if !defined(BASE_HPP_INCLUDED)
#define BASE_HPP_INCLUDED
#include<string>
#include<map>
using namespace std;

Never do this in a header!
class base
{
private:
protected:
static map<string, base *> c;
base(string name)
{
c[name] = this;
}
public:
static base *getInstance(string name)
{
map<string, base *>::iterator loc = c.find(name);
return ((loc == c.end()) ? 0 : loc->second);
}
virtual string getName() const = 0;
};
map<string, base *> base::c;

This will case base::c to be defined in any compilation unit that
included this header!
#include "base.hpp"

One definition of base::c.

#include "base.hpp"

Second definition of base::c.

#include "base.hpp"

Third definition of base::c.

When I compile, I get the duplicate static initialisation complaint:

$ g++ -o testStatic a.cpp b.cpp testStatic.cpp

/tmp/ccBJjSIu.o:(.bss+0x0): multiple definition of `base::c'
/tmp/ccqWdF5o.o:(.bss+0x0): first defined here
/tmp/cc8bDsEr.o:(.bss+0x0): multiple definition of `base::c'
/tmp/ccqWdF5o.o:(.bss+0x0): first defined here
collect2: ld returned 1 exit status

It would seem that the compiler sees a problem with the static map.
Is there a way of fixing this pattern? I have looked at the discussion
on:

You must have one and only one definition, in a source file, not a header.
http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.15

but unfortunately, these solutions won't work for me. I need to be
able
to create these objects by their string names, and want users to not
have
to do much more beyond deriving their class from base, and declare
a global object.

Any suggestions would be appreciated!

If you want to populate a map, create a small class to do it and
instantiate one instance of that class.
 
S

Sanatan

Never do this in a header!


This will case base::c to be defined in any compilation unit that
included this header!


One definition of base::c.



Second definition of base::c.



Third definition of base::c.

<snip>

You must have one and only one definition, in a source file, not a header..

If you want to populate a map, create a small class to do it and
instantiate one instance of that class.

Thank you! That was indeed a dumb thing to do.

So I created base.cpp with only the initialisation:
// BEGIN: base.cpp
#include "base.hpp"
map<string, base *> base::c;
// END: base.cpp

Compiling with:
$ g++ -o testStatic base.cpp a.cpp b.cpp testStatic.cpp
proceeds without any complaints.

However, the programme seg faults on being run. I traced to this to
the
point when the object globalB is constructed. When the base-class
constructor
base is called, it seg faults at the line c[name] = this.

It seems there are still issues with this map, base::c being
initialised properly.

What am I doing wrong?

--Sanatan
 
I

Ian Collins

However, the programme seg faults on being run. I traced to this to
the
point when the object globalB is constructed. When the base-class
constructor
base is called, it seg faults at the line c[name] = this.

It seems there are still issues with this map, base::c being
initialised properly.

What am I doing wrong?

You have fallen into the dreaded unspecified order of construct trap.
Either put the definition of base::c and globalB in the sane file (with
base::c defined first) or globalB will have to make sure base::c is
constructed (maybe change it to a pointer).

Have a look at some singleton patterns.
 
S

Sanatan

On 12/29/10 10:48 AM, Sanatan wrote:



You have fallen into the dreaded unspecified order of construct trap.
Either put the definition of base::c and globalB in the sane file (with
base::c defined first) or globalB will have to make sure base::c is
constructed (maybe change it to a pointer).

Have a look at some singleton patterns.

I see what you're saying. I need to redesign this.

Thanks for all your help!

--Sanatan
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,483
Members
44,902
Latest member
Elena68X5

Latest Threads

Top