a) Approach 1
// global_file.h
#ifndef GLOBAL_FILE
#define GLOBAL_FILE
static int const myGlobal1 = 0xCAFE ;
static double const PI = 3.1415926535897932384626433832795;
#endif
Approach 1 is akin to an unnamed (anonymous) namespace. Each
translation unit gets a copy of the member data. Simply not
acceptable with regards to the static initialization fiasco etc that
the FAQ highlights
b) Approach II - The ‘C’ solution. Make use of the extern keyword
// global_file.h
#ifndef GLOBAL_FILE
#define GLOBAL_FILE
extern int const myGlobal1;
extern double const PI ;
#endif
//global_file.cpp
int const Global1 = 0xCAFE;
double const PI = 3.1415926535897932384626433832795 ;
c) Approach III - use a class – make members public. (or_ make
member data private and use a “get/accessor’ function to access the
private data)
///global.h
class global {
foo()
: PI ( 3.141 )
{}
public:
double const PI ;
static int const Global1 = 0xCAFE ;
};
Approach III is what I’m leaning towards.
Approach IV constants (internal linkage):
a) Approach 1
// global_file.h
#ifndef GLOBAL_FILE
#define GLOBAL_FILE
int const myGlobal1 = 0xCAFE ;
double const PI = 3.1415926535897932384626433832795;
#endif
No need to worry about initialization fiasco or anything of the sort.
The compiler determines whether and if so, where, the storage is
allocated, but you don't need to move a finger.
V
--
Please remove capital 'A's when replying by e-mail
I do not respond to top-posted replies, please don't ask
There's no problem with initialization order in this code, nor in code
that uses these constants. They're statically initialized. The "static
initialization fiasco" refers to dynamically initialized objects with
file scope -- there is no guaranteed initialization order for objects
defined in different files.
--
Pete
Roundhouse Consulting, Ltd. (www.versatilecoding.com) Author of
"The Standard C++ Library Extensions: a Tutorial and Reference"
(www.petebecker.com/tr1book)
And, oddly enough, that's exactly the same as Aprosch 1, except that in
Approach 1 the "static" is explicit. Either way, the "static
initialization fiasco" doesn't apply.
There is no "fiasco" with that code. Furthermore, const variables are
static (i.e. have internal linkage) by default, so the following may
appear in multiple translation units with no problem:
namespace constant {
unsigned const java_class_file_magic_bytes = 0xcafebabe;
double const pi = 3.14159265358979;
}
None of the above approaches have the static initialization order
fiasco.
In approach 1, PI is stored in a group of globals with internal
linkage. The globals are are initialized with a const-expr. The
initialization of static class members and namespace members with
const-exprs (called "static initialization" by the standard) are done
before all other initialization of statics (called "dynamic
initialization" by the standard). (Yes, the static initialization
fiasco might be more aptly named the dynamic initialization order
fiasco, but I would guess the term dynamic initialization without
context may not sound like what the standard defines it to be, thus
the name "static initialization order fiasco".)
You can only get the static initialization order fiasco when you start
making namespace member or static class member initializations with
non-const-exprs.
(Note that a particularly mean compiler + linker may interlace the
initialization of translation units IIRC, as long as initializations
from a single translation unit are not swapped in order.)
(Also note that particularly mean compilers + linkers, at least one of
which exists, may omit translation units which are not referenced from
some outside translation unit and do not contain main. This comes from
a rarely used standard allowance which allows a compiler to delay
dynamic initialization of statics until the first use of that
translation unit, though practically speaking everyone assumes that
the compiler will not do this.)
Approach 2 similarly has no static initialization order fiasco
problems.
Approach 3. I'm not quite sure what you're going to do here. You'll
create an instance of type global every time you want access to PI?
Unnecessary and confusing.
I'd simply declare in the header
#ifndef HEADERNAME_HEADER_GUARD
#define HEADERNAME_HEADER_GUARD
double const PI = 3.1415926535897932384626433832795 ;
#endif
As Jeff Schwab noted, namespace const variables are by default static.
Each separate translation unit will get their own copy of the
variable. As you probably won't be taking its address, and with basic
constant propagation optimizations, the value will probably be
expanded inline into the code where used, taking up no space for the
variable, and probably less space overall with this approach.
(Presuming that a double takes less space than a loaddouble + address
instruction in the architecture.)
For starters, thanks to all who responded here (certainly cleared up
alot of misinfo that I was carrying around in my head).
Work with me here: "/basic/ constant propagation optimization". QOI
issue I suppose, nonetheless there's no compiler flags that i need to
set to alert GCC (3.4.6 is what I'm using). Hey! I want 'constant
propagation optimization'.
At present a view of the elf output reveals that each translation unit
has a copy of the member variable at global scope.
But are those discardable records or not?
Anyway, if you want to code the discardable thing explicitly you can use the
template const tric:
template< typename Dummy >
struct Math_
{
static double const pi;
};
template< typename Dummy >
double const Math_<Dummy>::pi = 3.14;
typedef Math_<void> Math;
// Here use Math::pi. There's only one in whole program.
Cheers & hth.,
- Alf
--
Due to hosting requirements I need visits to <url: http://alfps.izfree.com/>.
No ads, and there is some C++ stuff! :-) Just going there is good. Linking
to it is even better! Thanks in advance!
Could you elaborate on the template ocnst trick? Did a google search,
thumbed through Josuttis, C++ template text but I'm coming up short.
I suspect there's two parts of this that's throwing me for a loop
(template has a tendency to do that to me though :) )
template< typename Dummy >
double const Math_<Dummy>::pi = 3.14;
Here dummy is not deducible. T/F?
typedef Math_<void> Math;
void? I suspect is all part of the trick?
Taken astep further ..
template< typename Dummy >
struct Math_
{
static double const pi;
static int const test ;
// and so on.. ownder how far i could go with this.. unnamed
enums?
};
template< typename Dummy >
double const Math_<Dummy>::pi = 3.14;
template< typename Dummy >
int const Math_<Dummy>::test = 0xDEAD;
typedef Math_<void> Math;
Static members of class templates may be defined in headers, and appear
in multiple translation units.
> I suspect there's two parts of this that's throwing me for a loop
> (template has a tendency to do that to me though :) )
>
> template< typename Dummy >
> double const Math_<Dummy>::pi = 3.14;
>
> Here dummy is not deducible. T/F?
That's still just part of the template definition. Dummy is a
typename-id, not actually a typename.
> typedef Math_<void> Math;
> void? I suspect is all part of the trick?
The actual type doesn't matter. The point is that when the template is
instantiated, Math::pi will refer to Math_<your_type_here>::pi.
> Taken astep further ..
> template< typename Dummy >
> struct Math_
> {
> static double const pi;
> static int const test ;
> // and so on.. ownder how far i could go with this.. unnamed
> enums?
As far as you want. You can even do it with non-const, non-integral,
non-primitive, non-trivial types.
Template member definitions are instantiated only if they are accessed,
so you only pay for whichever variables or functions actually get used.
For example:
#include <iostream>
namespace app {
bool was_here1, was_here2;
struct kilroy { kilroy( bool& was_here ) { was_here = true; } };
namespace internal {
template<typename Dummy>
struct math { static kilroy k1, k2; };
template<typename Dummy> kilroy math<Dummy>::k1( was_here1 );
template<typename Dummy> kilroy math<Dummy>::k2( was_here2 );
}
typedef internal::math<void> math;
void main() {
std::cout
<< &math::k1 << '\n' // Instantiate k1, but not k2.
<< was_here1 << '\n' // 1
<< was_here2 << '\n'; // 0
}
}
int main() { app::main(); }
[OT] I'm reviewing coding standards that caution against declaring
global objects in the outer scope of a program or namespace. The
standard also states that global constants shall be declared as
'static const' within the public scope of a class. Global variable
data objects shall be avoided, but when used, they shall be
encapsulated in a class and accessed through a method call (Ex:
Singleton design pattern).
Not sure if I agree with this. Not all 'global' is bad and as such
requires 'encapsulation in a class and accessed through a singleton'
[ ... ]
> Not sure if I agree with this. Not all 'global' is bad and as such
> requires 'encapsulation in a class and accessed through a singleton'
Quite true. I seem to recall reading a study (sorry, can't remember
exactly where) that indicated globals weren't nearly as serious of a
problem as generally believed.
At the same time, my own take on things has generally been that globals
themselves aren't necessarily all that bad, but they tend to result from
code that isn't particularly great either. For some things like
configuration data, globals make sense -- but in a lot of casese, the
amount of communication that makes globals more attractive also
indicates that tasks haven't been broken up relatively poorly.
--
Later,
Jerry.
The universe is a figment of its own imagination.
That's certainly true as far as it goes, but I think global and
namespace-scoped variables, even in implementation (.cc) files, are
inherently evil enough to be worth avoiding. Globals shared among
multiple TU have their own problems, e.g. indeterminate initialization
order, but even internally linked variables can cause severe design
problems. I would only make an exception for immutable variables with
trivial constructors, e.g. static constants of primitive types. I also
occasionally use namespace-scoped variables in usenet posts, but only
for brevity.
The existence of a global (or module-static) variable hard-codes its own
uniqueness, i.e. it is a pseudo-member variable of a singleton
pseudo-object. That in itself can be a problem. Somebody writes a
parser with some module-static state, and everything is hunky-dory until
the need arises to parse multiple, simultaneous input files; suddenly,
multiple "instances" of all the static variables are needed. A low- or
mid-level developer is particularly screwed in this case, because
refactoring takes a significant amount of time, during which management
sees no visible progress.
I view global and static variables in roughly the same way that I view
public member variables: They often indicate an inflexible design (as
you said), and they're not hard to avoid.
I have a global deque variable here called compressedfiles.
http://webEbenezer.net/misc/File.cc
In a function called CalculateMarshallingSize, I compress
input files in order to figure out their sizes. I'm not
ready to marshall the compressed data yet, though, so I copy
the compressed file into the global. Then when I'm ready to
marshall the data I pop it off the deque. Technically it
wouldn't be hard to avoid the global by doing the
compression once in CalculateMarshallingSize and again in
Send. That doesn't seem the right answer to me though.
So I don't know of a way to avoid the global in this case.
Brian Wood
Ebenezer Enterprises
www.webEbenezer.net
For my money, the following would be a step in the right direction:
typedef std::vector<char> compressed_file;
typedef std::deque<compressed_file> compressed_file_deque;
compressed_file_deque& compressed_files() {
static compressed_file_deque deq;
return deq;
}
You would also have to change existing, direct accesses of the variable,
to call the function instead. Then you can change the function
definition at will without altering the code that uses the deque,
piggy-backing levels of abstraction or adding hooks for aspect-oriented
design.
For example, in the case I mentioned above, in which parallel instances
of the module become necessary, you can have compressed_files() return
thread-local deques, so that parallel calls do not interfere with each
other. Alternatively, if the threads need to share the deque, you can
insert a lock in the function to synchronize deque access.
Once you change the rest of the module to use function-call syntax, you
won't have to touch it again, even if you decide to go back to a
module-local variable:
class compressed_file_deque_accessor
{
compressed_file_deque m_deq;
public:
compressed_file_deque& operator()() {
return m_deq;
}
} compressed_files;
This is one type of module-local variable I'm OK with, and should have
mentioned earlier. It still gives you a place to insert hooks, e.g.
access counts, log messages, or thread locks, but allows flexible,
function-like syntax in client code.
Thanks. I guess it might be a step in the right direction
and I may use it.
Brian Wood
Ebenezer Enterpries
www.webEbenezer.net