Hi, I really need some help here. After upgrading to g++ 3.4 I have run
into all sorts of troubles that I'm sure depends on my lack of proper
understanding of C++. I would now like to get it right once and for all,
if possible.
Most severe is the problem illustrated by the code below. It's based on
the "pluggable factory pattern" described in
http://www.adtmag.com/joop/crarticle.asp?ID=1520
It's a generic factory class using templates to ease the addition of new
types to the hierarchy.
The code below is a simplified version of the system we use. It compiles
and runs fine and dandy with g++ 3.3.3, whereas 3.4.2 gives the error
message "too few template-parameter-lists". If I uncomment the
commented lines below (//template <>) both versions compile the code but
neither run properly - no output is produced. It has something to
do with instantiation of static members in template classes, AFAIU.
Expected output from the program is
DataTypeA
DataTypeB
Could someone please help me understand what's going on here? I'd be very
grateful. Code and some output below.
Thanks,
Erik
Successful run with g++ 3.3.3:
bash$ g++ -c main.cc
bash$ g++ -c generalmaker.cc
bash$ g++ -c typeAmaker.cc
bash$ g++ -c typeBmaker.cc
bash$ g++ -o makertest main.o generalmaker.o typeAmaker.o typeBmaker.o
bash$ ./makertest
DataTypeA
DataTypeB
bash$
Code:
-----8<-----
//main.cc
#include <iostream>
#include "generalmaker.h"
#include <string>
#include <list>
using namespace std;
int main() {
list<string> typelist = GeneralMaker::listRegistered();
for( list<string>::iterator it = typelist.begin(); it != typelist.end(); ++it ) {
cerr<<*it<<endl;
}
return 0;
}
-----8<-----
//generalmaker.h
#ifndef GENERALMAKER_H
#define GENERALMAKER_H
#include <map>
#include <string>
#include <list>
class GeneralData;
class GeneralMaker
{
public:
virtual ~GeneralMaker() {}
static GeneralData * newData( const std::string& );
static std::list<std::string> listRegistered();
protected:
GeneralMaker( ) {}
virtual GeneralData* makeData() = 0;
typedef std::map< std::string , GeneralMaker * > MakerMap;
static MakerMap & registry();
};
template <class Data, const char * str_>
class GeneralMakerTP : public GeneralMaker
{
protected:
GeneralMakerTP() : GeneralMaker( )
{
Data d;
std::string str = d.uniqueName();
GeneralMaker::registry().insert( make_pair( str, this ) );
}
GeneralData* makeData()
{
return new Data();
}
static const GeneralMakerTP<Data, str_> registerThis;
};
#endif
-----8<-----
//generalmaker.cc
#include "generalmaker.h"
#include "generaldata.h"
using namespace std;
list<string> GeneralMaker::listRegistered()
{
list<string> list;
for ( MakerMap::const_iterator it = registry().begin(); it != registry().end(); ++it )
{
list.push_back( it->first );
}
return list;
}
GeneralMaker::MakerMap & GeneralMaker::registry()
{
/* We use this because static instances of sub classes of GeneralMaker
make use of this map. The idea comes from c++ faq lite
[10.12] How do I prevent the "static initialization order fiasco"?
*/
static GeneralMaker::MakerMap reg;
return reg;
}
GeneralData* GeneralMaker::newData( const std::string& generalDataType )
{
GeneralMaker* maker =
(*registry().find( generalDataType )).second;
return maker ? maker->makeData() : NULL;
}
-----8<-----
//generaldata.h
#ifndef GENERALDATA_H
#define GENERALDATA_H
#include <string>
#include "generalmaker.h"
#include "generaldata.h"
class GeneralData
{
public:
GeneralData() {}
virtual ~GeneralData(){}
virtual std::string uniqueName() = 0;
};
#endif
-----8<-----
//datatypeA.h
#ifndef DATATYPEA_H
#define DATATYPEA_H
#include "generaldata.h"
#include <string>
class DataTypeA : public GeneralData
{
public:
DataTypeA( ) : GeneralData() {}
~DataTypeA() {}
std::string uniqueName() { return std::string("DataTypeA"); }
};
#endif
-----8<-----
//datatypeB.h
#ifndef DATATYPEB_H
#define DATATYPEB_H
#include "generaldata.h"
#include <string>
class DataTypeB : public GeneralData
{
public:
DataTypeB( ) : GeneralData() {}
~DataTypeB() {}
std::string uniqueName() { return std::string("DataTypeB"); }
};
#endif
-----8<-----
//typeAmaker.cc
#include "datatypeA.h"
#include "generalmaker.h"
char nameA[] = "DataTypeA";
//template<> //If I uncomment this, g++ 3.4 compiles but runs incorrectly
const GeneralMakerTP<DataTypeA, nameA> GeneralMakerTP<DataTypeA,nameA>::registerThis;
-----8<-----
//typeBmaker.cc
#include "datatypeB.h"
#include "generalmaker.h"
char nameB[] = "DataTypeB";
//template<> //If I uncomment this, g++ 3.4 compiles but runs incorrectly
const GeneralMakerTP<DataTypeB, nameB> GeneralMakerTP<DataTypeB,nameB>::registerThis;
--
My Hotmail address is a spam magnet. If replying by email,
please use erik dot arner at cgb dot ki dot se