473,325 Members | 2,872 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,325 software developers and data experts.

Instantiation of static variable in template class fails, why??


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

Jul 22 '05 #1
18 5720
Maybe (and I dont claim to be a guru) templates are generated code and
static members cause ambiguity, because they need to be specifically called
and 'typed'.
I had same problem and once I found a way around it, all hell came loose
when I tried to import/export.
So I removed the static members from the template and I called them in the
derived classes of the template
for example
class MyDerivedFromTemplate : public YourTemplate< SomeClass >
{
static int s_number_of_objects;
.....
};

hope this helps.

"Erik Arner" <er********@hotmail.com> wrote in message
news:pa****************************@hotmail.com...

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

Jul 22 '05 #2
Sorry, you may ask but I want to use the static data in the base template.
You use functions (they may have to be virtual) to return that data, but in
the derived class you make it return the appropriate static data.
In a way the base class is dealing with data that the derived class is going
to set. Lovely OOP.
Cheers.

"Charlie" <du*********@yahoo.com> wrote in message
news:nk*****************@news-server.bigpond.net.au...
Maybe (and I dont claim to be a guru) templates are generated code and
static members cause ambiguity, because they need to be specifically
called and 'typed'.
I had same problem and once I found a way around it, all hell came loose
when I tried to import/export.
So I removed the static members from the template and I called them in the
derived classes of the template
for example
class MyDerivedFromTemplate : public YourTemplate< SomeClass >
{
static int s_number_of_objects;
.....
};

hope this helps.

"Erik Arner" <er********@hotmail.com> wrote in message
news:pa****************************@hotmail.com...

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


Jul 22 '05 #3
On Mon, 22 Nov 2004 15:35:11 +0100, Erik Arner wrote:

It has something to
do with instantiation of static members in template classes, AFAIU.


OK, so I'm getting closer to the problem now. The problem is definitely
that the following lines in typeAmaker.cc (and correspondingly in
typeBmaker.cc) aren't parsed the way I expect them to:

char nameA[] = "DataTypeA";
template<>
const GeneralMakerTP<DataTypeA, nameA> GeneralMakerTP<DataTypeA,nameA>::registerThis;

It seems that the compiler does not interpret this as an instantiation of
a static variable. I have no clue as to what the compiler makes of the
above statement. Is it ill-formed?

If I add empty parenthesis after registerThis (= a call to the default
constructor, right?) the compiler complains that there's no member
function of that name declared.

If I add a dummy constructor that takes an int to the GeneralMakerTP class
and call it instead, the program works correctly after this modified
statement:

char nameA[] = "DataTypeA";
template<>
const GeneralMakerTP<DataTypeA, nameA> GeneralMakerTP<DataTypeA,nameA>::registerThis(0);

This finally makes the compiler understand that I'm initalizing the
static member. I really want to avoid this kind of ugly hackery. Help much
appreciated. Any ideas?

Thanks,
Erik

Jul 22 '05 #4

OK, I think my problem boils down to the following. How do I initialize
A::a using the default constructor in the code below?

Thanks,
Erik

Code:

#include <iostream>

using namespace std;

template <class T>
class A
{
public:
A() { cerr<<"A() called"<<endl; }
A(int) { cerr<<"A(int) called"<<endl; }

private:
static A a;
};

// template<> A<int> A<int>::a();
/*
g++ 3.4.2: error: no member function `a' declared in `A<int>'
g++ 3.3.3: error: no member function `a' declared in `A<int>'
*/

// A<int> A<int>::a;
/*
g++ 3.4.2: error: too few template-parameter-lists
g++ 3.3.3: Compiles, output: A() called
*/

// template<> A<int> A<int>::a;
/*
g++ 3.4.2: Compiles, but no output
g++ 3.3.3: Compiles, but no output
*/

template<> A<int> A<int>::a(0);
/*
g++ 3.4.2: Compiles, output: A(int) called
g++ 3.3.3: Compiles, output: A(int) called
*/

int main()
{
return 0;
}
--
My Hotmail address is a spam magnet. If replying by email,
please use erik dot arner at cgb dot ki dot se

Jul 22 '05 #5
Erik Arner wrote in news:pa****************************@hotmail.com in
comp.lang.c++:
OK, I think my problem boils down to the following. How do I initialize
A::a using the default constructor in the code below?

Thanks,
Erik

Code:

#include <iostream>

using namespace std;

template <class T>
class A
{
public:
A() { cerr<<"A() called"<<endl; }
A(int) { cerr<<"A(int) called"<<endl; }

private:
static A a;
};


Generic version:

template < typename T >
A< T > A< T >::a;

AFAICT you can't specialize a individual static member as
the syntax is needed for when the whole class is specialized:

template <> class A< int >
{
static A< double > a;
}

template <>
A< double > A< int >::a;

However no compiler I tried gave a sensible diagnostic when an
explicit specialization was provided *without* explicitly
specializing A< int >.

The best was Borland CBuilderX (EDG) that found A< int >::a was
an unresolved external for some reason. The others presumably
just ignored it.

You can however explicity (*) instantiate a specific static
member in a TU:

template A< int > A< int >::a;

HTH.

*) Can't remember the terminoligy for this, as I've never
needed to do it, "explicitly" is just a guess.

Rob.
--
http://www.victim-prime.dsl.pipex.com/
Jul 22 '05 #6
On 22 Nov 2004 19:53:10 GMT, Rob Williscroft <rt*@freenet.co.uk>
wrote:
Erik Arner wrote in news:pa****************************@hotmail.com in
comp.lang.c++:
OK, I think my problem boils down to the following. How do I initialize
A::a using the default constructor in the code below?

Thanks,
Erik

Code:

#include <iostream>

using namespace std;

template <class T>
class A
{
public:
A() { cerr<<"A() called"<<endl; }
A(int) { cerr<<"A(int) called"<<endl; }

private:
static A a;
};


Generic version:

template < typename T >
A< T > A< T >::a;

AFAICT you can't specialize a individual static member as
the syntax is needed for when the whole class is specialized:

template <> class A< int >
{
static A< double > a;
}

template <>
A< double > A< int >::a;


That's incorrect - if the whole class is specialized, you shouldn't
use the template<> bit. That should be:

A<double> A<int>::a;

Here's a full example:

template <class T>
struct A
{
static A a;
};

//general definition
template <class T>
A<T> A<T>::a;

//specialization of a for double, with template<>
template<>
A<double> A<double>::a;

//whole class specialization for float, with template<>
template <>
struct A<float>
{
static A<float> a;
};

//definition of A<float>::a, no template<>
A<float> A<float>::a;

int main()
{
A<float>::a, A<double>::a, A<char>::a; //check defined
}

See 14.7.3/5.

Tom
Jul 22 '05 #7
Tom Widmer wrote in news:bl********************************@4ax.com in
comp.lang.c++:

That's incorrect - if the whole class is specialized, you shouldn't
use the template<> bit. That should be:

A<double> A<int>::a;

See 14.7.3/5.


Thanks Tom, I've seen this before but managed to forget it.

However I modified your example:

#include <iostream>

template <class T>
struct A
{
A() { std::cout << "A< [" << typeid( T ).name() << "] >\n"; }
static A a;
};

//general definition
template <class T>
A<T> A<T>::a;

//specialization of a for double, with template<>
template<>
A<double> A<double>::a;

//whole class specialization for float, with template<>
template <>
struct A<float>
{
A() { std::cout << "A<float>\n"; }
static A<float> a;
};

//definition of A<float>::a, no template<>
A<float> A<float>::a;

template < typename T > void exists( T & ) {}

int main()
{
exists( A<float>::a );
exists( A<double>::a );
exists( A<char>::a );
}

The only compiler I have that compiled it was MSVC7.1 and it gave:

A<float>
A< [char] >

All the others (gcc/g++ 3.3,3.4,4.0 and BCCX 6.0 (EDG)) gave:

ccwlaaaa.o(.text+0xc5):test.cpp: undefined reference to `A<double>::a'

Or similar.

So I hope the OP can live with the generic version, as static member
specialization doesn't seem to be available yet :-(.

Rob.
--
http://www.victim-prime.dsl.pipex.com/
Jul 22 '05 #8

Thanks for the input! Comments below.

On Mon, 22 Nov 2004 19:53:10 +0000, Rob Williscroft wrote:
Erik Arner wrote in news:pa****************************@hotmail.com in
comp.lang.c++:
OK, I think my problem boils down to the following. How do I initialize
A::a using the default constructor in the code below?
#include <iostream>

using namespace std;

template <class T>
class A
{
public:
A() { cerr<<"A() called"<<endl; }
A(int) { cerr<<"A(int) called"<<endl; }

private:
static A a;
};

Generic version:

template < typename T >
A< T > A< T >::a;


OK, this compiles but doesn't initialize the static member (of course).
[SNIP]

You can however explicity (*) instantiate a specific static
member in a TU:

template A< int > A< int >::a;


Wow. I've never seen this syntax before. If I use it alone (without the
generic version above), g++ 3.4.2 (and 3.3.3) says "error: explicit
instantiation of `A<int>::a' but no definition available". If I add the
generic version before it, it compiles and runs correctly. See below for
complete code that now works.

The same goes for my more complex example program earlier in the thread
(see separate post).

My only problem right now is that I don't quite understand what's going
on. Specifically:

* Why does everything work as I expect it to when I instantiate the static
member using the constructor that takes a parameter but not with the
default constructor? What's the fundamental difference?

* Does this solution (see below for code) follow the standard or shall I
expect it to break the next time I upgrade the compiler?

Again, many thanks for the help.

Regards,
Erik

Output (as expected and desired):

bash$ g++ -o test test.cc
bash$ ./test
A() called
bash$

Code:

//file: test.cc
#include <iostream>

using namespace std;

template <class T>
class A
{
public:
A() { cerr<<"A() called"<<endl; }

private:
static A a;
};
template <class T> A<T> A<T>::a;//Generic definition
template A<int> A<int>::a;//Specific definition

int main()
{
return 0;
}

--
My Hotmail address is a spam magnet. If replying by email,
please use erik dot arner at cgb dot ki dot se

Jul 22 '05 #9
On Mon, 22 Nov 2004 15:35:11 +0100, Erik Arner wrote:

Could someone please help me understand what's going on here? I'd be very
grateful.


As stated elsewhere in the thread, I think I have the problem under
control. The solution is to add a generic definition of the static member
in generalmaker.h, and remove the "<>" from "template <>" statement in the
..cc files (typeAmaker.cc and typeBmaker.cc).

My understanding of what's going on is still very limited. First and
foremost I wonder if the code below is stable and standards compliant, and
will behave the same the next time I go to a higher gcc version. Any input
would be appreciated.

Thanks,
Erik
Successful run with g++ 3.4.2:

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;
};

// Following two lines added, generic definition of static member
template <class Data, const char * str_>
const GeneralMakerTP<Data, str_> 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
const GeneralMakerTP<DataTypeA, nameA> GeneralMakerTP<DataTypeA,nameA>::registerThis;
//N.B., no <> after template

-----8<-----
//typeBmaker.cc

#include "datatypeB.h"
#include "generalmaker.h"

char nameB[] = "DataTypeB";
template
const GeneralMakerTP<DataTypeB, nameB> GeneralMakerTP<DataTypeB,nameB>::registerThis;
//N.B., no <> after template
--
My Hotmail address is a spam magnet. If replying by email,
please use erik dot arner at cgb dot ki dot se

Jul 22 '05 #10
Erik Arner wrote in news:pa***************************@hotmail.com in
comp.lang.c++:
Generic version:

template < typename T >
A< T > A< T >::a;
OK, this compiles but doesn't initialize the static member (of
course).


static members without a initializer are default-initialized, in
this case that means A< T >::A() is invoked.

But see Tom's correction to My post too.
[SNIP]

You can however explicity (*) instantiate a specific static
member in a TU:

template A< int > A< int >::a;
Wow. I've never seen this syntax before. If I use it alone (without
the generic version above), g++ 3.4.2 (and 3.3.3) says "error:
explicit instantiation of `A<int>::a' but no definition available". If
I add the generic version before it, it compiles and runs correctly.
See below for complete code that now works.

The same goes for my more complex example program earlier in the
thread (see separate post).

My only problem right now is that I don't quite understand what's
going on. Specifically:

* Why does everything work as I expect it to when I instantiate the
static member using the constructor that takes a parameter but not
with the default constructor? What's the fundamental difference?


Not sure what you mean here, but (see my reply to Tom) there aren't
AFAICT any compilers that get all of this static initialization /
specalization business right, so maybe its a compiler bug.

* Does this solution (see below for code) follow the standard or shall
I expect it to break the next time I upgrade the compiler?


It's standard conforming (AFAICT) so it should work now and in the
future, it's also much simpler than trying to specialize a static
member so it should work everywhere now.

Rob.
--
http://www.victim-prime.dsl.pipex.com/
Jul 22 '05 #11
On 23 Nov 2004 11:17:17 GMT, Rob Williscroft <rt*@freenet.co.uk>
wrote:
However I modified your example:

#include <iostream>

template <class T>
struct A
{
A() { std::cout << "A< [" << typeid( T ).name() << "] >\n"; }
static A a;
};

//general definition
template <class T>
A<T> A<T>::a;

//specialization of a for double, with template<>
template<>
A<double> A<double>::a;


GCC bizarrely looks like treats that only as a declaration, and not a
definition. This fixed it for GCC:

template<>
A<double> A<double>::a = A<double>();

If I get como433 up and running again later, I'll give it a go and see
if it manages to compile the code.

Tom
Jul 22 '05 #12
On Tue, 23 Nov 2004 14:55:27 +0000, Rob Williscroft wrote:
Erik Arner wrote in news:pa***************************@hotmail.com in
comp.lang.c++:
Generic version:

template < typename T >
A< T > A< T >::a;


OK, this compiles but doesn't initialize the static member (of
course).


static members without a initializer are default-initialized, in
this case that means A< T >::A() is invoked.


But it doesn't produce any output until the next definition (see below) is
added, so I really don't understand what the statement actually does :-/
template A< int > A< int >::a;


This invokes the default constructor together with the generic version, it
seems. I still don't really grasp what's going on...

Thanks,
Erik

--
My Hotmail address is a spam magnet. If replying by email,
please use erik dot arner at cgb dot ki dot se

Jul 22 '05 #13
On Tue, 23 Nov 2004 14:58:13 +0000, Tom Widmer wrote:
On 23 Nov 2004 11:17:17 GMT, Rob Williscroft <rt*@freenet.co.uk>
wrote:
However I modified your example:

#include <iostream>

template <class T>
struct A
{
A() { std::cout << "A< [" << typeid( T ).name() << "] >\n"; }
static A a;
};

//general definition
template <class T>
A<T> A<T>::a;

//specialization of a for double, with template<>
template<>
A<double> A<double>::a;


GCC bizarrely looks like treats that only as a declaration, and not a
definition. This fixed it for GCC:

template<>
A<double> A<double>::a = A<double>();


OK, so now the question becomes: which method is "better", i.e. which will
probably be more stable and not break with a more strict, future compiler?
Do both versions follow the standard? Both versions compile and work with
both g++ 3.3.3 and 3.4.2, and both versions compile without warnings with
the online version of Comeau 4.3.3 (www.comeaucomputing.com/tryitout).

Version A:

template <class T> A<T> A<T>::a;
template<> A<double> A<double>::a;

Version B:

template<> A<double> A<double>::a = A<double>();

Regards,
Erik

--
My Hotmail address is a spam magnet. If replying by email,
please use erik dot arner at cgb dot ki dot se

Jul 22 '05 #14
Erik Arner wrote in news:pa****************************@hotmail.com in
comp.lang.c++:
static members without a initializer are default-initialized, in
this case that means A< T >::A() is invoked.


But it doesn't produce any output until the next definition (see
below) is added, so I really don't understand what the statement
actually does :-/
template A< int > A< int >::a;


This invokes the default constructor together with the generic
version, it seems. I still don't really grasp what's going on...


Ok I think I get it now, because A a class template its members
don't get implicitly instantiated until you try to use them.

In the case of A< int >::a its necessary to reference it in some
way, say by accessing a member or passing a refrence or pointer
to a function.

The:

template A< int > A< int >::a;

explicitly instantiates the member so it is no longer necessary
for the TU to reference it.

Rob.
--
http://www.victim-prime.dsl.pipex.com/
Jul 22 '05 #15
On Tue, 23 Nov 2004 15:24:34 +0000, Rob Williscroft wrote:

Ok I think I get it now, because A a class template its members
don't get implicitly instantiated until you try to use them.
Right.
In the case of A< int >::a its necessary to reference it in some
way, say by accessing a member or passing a refrence or pointer
to a function.

The:

template A< int > A< int >::a;

explicitly instantiates the member so it is no longer necessary
for the TU to reference it.


But I still need the generic definition somewhere before this statement,
otherwise it won't compile.

/Erik

--
My Hotmail address is a spam magnet. If replying by email,
please use erik dot arner at cgb dot ki dot se

Jul 22 '05 #16
Erik Arner wrote in news:pa***************************@hotmail.com in
comp.lang.c++:

The:

template A< int > A< int >::a;

explicitly instantiates the member so it is no longer necessary
for the TU to reference it.


But I still need the generic definition somewhere before this statement,
otherwise it won't compile.


Yes, the above is an instantiation not a declaration, so the
declaration is still needed.

Rob.
--
http://www.victim-prime.dsl.pipex.com/
Jul 22 '05 #17
Tom Widmer <to********@hotmail.com> wrote:
Rob Williscroft <rt*@freenet.co.uk> wrote:
Erik Arner wrote:
OK, I think my problem boils down to the following. How do I initialize
A::a using the default constructor in the code below?
template <class T>
struct A
{
static A a;
};

template <>
A< double > A< int >::a;


That's incorrect - if the whole class is specialized, you shouldn't
use the template<> bit. That should be:

A<double> A<int>::a;


(Bear with me, this is all new to me..)
I don't understand this line. It means that A<int> has a static
member whose type is A<double> ?
Supposing we then had:

A<float> A<int>::a;

and then in a function somewhere:
A<int> b;
foo(b.a)
how do we know whether it is foo(A<float>) or foo(A<double>)
that gets called?
Jul 22 '05 #18
Old Wolf wrote in news:84**************************@posting.google.c om
in comp.lang.c++:
Tom Widmer <to********@hotmail.com> wrote:
Rob Williscroft <rt*@freenet.co.uk> wrote:
template <class T>
struct A
{
static A a;
};

>template <>
>A< double > A< int >::a;


That's incorrect - if the whole class is specialized, you shouldn't
use the template<> bit. That should be:

A<double> A<int>::a;


(Bear with me, this is all new to me..)
I don't understand this line. It means that A<int> has a static
member whose type is A<double> ?
Supposing we then had:

A<float> A<int>::a;


But you can't the specalization A< int > that's been snipped is:

template <> class A< int >
{
static A< double > a;
}

So A<int>::a has the declared type of A< double > trying to define
it as A< float > will be an error (illegal redeclaration or some
such).

Rob.
--
http://www.victim-prime.dsl.pipex.com/
Jul 22 '05 #19

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

7
by: zhou | last post by:
Hi there, We have a compiler specific issue which requires us to force template instantiation. This works fine. The problem comes when I try using std:find() on vector. Since vector has no member...
16
by: Eric | last post by:
I have a static class member variable as follows: struct A { static void Set (int i) { v = i; } static int& Get () { return v; } static int v; }; int A::v; // define A::v in the cpp file
7
by: ank | last post by:
Hi, I was curious about how to define static data member of template class. Should I put the definition in a separate source file or in the same header file as its template class? And when this...
4
by: santosh | last post by:
Hello, I have a doubt about static variable. class B { private: static int x; public: static set(int y) {
12
by: mlimber | last post by:
This is a repost (with slight modifications) from comp.lang.c++.moderated in an effort to get some response. I am using Loki's Factory as presented in _Modern C++ Design_ for message passing in...
5
by: Hari | last post by:
Guys please help me to solve this strange problem what Iam getting as follows.. Trying to instantiate a global instance of a template class as follows :- when i build this code with debug and...
5
by: tthunder | last post by:
Hi @all, I am looking for a good (compiler-independent) way to generate meaningful error messages, if specific (unintended) templates are instantiated. e.g. ------------
3
by: erictham115 | last post by:
Error C2555 c:\C++ projects\stat1\stdmatrix_adapt.h(41) : error C2555: 'std_tools::Matrix_adapter<T>::at': overriding virtual function return type differs and is not covariant from...
0
by: greek_bill | last post by:
Hi, I have a template function for which I use SFINAE to restrict one of the parameters. Then I also have a partial specialization of this function.I would like to provide an explicit...
1
isladogs
by: isladogs | last post by:
The next Access Europe meeting will be on Wednesday 6 Mar 2024 starting at 18:00 UK time (6PM UTC) and finishing at about 19:15 (7.15PM). In this month's session, we are pleased to welcome back...
0
by: ArrayDB | last post by:
The error message I've encountered is; ERROR:root:Error generating model response: exception: access violation writing 0x0000000000005140, which seems to be indicative of an access violation...
1
by: PapaRatzi | last post by:
Hello, I am teaching myself MS Access forms design and Visual Basic. I've created a table to capture a list of Top 30 singles and forms to capture new entries. The final step is a form (unbound)...
1
by: CloudSolutions | last post by:
Introduction: For many beginners and individual users, requiring a credit card and email registration may pose a barrier when starting to use cloud servers. However, some cloud server providers now...
1
by: Defcon1945 | last post by:
I'm trying to learn Python using Pycharm but import shutil doesn't work
1
by: Shællîpôpï 09 | last post by:
If u are using a keypad phone, how do u turn on JavaScript, to access features like WhatsApp, Facebook, Instagram....
0
by: af34tf | last post by:
Hi Guys, I have a domain whose name is BytesLimited.com, and I want to sell it. Does anyone know about platforms that allow me to list my domain in auction for free. Thank you
0
by: Faith0G | last post by:
I am starting a new it consulting business and it's been a while since I setup a new website. Is wordpress still the best web based software for hosting a 5 page website? The webpages will be...
0
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 3 Apr 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome former...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.