how initialize an object of a class which is defined within a name space

I am trying to initialize the ex and ey variables in the following piece of code but unfortunately I keep getting linker error. thanks in advance.

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
namespace LatticeBoltzmann {

	constexpr int NumDir = 9;

	class Cell
	{
	public:
		Cell() = default;


		static std::array<signed char, NumDir> ex;
		static std::array<signed char, NumDir> ey;

		static std::array<double, NumDir> coeff;

		std::array<double, NumDir> density;

		enum class Direction
		{
			none = 0,
			N,
			NE,
			E,
			SE,
			S,
			SW,
			W,
			NW
		};


		void Init(){};

		inline static std::pair<int, int> GetNextPosition(Direction direction, int x, int y)
		{
			return std::make_pair(x + ex[static_cast<size_t>(direction)], y - ey[static_cast<size_t>(direction)]);
		}
};
}

and here is the main,

int main (int argc, char* argv[])
{
	int myrank, num_procs;
	MPI_Init(&argc, &argv);
	MPI_Comm_rank(MPI_COMM_WORLD, &myrank);
	MPI_Comm_size(MPI_COMM_WORLD, &num_procs);
	LatticeBoltzmann::NumDir;
	LatticeBoltzmann::Cell Cell;
	LatticeBoltzmann::Cell::ex = { 0 };
	Cell.GetNextPosition(LatticeBoltzmann::Cell::Direction::NE, 1, 1);

	//LBMSOLVER LBMSOLVER(D2Q9,Nx, Ny);
	MPI_Finalize();
}
Last edited on
Just assigning to a static object inside a function does not define the member. You have to do it in global scope. Alternatively, you can declare the member as inline static and the compiler will treat that as the definition.
helios wrote:
You have to do it in global scope.

In a .cpp file, do the following:

1
2
3
4
5
namespace LatticeBoltzmann
{
	std::array<signed char, NumDir> Cell::ex;
	std::array<signed char, NumDir> Cell::ey;
}

Or

1
2
std::array<signed char, NumDir> LatticeBoltzmann::Cell::ex;
std::array<signed char, NumDir> LatticeBoltzmann::Cell::ey;

Whichever you prefer.

helios wrote:
Alternatively, you can declare the member as inline static and the compiler will treat that as the definition.

This alternative is probably the easiest but requires that you use at least C++17.

1
2
3
4
5
6
7
8
9
10
11
namespace LatticeBoltzmann
{
	//...
	class Cell
	{
		//...
		static inline std::array<signed char, NumDir> ex;
		static inline std::array<signed char, NumDir> ey;
		//...
	};
}

Note that the only difference to your code is the additional inline keyword.
Last edited on
If, as here, you are using MPI I would be very wary about putting any data in global scope. It is not a shared-memory paradigm.
Last edited on
Thank you all for your answers.
@lastchane Yes, I'm using MPI and that's why I didn't define in global scope.
From a memory layout standpoint, there's no difference between a static data member and a global object. A static data member is pretty much just a global object. If you need to share the same instance of Cell::ex between separate processes then you'll need to figure out somewhere else to put it. Usually this would be done by allocating shared memory from the system and constructing an object on it.
Yes, the MPI evolved when computers had only one core each and hence each process did run in different computer. Presumably a well optimized MPI implementation can utilize shared memory for passing messages between processes when they are in the same machine (and what is that Infiniband RDMA thingy?), but that is not our problem.

The (MPI) user code has to run "send from my var" and "receive to my var" in respective processes, if data should be transferred. Each process will have their own array "ex".
Mmm, but a straightforward "global" variable doesn't look good in MPI, because every processor ends up with its own copy ... and can amend it to completely different values.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
#include "mpi.h"
using namespace std;

int A = 0;                    // global variable

int main( int argc, char* argv[] )
{
   int rank;

   A++;
   cout << "First value of A is " << A << '\n';

   MPI_Init( &argc, &argv );
   MPI_Comm_rank( MPI_COMM_WORLD, &rank );

   A += rank;
   cout << "Processor " << rank << " modifies A to " << A << '\n';

   MPI_Finalize();

   cout << "Last value of A is " << A << '\n';
}


With 4 processors this produces (e.g.)
First value of A is 1
Processor 2 modifies A to 3
Last value of A is 3
First value of A is 1
Processor 0 modifies A to 1
Last value of A is 1
First value of A is 1
Processor 3 modifies A to 4
Last value of A is 4
First value of A is 1
Processor 1 modifies A to 2
Last value of A is 2


Apparently, MPI-3 (which I don't have) has some form of shared memory model.

If you have more than one core at each node it's probably easier to hybridise MPI and OpenMP.

I haven't work with OpenMP before. but thanks for your guide. I will go for it.
Mmm, but a straightforward "global" variable doesn't look good in MPI, because every processor ends up with its own copy ... and can amend it to completely different values.

That is true for every variable in MPI program. Every process has their own variables.

Lets say you want to add to "same" variable, then you probably do something like:
1
2
3
4
5
A++;
A += rank;
int B = 0;
MPI_Reduce( &A, &B, 1, MPI_INT, MPI_SUM, 0, comm );
if ( !rank ) cout << "Sum of A is " << B << '\n';

Note that even now only the rank 0 has the sum in B.
Well, strictly you could use MPI_Allreduce, but I take your point.
Topic archived. No new replies allowed.