#define question

Discussion in 'C Programming' started by Adam L., Aug 29, 2007.

  1. Adam L.

    Adam L. Guest

    Howdy all.

    Quick background ? Pascal/Delphi programmer writing a software program
    in Pascal, coding the same program in parallel, in C. This is my long
    overdue goal to learn the C language. Been putting it off for about 6
    years. :)

    So far, so good. Just in the beginnings, but it's coming along. There
    is one thing that I have seen a few times that I can't seem to make sense of:

    #ifdef SOME_VAR // Conditional seems to vary
    #define EXTERN
    #else
    #define EXTERN extern
    #endif


    Now, I've read a few posts and visited several web pages
    about the above code and the explanations are more confusing than the code.
    Would someone happen to have a good ?in Pascal it would work like this? or
    ?if it were in BASIC, it would work like this? explanation? I'd even settle
    for some ?clueless about C? plain English.

    Thanks in advance!

    -Adam

    --


    My e-mail address is not that colorful.
    Adam L., Aug 29, 2007
    #1
    1. Advertising

  2. Adam L.

    Ark Khasin Guest

    Adam L. wrote:
    > Howdy all.
    >
    > Quick background ? Pascal/Delphi programmer writing a software program
    > in Pascal, coding the same program in parallel, in C. This is my long
    > overdue goal to learn the C language. Been putting it off for about 6
    > years. :)
    >
    > So far, so good. Just in the beginnings, but it's coming along. There
    > is one thing that I have seen a few times that I can't seem to make sense of:
    >
    > #ifdef SOME_VAR // Conditional seems to vary
    > #define EXTERN
    > #else
    > #define EXTERN extern
    > #endif
    >
    >
    > Now, I've read a few posts and visited several web pages
    > about the above code and the explanations are more confusing than the code.
    > Would someone happen to have a good ?in Pascal it would work like this? or
    > ?if it were in BASIC, it would work like this? explanation? I'd even settle
    > for some ?clueless about C? plain English.
    >
    > Thanks in advance!
    >
    > -Adam
    >

    You've seen it in VERY BAD code (or perhaps very old code gone bad,
    which case I won't cover). Never do it in yours!

    The intended meaning is this:
    foo.c owns, say, int foo. It is of external linkage; other translation
    units need to say
    extern int foo;
    in order to see the foo.

    Mere mortals do it like that: we say someplace in foo.c
    int foo;
    Then we invent a header file, foo.h, where we place external interfaces
    of foo.c. There, we say
    extern int foo;
    Every source that does
    #include "foo.h"
    now has access to foo. foo.c also includes "foo.h", just to make sure
    that we don't accidentally have a declaration (in .h) different from
    definition (in .c). But you knew that.

    Now, artistic types might write, in foo.h
    EXTERN int foo;
    and in foo.h write
    #define SOME_VAR
    #include "header-where-you-saw-that-passage.h"
    #include "foo.h"

    The effect is that foo.h included in foo.c produces a /definition/ of
    foo, and if included in other source files, produces /declarations/
    provided that SOME_VAR is not #define'd anywhere else.

    This clever trick allows you to place all globals marked as EXTERN in
    one translation unit; it is not good for anything else.

    The drawback is that one develops a bad habit of defining a macro (in
    this case, EXTERN) differently in different translation units; that's a
    demonstrated recipe for disaster. Oh yeah, you could convince yourself
    it's OK to sin "just once" if there is a tangible benefit. But in this
    case the benefit is negative; the trick is designed to destroy any code
    modularity. If SOME_VAR is defined in several files, the behavior is
    undefined (but realistically, probably a link error).

    -- Ark
    Ark Khasin, Aug 29, 2007
    #2
    1. Advertising

  3. Adam L.

    Richard Bos Guest

    "Adam L." <> wrote:

    > So far, so good. Just in the beginnings, but it's coming along. There
    > is one thing that I have seen a few times that I can't seem to make sense of:
    >
    > #ifdef SOME_VAR // Conditional seems to vary
    > #define EXTERN
    > #else
    > #define EXTERN extern
    > #endif


    This is for when you want to #define a macro to one of two (or more)
    values, depending on whether you have or have not set another macro
    earlier on. SOME_VAR (which is _not_ a variable, but must also be a
    macro) will usually be a constant set by a specific compiler, but not by
    others; or a constant set in one header and/or .c file, but not in
    others.
    In this case, it is specifically so that you can have one single header
    file to #include to _define_ certain objects in one place, but only
    _declare_ them elsewhere. This is important, because you're not allowed
    to define objects more than once, but you can declare them as often as
    you use them. It is perhaps more easy to grasp with a practical example.

    Let's say that you have a big program, which you split into several .c
    files. Some of these .c files define objects which are useful for the
    other parts of the program, and those other parts want to declare the
    existence of these objects so they can use them. Let us say, for
    example, that you have a fast-file-reading unit, which contains, as part
    of the objects it wants to share, a file contents buffer. Now normally,
    in fastfile.c, you'd _define_

    unsigned char ffr_buffer[FFR_BUFFERSIZE];

    and everywhere else you'd want to use that buffer, you would _declare_

    extern unsigned char ffr_buffer[FFR_BUFFERSIZE];

    See the difference? Only that extern. In object declarations, extern
    means "this object should not be created here and now; some other part
    of the program will create this object, but I want to know what it is
    called and what type it has, so I can use it here". By contrast, without
    the extern that definition simply means "create an object with this name
    and give it this type".

    Now you want to simplify this. For that reason, you create a header
    called fastfile.h. This header contains all declarations which any .c
    file which uses functionality from fastfile.c would want to declare. In
    particular, it contains

    extern unsigned char ffr_buffer[FFR_BUFFERSIZE];

    (and of course, it also contains #define FFR_BUFFERSIZE 32768, or
    whatever). Now you can #include fastfile.h in any .c file which uses
    fast file functionality, and you will automatically have the right
    declarations without having to retype them.
    In fact, you will even be able to #include fastfile.h into fastfile.c -
    declaring _and_ defining an object in the same file is allowed, it's
    only defining it more than once (anywhere) which is a problem - and that
    will make sure that the compiler checks whether the declaration in the
    header is the same as the definition in the .c file. That way, if you
    change the definition of ffr_buffer (say, to an unsigned int array), you
    will be warned to change the header file so that the declaration other
    files have of it remains the same as the definition in fastfile.c.

    So far, so good. But now you want to introduce another timesaver. Notice
    how the declaration and the definition are almost the same? What if you
    could get the compiler to accept the declaration as a definition only in
    fastfile.c, and as a normal declaration elsewhere? Then you'd _only_
    have to maintain the header file, and you wouldn't have to write a
    separate definition in fastfile.c. This would save typing, but more
    importantly, it would ensure that you wouldn't _need_ to change the
    declaration when the definition changes, because they'd be one and the
    same line of code. Changing one would automatically change the other,
    and hey presto: one less point of breakage.
    Well, you can. Using the preprocessor. Here's what you do. You remove
    the separate definition from fastfile.c, and change the declaration in
    fastfile.h to

    EXTERN unsigned char ffr_buffer[FFR_BUFFERSIZE];

    Note: capital EXTERN. Unlike Pascal, C is case-sensitive; EXTERN is not
    extern. What you do now is change the definition of EXTERN, which as yet
    has none, to be _either_ the keyword extern - making this line a normal
    declaration - _or_ nothing at all - turning it into a definition. And
    which of these you do, you let depend on whether you're in fastfile.c,
    where you want the definition, or elsewhere, where you do not.
    So now you have, in fastfile.h,

    #if <something we'll come to next>
    #define EXTERN
    #else
    #define EXTERN extern
    #endif

    #define FFR_BUFFERSIZE 32768
    EXTERN unsigned char ffr_buffer[FFR_BUFFERSIZE];

    If the <something> holds, this results in FFR_BUFFERSIZE being #defined,
    and ffr_buffer being defined as well, because EXTERN is blank. If
    <something> does not hold, it results in FFR_BUFFERSIZE being #defined
    as well (which, unlike for objects, is legal for macros, because macros
    are only a bit of text substitution in the compiler, not a real object
    in the resulting program), but in ffr_buffer only being declared,
    because now EXTERN is extern. Clearly, we want <something> to hold only
    when fastfile.h is #included in fastfile.c, and not anywhere else.

    But how do you do that? Can the preprocessor automatically detect in
    which source file it is? Not really. There's __FILE__, but that isn't
    suitable here. It's much better to #define your own constant that
    explicitly tells the header where it is. For example, let's assume a
    macro called FFR_MAIN_FILE, which is either defined or not. We can then
    change the above to

    #ifdef FFR_MAIN_FILE
    #define EXTERN
    #else
    #define EXTERN extern
    #endif

    #define FFR_BUFFERSIZE 32768
    EXTERN unsigned char ffr_buffer[FFR_BUFFERSIZE];

    Now we'll get the definition where FFR_MAIN_FILE has been #defined, and
    the declaration where it has not. If you prefer, you can spell #ifdef
    FFR_MAIN_FILE as #if defined FFR_MAIN_FILE (or even as #if defined
    (FFR_MAIN_FILE) ); they're equivalent. All of these check whether
    FFR_MAIN_FILE has previously been #defined in that source file, or in
    the one which currently #includes it, or the one which #includes that,
    and so on. It does not, though, check whether FFR_MAIN_FILE is #defined
    _anywhere_; only if it is #defined _here and now_.
    Using this knowledge, you can now put the following in fastfile.c:

    #define FFR_MAIN_FILE
    #include "fastfile.h"

    and that will be equivalent to

    #define FFR_BUFFERSIZE 32768
    unsigned char ffr_buffer[FFR_BUFFERSIZE];

    and in database.c, which uses the fast file capabilities, you can do

    #include "fastfile.h"

    _without_ the #define FFR_MAIN_FILE first, and that will result in

    #define FFR_BUFFERSIZE 32768
    extern unsigned char ffr_buffer[FFR_BUFFERSIZE];

    A definition in one file only, a declaration everywhere else. Precisely
    what you should have. And all that using only a single line to
    define/declare the object, plus a single macro definition to make the
    difference.

    If that sounds like a lot of work to avoid just a single retyped line,
    it is. But it can easily be worth it if a library defines a couple more
    objects; the advantages add up, but the extra trouble stays the same.

    Richard
    Richard Bos, Aug 29, 2007
    #3
    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. DrUg13

    ifndef define question

    DrUg13, Feb 7, 2004, in forum: C++
    Replies:
    5
    Views:
    16,793
    EventHelix.com
    Feb 8, 2004
  2. Alan Pong

    Question about many #define

    Alan Pong, Sep 18, 2004, in forum: C++
    Replies:
    3
    Views:
    297
    Greg Comeau
    Sep 19, 2004
  3. theotyflos
    Replies:
    3
    Views:
    448
    Thomas Matthews
    Feb 19, 2004
  4. robin liu
    Replies:
    3
    Views:
    810
    Robin Liu
    Apr 21, 2006
  5. Brian Takita

    #define _ and #define __

    Brian Takita, Jan 23, 2006, in forum: Ruby
    Replies:
    0
    Views:
    444
    Brian Takita
    Jan 23, 2006
Loading...

Share This Page