|
-
July 26th, 2002, 06:08 AM
#1
Initialization order for constant static data in C++
Gurus,
For quite some time, I have been perplexed by the rules which govern the initialization order of global static constant data. I am under the impression that there is no standardized order for the initialization of global static constant data. I think that it is unspecified both for cases involving variables in one file as well as project-wide variables, possibly made accessible with the extern keyword. Furthermore, I think that it is poor design to rely on an assumed order for global static constant data.
Please look at the sample program below.
There are (at least) three questions that come to mind:
1) Is there a guarantee that the global static constant pi_over_two is initialized at the time of the call of ::acos_1 for the initialization of ::val_acos_1? I think definitely not. Please verify.
2) Is there a guarantee that pi_over_two_local is properly initialized for the execution of ::acos_2 needed by the initialization of ::val_acos_2? I am really uncertain here.
3) How many times does the initialization code for the initialization of local constant variables run? An example is the initialization of pi_over_two_local. Please assume here that the initialization question pertains to codes which actually require run-time, not just which can be handled by the pre-processor. Does such initialization code run once before entry to main? Does it run each and every time the subroutine is called? Does it run once the first time the subroutine is called and not again thereafter. Or is it implementation specific?
Thank you very much.
Sincerely,
Chris
a.k.a. dude_1967
P.S. This post should not be taken as a treatise on the sensible use of mathematical functions.
Code:
#include <iostream>
#include <math.h>
using namespace std;
const double acos_1(const double d);
const double acos_2(const double d);
static const double pi_over_two = 3.1415926535897932384626434 / 2;
static const double val_acos_1 = ::acos_1(0.8);
static const double val_acos_2 = ::acos_2(0.8);
const double acos_1(const double d)
{
// Bad?
// Is there a guarantee that pi_over_two is initialized?
return pi_over_two - ::asin(d);
}
const double acos_2(const double d)
{
// Is there a guarantee that pi_over_two_local is initialized?
// Will the initialization code of pi_over_two_local run more than once?
static const double pi_over_two_local = 3.1415926535897932384626434 / 2;
return pi_over_two_local - ::asin(d);
}
int main(int argc, char* argv[])
{
cout.precision(15);
cout << val_acos_1 << endl;
cout << val_acos_2 << endl;
return 1;
}
You're gonna go blind staring into that box all day.
-
July 26th, 2002, 06:28 AM
#2
According to "The C++ Programming Language 3d ed.":
"In principle, a variable defined outside any function (that is, global, namespace, and class static variables) is initialized before main() is invoked. Such nonlocal variables in a translation unit are initialized in their declaration order (§10.4.2)."
Also
"There is no guaranteed order of initialization of global variables in different translation units."
As for the local static variable, it will only be initialised once, the first time the function is called.
I hope this answers your questions.
-
July 26th, 2002, 07:14 AM
#3
Thanks amag: This clears it up pretty much.
Furthermore I would like to know if anyone has any experience with the typical physical storage class of constant data for hard real-time systems (firmware in microcontrollers).
In the C language one can write:
Code:
static const int one = 1;
The integer one will be placed by the linker in the constant data ROM section. In C it is not possible to initialize constant data to the value of a subroutine. However, in C++ it is possible to do this.
Thus in C++ one can write:
Code:
int value_one(void) { return 1; }
static const int one = value_one();
In this case the value of the integer one, although constant, is not known at compile time. Thus this datum can not be placed in ROM-memory.
I imagine a typical compiler solution would be to carry the variable one around in volatile RAM, thus using up this costly (for microcontroller applications) resource.
Does anyone have experience with this matter?
Thanks.
Chris.
You're gonna go blind staring into that box all day.
-
July 26th, 2002, 07:56 AM
#4
A couple of points:
the use of global static inside a translation unit is deprecated; use unnamed namespaces instead:
Code:
static int local_to_TU; // deprecated
namespace
{
int local_to_TU; // preferred
}
Rather than global variables, consider using the singleton pattern. This has the advantage that it allows you to express dependencies:
Code:
// singleton1.h
class singleton1
{
public:
static singleton1* instance()
{
if (!instance_)
instance_ = new singleton1;
return instance_;
}
private:
static singleton1* instance_; // defined == 0 in .cpp file
};
// singleton2.h
#include "singleton1.h"
class singleton2
{
public:
static singleton2* instance()
{
if (!instance_)
instance_ = new singleton2;
return instance_;
}
private:
singleton2() : other_(singleton1::instance()) {}
static singleton2* instance_; // defined == 0 in .cpp file
singleton1* other_;
};
Here, any usage of singleton2 ensures that singleton1 gets created first.
See Andrei Alexandrescu's book on generic programming ("Modern C++ design") for more details.
Correct is better than fast. Simple is better than complex. Clear is better than cute. Safe is better than insecure.
-- Sutter and Alexandrescu, C++ Coding Standards
Programs must be written for people to read, and only incidentally for machines to execute.
-- Harold Abelson and Gerald Jay Sussman
The cheapest, fastest and most reliable components of a computer system are those that aren't there.
-- Gordon Bell
-
July 26th, 2002, 08:13 AM
#5
dude_1967:
Interesting question. Aside of Graham's comments (which I agree to), let's think of following scenario:
Code:
int i;
int foo(int i)
{
return i++;
}
static const int j = foo(i);
In this case, i is first initialized to 0 (TC++PL, 3rd Ed., P.217), and the compiler has all the information to correctly initialize j, and place it to the ROM.
It seems obvious that the compiler has to "execute" foo() at compile time -- but I must say that I never saw that written down somewhere. Maybe someone could enlighten me whether my guess is right or wrong 
If we have coded:
Code:
extern int i;
int foo(int i)
{
return i++;
}
static const int j = foo(i);
I guess we were looking for trouble. The compiler would probably take whatever value i had, use it in the faith that it is correct, initialize j, and place j into the ROM.
Altough I have programmed embedded devices that used RAM and ROM chips, I never did that in C++, but only in C and Assembler.
I think the initialization rules apply regardless of the initialized variable being const or not.
-
July 26th, 2002, 08:42 AM
#6
This had me thinking for a while as well...
The thing is that by using the const-keyword you inform the compiler that you're not going to change the value. It's then up to the compiler to put that data in ROM or RAM.
Some points. First it's valid C (I think since I've seen it in C-sources) to have:
Code:
void foo(int k)
{
int i;
for(i = 0; i < k; i++)
{
const int j = i * k;
// do something with j...
}
}
This obviously means that the compiler can't put j in ROM, thus even for a C-program one can't be sure that const vars are put in ROM (if applicable).
In C++ we also have the mutable keyword. So even for an object that is const, parts of it may be changed. Then we have const_cast and the nasty C-style cast (both should be used with great care).
So my final point is this, I believe few compilers actually put const vars in ROM, in most cases it's merely a compile-time check. But the const vars *may* be placed in ROM since the standard is open on this, so one should be careful with the casts above.
Another issue here (and now I'm guessing wildly) is that if the program (which would probably be located in ROM for a micro-controller) could also be executed from that ROM (not beeing loaded into RAM), code such as:
Code:
int main()
{
char *str = "Hello World!";
str[5] = '@'; // or whatever
return 0;
}
is dangerous...
-
July 26th, 2002, 09:23 AM
#7
Originally posted by amag
Code:
int main()
{
char *str = "Hello World!";
str[5] = '@'; // or whatever
return 0;
}
is dangerous...
That IS dangerous. It would simply not work with the Mitsubishi M37703 based platform and the C cross-compiler I was working with. str would point into the (EP)ROM, the MCU would try to change it and wouldn't even notice that it didn't work.
-
July 26th, 2002, 05:55 PM
#8
Gabriel said:
It seems obvious that the compiler has to "execute" foo() at compile time -- but I must say that I never saw that written down somewhere. Maybe someone could enlighten me whether my guess is right or wrong
That guess is wrong. Compilers are barely possible to convert a piece of source code into executable code. It is impossible for the compiler to execute a piece of code it just compiled.
And even if it is possible, it is ETHICALLY WRONG and IMMORAL for a compiler to do so, because, the compiler expects a piece of source code to be in the middle of working, hence is wrong and imature and un-workable, even if it can compile the code successfully.
Check it out for yourself. The functions that initialize a static const variable are actually called at RUN TIME. If it is outside any functions, it is called just once. When it is inside some function, it may potentially be called more than once, and compiler use some hidden global flag to ensure that doesn't happen.
-
July 27th, 2002, 12:39 AM
#9
Originally posted by AnthonyMai
That guess is wrong. Compilers are barely possible to convert a piece of source code into executable code. It is impossible for the compiler to execute a piece of code it just compiled.
There is a topic called "template metaprogramming". That is as close as you can get with using the compiler to actually generate and "run" code. An example of computing the factorial at compile time is given here:
http://community.borland.com/article...,10526,00.html
Books such as "Modern C++ Design" by Andrei Alexandrescu also uses these techniques to determine things such as whether an object is derived from another at compile-time, compile-time assertions, etc.
Regards,
Paul McKenzie
-
July 27th, 2002, 03:36 AM
#10
Well, I've posted the question to c.l.c++ and c.l.c++.mod and the answers boil down to following:
- the const attribute is a logical one, not a physical one. That means, that the compiler **may** place a const variable to the ROM. In any case, the compiler will flag any attempt to modify the value as an error. If you cast away the const-ness of the variable, you do it on your own risk.
- if the constant is initialized by a function, the compiler **could** expand that function, and, by means of compile time analysis determine its value at compile time. There is no guarantee that this will happen.
- the Standard doesn't say anything about RAM or ROM, so the behaviour is implementation defined.
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|