Help with 2-level macro expansion

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Srinivas Mudireddy

    Help with 2-level macro expansion

    Hi,
    We have bunch of message levels and depending on whether that level is
    turned on, messages of that level are printed. For example, we have
    levels like MSG_LOW, MSG_MED etc.

    We want to provide a wrapper API on top of this to print various kinds
    of messages and map the wrapper API to either MSG_LOW or MSG_MED. For
    example, our wrapper API have macros such as
    MSG_FUNCTION_EN TRY, MSG_FUNCTION_EX IT, MSG_STATE_CHANG E,
    MSG_INVALID_INP UT etc. Whenever a function is called, we call
    MSG_FUNCTION_EN TRY macro. This shields the programmer from whether
    this kind of messages is mapped to LOW or MED and it allows us to
    configure what kind of messages we want to print in various flavors of
    a build. In debug build, we might map MSG_FUNCTION_EN TRY to MSG_MED
    and in production build, we might map it to MSG_LOW.

    I wrote following macros to get this functionality.

    #define IS_MSG_LOW_ON 0
    #define IS_MSG_MED_ON 1

    #define MSG_FUNCTION_EN TRY_LEVEL MSG_MED
    #define MSG_FUNCTION_EX IT_LEVEL MSG_MED

    #define MSG_FUNCTION_EN TRY(fmt_string, x, y, z) \
    #if (IS_ ## MSG_FUNCTION_EN TRY_LEVEL_ ## ON == 1) \
    printf(fmt_stri ng, x, y, z);

    But this is not working as IS_ ## MSG_FUNCTION_EN TRY_LEVEL_ ## ON is
    translated to IS_MSG_FUNCTION _ENTRY_LEVEL_ON instead of IS_MSG_MED_ON.
    So how do I get this to work?

    Thx,
    Srinivas
  • Peter Nilsson

    #2
    Re: Help with 2-level macro expansion

    Srinivas Mudireddy wrote:
    ...I wrote following macros to get this functionality.
    >
    #define IS_MSG_LOW_ON 0
    #define IS_MSG_MED_ON 1
    >
    #define MSG_FUNCTION_EN TRY_LEVEL MSG_MED
    #define MSG_FUNCTION_EX IT_LEVEL MSG_MED
    >
    #define MSG_FUNCTION_EN TRY(fmt_string, x, y, z) \
    #if (IS_ ## MSG_FUNCTION_EN TRY_LEVEL_ ## ON == 1) \
    printf(fmt_stri ng, x, y, z);
    >
    But this is not working...
    See...



    --
    Peter

    Comment

    • Ben Bacarisse

      #3
      Re: Help with 2-level macro expansion

      Srinivas Mudireddy <srinivas.mudir eddy@gmail.comw rites:

      This seems not have been answered, so I'll have a go...
      We want to provide a wrapper API on top of this to print various kinds
      of messages and map the wrapper API to either MSG_LOW or MSG_MED.
      <snip>
      I wrote following macros to get this functionality.
      >
      #define IS_MSG_LOW_ON 0
      #define IS_MSG_MED_ON 1
      >
      #define MSG_FUNCTION_EN TRY_LEVEL MSG_MED
      #define MSG_FUNCTION_EX IT_LEVEL MSG_MED
      >
      #define MSG_FUNCTION_EN TRY(fmt_string, x, y, z) \
      #if (IS_ ## MSG_FUNCTION_EN TRY_LEVEL_ ## ON == 1) \
      printf(fmt_stri ng, x, y, z);
      >
      But this is not working as IS_ ## MSG_FUNCTION_EN TRY_LEVEL_ ## ON is
      translated to IS_MSG_FUNCTION _ENTRY_LEVEL_ON instead of IS_MSG_MED_ON.
      So how do I get this to work?
      Three things need to be sorted. First, a typo:

      #if (IS_ ## MSG_FUNCTION_EN TRY_LEVEL ## _ON == 1) \

      (note the _ON). Second, you will never be able to generate a
      pre-processing directive (in this case a #if) using a macro. You
      just can't. If you can live with the macro expanding to an "if"
      (which will in all likelyhood be optimised away) then you need to sort
      out the "real" problem. To get MSG_FUNCTION_EN TRY_LEVEL expanded
      rather than literally token pasted, you need another round of macro
      expansion:

      #define XP(s) PASTE(s)
      #define PASTE(s) IS_ ## s ## _ON

      #define MSG_FUNCTION_EN TRY(fmt_string, x, y, z) \
      if (XP(MSG_FUNCTIO N_ENTRY_LEVEL) == 1) \
      printf(fmt_stri ng, x, y, z)

      (You might dream up better names).

      Finally, if you have C99's variadic macros, you don't need to have
      three fixed arguments to printf.

      #define MSG_FUNCTION_EN TRY(fmt_string, ...) \
      if (XP(MSG_FUNCTIO N_ENTRY_LEVEL) == 1) \
      printf(fmt_stri ng, __VA_ARGS__)

      If you don't have that, you can consider the "double parentheses"
      trick:

      #define MSG_FUNCTION_EN TRY(args) \
      if (XP(MSG_FUNCTIO N_ENTRY_LEVEL) == 1) \
      printf args

      where you use it like this:

      MSG_FUNCTION_EN TRY(("A message\n"));
      MSG_FUNCTION_EN TRY(("An int %d\n", x));

      Note, also, that I have removed the trailing ; from the macro. I
      prefer to have it added at the invocation.

      I hope that is useful.

      --
      Ben.

      Comment

      • Srinivas Mudireddy

        #4
        Re: Help with 2-level macro expansion

        On Apr 28, 9:43 am, Ben Bacarisse <ben.use...@bsb .me.ukwrote:
        Srinivas Mudireddy <srinivas.mudir e...@gmail.comw rites:
        >
        This seems not have been answered, so I'll have a go...
        >
        >
        >
        >
        >
        We want to provide a wrapper API on top of this to print various kinds
        of messages and map the wrapper API to either MSG_LOW or MSG_MED.
        <snip>
        I wrote following macros to get this functionality.
        >
        #define IS_MSG_LOW_ON 0
        #define IS_MSG_MED_ON 1
        >
        #define MSG_FUNCTION_EN TRY_LEVEL MSG_MED
        #define MSG_FUNCTION_EX IT_LEVEL MSG_MED
        >
        #define MSG_FUNCTION_EN TRY(fmt_string, x, y, z) \
        #if (IS_ ## MSG_FUNCTION_EN TRY_LEVEL_ ## ON == 1) \
        printf(fmt_stri ng, x, y, z);
        >
        But this is not working as IS_ ## MSG_FUNCTION_EN TRY_LEVEL_ ## ON is
        translated to IS_MSG_FUNCTION _ENTRY_LEVEL_ON instead of IS_MSG_MED_ON.
        So how do I get this to work?
        >
        Three things need to be sorted. First, a typo:
        >
        #if (IS_ ## MSG_FUNCTION_EN TRY_LEVEL ## _ON == 1) \
        >
        (note the _ON). Second, you will never be able to generate a
        pre-processing directive (in this case a #if) using a macro. You
        just can't. If you can live with the macro expanding to an "if"
        (which will in all likelyhood be optimised away) then you need to sort
        out the "real" problem. To get MSG_FUNCTION_EN TRY_LEVEL expanded
        rather than literally token pasted, you need another round of macro
        expansion:
        >
        #define XP(s) PASTE(s)
        #define PASTE(s) IS_ ## s ## _ON
        >
        #define MSG_FUNCTION_EN TRY(fmt_string, x, y, z) \
        if (XP(MSG_FUNCTIO N_ENTRY_LEVEL) == 1) \
        printf(fmt_stri ng, x, y, z)
        >
        (You might dream up better names).
        >
        Finally, if you have C99's variadic macros, you don't need to have
        three fixed arguments to printf.
        >
        #define MSG_FUNCTION_EN TRY(fmt_string, ...) \
        if (XP(MSG_FUNCTIO N_ENTRY_LEVEL) == 1) \
        printf(fmt_stri ng, __VA_ARGS__)
        >
        If you don't have that, you can consider the "double parentheses"
        trick:
        >
        #define MSG_FUNCTION_EN TRY(args) \
        if (XP(MSG_FUNCTIO N_ENTRY_LEVEL) == 1) \
        printf args
        >
        where you use it like this:
        >
        MSG_FUNCTION_EN TRY(("A message\n"));
        MSG_FUNCTION_EN TRY(("An int %d\n", x));
        >
        Note, also, that I have removed the trailing ; from the macro. I
        prefer to have it added at the invocation.
        >
        I hope that is useful.
        >
        --
        Ben.
        Thx for the help Ben.

        Comment

        • Peter Nilsson

          #5
          Re: Help with 2-level macro expansion

          Ben Bacarisse wrote:
          Srinivas Mudireddy <srinivas.mudir eddy@gmail.comw rites:
          >
          This seems not have been answered, so I'll have a go...
          I directed the OP to the FAQ chapter on preprocessing.
          #if (IS_ ## MSG_FUNCTION_EN TRY_LEVEL_ ## ON == 1) \
          printf(fmt_stri ng, x, y, z);

          But this is not working as IS_ ## MSG_FUNCTION_EN TRY_LEVEL_ ## ON is
          translated to IS_MSG_FUNCTION _ENTRY_LEVEL_ON instead of IS_MSG_MED_ON.
          So how do I get this to work?
          >
          Three things need to be sorted. First, a typo:
          >
          #if (IS_ ## MSG_FUNCTION_EN TRY_LEVEL ## _ON == 1) \
          >
          (note the _ON).
          Note the LEVEL_. Also note that _ON is a reserved (for any use)
          identifier.

          <snip>

          --
          Peter

          Comment

          • Ben Bacarisse

            #6
            Re: Help with 2-level macro expansion

            Peter Nilsson <airia@acay.com .auwrites:
            Ben Bacarisse wrote:
            >Srinivas Mudireddy <srinivas.mudir eddy@gmail.comw rites:
            >>
            >This seems not have been answered, so I'll have a go...
            >
            I directed the OP to the FAQ chapter on preprocessing.
            >
            #if (IS_ ## MSG_FUNCTION_EN TRY_LEVEL_ ## ON == 1) \
            printf(fmt_stri ng, x, y, z);
            >
            But this is not working as IS_ ## MSG_FUNCTION_EN TRY_LEVEL_ ## ON is
            translated to IS_MSG_FUNCTION _ENTRY_LEVEL_ON instead of IS_MSG_MED_ON.
            So how do I get this to work?
            >>
            >Three things need to be sorted. First, a typo:
            >>
            > #if (IS_ ## MSG_FUNCTION_EN TRY_LEVEL ## _ON == 1) \
            >>
            >(note the _ON).
            >
            Note the LEVEL_.
            Yes, I changed both halves but only pointed one out. Presumably you
            are suggesting the original form was deliberate (and the real error
            was not using MSG_FUNCTION_EN TRY_LEVEL_ earlier). The reason being to
            avoid:
            Also note that _ON is a reserved (for any use)
            identifier.
            Ouch! Good catch. However, does it matter in this case? Isn't _ON
            is only a preprocessing token and never gets to be real grown up
            identifier.

            --
            Ben.

            Comment

            Working...