Originally posted by: Annie Cheng
The holiday season is always
a good time to reflect on the past. Here I am thinking about a problem that
brought me the "ah-hah" moment last month.
Problem
While compiling the following C++ program,
you might assume that the objects from the module that is compiled first are
initialized first. This assumption might lead to unexpected results.
X.h :
#include <string>
class CObjet {
public:
static const string STRINGX;
};
X.cpp:
#include <X.h>
using namespace std;
const string CObjet::STRINGX =
"001";
Y.cpp:
#include <iostream>
#include <X.h>
const string STRINGY= CObjet::STRINGX;
int main () {
cout << "CObjet::STRINGX [" <<CObjet::STRINGX
<< "]" << endl;
cout << "STRINGY [" << STRINGY <<
"]" << endl;
return 0;
}
If you use the following commands to compile the files:
xlC -c -I./ X.cpp -o X.o
xlC -c -I./ Y.cpp -o Y.o
xlC -o binary X.o Y.o
You will get the following result when you
run the executable file:
./binary
CObjet::STRINGX [001]
STRINGY []
From the result, you can see that STRINGY is not initialized to the value of
STRINGX as expected.
Cause
STRINGX and STRINGY are global static
objects. STRINGY is dependent on the initialization of STRINGX. They are
defined in different source files.
Although the C++ language definition specifies the initialization order for
these objects within a file (which follows the order in which they are
declared), it does not specify the initialization order for such objects across
files and libraries.
So STRINGX might not be initialized before STRINGY even though module X.o is
compiled first, resulting in empty string for STRINGY. Depending on the
compiler and operating system buffer value at the time, STRINGY might even
contain trash value to crash the program when it is run.
Solution
To solve the problem, some programmers move
each non-local static object into its own function, where the object is
declared static. They then have the function return a reference to its enclosed
local static object and call the function in the sequence they want the objects
to be initialized. This is usually a portable approach, but it involves tedious
code changes.
The XL C/C++ compiler can make this easy for you. You can use the -qpriority or
-qmkshrobj=priority or -Wm,-c options to specify the initialization order of
the static objects that are declared in various files and libraries in your
program. These options can assign the module a priority number and thus control
the initialization order of objects.
The module that contains the main() function
always has priority 0. The lowest number has the highest priority. In the above
example, specifying -qpriority=-100 when compiling the X.o object can ensure
objects in X.o are initialized before objects in Y.o.
Likewise, if you are building X.o into a shared library, you can use
-qmkshrobj=-100 when building the library.
You can use the following commands to do the compilation:
xlC -c -I./ -qpriority=-100 X.cpp -o
X.o
xlC -c -I./ Y.cpp -o Y.o
xlC -o binary X.o Y.o
or for building shared libraries:
xlC -c -I./ X.cpp -o X.o
xlC -c -I./ Y.cpp -o Y.o
xlC -qmkshrobj=-100 -o libX.so X.o
xlC -o binary -btrl Y.o -L. -lX
You will get the following result when you
run the executable file:
./binary
CObjet::STRINGX [001]
STRINGY [001]
Do you have similar experience? Do you have an "ah-hah" moment when you encountered a programming problem to share? Post a comment here or send me your sample code, maybe we can help to spotlight them here in the new year to help others along the way!
Anyhow, Happy New Year to everyone!
#C/C++andFortran#C/C++-compilers-for-AIX#c++