Click here to Skip to main content
15,889,739 members
Please Sign up or sign in to vote.
4.00/5 (1 vote)
See more:
K, what i have here is a base class and numerous derived classes. below the base class, in the same file, i have a couple objects responsible for registering the derived objects in a map object, with std::string indices, and then an object that will instantiate the objects, given a valid string index. To register a class object, i declare an object of type DerivedRegister in the private members of the derived class, with a template parameter of the class, and then call it's constructor outside the class. Simple enough right? wrong. Because of the way the objects are set up and declared, i keep getting syntax errors when tweaking the base class ever so sligthly. Here is my base.h :

C++
#ifndef BASE_H
#define BASE_H

#include <map>
#include <string>
#include <fstream>
using namespace std;

class base{
    #define DECLARE_ENTITY(T) static DerivedRegister<T> reg;
    #define LINK_ENTITY(T, name) DerivedRegister<T> T::reg(name);
public:
    int getMeh() {return m_meh;}
    virtual void GetParams(fstream & stream);
protected:
    int m_meh;
    string m_EntityName;
};

template<class T> base * createT() {return new T;}
class BaseFactory{
public:
    typedef map<string, base *(*)()> map_type;

    static base * createInstance(const string & s) {
        if ( !getMap()->count(s) )
            return NULL;
        map_type::iterator it = getMap()->find(s);
        return it->second();
    }

protected:

    static map_type * getMap() {
        if(!m_Map) {m_Map = new map_type;}
        return m_Map;
    }

private:
    static map_type * m_Map;
};

template<class T>
class DerivedRegister : BaseFactory {
public:
    DerivedRegister(std::string const & s)
    {
        getMap()-> insert( pair<string, base*(*)()> (s, &createT<T>));
    }
};

BaseFactory::map_type * BaseFactory::m_Map = new map_type();

#endif


here is base.cpp when i'm trying to define a member of base:

C#
#include "base.h"

void base::GetParams(fstream & stream)
{
    getline(stream, m_EntityName, ' ');
}


however, for some reason, i get this error message:

base.cpp Line 5 : multiple definition of BaseFactory::m_Map
main.cpp Line 11: first defined here

here is my main.cpp where i was testing out the instantiator:


C++
#include <iostream>
#include <vector>
using namespace std;
#include "base.h"
#include "derived.h"
#include "second.h"
#include "third.h"
#include "fourth.h"

int main()
{
    BaseFactory yosh;
    vector<base *> meh;

    for(int i = 0; i < 20; i++)
    {
        if(i % 4 == 0)
            meh.push_back(yosh.createInstance("derived"));
        else if( i % 4 == 1)
            meh.push_back(yosh.createInstance("second"));
        else if( i % 4 == 2)
            meh.push_back(yosh.createInstance("third"));
        else
            meh.push_back(yosh.createInstance("fourth"));
    }

    for(int i = 0; i < (int) meh.size(); i++)
    {
        if(meh[i])
            cout << meh[i]->getMeh() << "\n";
        else
            cout << "invalid\n";
    }

    for(int i = 0; i < (int) meh.size(); i++)
    {
        if(meh[i]) delete meh[i];
    }
    return 0;
}


derived, second, third, and fourth are all just test classes that are the same except for there constructors which assign different values to m_Meh. It was very enlightening. Anyway, the problem i'm having with this error, is that the moment i omit that virtual function, GetParams from the base class, and then omit basically everything within the base.cpp file, it compiles and works just fine. so why is it that when i try and add a method to the base class, i get errors about the redefinition of m_Map?

EDIT: i realized just now that the map of the basefactory might need some clarification. the m_map object in there has indices of type std::string, and then the indices refer to pointers to functions. the function is defined just above the class there. when a derived class is registered, they pass in the template parameter to their own class, and a const string. these are used in a new index of the map object where the const string and the templated function pointer are paired together. then anytime i call the member create instance, it passes in the const string, sees if there is a valid index, and if there is, it returns the function at that index, which is called and returns a new instance of the object registered to that const string.
Posted
Updated 29-Nov-11 5:18am
v6

Move
C++
BaseFactory::map_type * BaseFactory::m_Map = new map_type();
to your .cpp file.

Instantiating it in the header, means that it will create a new instance everytime you include the header.

This is why the linker is seeing multiple definitions.
 
Share this answer
 
Comments
FatalCatharsis 29-Nov-11 16:56pm    
interesting. didn't think it would be that simple :\ . isn't that what the:

#ifndef BASE_H
#define BASE_H
...
#endif

does? Like, once it's included once then BASE_H is defined, and then it won't cycle through it again?
JackDingler 29-Nov-11 17:12pm    
That limits it to getting included, once per cpp file.

The cpp file is where you want to do actual instantiation. The header's main purpose is to just declare your types and definitions.
FatalCatharsis 29-Nov-11 17:42pm    
alrighty then. good to know :P
I don't think you need the final line in base.h. Why are trying to instantiate a class member there?
BaseFactory::map_type * BaseFactory::m_Map = new map_type();
 
Share this answer
 
Comments
FatalCatharsis 29-Nov-11 13:32pm    
it is because m_Map is defined statically. If that line is omitted, the static functions inside the class no longer have access to the m_Map member. So it's declared globally. I get undefined reference errors otherwise. I need only one single map object even though there is multiple derivedRegister objects. Defined statically, the map object is the same object for all instances of derivedRegister.
Chris Meech 29-Nov-11 16:23pm    
Apologies. I missed that keyword static.
Moved this to be a reply to your comment, instead of a reply to my Solution. :slaps head:

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900