Best Way to Define/Declare/Initialize Variables Simultaneously?

  • Thread starter David T. Ashley
  • Start date
D

David T. Ashley

Hi,

In my project, I typically declare and define variables in the .H
file, i.e.

DECMOD_MAIN UINT8 can_message_201_status_global
#ifdef MODULE_MAIN
= HAS_NEVER_BEEN_RECEIVED
#endif
;

where DECMOD_MAIN expands to nothing when compiling MAIN.C and expands
to "extern" when compiling other modules.

Is there a neater and more compact way to handle the initializers
(which appear in the definition but not in the declaration)? The
above (in the MAIN.H file) works, but it is inelegant. What are
others doing?

Again the goal is to define, declare, and initialize variables all in
the same place (to avoid having to keep consistency between the .C and
..H files).

Thanks, Dave.
 
E

Eric Sosman

David said:
Hi,

In my project, I typically declare and define variables in the .H
file, i.e.

DECMOD_MAIN UINT8 can_message_201_status_global
#ifdef MODULE_MAIN
= HAS_NEVER_BEEN_RECEIVED
#endif
;

where DECMOD_MAIN expands to nothing when compiling MAIN.C and expands
to "extern" when compiling other modules.

Is there a neater and more compact way to handle the initializers
(which appear in the definition but not in the declaration)? The
above (in the MAIN.H file) works, but it is inelegant. What are
others doing?

Again the goal is to define, declare, and initialize variables all in
the same place (to avoid having to keep consistency between the .C and
.H files).

The scheme you're using (or variants of it) is one way.

Another is to use a "helper" program to generate both
the .c and .h file from a single common source, which can
be written in a "little language" designed for the purpose.

HOWEVER, neither method will save you much work. Or to
put it differently, neither method *ought* to save you much
work, because the number of global variables should usually
be rather small. Using a global variable to pass information
between modules is admittedly convenient, but its convenience
is also its drawback: you lose all ability to control or
monitor the passage of the information. Some people like to
say that global variables "increase the coupling" between
modules, thus making them less modular, more interdependent,
and more interrelated in ways that are hard to analyze and
understand. If you've got a lot of global variables lying
around, you may be better off thinking of ways to get rid of
some of them than of ways to make it easier to add more.
 
M

Mark A. Odell

(e-mail address removed) (David T. Ashley) wrote in
Hi,

In my project, I typically declare and define variables in the .H
file, i.e.

DECMOD_MAIN UINT8 can_message_201_status_global
#ifdef MODULE_MAIN
= HAS_NEVER_BEEN_RECEIVED
#endif
;

where DECMOD_MAIN expands to nothing when compiling MAIN.C and expands
to "extern" when compiling other modules.

Is there a neater and more compact way to handle the initializers
(which appear in the definition but not in the declaration)? The
above (in the MAIN.H file) works, but it is inelegant. What are
others doing?

This is a poor hack, IMHO. Here's how I do it if I absolutely must expose
the variables themselves, e.g. if an accessor function is too much
overhead:

/* board.h - declaration only
*/
extern void *pBrdRamBaseAddr;
extern size_t brdRamSize;


/* board_product_1.c - definition
*/
void *pBrdRamBaseAddr = 0xFFF00100;
size_t brdRamBaseAddr = 0x00010000;


/* board_product_2.c - definition
*/
void *pBrdRamBaseAddr = 0xEEE00100;
size_t brdRamBaseAddr = 0x00020000;


/* board_product_3.c - definition
*/
void *pBrdRamBaseAddr = 0xDDD00100;
size_t brdRamBaseAddr = 0x00030000;


Now suppose I have foo.c and bar.c that do work with RAM in a generic
fashion on all three products. Foo.c and bar.c need only include board.h,
we'll let the makefile and linker do the magic.

product1.exe:
compile foo.c bar.c board_product_1.c
link foo.o bar.o board_product_1.o into product1.exe

product2.exe:
compile foo.c bar.c board_product_2.c
link foo.o bar.o board_product_2.o into product2.exe

product3.exe:
compile foo.c bar.c board_product_3.c
link foo.o bar.o board_product_3.o into product3.exe

all: product1.exe product2.exe product3.exe

Now when the RAM size in product2.exe changes to 0x00040000 I change it in
one place, board_product_2.c, and rebuild. Only one file gets
recompiled and only one exe gets relinked. If I had done this your way you
would have to recompile foo.c 3 times for each product, bar.c 3 times for
each product, and you would have had a single board.c file that would need
to get recompiled 3 times. Then 3 relinks. All this because only one value
changed that had no compile time impact on any source file except the
board file for product 2.
Again the goal is to define, declare, and initialize variables all in
the same place (to avoid having to keep consistency between the .C and
.H files).

This does not make any sense. H files tell you type and names of variables
and the C file tells you the value of the variable.
 
D

David T. Ashley

Eric Sosman said:
Another is to use a "helper" program to generate both
the .c and .h file from a single common source, which can
be written in a "little language" designed for the purpose.

HOWEVER, neither method will save you much work. Or to
put it differently, neither method *ought* to save you much
work, because the number of global variables should usually
be rather small. Using a global variable to pass information
between modules is admittedly convenient, but its convenience
is also its drawback: you lose all ability to control or
monitor the passage of the information. Some people like to
say that global variables "increase the coupling" between
modules, thus making them less modular, more interdependent,
and more interrelated in ways that are hard to analyze and
understand. If you've got a lot of global variables lying
around, you may be better off thinking of ways to get rid of
some of them than of ways to make it easier to add more.

Eric,

In my own defense ...

I'm well familiar with the arguments against global variables. Essentially,
they lead to unrestrained connectivity, ill-defined interfaces, and a state
space which is too large (and is not shed as the program returns up the
calling tree). No argument from me on that one.

However, embedded work is done with processors that have a weak instruction
set (and don't support stack frames well), as well as small ROM sizes (about
30K). Programming the "right" way leads to ROM bloat. Global variables is
THE preferred interface technique. There is no other way. The customer
expects certain functionality, and it has to fit in the cheapest
microcontroller.

That being said ... what one is striving for is a minimal basis set (no
redundancy of information). So, generating the .C and .H from the same
script or input file makes sense. I've had good luck with Tcl/Tk.

Best regards, Dave.
 
T

Tim Rentsch

Eric Sosman said:
The scheme you're using (or variants of it) is one way.

Another is to use a "helper" program to generate both
the .c and .h file from a single common source, which can
be written in a "little language" designed for the purpose.

A refinement of this approach that in my experience works better is to
have the definition be actual source code, for example in a .c file,
and derive the declaration from it as a portion of a generated header
file. This way if there are any warnings or errors on the variable
definition, standard tools will take you right to the actual source
to be corrected. The case of the errors/warnings appearing on the
declaration still need further investigation, of course.

HOWEVER, neither method will save you much work. Or to
put it differently, neither method *ought* to save you much
work, because the number of global variables should usually
be rather small. Using a global variable to pass information
between modules is admittedly convenient, but its convenience
is also its drawback: you lose all ability to control or
monitor the passage of the information. Some people like to
say that global variables "increase the coupling" between
modules, thus making them less modular, more interdependent,
and more interrelated in ways that are hard to analyze and
understand. If you've got a lot of global variables lying
around, you may be better off thinking of ways to get rid of
some of them than of ways to make it easier to add more.

Interesting story - a few years ago I implemented a lightweight
development environment that automatically generates function
prototypes for all functions. At the time I made conscious decision
(minor, but conscious) not to process variable definitions in a
similar way, pretty much for the reasons given in the above paragraph.
Later, for other reasons, I ended up adding the automatic generation
for variable declarations, and to my surprise it ended up helping a
lot even though the number of global variables was much smaller than
the number of functions. So even when good practices are followed and
the number of global variables is small, using software tools to
synchronize their declarations with the definitions yields a tangible
benefit.
 
C

CBFalconer

Tim said:
.... snip ...

Interesting story - a few years ago I implemented a lightweight
development environment that automatically generates function
prototypes for all functions. At the time I made conscious

I would normally consider that a mistake. I certainly don't want
prototypes for all my functions - I prefer to have their complete
definition form their own prototypes, which simply means declare
before use. At the same time I can mark most functions as being
static, and not clutter up the link system nor force a maintainer
to check myriad files for usage. Similarly for any file scope
variables.

"static" is not used nearly as often as it should in C.
 
T

Tim Rentsch

CBFalconer said:
I would normally consider that a mistake. I certainly don't want
prototypes for all my functions - I prefer to have their complete
definition form their own prototypes, which simply means declare
before use. At the same time I can mark most functions as being
static, and not clutter up the link system nor force a maintainer
to check myriad files for usage. Similarly for any file scope
variables.

"static" is not used nearly as often as it should in C.

Sorry, apparently I implied something that isn't so.

The development environment allows and recognizes static functions,
and differentiates static function prototypes and non-static function
prototypes. Prototypes for static functions are available only in the
compilation of the file where those functions are defined. Only
public functions have their prototypes make it into a public
interface (header) file, where they are available to be #include'd
by other compilation units. (The mechanism for variables is similar
although different in details.)

On the topic of using function order so most function definitions also
serve the purpose of prototypes - certainly that's something that's
common in C code, and indeed something I used to do myself before
having automatic prototype generation in the development environment.
I can only report that the experience of developers using the DE has
been that having automatic prototype generation for all functions is
extremely helpful, and the freedom to place functions without having
to worry about order dependencies has turned out to be much more of a
boon than was expected.

Compilations done using the DE normally include all the following
compilation flags (among others):

-Wimplicit-function-declaration
-Wstrict-prototypes
-Wmissing-prototypes
-Wmissing-declarations
-Wredundant-decls
-Werror

As you would expect, having these flags turned on all the time gets
rid of certain kinds of program errors and helps carry out various
program restructurings (or refactorings, to use the currently popular
term). The completely automatic generation of prototypes for all
functions allows this mode to be used with essentially no "mindless
editing" overhead.
 

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,755
Messages
2,569,536
Members
45,020
Latest member
GenesisGai

Latest Threads

Top