Q: Type'ing the infamous 'flags' field

Discussion in 'C Programming' started by Mark A. Odell, Sep 16, 2004.

  1. I write a lot of drivers and there is inevitably some hardware register or
    buffer descriptor field called 'flags'. The flags are defined, typically,
    as bit positions. I was thinking I could get some compile-time type
    checking when assigning to a flag field but I don't think I can. Here's
    what I was thinking:

    typedef unsigned long HwFlag;
    struct HwThing
    {
    SomeType fieldOne;
    SomeType fieldTwo;
    HwFlag flags;
    };

    static const HwFlag flagA = 1 << 0;
    static const HwFlag flagB = 1 << 1;
    static const HwFlag flagC = 1 << 2;

    But the problem is when combining flags:

    struct HwThing dev;

    dev.flags = flagA | flagB;

    I think I require a cast back to HwFlag since flagA and B will promote to
    some integer type won't they? That is, I'd need to write:

    dev.flags = (HwFlag) (flagA | flagB);

    which rather defeats my intent. Any suggestions?

    Thanks.

    --
    - Mark ->
    --
     
    Mark A. Odell, Sep 16, 2004
    #1
    1. Advertising

  2. Mark A. Odell

    Dan Pop Guest

    In <Xns95665C1F26A7ECopyrightMarkOdell@130.133.1.4> "Mark A. Odell" <> writes:

    >I write a lot of drivers and there is inevitably some hardware register or
    >buffer descriptor field called 'flags'. The flags are defined, typically,
    >as bit positions. I was thinking I could get some compile-time type
    >checking when assigning to a flag field but I don't think I can.


    What kind of type checking do you expect in C, when all the operands have
    integral types?

    >Here's what I was thinking:
    >
    >typedef unsigned long HwFlag;
    >struct HwThing
    >{
    > SomeType fieldOne;
    > SomeType fieldTwo;
    > HwFlag flags;
    >};
    >
    >static const HwFlag flagA = 1 << 0;
    >static const HwFlag flagB = 1 << 1;
    >static const HwFlag flagC = 1 << 2;
    >
    >But the problem is when combining flags:
    >
    >struct HwThing dev;
    >
    >dev.flags = flagA | flagB;


    HwFlag HwFlag HwFlag

    >I think I require a cast back to HwFlag since flagA and B will promote to
    >some integer type won't they? That is, I'd need to write:


    Since the type of flagA and flagB is unsigned long, they are guaranteed
    not to be touched by the integral promotions. Furthermore, it is
    guaranteed that the type of flagA | flagB is unsigned long.

    Even if HwFlag were a type affected by the integral promotions (unsigned
    char or unsigned short), and the type of flagA | flagB became int, your
    assignment would still work as intended, because type int is guaranteed
    to be able to represent all values of type HwFlag.

    >dev.flags = (HwFlag) (flagA | flagB);
    >
    >which rather defeats my intent.


    Do you have the slightest clue about how the C assignment operator works?
    Even if flagA | flagB had a different integer type than dev.flags, the
    conversion would be *automatically* performed by the assignment operator.

    >Any suggestions?


    Engage your brain.

    Dan
    --
    Dan Pop
    DESY Zeuthen, RZ group
    Email:
    Currently looking for a job in the European Union
     
    Dan Pop, Sep 16, 2004
    #2
    1. Advertising

  3. (Dan Pop) wrote in news:cicbkl$bla$:

    >>dev.flags = (HwFlag) (flagA | flagB);
    >>
    >>which rather defeats my intent.

    >
    > Do you have the slightest clue about how the C assignment operator
    > works?


    I guess not. I thought it assigned the value of rvalue to the
    location of the lvalue if the types were compatible at compile time.

    > Even if flagA | flagB had a different integer type than
    > dev.flags, the conversion would be *automatically* performed by the
    > assignment operator.


    That's great but I want a compile time warning if someone attempts:

    int myIllegalFlagValue = 75;

    HwFlag dev;

    dev.flags = myIllegalFlagValue; // Warning of some sort please.

    >>Any suggestions?

    >
    > Engage your brain.


    Thanks Dan, as usual. Ever considered a job in customer support? :)

    --
    - Mark ->
    --
     
    Mark A. Odell, Sep 16, 2004
    #3
  4. Mark A. Odell

    Dan Pop Guest

    In <Xns956679156CCF0CopyrightMarkOdell@130.133.1.4> "Mark A. Odell" <> writes:

    > (Dan Pop) wrote in news:cicbkl$bla$:
    >
    >> Even if flagA | flagB had a different integer type than
    >> dev.flags, the conversion would be *automatically* performed by the
    >> assignment operator.

    >
    >That's great but I want a compile time warning if someone attempts:
    >
    >int myIllegalFlagValue = 75;
    >
    >HwFlag dev;
    >
    >dev.flags = myIllegalFlagValue; // Warning of some sort please.


    Then learn Pascal. As I said in my previous post, C is not the right
    tool for what you want.

    >>>Any suggestions?

    >>
    >> Engage your brain.

    >
    >Thanks Dan, as usual. Ever considered a job in customer support? :)


    My current job involves a great deal of user support. E.g.

    Date: Thu, 16 Sep 2004 10:53:14 +0200
    From: Carsten Urbach <-berlin.de>
    To: Dan Pop <>
    Subject: Re: Problems with PBS jobs

    Hi Dan,

    Thanks a lot for your fast help!

    Carsten

    Dan
    --
    Dan Pop
    DESY Zeuthen, RZ group
    Email:
    Currently looking for a job in the European Union
     
    Dan Pop, Sep 16, 2004
    #4
  5. (Dan Pop) wrote in news:cichg7$6sh$:

    >>> Even if flagA | flagB had a different integer type than
    >>> dev.flags, the conversion would be *automatically* performed by the
    >>> assignment operator.

    >>
    >>That's great but I want a compile time warning if someone attempts:
    >>
    >>int myIllegalFlagValue = 75;
    >>
    >>HwFlag dev;
    >>
    >>dev.flags = myIllegalFlagValue; // Warning of some sort please.

    >
    > Then learn Pascal. As I said in my previous post, C is not the right
    > tool for what you want.


    This, is the answer I sought. Thanks.

    >>> Engage your brain.

    >>
    >>Thanks Dan, as usual. Ever considered a job in customer support? :)

    >
    > My current job involves a great deal of user support. E.g.
    >
    > Date: Thu, 16 Sep 2004 10:53:14 +0200
    > From: Carsten Urbach <-berlin.de>
    > To: Dan Pop <>
    > Subject: Re: Problems with PBS jobs
    >
    > Hi Dan,
    >
    > Thanks a lot for your fast help!
    >
    > Carsten


    I suspect you did not tell Carsten to "engage his brain".

    Best regards,

    --
    - Mark ->
    --
     
    Mark A. Odell, Sep 16, 2004
    #5
  6. Mark A. Odell

    Chris Torek Guest

    In article <news:Xns95665C1F26A7ECopyrightMarkOdell@130.133.1.4>
    Mark A. Odell <> wrote:
    >I write a lot of drivers and there is inevitably some hardware
    >register or buffer descriptor field called 'flags' [, usually just
    >some individual bits]. I [wish I] could get some compile-time type
    >checking when assigning [named constants] to a flag field ...
    >Any suggestions?


    Ada. :)

    Seriously, the only way to get serious type-checking in C is to
    make new types, using C's type-creation mechanism, the "struct".
    This is not generally compatible with low-level bit-twiddling.

    You can do clever stuff in C++ at compile time with templates, so
    that the actual code emitted is just straight integer values going
    into the bit-field. This is incredibly ugly, though.

    Ada really does have just what you want, *and* is (partly) designed
    for use in small embedded systems. In many ways it is a shame we
    all use C to program them instead. :)
    --
    In-Real-Life: Chris Torek, Wind River Systems
    Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W) +1 801 277 2603
    email: forget about it http://web.torek.net/torek/index.html
    Reading email is like searching for food in the garbage, thanks to spammers.
     
    Chris Torek, Sep 16, 2004
    #6
  7. Chris Torek <> wrote in
    news::

    >>I write a lot of drivers and there is inevitably some hardware
    >>register or buffer descriptor field called 'flags' [, usually just
    >>some individual bits]. I [wish I] could get some compile-time type
    >>checking when assigning [named constants] to a flag field ...
    >>Any suggestions?

    >
    > Ada. :)
    >
    > Seriously, the only way to get serious type-checking in C is to
    > make new types, using C's type-creation mechanism, the "struct".
    > This is not generally compatible with low-level bit-twiddling.

    [snip]

    Thanks Chris and Dan for the replies. I appreciate your time.

    Regards.

    --
    - Mark ->
    --
     
    Mark A. Odell, Sep 16, 2004
    #7
  8. Mark A. Odell

    Old Wolf Guest

    "Mark A. Odell" <> wrote:
    > I write a lot of drivers and there is inevitably some hardware register or
    > buffer descriptor field called 'flags'. The flags are defined, typically,
    > as bit positions. I was thinking I could get some compile-time type
    > checking when assigning to a flag field but I don't think I can. Here's
    > what I was thinking:
    >
    > typedef unsigned long HwFlag;
    > struct HwThing
    > {
    > SomeType fieldOne;
    > SomeType fieldTwo;
    > HwFlag flags;
    > };
    >
    > static const HwFlag flagA = 1 << 0;
    > static const HwFlag flagB = 1 << 1;
    > static const HwFlag flagC = 1 << 2;


    You should replace these '1's with '1UL'. For example if
    int is 32bit and long is 64bit then 1 << 32 is undefined
    behaviour.

    >
    > But the problem is when combining flags:
    >
    > struct HwThing dev;
    >
    > dev.flags = flagA | flagB;
    >
    > I think I require a cast back to HwFlag since flagA and B will promote to
    > some integer type won't they? That is, I'd need to write:
    >
    > dev.flags = (HwFlag) (flagA | flagB);


    No, since dev.flags is of type HwFlag, then when you make the
    assignment it will be implicitly converted. Some compilers may
    issue a warning about loss of precision but you can turn those
    warnings off (they're more annoying than useful).

    Anyway, flagA and flagB are already "some integer type" (in
    this case, unsigned long), so no promotions will occur.
    Promotions would only occur if HwFlag was 'short' or 'char'
    (or their unsigned or qualified types etc.)

    My usual idiom is the same as yours except that I #define
    the flags instead of 'static const'ing them (because some
    of the compilers I have to put up with, would actually
    allocate memory for the static consts (one per TU that the
    header is included, even), and memory is at a premium).
    Also I use 0x1, 0x2, 0x4 etc. These constants are unsigned,
    it avoids the shift issue, and will give a compiler warning
    if you try and use one that's too big for the flags typedef.
     
    Old Wolf, Sep 16, 2004
    #8
  9. Mark A. Odell wrote:
    >
    > I write a lot of drivers and there is inevitably some hardware register or
    > buffer descriptor field called 'flags'. The flags are defined, typically,
    > as bit positions. I was thinking I could get some compile-time type
    > checking when assigning to a flag field but I don't think I can.


    I'll mention the tool we use for the Linux kernel, because some people on
    comp.lang.c migth actually find it interesting.

    It's called "sparse" (for "semantic parse"), and it extends the C type
    system with various flags (much of them for checking "address spaces" of
    pointers, since the kernel has to work with pointers that are in distinct
    address spaces like "user" and "iomem"), and it also solves your particular
    problem.

    What you do is make a integer typedef that is restricted to "bitwise"
    operations:

    #ifdef __CHECKER__ /* Magic sparse flag */
    #define __bitwise __attribute__((bitwise))
    #else
    #define __bitwise
    #endif

    typedef unsigned int __bitwise cmd_flag_t;

    ...
    #define CMD_ACTIVE ((cmd_flag_t) 0x1000)
    ...

    and you now have a magic new type that you can only do operations on with
    the EXACT SAME TYPE (and only bitwise operations too, which is why it's
    called "bitwise". We aren't very innovative name-wise in the kernel ;)

    So if you do multiple different "typedef"s, they'll all create new
    (independent and incompatible) types, so you can create any number of these
    different "flags" you want, with different rules.

    After that, trying to pass a "cmd_flag_t" variable as an integer will warn
    you in various ways. Very convenient. In the kernel we use it to
    automatically check that variables that have been marked to be of a
    specific byte-order are properly accessed (ie that you don't just take a
    little-endian value and think you can use it directly - you have to convert
    it to CPU endianness first).

    Latest sparse sources available at

    http://www.codemonkey.org.uk/projects/bitkeeper/sparse/

    (and if you use bitkeeper, at various other sites).

    Linus
     
    Linus Torvalds, Sep 17, 2004
    #9
  10. Mark A. Odell

    Jack Klein Guest

    On 16 Sep 2004 13:03:21 GMT, "Mark A. Odell" <>
    wrote in comp.lang.c:

    > I write a lot of drivers and there is inevitably some hardware register or
    > buffer descriptor field called 'flags'. The flags are defined, typically,
    > as bit positions. I was thinking I could get some compile-time type
    > checking when assigning to a flag field but I don't think I can. Here's
    > what I was thinking:
    >
    > typedef unsigned long HwFlag;
    > struct HwThing
    > {
    > SomeType fieldOne;
    > SomeType fieldTwo;
    > HwFlag flags;
    > };
    >
    > static const HwFlag flagA = 1 << 0;
    > static const HwFlag flagB = 1 << 1;
    > static const HwFlag flagC = 1 << 2;
    >
    > But the problem is when combining flags:
    >
    > struct HwThing dev;
    >
    > dev.flags = flagA | flagB;
    >
    > I think I require a cast back to HwFlag since flagA and B will promote to
    > some integer type won't they? That is, I'd need to write:
    >
    > dev.flags = (HwFlag) (flagA | flagB);
    >
    > which rather defeats my intent. Any suggestions?
    >
    > Thanks.


    What you can do is make it a number of bit-fields.

    struct FlagsReg
    {
    unsigned FlagA: 1;
    unsigned FlagB: 1;
    unsigned FlagC: 1;
    /* ... */
    };

    struct HwThing
    {
    SomeType fieldOne;
    SomeType fieldTwo;
    struct FlagsReg HwFlags;
    };

    Now simple integer assignments can't be made, you must do:

    theThing.HwFlags.FlagA = 1;

    This is find unless you very often need to set or clear several bits
    at the same time.

    TI provides headers for this for all the on-chip peripherals I am
    working with, and (OT) no-load section definitions for the linker (OT)
    to map them by name as ordinary external objects of the type from C
    source.

    --
    Jack Klein
    Home: http://JK-Technology.Com
    FAQs for
    comp.lang.c http://www.eskimo.com/~scs/C-faq/top.html
    comp.lang.c++ http://www.parashift.com/c -faq-lite/
    alt.comp.lang.learn.c-c++
    http://www.contrib.andrew.cmu.edu/~ajo/docs/FAQ-acllc.html
     
    Jack Klein, Sep 17, 2004
    #10
  11. Mark A. Odell

    Dan Pop Guest

    In <Xns95668C9A0DF92CopyrightMarkOdell@130.133.1.4> "Mark A. Odell" <> writes:

    >I suspect you did not tell Carsten to "engage his brain".


    I didn't have to: he always does.

    Dan
    --
    Dan Pop
    DESY Zeuthen, RZ group
    Email:
    Currently looking for a job in the European Union
     
    Dan Pop, Sep 17, 2004
    #11
  12. Mark A. Odell

    Dan Pop Guest

    In <> (Old Wolf) writes:

    >"Mark A. Odell" <> wrote:
    >>
    >> static const HwFlag flagA = 1 << 0;
    >> static const HwFlag flagB = 1 << 1;
    >> static const HwFlag flagC = 1 << 2;

    >
    >You should replace these '1's with '1UL'. For example if
    >int is 32bit and long is 64bit then 1 << 32 is undefined
    >behaviour.


    As long as the shift count does not exceed 14 (and it doesn't in the
    posted code), Mark's expressions are as correct and portable as you
    can get. No need to replace 1 by anything else.

    Dan
    --
    Dan Pop
    DESY Zeuthen, RZ group
    Email:
    Currently looking for a job in the European Union
     
    Dan Pop, Sep 17, 2004
    #12
  13. Linus Torvalds <> wrote in
    news:cid94g$7io$:

    > Mark A. Odell wrote:
    >>
    >> I write a lot of drivers and there is inevitably some hardware register
    >> or buffer descriptor field called 'flags'. The flags are defined,
    >> typically, as bit positions. I was thinking I could get some
    >> compile-time type checking when assigning to a flag field but I don't
    >> think I can.

    >
    > I'll mention the tool we use for the Linux kernel, because some people
    > on comp.lang.c migth actually find it interesting.
    >
    > It's called "sparse" (for "semantic parse"), and it extends the C type
    > system with various flags (much of them for checking "address spaces" of
    > pointers, since the kernel has to work with pointers that are in
    > distinct address spaces like "user" and "iomem"), and it also solves
    > your particular problem.
    >
    > What you do is make a integer typedef that is restricted to "bitwise"
    > operations:
    >
    > #ifdef __CHECKER__ /* Magic sparse flag */
    > #define __bitwise __attribute__((bitwise))
    > #else
    > #define __bitwise
    > #endif
    >
    > typedef unsigned int __bitwise cmd_flag_t;
    >
    > ...
    > #define CMD_ACTIVE ((cmd_flag_t) 0x1000)

    [snip]

    Thanks for the reply Linus. This looks very interesting. We use Diab's
    tool chain (not gcc) but there may be some way to do this. Of course we're
    moving off into non-ISO C but that may be my only choice.

    Regards.

    --
    - Mark ->
    --
     
    Mark A. Odell, Sep 17, 2004
    #13
  14. On 16 Sep 2004 19:25:40 GMT, Chris Torek <> wrote
    (reordered slightly for convenience):

    > In article <news:Xns95665C1F26A7ECopyrightMarkOdell@130.133.1.4>
    > Mark A. Odell <> wrote:
    > >I write a lot of drivers and there is inevitably some hardware
    > >register or buffer descriptor field called 'flags' [, usually just
    > >some individual bits]. I [wish I] could get some compile-time type
    > >checking when assigning [named constants] to a flag field ...
    > >Any suggestions?

    >
    > Ada. :)
    >
    > Ada really does have just what you want, *and* is (partly) designed
    > for use in small embedded systems. In many ways it is a shame we
    > all use C to program them instead. :)


    Concur. And FWIW Ada has language-defined (standardized) interface to
    C. And Fortran and COBOL, which are less likely to be useful in a
    lowlevel hardware application. The GNU implementation GNAT also does
    C++ (g++) and will "play fairly nicely" in a C main -- I don't know
    about others -- but the OP said elsethread he isn't using GNU.

    > Seriously, the only way to get serious type-checking in C is to
    > make new types, using C's type-creation mechanism, the "struct".
    > This is not generally compatible with low-level bit-twiddling.
    >
    > You can do clever stuff in C++ at compile time with templates, so
    > that the actual code emitted is just straight integer values going
    > into the bit-field. This is incredibly ugly, though.
    >

    To be clear you can't template a type as such; you would template an
    "integer-like" class containing inlined methods, which would as you
    say actually compile the same as integers but with added type checks.

    Or in C++ you can just use enum types. Unlike C where enum types and
    values are just (syntactic sugar for) integers, in C++ they are
    distinct types for purposes of overloading and conversion although
    they still behave like integer types, and you can't (accidentally)
    convert *to* a (different) enum type without a cast. (And also, a
    minor point, they can be as wide as long, if that is wider than int.)

    - David.Thompson1 at worldnet.att.net
     
    Dave Thompson, Sep 23, 2004
    #14
    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. Thomas
    Replies:
    0
    Views:
    455
    Thomas
    Sep 7, 2005
  2. Eigenvector

    The infamous ^Z problem

    Eigenvector, May 23, 2007, in forum: C Programming
    Replies:
    15
    Views:
    735
    Keith Thompson
    May 24, 2007
  3. =?Utf-8?B?cm9kY2hhcg==?=

    infamous ie message

    =?Utf-8?B?cm9kY2hhcg==?=, Nov 12, 2007, in forum: ASP .Net
    Replies:
    4
    Views:
    293
    rodchar
    Nov 19, 2007
  4. Steve Holden
    Replies:
    0
    Views:
    790
    Steve Holden
    Feb 8, 2009
  5. Replies:
    2
    Views:
    438
    James Kanze
    May 23, 2009
Loading...

Share This Page