Protected static members - Linker error

Is there any information on what happens if I have a protected static member? Let me give you an example from a code I am myself writing:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class atom : public particle {
protected:
	// These parameters will be common to all atoms in the simulation.
	// So make sure you simulate a gas of only one type of atoms
	// All atoms are identical, so every collision will simply result in
	// momentum exchange
	static double 	dia;					// Diameter
	static double 	mass;					// Mass of the atom
public:
	atom ();
	void setatom (double, double);
	void updateEv ();
	virtual double dispersion (vector);
	virtual vector groupv (vector);
	friend class simulation;
};


When I compile this code, it compiles perfectly, but I get a linker error:


Info: resolving _gsl_rng_taus by linking to __imp__gsl_rng_taus (auto-import)
c:\MinGW\bin/ld.exe: warning: auto-importing has been activated without --enable-auto-import specified on the command line.
This should work unless it involves constant data structures referencing symbols from auto-imported DLLs.
obj/particle.o:particle.cpp:(.text+0x32): undefined reference to `atom::mass'
obj/particle.o:particle.cpp:(.text+0xe8): undefined reference to `atom::mass'
obj/particle.o:particle.cpp:(.text+0x831): undefined reference to `atom::dia'
obj/particle.o:particle.cpp:(.text+0x843): undefined reference to `atom::mass'
obj/particle.o:particle.cpp:(.text+0x868): undefined reference to `atom::dia'
obj/particle.o:particle.cpp:(.text+0x871): undefined reference to `atom::mass'
Info: resolving _gsl_rng_gfsr4 by linking to __imp__gsl_rng_gfsr4 (auto-import)
make[2]: Leaving directory `/c/Users/Balaji/Documents/Programs/moscato'
make[1]: Leaving directory `/c/Users/Balaji/Documents/Programs/moscato'

collect2: ld returned 1 exit status
make[2]: *** [moscato.exe] Error 1
make[1]: *** [.build-conf] Error 2
make: *** [.build-impl] Error 2

BUILD FAILED (exit value 2, total time: 5s)


Is there any help you can offer me to understand this linker error? If I remove the static qualifier, this error goes away. But it makes a lot of sense for me to have this as static because all atoms in my code have the same mass and diameter. So it saves a lot of memory space in the case of large simulations.

Any help will be valuable.
Last edited on
Declaring a static member variable is not enough, you need to also define it somewhere. This should be in a .cpp file that includes the header (.h) file that declared them:
1
2
double atom::dia = 1.0;
double atom::mass = 1.0;
Actually, these elements are defined when the atoms are created. So as an example, in my particle.cpp file:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
atom::atom() {
	r = genrandomvector(rspace);
	dia = (2*GSL_CONST_MKSA_BOHR_RADIUS);
	mass = GSL_CONST_MKSA_MASS_PROTON;
	do {
		k = simulation::genrandomvector(kspace);
		updateEv();
	} while(simulation::genrandomreal(0, 1) >= BoltzmannDistrib(E));
	//Canonical Ensemble of probabilities
	//The above means that we will generate some momentum values and if the resulting energy is such that the occupation Boltzmann statistics will allow it to have that energy
	//otherwise, we will generate another momenutm vector and try it.
	//MFP members from particle class are initialized
	distance = 0; collided = false; ncolls = 0; mfp = 0;
}


Won't this do my work? In other words, is it not possible to define it only when I want to?

Also, suppose I do need to define it explicitly somewhere, I don't want all definitions to clutter up my code and I want to allow the user to choose the atom type he wants so for example, I'd rather like to make many namespaces and pick the one I'd like for the specific simulation:

1
2
3
4
5
6
7
8
9
namespace Hydrogen {
	atom::dia = (2*GSL_CONST_MKSA_BOHR_RADIUS);
	atom::mass = GSL_CONST_MKSA_MASS_PROTON;
}

namespace Helium {
	atom::dia = (2.34*GSL_CONST_MKSA_BOHR_RADIUS);
	atom::mass = 2*(GSL_CONST_MKSA_MASS_PROTON + GSL_CONST_MKSA_MASS_NEUTRON);
}


But the trouble is that the compiler doesn't allow me to do it unless I place the class definition insider the namespace.

g++ -I. -I./include  -W -Wall -fno-exceptions  -O2 -c ./src/main.cpp -o obj/main.o
In file included from ./src/main.cpp:2:0:
./include/particle.h:50:18: error: definition of 'atom::dia' is not in namespace enclosing 'atom'
./include/particle.h:51:18: error: definition of 'atom::mass' is not in namespace enclosing 'atom'
make[2]: *** [obj/main.o] Error 1
make[1]: *** [.build-conf] Error 2
make: *** [.build-impl] Error 2
make[2]: Leaving directory `/c/Users/Balaji/Documents/Programs/moscato'
make[1]: Leaving directory `/c/Users/Balaji/Documents/Programs/moscato'

BUILD FAILED (exit value 2, total time: 2s)


In fact, I have never been able to use namespaces to see their real benefit. It has always come with some or the other error except in the trivial programs that I can find online. Any help will be much appreciated. I have been trying to code this efficiently, such that the generic code for all atoms can be used by Hydrogen or Helium or Argon, but don't want to end up defining constants in my main.cpp file.
Last edited on
What you are doing in your constructor is simply assigning values to your statics. You are not actually defining them. That needs to be done by adding the lines I showed you to the particle.cpp.

Also you can't put them into a different namespace to the one you declared them in. If you want to have different particle types with different mass/dia values then they should not be static at all. They should be normal member variables.
what about this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
template<double &mass, double &diameter>
class atom {}
//...
namespace hydro{
   double mass = H_MASS, diameter = H_DIAMETER;
}
namespace helium{
   double mass = He_MASS, diameter = He_DIAMETER;
}
//...
int main(){
   atom<hydro::mass, hydro::diameter> alpha;
   atom<helium::mass, helium::diameter> beta;
}
If you want different types of atoms with different mass/radii then I would be tempted to investigate this kind of structure:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

class atom
{
protected:
	double mass;
	double diam;
public:
	atom(double m, double d)
	: mass(m)
	, diam(d)
	{

	}
};

class hydrogen : public atom
{
public:
	hydrogen()
	: atom(GSL_CONST_MKSA_MASS_PROTON, 2*GSL_CONST_MKSA_BOHR_RADIUS)
	{

	}
};

class helium : public atom
{
public:
	helium()
	: atom(2*(GSL_CONST_MKSA_MASS_PROTON + GSL_CONST_MKSA_MASS_NEUTRON)
		, 2.34*GSL_CONST_MKSA_BOHR_RADIUS)
	{

	}
};
The only problem is that all the hydrogen atoms have the same mass, but that information is repeated in every instance. (a waste of space)
You can do this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class hydro{
protected:
	static double mass, diameter;
};

class helium{
protected:
   static double mass, diameter;
};

template<class Symbol>
class atom : public Symbol{
//....
//Access mass an diameter by
//Symbol::mass,     Symbol::diameter
}


int main(){
  atom<helium> beta
}
by the way. If I've got something like
1
2
3
class hydro{
   static const double mass;
}

How I initialise mass?
The compiler yields
'const double hydro::mass' is a static data member; it can only be initialized at its definition


And if I do
static const double mass = 1
 ISO C++ forbids initialization of member constant 'mass' of non-integral type 'const double'
It has to be integral, meaning it cannot be a float or a double.
Thanks a lot everyone. I think I have solved my problem, but using a different method. I am actually sorry for not providing you all the complete context of what I am doing, so that you could understand the problem I was having. Nevertheless, all of your solutions are definitely intuitive and surely each one of them would have been correct had it not been for the specific application. But my own implementation may be just one of the possible ways of doing it and perhaps you may have better suggestions if I tell you exactly what is my problem. Please feel free to decide whether or not you wish to read on and give me better suggestions, but all suggestions will definitely be welcome.

First of all, this is an elaborate Monte Carlo simulation program which I had originally written for electrons. I am trying to extend this to atoms, ions, phonons, photons protons and specific nuclei. The applications could vary from being used in CERN, research universities, nuclear power plants, defense organizations, semiconductor industry, chemical industry and so on. The program should do the following:

1. Generate a large number of particles about 1 million of them. This is a small number but because each particle object will occupy a minimum of 104 bytes of memory, this will soon explode in size, especially if we are simulating different kinds of particles in the same closed system. Note that even small systems in real life will have 1020+ particles per cm3. It is obviously impossible to make a program that can replicate that. So we use a scaling factor and upscale all results after the simulation.

2. Distribute the particles with random energy, momenta and positions in the system depending on certain specific conditions that will be application specific. Each type of particle will obey a certain thermodynamic statistics and that will be encoded separately.

3. In the presence of a particular kind of field (gravitational, electric or magnetic) the particles will move, but in the process they are likely to collide into neighbors. The simulation program's strength is in calculating this for different types of particles since the physics for each type of particle is slightly different. For example, F = ma wouldn't be right for electrons in crystals, nor would it be right for photons or phonons.

4. This iterative scattering is repeated over several cycles and the results are aggregated. The repetition over several cycles is normally sent to a MPI (message passing interface) to make it faster.

Now, if I hadn't this large an application, making mass and dia into non-static members would have been fine. But that would mean more space. Each particle contains two vectors (position and momentum) and a scalar, Energy along with other data for calculating the mean free path of the particle. So each byte comes at a premium. So static members are important wherever the data is identical. At the same time, I also needed that I can make atoms of different types. So in the program, I wanted to define dia and mass for a Hydrogen atom as well as for a Helium atom, but while using static members. One possible way, was to make Helium and Hydrogen as inherited classes of atom, but that would mean that we'll have to define every single kind of atom. Since the physics for atoms in a gas is nearly identical across elements, except for these two parameters, I did not want to keep defining every type of atom.

Instead, I did the following:

I moved the class definition of atom to a separate file atom.h. In another file called atomtypes.h I created several namespaces as follows:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
namespace Hydrogen {
#ifndef __ATOM_H
#include "atom.h"
#endif

    double atom::dia = (2*GSL_CONST_MKSA_BOHR_RADIUS);
    double atom::mass = GSL_CONST_MKSA_MASS_PROTON;
}


namespace Helium {
#ifndef __ATOM_H
#include "atom.h"
#endif

    double atom::dia = (2.34*GSL_CONST_MKSA_BOHR_RADIUS);
    double atom::mass = 2*(GSL_CONST_MKSA_MASS_PROTON + GSL_CONST_MKSA_MASS_NEUTRON);
}



So in my main program, I could simply say:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int main() {
	system("del gas.mat");
	System gas;
	gas.setsize(1.0e-5, 1.0e-5, 1.0e-5);
	gas.setTemp(650);						//in Kelvin scale
	
	simulation<Hydrogen::atom> Hsim;
	Hsim.setup(NUMPARTICLES, NUMTSTEPS, 1.0e-12, &gas);
	simulation<Hydrogen::atom> Hesim;
	Hesim.setup(NUMPARTICLES, NUMTSTEPS, 1.0e-12, &gas);
	mpich.runparallel(Hsim.simulate, Hesim.simulate);
	simulation.dumpdata(gas.mat);			//In MATLAB format
	return 0;
}


Any of you have a better software architecture for this problem?
Last edited on
ne555, I guess you'll have to say:

double hydro::mass = 1;

Btw, I like your method quite a lot. It definitely does the job very well. But if you read what I have written above about the kind of program application I have, do you think I can create different simulation entities (where simulation is a template class) of different types of particles simultaneously and associate all of them with a single system? Anyway, thanks for your suggestion. It is a good idea, that I could use elsewhere in my code at least.

L B, that's not true. You can initialize it to a double or floating point number.

Galik wrote:
1
2
double atom::dia = 1.0;
double atom::mass = 1.0;

balajiram wrote:
double hydro::mass = 1;


Sorry, I forgot. That did the trick, thanks.


@balajiram: I'm not sure about the namespace. I think that you are overwriting the values of all atoms
Could you test this?
1
2
3
4
Hydrogen::atom alpha;
Helium::atom beta;

alpha.mass == beta.mass
Thanks for asking me to check it. I think it works. Here is the main() function:


1
2
3
4
5
6
7
8
9
10
int main () {
    ofstream ofile ("test.txt", ios::app);
    Hydrogen::atom Hatom;
    ofile<<"Hydrogen:\n"<<Hatom<<endl;

    Helium::atom Heatom;
    ofile<<"Helium:\n"<<Heatom<<endl;
    ofile.close();
    return 0;
}


The output of this code was as follows:
Hydrogen:
Diameter: 1.05835e-010	Mass: 1.67262e-027
Helium:
Diameter: 1.23827e-010	Mass: 6.6951e-027


However, I must note for the benefit of others that my initial atomtypes.h file was not correct. I had to make the following modifications:
1. Remove the #define __ATOM_H from atom.h
2. Remove the lines #ifndef __ATOM_H etc. from atomtypes.h and simply have the following:


1
2
3
4
5
6
7
8
9
10
11
12
13
namespace Hydrogen {
#include "atom.h"

    double atom::dia = (2*GSL_CONST_MKSA_BOHR_RADIUS);
    double atom::mass = GSL_CONST_MKSA_MASS_PROTON;
}

namespace Helium {
#include "atom.h"

    double atom::dia = (2.34*GSL_CONST_MKSA_BOHR_RADIUS);
    double atom::mass = 2*(GSL_CONST_MKSA_MASS_PROTON+GSL_CONST_MKSA_MASS_NEUTRON);
}


Meanwhile, thanks a lot for all your suggestions.
Topic archived. No new replies allowed.