Conditional declaration (A matter of style)

Discussion in 'C Programming' started by Anders Wegge Keller, Oct 13, 2012.

  1. When writing modular code, I sometimes want to make a particular data
    structure read-only outside the module that need to write to it. The
    most obvious example is global options, that is only written to by the
    command-line parsing module, and should be const elsewhe. However,
    getting this declaration to change depending on where it's seen
    becomes a bit unsightly, so I wonder if there is a better way of doing
    this:


    <<options.h>>

    ....

    typedef struct {

    int option_1;
    int option_2;
    ...
    int option_N;
    } Options_t;

    #ifdef OPTION_RW
    extern Options_t *options;
    #else
    extern const Options_t * const options;
    #endif



    <<options.c>>

    #define OPTIONS_RW
    #include <options.h>
    #undefine OPTIONS_RW

    ....

    options = malloc (sizeof Options_t);

    ....



    What I'd like to do is getting rid of the OPTIONS_RW definition in
    options.c. Creating two extra header files (options_public.h and
    options_private.h), and keeping the declaration in those two isn't an
    improvement - in my opinion - on the definition above.

    --
    /Wegge

    Leder efter redundant peering af dk.*,linux.debian.*
    Anders Wegge Keller, Oct 13, 2012
    #1
    1. Advertising

  2. Anders Wegge Keller <> writes:

    > When writing modular code, I sometimes want to make a particular data
    > structure read-only outside the module that need to write to it. The
    > most obvious example is global options, that is only written to by the
    > command-line parsing module, and should be const elsewhe. However,
    > getting this declaration to change depending on where it's seen
    > becomes a bit unsightly, so I wonder if there is a better way of doing
    > this:
    >
    >
    > <<options.h>>
    >
    > ...
    >
    > typedef struct {
    >
    > int option_1;
    > int option_2;
    > ...
    > int option_N;
    > } Options_t;
    >
    > #ifdef OPTION_RW
    > extern Options_t *options;
    > #else
    > extern const Options_t * const options;
    > #endif
    >
    >
    >
    > <<options.c>>
    >
    > #define OPTIONS_RW
    > #include <options.h>
    > #undefine OPTIONS_RW
    >
    > ...
    >
    > options = malloc (sizeof Options_t);
    >
    > ...
    >
    >
    >
    > What I'd like to do is getting rid of the OPTIONS_RW definition in
    > options.c. Creating two extra header files (options_public.h and
    > options_private.h), and keeping the declaration in those two isn't an
    > improvement - in my opinion - on the definition above.


    You might make the pointer static:

    static Options_t *options;

    and provide a one-line function

    const Options_t *get_options_ptr(void) { return options; }

    You don't get two header files, you get just one but that's an advantage
    isn't it?

    --
    Ben.
    Ben Bacarisse, Oct 13, 2012
    #2
    1. Advertising

  3. Ben Bacarisse <> writes:

    > Anders Wegge Keller <> writes:
    >
    > > When writing modular code, I sometimes want to make a particular data
    > > structure read-only outside the module that need to write to it. The
    > > most obvious example is global options, that is only written to by the
    > > command-line parsing module, and should be const elsewhe. However,
    > > getting this declaration to change depending on where it's seen
    > > becomes a bit unsightly, so I wonder if there is a better way of doing
    > > this:
    > >
    > >
    > > <<options.h>>
    > >
    > > ...
    > >
    > > typedef struct {
    > >
    > > int option_1;
    > > int option_2;
    > > ...
    > > int option_N;
    > > } Options_t;
    > >
    > > #ifdef OPTION_RW
    > > extern Options_t *options;
    > > #else
    > > extern const Options_t * const options;
    > > #endif
    > >
    > >
    > >
    > > <<options.c>>
    > >
    > > #define OPTIONS_RW
    > > #include <options.h>
    > > #undefine OPTIONS_RW
    > >
    > > ...
    > >
    > > options = malloc (sizeof Options_t);
    > >
    > > ...
    > >
    > >
    > >
    > > What I'd like to do is getting rid of the OPTIONS_RW definition in
    > > options.c. Creating two extra header files (options_public.h and
    > > options_private.h), and keeping the declaration in those two isn't an
    > > improvement - in my opinion - on the definition above.

    >
    > You might make the pointer static:
    >
    > static Options_t *options;
    >
    > and provide a one-line function
    >
    > const Options_t *get_options_ptr(void) { return options; }
    >
    > You don't get two header files, you get just one but that's an advantage
    > isn't it?


    Much better. Thank you for that suggestion.
    --
    /Wegge

    Leder efter redundant peering af dk.*,linux.debian.*
    Anders Wegge Keller, Oct 13, 2012
    #3
  4. Anders Wegge Keller

    Kaz Kylheku Guest

    On 2012-10-13, Anders Wegge Keller <> wrote:
    >
    > When writing modular code, I sometimes want to make a particular data
    > structure read-only outside the module that need to write to it. The
    > most obvious example is global options, that is only written to by the
    > command-line parsing module, and should be const elsewhe. However,
    > getting this declaration to change depending on where it's seen
    > becomes a bit unsightly, so I wonder if there is a better way of doing
    > this:


    If you define an external object unqualified, and in other translation units
    use const declarations to refer to it, it is, formally, undefined behavior.

    Paragraph 2 in "6.2.7 Compatible type and composite type" [ISO/IEC 9899:1999]
    says "All declarations that refer to the same object or function shall have
    compatible type; otherwise, the behavior is undefined."

    A const-qualified type is not compatible with a qualified type:

    Paragraph 9 in "6.3.7 Type Qualifiers" [ISO/IEC 9899:1999] says "For two
    qualified types to be compatible, both shall have the identically qualified
    version of a compatible type."

    You can access an unqualified object through a const-qualified lvalue, but that
    is not quite the same thing because you're not declaring that the underlying
    object is const.

    Your compiler and linker may let you get away with this, but one has to
    question the wisdom of invoking undefined behavior only to get better error
    checking for some convention in your program.

    I think this can work if you use it for checking only during compilation,
    and not for actually building the program. Lying to the compiler about the
    type of an external object seems harmless if the translation units are never
    combined into a program. The rule in 6.2.7 cannot possibly apply to
    translation units before it is indicated that they are to be one program.

    > #ifdef OPTION_RW
    > extern Options_t *options;
    > #else
    > extern const Options_t * const options;
    > #endif


    So you're invoking undefined behavior here by qualifying the pointer.

    (But the const on the Options_t type is fine because that just determines
    the type used for accessing the options structure without declaring
    it to be that type.)

    But if you're willing to undergo the inconvenience of the pointer, you might as
    well have two pointers:

    extern const Options_t *const popt; /* public, visible to all */

    #ifdef OPTION_MODULE /* present if option-related module is being compiled */
    extern Options_t *wpopt;
    #endif

    Then in the main options module:

    #define OPTION_MODULE
    #include "options.h"

    const Options_t *const popt = &g_options;
    Options_t *wpopt = &g_options;

    Use wpopt in the implementation to write to the options. If the options
    module is just one source file, wr_options can just be static (internal
    linkage) inside it and so not mentioned in the header.

    The point is that the options module does not have to use the same interface to
    manipulate the options.

    > #define OPTIONS_RW
    > #include <options.h>
    > #undefine OPTIONS_RW


    What, angle brackets to refer to your own header? That is foolery.

    > options = malloc (sizeof Options_t);


    See, now you're lying to the translation units where options is declared const.
    It's possible that the const declaration is believed and the compiler emits
    code which caches the original null value.

    An external, file scope const is really a constant. Not in the sense that
    evaluating it is a constant expression (that is in C++ only) but in the
    sense that it can be relied upon never to have two or more values over the
    lifetime of the program.

    There is no need to malloc global options. A static pointer can be initialized
    to hold the address of a static structure.
    You can build entire linked lists and trees that way, even ones with cycles.

    Years ago I build a hyperlinked help system for a program written in C (which
    had a GUI). The graph of linked help screens was assembled with static
    definitions containing pointers to each other.

    /* circular list */
    struct node { struct node *next; } node_b; /* forward decl */
    struct node node_a = { &node_b };
    struct node node_b = { &node_a };
    Kaz Kylheku, Oct 13, 2012
    #4
    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. earthling
    Replies:
    0
    Views:
    385
    earthling
    Mar 15, 2005
  2. Johan Tibell
    Replies:
    66
    Views:
    1,044
    Chris Torek
    Aug 7, 2006
  3. Henning Hasemann
    Replies:
    3
    Views:
    363
    Henning Hasemann
    Jun 14, 2006
  4. mzdude
    Replies:
    19
    Views:
    527
    James Kanze
    Aug 14, 2009
  5. Bas van Gils

    a matter of style

    Bas van Gils, Jun 11, 2007, in forum: Ruby
    Replies:
    21
    Views:
    329
    Chad Perrin
    Jun 13, 2007
Loading...

Share This Page