compound literals

Discussion in 'C Programming' started by Mantorok Redgormor, Sep 14, 2003.

  1. What is the point of them?

    When should I use them?

    They seem to be rather useless but hopefully someone can prove me
    wrong. I think they are just additions to c99 but didn't exist in
    c89/90.


    Also, when is it appropriate to cast? Casting seems to be frowned up
    on so it makes me wonder when it is appropriate to ever cast.
    Mantorok Redgormor, Sep 14, 2003
    #1
    1. Advertising

  2. Mantorok Redgormor

    Nick Austin Guest

    On 13 Sep 2003 22:35:51 -0700, (Mantorok Redgormor)
    wrote:

    >What is the point of them?
    >
    >When should I use them?


    Passing an argument to a function without using a temporary:

    foo( (struct bar){ 1, 2 } );

    Adding storage qualifiers:

    (const char[]){ "foo" }

    Nick.
    Nick Austin, Sep 14, 2003
    #2
    1. Advertising

  3. Mantorok Redgormor

    Kevin Bracey Guest

    In message <>
    (Mantorok Redgormor) wrote:

    > What is the point of them?
    >
    > When should I use them?
    >
    > They seem to be rather useless but hopefully someone can prove me
    > wrong. I think they are just additions to c99 but didn't exist in
    > c89/90.


    They are new in C99 (and hence don't exist in C++ either).

    They're not a massively useful construct, but when you do find an application
    for them, they can help tidy up code quite well. Here are two real-life
    examples, from some SCSI software:

    /* Array literal constants defining well-known SCSI commands */
    #define CDB_TEST_READY ((const uint8_t[6]) { 0x00 })
    #define CDB_INQUIRY_L ((const uint8_t[6]) { 0x12, 0, 0, 0, 36, 0 })
    #define CDB_CAPACITY ((const uint8_t[10]) { 0x25 })

    With these defines, a whole SCSI command can be simply defined and passed
    to an API by giving the macro name, without having to manually declare and
    initialise a variable.

    Now a more complex example, which will lead in nicely to your cast question.
    The software internally uses error codes (from 0-255), but has to supply the
    outside world with a pointer to an error structure (consisting of a code,
    followed by a variable length string). A simple (!) form of knocking up a
    mapping table, ignoring internationalisation issues, can work as follows:

    --- errors.h ---

    // typedef struct { int errnum; char errmess[252]; } oserror;

    #define SCSI_ErrorBase 0x20100

    enum
    {
    SCSI_NoRoom = 0x00,
    SCSI_SWIunkn,
    SCSI_RCunkn,
    ...
    };

    oserror *scsi_error(unsigned err);

    --- errors.c ---

    #define ErrType(str) const struct { int errnum; char errmess[sizeof str]; }
    #define ErrBlock(num, str) (ErrType(str)) { num, str }
    #define ErrEntry(num, str) [num] = (oserror *) &ErrBlock(SCSI_ErrorBase+num,str)

    static oserror * const errtable[256] =
    {
    ErrEntry(SCSI_NoRoom, "No room for SCSI driver workspace"),
    ErrEntry(SCSI_SWIunkn, "Unknown SCSI SWI number"),
    ErrEntry(SCSI_RCunkn, "Unknown reason code for SCSI SWI"),
    ...
    };

    oserror *scsi_error(unsigned err)
    {
    if (err >= 256) return (oserror *) err;

    if (errtable[err]) return errtable[err];

    return scsi_error(SCSI_InvalidParms);
    }

    If you pick through the macro definitions, you'll note that this also uses
    designated initialisers, another C99 feature, to match the errtable array
    entries to the enum. Also be aware that the code is not portable for other
    reasons (mentioned below).

    > Also, when is it appropriate to cast? Casting seems to be frowned up
    > on so it makes me wonder when it is appropriate to ever cast.


    Casting is generally frowned upon, yes, as it is usually unnecessary - if you
    find yourself needing to cast you're probably doing something non-portable or
    you're patching over an error. Here are some examples, in ascending order of
    frown-factor.

    It can be used straightforwardly to perform a conversion (eg manually
    converting a float to an int), but this is usually not required, as implicit
    conversions happen on assignment and when passing values to prototyped
    functions. You might need casts in expressions; one valid example might be:

    int m = <whatever>, n = <whatever>;
    float ratio = (float) m / (float) n;

    Without the casts, the division would be performed as an integer division.

    It can be used to fudge qualifiers like "const" - for example a definition of
    strchr (from <string.h>) would have to have a cast to coerce the "const char
    *" passed in into a returned "char *".

    It can be used to change the interpretation of a pointer (eg the cast of the
    arbitrary sized compound literal to oserror * in the ErrEntry macro above) -
    this sort of casting is only valid if you know that the thing being pointed
    to can be accessed through the new type of pointer; C gives some assurances
    about when this works, eg when structures have common initial members, but
    otherwise you have to be careful when writing portable code.

    Most dubiously, you can convert between fundamentally different types - eg
    the cast of an unsigned to an oserror * in the scsi_error function. That is
    definitely non-portable. Don't do it.

    --
    Kevin Bracey, Principal Software Engineer
    Tematic Ltd Tel: +44 (0) 1223 503464
    182-190 Newmarket Road Fax: +44 (0) 1223 503458
    Cambridge, CB5 8HE, United Kingdom WWW: http://www.tematic.com/
    Kevin Bracey, Sep 15, 2003
    #3
  4. Kevin Bracey <> wrote in message news:<>...
    > In message <>
    > (Mantorok Redgormor) wrote:
    >
    > > What is the point of them?
    > >
    > > When should I use them?
    > >
    > > They seem to be rather useless but hopefully someone can prove me
    > > wrong. I think they are just additions to c99 but didn't exist in
    > > c89/90.

    >
    > They are new in C99 (and hence don't exist in C++ either).
    >
    > They're not a massively useful construct, but when you do find an application
    > for them, they can help tidy up code quite well. Here are two real-life
    > examples, from some SCSI software:
    >
    > /* Array literal constants defining well-known SCSI commands */
    > #define CDB_TEST_READY ((const uint8_t[6]) { 0x00 })
    > #define CDB_INQUIRY_L ((const uint8_t[6]) { 0x12, 0, 0, 0, 36, 0 })
    > #define CDB_CAPACITY ((const uint8_t[10]) { 0x25 })
    >
    > With these defines, a whole SCSI command can be simply defined and passed
    > to an API by giving the macro name, without having to manually declare and
    > initialise a variable.
    >
    > Now a more complex example, which will lead in nicely to your cast question.
    > The software internally uses error codes (from 0-255), but has to supply the
    > outside world with a pointer to an error structure (consisting of a code,
    > followed by a variable length string). A simple (!) form of knocking up a
    > mapping table, ignoring internationalisation issues, can work as follows:
    >
    > --- errors.h ---
    >
    > // typedef struct { int errnum; char errmess[252]; } oserror;
    >
    > #define SCSI_ErrorBase 0x20100
    >
    > enum
    > {
    > SCSI_NoRoom = 0x00,
    > SCSI_SWIunkn,
    > SCSI_RCunkn,
    > ...
    > };
    >
    > oserror *scsi_error(unsigned err);
    >
    > --- errors.c ---
    >
    > #define ErrType(str) const struct { int errnum; char errmess[sizeof str]; }
    > #define ErrBlock(num, str) (ErrType(str)) { num, str }
    > #define ErrEntry(num, str) [num] = (oserror *) &ErrBlock(SCSI_ErrorBase+num,str)
    >
    > static oserror * const errtable[256] =
    > {
    > ErrEntry(SCSI_NoRoom, "No room for SCSI driver workspace"),
    > ErrEntry(SCSI_SWIunkn, "Unknown SCSI SWI number"),
    > ErrEntry(SCSI_RCunkn, "Unknown reason code for SCSI SWI"),
    > ...
    > };
    >
    > oserror *scsi_error(unsigned err)
    > {
    > if (err >= 256) return (oserror *) err;
    >
    > if (errtable[err]) return errtable[err];
    >
    > return scsi_error(SCSI_InvalidParms);
    > }
    >
    > If you pick through the macro definitions, you'll note that this also uses
    > designated initialisers, another C99 feature, to match the errtable array
    > entries to the enum. Also be aware that the code is not portable for other
    > reasons (mentioned below).
    >
    > > Also, when is it appropriate to cast? Casting seems to be frowned up
    > > on so it makes me wonder when it is appropriate to ever cast.

    >
    > Casting is generally frowned upon, yes, as it is usually unnecessary - if you
    > find yourself needing to cast you're probably doing something non-portable or
    > you're patching over an error. Here are some examples, in ascending order of
    > frown-factor.
    >
    > It can be used straightforwardly to perform a conversion (eg manually
    > converting a float to an int), but this is usually not required, as implicit
    > conversions happen on assignment and when passing values to prototyped
    > functions. You might need casts in expressions; one valid example might be:
    >
    > int m = <whatever>, n = <whatever>;
    > float ratio = (float) m / (float) n;
    >
    > Without the casts, the division would be performed as an integer division.
    >
    > It can be used to fudge qualifiers like "const" - for example a definition of
    > strchr (from <string.h>) would have to have a cast to coerce the "const char
    > *" passed in into a returned "char *".
    >
    > It can be used to change the interpretation of a pointer (eg the cast of the
    > arbitrary sized compound literal to oserror * in the ErrEntry macro above) -
    > this sort of casting is only valid if you know that the thing being pointed
    > to can be accessed through the new type of pointer; C gives some assurances
    > about when this works, eg when structures have common initial members, but
    > otherwise you have to be careful when writing portable code.
    >
    > Most dubiously, you can convert between fundamentally different types - eg
    > the cast of an unsigned to an oserror * in the scsi_error function. That is
    > definitely non-portable. Don't do it.


    Thanks for the great post! Very informative.
    Mantorok Redgormor, Sep 16, 2003
    #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. =?Utf-8?B?TWlrZUw=?=
    Replies:
    0
    Views:
    381
    =?Utf-8?B?TWlrZUw=?=
    Nov 19, 2004
  2. John Goche
    Replies:
    8
    Views:
    16,429
  3. William Ahern

    Compound literals and VLA's

    William Ahern, Aug 24, 2005, in forum: C Programming
    Replies:
    6
    Views:
    698
    Robert Gamble
    Aug 24, 2005
  4. Marcus Harnisch

    Compound literals efficiency

    Marcus Harnisch, Jan 5, 2009, in forum: C Programming
    Replies:
    10
    Views:
    579
    Marcus Harnisch
    Jan 12, 2009
  5. kid joe

    Compound literals (anonymous aggregates)

    kid joe, Apr 21, 2009, in forum: C Programming
    Replies:
    21
    Views:
    1,104
    Richard Bos
    Apr 25, 2009
Loading...

Share This Page