Best Way to Define/Declare/Initialize Variables Simultaneously?

Discussion in 'C Programming' started by David T. Ashley, Dec 3, 2004.

  1. 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.
     
    David T. Ashley, Dec 3, 2004
    #1
    1. Advertising

  2. David T. Ashley

    Eric Sosman Guest

    David T. Ashley wrote:
    > 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.

    --
     
    Eric Sosman, Dec 3, 2004
    #2
    1. Advertising

  3. (David T. Ashley) wrote in
    news::

    > 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.

    --
    - Mark ->
    --
     
    Mark A. Odell, Dec 3, 2004
    #3
  4. "Eric Sosman" <> wrote in message
    news:coqf0k$ai9$...
    > 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.
     
    David T. Ashley, Dec 4, 2004
    #4
  5. David T. Ashley

    Tim Rentsch Guest

    Eric Sosman <> writes:

    > David T. Ashley wrote:
    > > 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.


    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.
     
    Tim Rentsch, Dec 4, 2004
    #5
  6. David T. Ashley

    CBFalconer Guest

    Tim Rentsch wrote:
    >

    .... 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.

    --
    Chuck F () ()
    Available for consulting/temporary embedded and systems.
    <http://cbfalconer.home.att.net> USE worldnet address!
     
    CBFalconer, Dec 5, 2004
    #6
  7. David T. Ashley

    Tim Rentsch Guest

    CBFalconer <> writes:

    > Tim Rentsch wrote:
    > >

    > ... 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.


    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.
     
    Tim Rentsch, Dec 5, 2004
    #7
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. David T. Ashley
    Replies:
    6
    Views:
    566
    Tim Rentsch
    Dec 5, 2004
  2. Replies:
    1
    Views:
    364
    Chris Brat
    Sep 17, 2006
  3. Brian Sammon

    declare vs define of global variables

    Brian Sammon, Oct 5, 2004, in forum: C Programming
    Replies:
    7
    Views:
    501
    Flash Gordon
    Oct 8, 2004
  4. Brent
    Replies:
    0
    Views:
    130
    Brent
    Dec 26, 2003
  5. yoodavid
    Replies:
    64
    Views:
    625
    Tim Rentsch
    Oct 24, 2013
Loading...

Share This Page