Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C Programming > Conditional declaration (A matter of style)

Reply
Thread Tools

Conditional declaration (A matter of style)

 
 
Anders Wegge Keller
Guest
Posts: n/a
 
      10-13-2012

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.*
 
Reply With Quote
 
 
 
 
Ben Bacarisse
Guest
Posts: n/a
 
      10-13-2012
Anders Wegge Keller <(E-Mail Removed)> 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.
 
Reply With Quote
 
 
 
 
Anders Wegge Keller
Guest
Posts: n/a
 
      10-13-2012
Ben Bacarisse <(E-Mail Removed)> writes:

> Anders Wegge Keller <(E-Mail Removed)> 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.*
 
Reply With Quote
 
Kaz Kylheku
Guest
Posts: n/a
 
      10-13-2012
On 2012-10-13, Anders Wegge Keller <(E-Mail Removed)> 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 };

 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
forwarddeclaration, declaration, definition, order still a matter? Henning Hasemann C++ 3 06-14-2006 09:22 PM
Can I use a conditional in a variable declaration? volcs0@gmail.com Python 11 03-21-2006 03:59 PM
? ELSE Conditional Comment / Using Conditional Comments Inside Other Tags To Comment Out Attributes Alec S. HTML 10 04-16-2005 02:21 AM
Whattsa Matter, Dark Matter?? A.Melon DVD Video 0 05-16-2004 07:05 AM
Conditional signal declaration Willem Oosthuizen VHDL 5 07-17-2003 03:25 PM



Advertisments