top-level initializers

K

kj

I'd like to have this at the top level:

#include <stdio.h>
/* ... */
FILE *LOG = stdout; /* default output stream */

....but this does not work in general because stdout need not be a
constant. For example, with gcc 4.4.1 I get the error message:

foo.c:18: error: initializer element is not constant

The following top-level code also fails:

#include <stdio.h>
/* ... */
FILE *LOG;
LOG = stdout;

This time I get:

foo.c:19: warning: data definition has no type or storage class

....plus subsequent errors and warnings ensuing from this.

My aim here is to have a default global logging stream that can be
modified at runtime by some suitable set_log_stream function that
redefines the file-global variable LOG[1].

My first question is: how can I best achieve this?

More generally, this example illustrates a common problem: how to
set-up compilation-unit-wide default values that can only be known
at runtime.

One solution is to have an init function for the compilation unit;
this init function has to be called by main. I find this solution
very unsatisfying; I'd like such initialization should happen
automatically.

Another approach is to have another file-scoped variable, a boolean
flag that keeps track of whether the required initialization has
taken place. Then all the functions that depend on this initialization
first check the value of this flag, and call the init function if
it is false. The init function sets the flag to true before
returning.

This approach is at least automatic, but it adds a test to all the
functions, and, to boot, it is a test that will be true only once
during the code's execution.

Since this situation is pretty common, I'm hoping that there are
better ways to handle it. I look forward to reading your suggestions.

TIA!

~K

[1] Generally, I try to keep the scope of variables as small as
possible, which means that I avoid using file-scoped variables like
LOG above. But this code is part of a one-time research-oriented
data-analysis project; its expected shelf-life is about 4 weeks;
for this reason I'm not bothering with setting up a more sophisticated
logging scheme. Given the circumstances, I relax my rule against
file-scoped variables slightly, to allow this LOG variable.
 
B

Ben Bacarisse

kj said:
I'd like to have this at the top level:

#include <stdio.h>
/* ... */
FILE *LOG = stdout; /* default output stream */

...but this does not work in general because stdout need not be a
constant. For example, with gcc 4.4.1 I get the error message:

foo.c:18: error: initializer element is not constant

The following top-level code also fails:

#include <stdio.h>
/* ... */
FILE *LOG;
LOG = stdout;

This time I get:

foo.c:19: warning: data definition has no type or storage class

...plus subsequent errors and warnings ensuing from this.

My aim here is to have a default global logging stream that can be
modified at runtime by some suitable set_log_stream function that
redefines the file-global variable LOG[1].

My first question is: how can I best achieve this?

As stated, you can't. The rules of the language just don't allow it.
Other programming languages are available.
More generally, this example illustrates a common problem: how to
set-up compilation-unit-wide default values that can only be known
at runtime.

One solution is to have an init function for the compilation unit;
this init function has to be called by main. I find this solution
very unsatisfying; I'd like such initialization should happen
automatically.

Another approach is to have another file-scoped variable, a boolean
flag that keeps track of whether the required initialization has
taken place. Then all the functions that depend on this initialization
first check the value of this flag, and call the init function if
it is false. The init function sets the flag to true before
returning.

This approach is at least automatic, but it adds a test to all the
functions, and, to boot, it is a test that will be true only once
during the code's execution.

Since this situation is pretty common, I'm hoping that there are
better ways to handle it. I look forward to reading your suggestions.

The only alternative that springs to mind is a variation of the schemes
above: choose a recognisably invalid constant default initialiser (NULL
is good for pointers like LOG) and check that in the paces were it
matters. I.e. the logging function will either

fputs(my_message, LOG ? LOG : stdout);

or it will

if (!LOG) LOG = stdout;

A small variation of your Boolean flag scheme can be implemented without
a test using a function pointer:

static FILE *LOG;

void (*init_logging)(void); /* tentative definition */

static void init_done(void) {}

static void init_module(void)
{
LOG = stdout;
init_logging = init_done;
}

void (*init_logging)(void) = init_module;

and you call init_logging() either from main or from every function that
needs the module to be initialised. If you take the latter approach,
the function pointer can be static and the name can be reused (i.e. you
could call it init_module_ptr in every file).
[1] Generally, I try to keep the scope of variables as small as
possible, which means that I avoid using file-scoped variables like
LOG above. But this code is part of a one-time research-oriented
data-analysis project; its expected shelf-life is about 4 weeks;
for this reason I'm not bothering with setting up a more sophisticated
logging scheme. Given the circumstances, I relax my rule against
file-scoped variables slightly, to allow this LOG variable.

Famous last words!
 
E

Ersek, Laszlo

One solution is to have an init function for the compilation unit; this
init function has to be called by main. I find this solution very
unsatisfying; I'd like such initialization should happen automatically.

Oh no, you wouldn't.

Two examples:

1) [10.12] What's the "static initialization order fiasco"?

http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.12

2) "6.29 Declaring Attributes of Functions / constructor (priority)"

http://gcc.gnu.org/onlinedocs/gcc-4..._007bconstructor_007d-function-attribute-2343

lacos
 
E

Eric Sosman

I am confused. In my code I make these assignments without
trouble with the qualification that they are static, e.g.,


static FILE * errfile = stderr;

with makefile flags

OPTFLAGS = -O2
DEBUGFLAGS = -ansi -pedantic -posix -Wall
CFLAGS = $(OPTFLAGS) $(DEBUGFLAGS) -I$(INC)

(In a test run the code compiled without the static keyword;
stdout also works.)

Are you saying that the language doesn't allow this?

The language allows it, but does not require it to work.
The actual requirement is that stdin/out/err be "expressions of
type `pointer to FILE'" (7.19.1p3, toward the end). They may
be constant expressions suitable for initializing statics, or
they may be expressions that can only be evaluated at run-time.
The Standard does not require that they be run-time expressions,
but it also doesn't require them to be constant expressions.

(6.6p10 permits an implementation to accept "other forms of
constant expressions," but that's of little comfort: stdin/out/err
*may* be acceptable "other forms," but are not required to be.)

The language "allows" this to work, but does not "require"
it to work. The requirement is that stdxxx be "expressions of
type `pointer to FILE'." In some implementations the "expressions"
are suitable to initialize static variables, but the Standard does
not require that
If it
matters, the GCC version I'm using is 3.3.4. Is this a case
where earlier versions of GCC produced a constant expression for
stdin etc whereas later versions don't, or is there something
more subtle going on?

The difference is probably in the library (as reflected in
<stdio.h>) rather than in the compiler per se, and the library is
not only time-varying but platform-varying. But yes, that's most
likely what's going on.
As a side note I take the view that resources go with a resource
package. Thus instead of a global variable called LOG use a
function that is defined in a package dedicated to IO management.

Seconded. Instead of fprintf(LOG, "Hello from %s!\n", __FILE__)
throughout the code, logprint("Hello from %s!\n", __FILE__) would be
better. The logprint() function can take care of initializing the
log stream if need be, or could just return harmlessly if logging
were disabled. It's also easily extensible to filtering log messages
by "severity" with logprint(INFORMATIVE, "Hello from %s!\n", __FILE__)
with logprint() deciding whether to print INFORMATIVE messages or to
ignore things below the DISASTER level.
 
I

Ian Collins

kj said:
I'd like to have this at the top level:

#include<stdio.h>
/* ... */
FILE *LOG = stdout; /* default output stream */

...but this does not work in general because stdout need not be a
constant. For example, with gcc 4.4.1 I get the error message:

foo.c:18: error: initializer element is not constant

The following top-level code also fails:

#include<stdio.h>
/* ... */
FILE *LOG;
LOG = stdout;

This time I get:

foo.c:19: warning: data definition has no type or storage class

...plus subsequent errors and warnings ensuing from this.

My aim here is to have a default global logging stream that can be
modified at runtime by some suitable set_log_stream function that
redefines the file-global variable LOG[1].

My first question is: how can I best achieve this?

As stated, you can't. The rules of the language just don't allow it.
Other programming languages are available.

I am confused. In my code I make these assignments without
trouble with the qualification that they are static, e.g.,

static FILE * errfile = stderr;

The OP wasn't initialising the variable in the declaration. So he was
writing code outside of a function (LOG = stdout;) which isn't premitted.
with makefile flags

OPTFLAGS = -O2
DEBUGFLAGS = -ansi -pedantic -posix -Wall
CFLAGS = $(OPTFLAGS) $(DEBUGFLAGS) -I$(INC)

(In a test run the code compiled without the static keyword;
stdout also works.)

stdout is a compile time constant, so it can be used in a global
variable assignment.
 
B

Ben Pfaff

Ian Collins said:
stdout is a compile time constant, so it can be used in a global
variable assignment.

It's not necessarily a compile-time constant. The standard just
says:

...
stderr
stdin
stdout
which are expressions of type ``pointer to FILE'' that point
to the FILE objects associated, respectively, with the
standard error, input, and output streams.

Indeed, in glibc stdio.h has:

/* Standard streams. */
extern struct _IO_FILE *stdin; /* Standard input stream. */
extern struct _IO_FILE *stdout; /* Standard output stream. */
extern struct _IO_FILE *stderr; /* Standard error output stream. */
/* C89/C99 say they're macros. Make them happy. */
#define stdin stdin
#define stdout stdout
#define stderr stderr
 
S

Seebs

The following top-level code also fails:

Of course it does.
#include <stdio.h>
/* ... */
FILE *LOG;
LOG = stdout;

Why on earth would you think you could have code outside a function?
Since this situation is pretty common, I'm hoping that there are
better ways to handle it. I look forward to reading your suggestions.

Initialization functions of some sort are pretty popular. Usually, I'd make
sure that only one function ever actually talked to the log file, and then
probably do something like:

FILE *log_file;

void
log(char *fmt, ...) {
va_list ap;
if (!fmt)
return;
va_start(ap, fmt);
vfprintf(log_file ? log_file : stderr, fmt, ap);
va_end(ap);
}


-s
 
K

Keith Thompson

I am confused. In my code I make these assignments without
trouble with the qualification that they are static, e.g.,


static FILE * errfile = stderr;

with makefile flags

OPTFLAGS = -O2
DEBUGFLAGS = -ansi -pedantic -posix -Wall
CFLAGS = $(OPTFLAGS) $(DEBUGFLAGS) -I$(INC)

(In a test run the code compiled without the static keyword;
stdout also works.)

Are you saying that the language doesn't allow this? If it
matters, the GCC version I'm using is 3.3.4. Is this a case
where earlier versions of GCC produced a constant expression for
stdin etc whereas later versions don't, or is there something
more subtle going on?

The C standard says that an initializer for an object with static
storage duration must be constant (C99 6.7.8p4). It says that stdin,
stdout, and stderr are macros that expand to expression of type
"pointer to FILE"; it doesn't specify whether those expressions
are constant or not (C99 7.19.1p3). It's entirely possible that
the constant-ness of stderr could change from one version of an
implementation to another. (Note that stdin and friends are provided
by the C library. gcc just provides the compiler and a few small
pieces of the standard library; for the rest of the library it just
uses whatever C runtime library came with the system, which might
or might not be glibc.)

------

As a side note I take the view that resources go with a resource
package. Thus instead of a global variable called LOG use a
function that is defined in a package dedicated to IO management.

Are you suggesting that the package should provide a function that
prints a log message. Another possibility might be to provide a
function that returns a FILE*, which can then be passed to any of
the appropriate stdio functions. So you could write:

fprintf(errfile(), "Error message: ...\n");

The overhead of the extra function call should be trivial, and the
errfile() function itself could take care of initializing a static
FILE* object on the first call.
 
N

Nick

Seebs said:
Of course it does.


Why on earth would you think you could have code outside a function?

A bit harsh, surely.

Perhaps he thinks it should work because, say
#include <stdio.h>
/* ... */
int LOG;
LOG = 36;

is perfectly legal. Yes, it's been well explained that stdout isn't a
constant, but it's not that obvious.

In fact, without checking I can't remember whether:

void log_function(char *msg) {
static FILE *log = stdout;
...
}

is legal or not.
 
K

Keith Thompson

Nick said:
A bit harsh, surely.

Perhaps he thinks it should work because, say
#include <stdio.h>
/* ... */
int LOG;
LOG = 36;

is perfectly legal.

Is it? You're thinking of "LOG = 36;" as a declaration with an implicit
type of int, right? That's illegal in C99, and poor style in C90.

Yes, it's been well explained that stdout isn't a
constant, but it's not that obvious.

Yes, it's a subtle point, especially since stdout *can* be a constant
(meaning that some implementations accept it without warning).
In fact, without checking I can't remember whether:

void log_function(char *msg) {
static FILE *log = stdout;
...
}

is legal or not.

It's not; an initializer for an object with static storage duration must
be constant. (If a non-constant initializer were permitted, when would
the expression be evaluated? That question could be answered, but C
doesn't answer it.)
 
N

Nick

Keith Thompson said:
Is it? You're thinking of "LOG = 36;" as a declaration with an implicit
type of int, right? That's illegal in C99, and poor style in C90.

Yes of course I am. Idiot me.

But it's still not obvious that a file-scope
FILE *LOG = stdout;
shouldn't work.
Yes, it's a subtle point, especially since stdout *can* be a constant
(meaning that some implementations accept it without warning).


It's not; an initializer for an object with static storage duration must
be constant. (If a non-constant initializer were permitted, when would
the expression be evaluated? That question could be answered, but C
doesn't answer it.)

That makes sense - statics and file scopes are both built, with initial
values, as part of the compilation (I often find it easier to think of a
static variable as a "global" one that is only visible inside the
function).

Of course it could be translated to the equivalent of:
static FILE *log = NULL;
if(log == NULL)
log = stdout;

But, as you say, it's not.
 
D

David Thompson

Is it? You're thinking of "LOG = 36;" as a declaration with an implicit
type of int, right? That's illegal in C99, and poor style in C90.
It's illegal in C89 and even K&R1. You must have nonempty decl-specs,
though they needn't include the type. No specifiers at all was legal
in very early pre-K&R1 C, and some later compilers kept it as an
extension. Even gcc, whose implementors should have known better.

What was legal pre-C99 was no specifiers on a *function definition*.

Plus, pre-C99 LOG as a file-scope name with (implicitly) external
linkage conflicts with a reserved library function name. <G!>
 
B

Ben Bacarisse

David Thompson said:
It's illegal in C89 and even K&R1. You must have nonempty decl-specs,
though they needn't include the type.

Given your posting history you are probably right but your comment did
not match my recollection so I went to look in K&R1 and could not find
where it states that there must be nonempty decl-specs.
No specifiers at all was legal
in very early pre-K&R1 C, and some later compilers kept it as an
extension.

This extension may be the source of my recollection. I certainly can't
say I remember /knowing/ that it was legal!

<snip>
 
L

Luca Forlizzi

Given your posting history you are probably right but your comment did
not match my recollection so I went to look in K&R1 and could not find
where it states that there must be nonempty decl-specs.


This extension may be the source of my recollection.  I certainly can't
say I remember /knowing/ that it was legal!

<snip>

AFAIW, in C89 a declaration shall begin with a nonempty decl-specs.
Probably this is not stated in the text but
is prescribed by the formal syntax.
Don't know about K&R1
 
B

Ben Bacarisse

AFAIW, in C89 a declaration shall begin with a nonempty decl-specs.
Probably this is not stated in the text but
is prescribed by the formal syntax.
Don't know about K&R1

I was talking only about K&R1. The syntax summary in K&R1 says that a
program is a sequence of external definitions, each of which is either a
function or a data definition. The case in point was a data definition
about which K&R1 says:

/data-definition/:
extern<opt> /type-specifier/<opt> /init-declarator-list/<opt> ;
static<opt> /type-specifier/<opt> /init-declarator-list/<opt> ;

The text may well expand on the syntax by limiting the allowed
possibilities but, if it does, I could not find it. From the syntax
alone 'LOG=36;' is a valid K&R1 data-definition.

In K&R1, declaration-specifiers is always non-empty (just as it is in
C89) but "top-level" data definitions seem to be a special case. This
means that you certainly can't omit the int in any of these cases:

struct s { int a, b; };

f(a, b)
int a, b;
{
int x, y;
/* do stuff ... */
}

Just as in C89, functions (like f above) are given special permission to
have empty declaration-specifiers.
 
W

Wolfgang.Draxinger

Since this situation is pretty common, I'm hoping that there are
better ways to handle it. I look forward to reading your suggestions.

Initializer functions are the de-facto standard solution for this
problem. C++ does it by having each compilation unit create an implicit
initializer segment, which will be bundled with the other initializers,
together with the static constructor calls into the startup code to be
executed prior to main.

The problem with C++'s approach is, that you can't control the order in
which things happen. This is bad, as some static constructors may do
things with stuff, previously initialized things rely on.

Laszlo Ersek already posted these links:
| 1) [10.12] What's the "static initialization order fiasco"?
| http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.12
|
| 2) "6.29 Declaring Attributes of Functions / constructor (priority)"
|
| http://gcc.gnu.org/onlinedocs/gcc-4..._007bconstructor_007d-function-attribute-2343


A different situation arises from the use of dynamically loaded shared
objects, i.e. DLLs/DSOs which are not linked by the OS/dynamic
linker, but sometime later during program execution, using
dlopen(...)/LoadLibrary(...). In that case each DLL/DSO may export a
initializer function, which gets called automatically by the dynamic
linker after the shared object has been fully loaded and all
prerequisites (like other DLLs/DSOs) have been initialized in the same
way (recursively). Since dependencies are dealt with by the dynamic
linker (or if such a DLL/DSO links to others with dlopen/LoadLibrary),
you don't end up in the "static initialization order fiasco" due to the
simple fact, that it's not static.
[1] Generally, I try to keep the scope of variables as small as
possible, which means that I avoid using file-scoped variables like
LOG above.

Take a look at the 'static' keywords. That's what this is used for
mostly.


Wolfgang
 
L

Luca Forlizzi

I was talking only about K&R1.  The syntax summary in K&R1 says that a
program is a sequence of external definitions, each of which is either a
function or a data definition.  The case in point was a data definition
about which K&R1 says:

sorry Ben, I should have known that you knew C's syntax & semantics
way better than me ;-)
Thanks for interesting info about K&R1!
 
D

David Thompson

Given your posting history you are probably right but your comment did
not match my recollection so I went to look in K&R1 and could not find
where it states that there must be nonempty decl-specs.
Actually I may have spoken too quickly. Nonempty specs were clearly in
C89. My recollection is that wasn't a change then, it was already
established practice; I *thought* it was established by K&R1, but I
have no specific recollection of that, maybe it was just consensus.
(I never had my own copy of 1, only shared a lab-wide copy.)
This extension may be the source of my recollection. I certainly can't
say I remember /knowing/ that it was legal!
ISTR some of the original Unix tools used this -- after all they were
written when B was still a fresh memory -- and I'd guess (but didn't
and don't know) that later C implementors didn't want to break too
sharply from that legacy. (No pun on 'break' intended.)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,071
Latest member
MetabolicSolutionsKeto

Latest Threads

Top