A variation of #

Discussion in 'C Programming' started by Andrey Vul, Nov 7, 2011.

  1. Andrey Vul

    Andrey Vul Guest

    To stringify pp-constants, we use #foo notation.
    Is there a method of charifying constants defined on the preprocessor
    level, assuming the precondition of length being 1 is true?
    That is, does there exist a pp-builtin CHARIFY such that CHARIFY(x) =
    'x' ?
    Andrey Vul, Nov 7, 2011
    #1
    1. Advertising

  2. Andrey Vul

    Ian Collins Guest

    On 11/ 8/11 07:53 AM, Andrey Vul wrote:
    > To stringify pp-constants, we use #foo notation.
    > Is there a method of charifying constants defined on the preprocessor
    > level, assuming the precondition of length being 1 is true?
    > That is, does there exist a pp-builtin CHARIFY such that CHARIFY(x) =
    > 'x' ?


    Something like

    #define CHAR(x) #x [0]

    ?

    --
    Ian Collins
    Ian Collins, Nov 7, 2011
    #2
    1. Advertising

  3. Andrey Vul

    Andrey Vul Guest

    On Nov 7, 1:59 pm, Ian Collins <> wrote:
    > On 11/ 8/11 07:53 AM, Andrey Vul wrote:
    >
    > > To stringify pp-constants, we use #foo notation.
    > > Is there a method of charifying constants defined on the preprocessor
    > > level, assuming the precondition of length being 1 is true?
    > > That is, does there exist a pp-builtin CHARIFY such that CHARIFY(x) =
    > > 'x' ?

    >
    > Something like
    >
    > #define CHAR(x) #x [0]


    Can't be used inside switch or anything else requiring a const
    expression.
    Andrey Vul, Nov 7, 2011
    #3
  4. Andrey Vul

    Andrey Vul Guest

    On Nov 7, 1:59 pm, Ian Collins <> wrote:
    > On 11/ 8/11 07:53 AM, Andrey Vul wrote:
    >
    > > To stringify pp-constants, we use #foo notation.
    > > Is there a method of charifying constants defined on the preprocessor
    > > level, assuming the precondition of length being 1 is true?
    > > That is, does there exist a pp-builtin CHARIFY such that CHARIFY(x) =
    > > 'x' ?

    >
    > Something like
    >
    > #define CHAR(x) #x [0]
    >


    Can't be used inside switch.
    Andrey Vul, Nov 7, 2011
    #4
  5. Andrey Vul

    James Kuyper Guest

    On 11/07/2011 02:11 PM, Andrey Vul wrote:
    > On Nov 7, 1:59 pm, Ian Collins <> wrote:
    >> On 11/ 8/11 07:53 AM, Andrey Vul wrote:
    >>
    >>> To stringify pp-constants, we use #foo notation.
    >>> Is there a method of charifying constants defined on the preprocessor
    >>> level, assuming the precondition of length being 1 is true?


    I presume there's some reason why the constants cannot, themselves, be
    defined as character constants?

    >>> That is, does there exist a pp-builtin CHARIFY such that CHARIFY(x) =
    >>> 'x' ?

    >>
    >> Something like
    >>
    >> #define CHAR(x) #x [0]
    >>

    >
    > Can't be used inside switch.


    There's no simple way I can think of to define a macro that generates a
    character constant whose value depends upon the expansion of another
    macro. I did, however, manage to come up with this ugly monstrosity,
    which cannot possibly qualify as "simple", which I've inserted into some
    test code:

    #include <stdlib.h>
    #define TRICAT(a, b, c) a ## b ## c
    #define CHAR(a, b, c) TRICAT(a, b, c)

    #define CASE1 x

    int main(int argc, char *argv[])
    {
    if(argc <= 1)
    return EXIT_FAILURE;
    switch (*argv[1])
    {
    case CHAR('
    , CASE1, '
    ): break;
    default:
    return EXIT_SUCCESS;
    }
    return EXIT_FAILURE;
    }

    The key thing that makes this work is that ' immediately followed by a
    newline is not recognized as any other type of pre-processing token, so
    it counts as a preprocessing token of it's own, under the classification
    "each non-white-space character that cannot be one of the above" (6.4p1).

    The two-level expansion allows CASE1 to be expanded to x before being
    concatenated with the ' tokens.

    If you can convince the relevant person to define the relevant constants
    as character constants, that would make things a lot easier.
    James Kuyper, Nov 7, 2011
    #5
  6. Andrey Vul

    James Kuyper Guest

    On 11/07/2011 05:53 PM, wrote:
    > James Kuyper <> wrote:
    >>
    >> #include <stdlib.h>
    >> #define TRICAT(a, b, c) a ## b ## c
    >> #define CHAR(a, b, c) TRICAT(a, b, c)
    >>
    >> #define CASE1 x
    >>

    > ...
    >> case CHAR('
    >> , CASE1, '
    >> ): break;

    >
    > Nice try, but no cigar. The ## operator requires that the result be a
    > valid pp-token and, depending on the order in which the operators are
    > evaluated, you either end up with 'x or x', neither of which are valid
    > pp-tokens.


    I hadn't considered that issue, but taking a look at 6.10.3.3p3, I found
    that it says "each each instance of a ## preprocessing token
    in the replacement list (not from an argument) is deleted and the
    preceding preprocessing token is concatenated with the following
    preprocessing token. ... If the result is not a valid preprocessing
    token, the behavior is undefined." It seems to me ambiguous whether "the
    result" it refers to is the result of each concatenation, or the
    combined result of all of the concatenations.

    I'm not going to argue strongly over this. I think this is a horribly
    ugly solution to what should be a simple problem; if it actually fails
    to be a solution by reason of having undefined behavior, I'm not going
    to lose any sleep over it.
    James Kuyper, Nov 7, 2011
    #6
  7. Andrey Vul <> wrote:
    > To stringify pp-constants, we use #foo notation.
    > Is there a method of charifying constants defined
    > on the preprocessor level, assuming the precondition
    > of length being 1 is true? That is, does there exist
    > a pp-builtin CHARIFY such that CHARIFY(x) = 'x' ?


    This is a good example of a question on a possible
    solution to an unstated problem. As James Juyper asks,
    what is the real problem that you're trying to solve?

    In short, if you're already hardcoding x, why can't
    you hardcode 'x'?

    There are ways of synchronising elements using the
    preprocessor. But different methods have different
    advantages and disadvantages. The following will work
    for some characters but not for others...

    #define char_x 'x'
    #define char_y 'y'

    #define CHARIFY(x) char_ ## x

    switch (blah)
    {
    case CHARIFY(x):
    ..
    case CHARIFY(y):
    ..
    }

    --
    Peter
    Peter Nilsson, Nov 8, 2011
    #7
  8. Andrey Vul

    Andrey Vul Guest

    On Nov 7, 3:07 pm, James Kuyper <> wrote:
    > On 11/07/2011 02:11 PM, Andrey Vul wrote:
    >
    > > On Nov 7, 1:59 pm, Ian Collins <> wrote:
    > >> On 11/ 8/11 07:53 AM, Andrey Vul wrote:

    >
    > >>> To stringify pp-constants, we use #foo notation.
    > >>> Is there a method of charifying constants defined on the preprocessor
    > >>> level, assuming the precondition of length being 1 is true?

    >
    > I presume there's some reason why the constants cannot, themselves, be
    > defined as character constants?
    >
    > >>> That is, does there exist a pp-builtin CHARIFY such that CHARIFY(x) =
    > >>> 'x' ?

    >
    > >> Something like

    >
    > >> #define CHAR(x) #x [0]

    >
    > > Can't be used inside switch.

    >
    > There's no simple way I can think of to define a macro that generates a
    > character constant whose value depends upon the expansion of another
    > macro. I did, however, manage to come up with this ugly monstrosity,
    > which cannot possibly qualify as "simple", which I've inserted into some
    > test code:
    >
    > #include <stdlib.h>
    > #define TRICAT(a, b, c) a ## b ## c
    > #define CHAR(a, b, c) TRICAT(a, b, c)
    >
    > #define CASE1 x
    >
    > int main(int argc, char *argv[])
    > {
    >     if(argc <= 1)
    >         return EXIT_FAILURE;
    >     switch (*argv[1])
    >     {
    >     case CHAR('
    >         , CASE1, '
    >         ): break;
    >     default:
    >         return EXIT_SUCCESS;
    >     }
    >     return EXIT_FAILURE;
    >
    > }
    >
    > The key thing that makes this work is that ' immediately followed by a
    > newline is not recognized as any other type of pre-processing token, so
    > it counts as a preprocessing token of it's own, under the classification
    > "each non-white-space character that cannot be one of the above" (6.4p1).
    >
    > The two-level expansion allows CASE1 to be expanded to x before being
    > concatenated with the ' tokens.
    >


    That looks like it shouldn't even compile!

    In the end, I figured it's best to define as a char in the first place
    and define the string as { 'f', 'o', 'o', 'b', 'a', 'r', ... } instead
    of "foo" "bar" "...".

    It's not like I'm making use of null-termination in the string
    anyways.
    Andrey Vul, Nov 8, 2011
    #8
  9. Andrey Vul

    James Kuyper Guest

    On 11/07/2011 08:32 PM, Andrey Vul wrote:
    > On Nov 7, 3:07�pm, James Kuyper <> wrote:

    ....
    >> There's no simple way I can think of to define a macro that generates a
    >> character constant whose value depends upon the expansion of another
    >> macro. I did, however, manage to come up with this ugly monstrosity,
    >> which cannot possibly qualify as "simple", which I've inserted into some
    >> test code:
    >>
    >> #include <stdlib.h>
    >> #define TRICAT(a, b, c) a ## b ## c
    >> #define CHAR(a, b, c) TRICAT(a, b, c)
    >>
    >> #define CASE1 x
    >>
    >> int main(int argc, char *argv[])
    >> {
    >> � � if(argc <= 1)
    >> � � � � return EXIT_FAILURE;
    >> � � switch (*argv[1])
    >> � � {
    >> � � case CHAR('
    >> � � � � , CASE1, '
    >> � � � � ): break;
    >> � � default:
    >> � � � � return EXIT_SUCCESS;
    >> � � }
    >> � � return EXIT_FAILURE;
    >>
    >> }
    >>
    >> The key thing that makes this work is that ' immediately followed by a
    >> newline is not recognized as any other type of pre-processing token, so
    >> it counts as a preprocessing token of it's own, under the classification
    >> "each non-white-space character that cannot be one of the above" (6.4p1).
    >>
    >> The two-level expansion allows CASE1 to be expanded to x before being
    >> concatenated with the ' tokens.
    >>

    >
    > That looks like it shouldn't even compile!


    It does compile, and works as intended - with gcc, with maximal
    conformance and warning levels turned on. However, whether or not it's
    required to compile is, at best, ambiguous. See Lawrence Jone's message
    on this same thread - he's a fairly authoritative source, which doesn't
    mean he's necessarily right - but I wouldn't recommend betting a lot of
    money against him.

    I'm curious - is there any specific feature that bothers you about that
    code, other than the issue that Larry raised?

    > In the end, I figured it's best to define as a char in the first place
    > and define the string as { 'f', 'o', 'o', 'b', 'a', 'r', ... } instead
    > of "foo" "bar" "...".


    Yes, that's by far the better approach, even if there were no ambiguity
    about my "solution".
    --
    James Kuyper
    James Kuyper, Nov 8, 2011
    #9
  10. Andrey Vul

    Thad Smith Guest

    On 11/7/2011 3:53 PM, wrote:
    > James Kuyper<> wrote:
    >>
    >> #include<stdlib.h>
    >> #define TRICAT(a, b, c) a ## b ## c
    >> #define CHAR(a, b, c) TRICAT(a, b, c)
    >>
    >> #define CASE1 x
    >>

    > ...
    >> case CHAR('
    >> , CASE1, '
    >> ): break;

    >
    > Nice try, but no cigar. The ## operator requires that the result be a
    > valid pp-token and, depending on the order in which the operators are
    > evaluated, you either end up with 'x or x', neither of which are valid
    > pp-tokens.


    So if we are concatenating 3 pp-tokens, not only do the individual items (a,b,c)
    and the final concatenation need to be valid pp-tokens, but also all potential
    _intermediates_, as determined by the unspecified processor order. That seems a
    needless and complicating restriction.

    If I understand correctly, CHAR(<,<,=) will work because both << and <= are
    valid pp-tokens, but CHAR(., ., .) will not because .. isn't.

    Finally the result of CHAR(%:, %, :) is undefined/unspecified, since order of
    concatenation determines whether the intermediate is %:% ## : or %: ## %: where
    %:% is not a pp-token. I noticed that CHAR(C,3,_) is only required to work
    because 3_ is a valid pp-number!

    It seems that the rule could be replaced by a greedy algorithm that concatenates
    all ##-connected pp-tokens before requiring a pp-token result. As far as I can
    see such replacement would have no incompatibility with the current standard and
    would add little additional wording and processing. It would simplify the
    concept by not relying on happenstance intermediates as noted above.

    Thad
    Thad Smith, Nov 8, 2011
    #10
  11. Andrey Vul

    Guest

    James Kuyper <> wrote:
    >
    > I hadn't considered that issue, but taking a look at 6.10.3.3p3, I found
    > that it says "each each instance of a ## preprocessing token
    > in the replacement list (not from an argument) is deleted and the
    > preceding preprocessing token is concatenated with the following
    > preprocessing token. ... If the result is not a valid preprocessing
    > token, the behavior is undefined." It seems to me ambiguous whether "the
    > result" it refers to is the result of each concatenation, or the
    > combined result of all of the concatenations.


    I think it's pretty clear from context that it's talking about each
    replacement individually. For a token-based preprocessor, you either
    need each result to be a token or you need the concept of executing a
    bunch of operators in parallel (which we don't have in C).
    --
    Larry Jones

    Hey! What's the matter? Can't you take a joke?! It was a JOKE! -- Calvin
    , Nov 8, 2011
    #11
  12. On Nov 7, 11:53 pm, wrote:
    > James Kuyper <> wrote:
    > > #include <stdlib.h>
    > > #define TRICAT(a, b, c) a ## b ## c
    > > #define CHAR(a, b, c) TRICAT(a, b, c)

    >
    > > #define CASE1 x

    >
    > ...
    > >     case CHAR('
    > >         , CASE1, '
    > >         ): break;

    >
    > Nice try, but no cigar.  The ## operator requires that the result be a
    > valid pp-token and, depending on the order in which the operators are
    > evaluated, you either end up with 'x or x', neither of which are valid
    > pp-tokens.


    Even if ## were redefined so that intermediate results of a ## b ## c
    are ignored, the behaviour is explicitly undefined.

    6.4:
    preprocessing-token:
    [...]
    each non-white-space character that cannot be one of the above

    6.4p3:
    If a ' or a " character matches the last category, the behavior is
    undefined.
    Harald van Dijk, Nov 8, 2011
    #12
  13. Andrey Vul

    James Kuyper Guest

    On 11/08/2011 12:01 PM, Harald van Dijk wrote:
    > On Nov 7, 11:53 pm, wrote:
    >> James Kuyper <> wrote:
    >>> #include <stdlib.h>
    >>> #define TRICAT(a, b, c) a ## b ## c
    >>> #define CHAR(a, b, c) TRICAT(a, b, c)

    >>
    >>> #define CASE1 x

    >>
    >> ...
    >>> case CHAR('
    >>> , CASE1, '
    >>> ): break;

    >>
    >> Nice try, but no cigar. The ## operator requires that the result be a
    >> valid pp-token and, depending on the order in which the operators are
    >> evaluated, you either end up with 'x or x', neither of which are valid
    >> pp-tokens.

    >
    > Even if ## were redefined so that intermediate results of a ## b ## c
    > are ignored, the behaviour is explicitly undefined.
    >
    > 6.4:
    > preprocessing-token:
    > [...]
    > each non-white-space character that cannot be one of the above
    >
    > 6.4p3:
    > If a ' or a " character matches the last category, the behavior is
    > undefined.


    Yes, I forgot about that item, too.
    James Kuyper, Nov 8, 2011
    #13
    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. Alison Bowes
    Replies:
    0
    Views:
    462
    Alison Bowes
    Feb 18, 2005
  2. Erlend Andreas Garberg

    Travelling salesman variation in python

    Erlend Andreas Garberg, Apr 1, 2004, in forum: Python
    Replies:
    20
    Views:
    4,149
    Günter Jantzen
    Apr 2, 2004
  3. j0mbolar

    opinions on logical OR variation

    j0mbolar, Sep 28, 2004, in forum: C Programming
    Replies:
    3
    Views:
    297
    Tim Rentsch
    Sep 29, 2004
  4. yanyo

    standard variation

    yanyo, Oct 6, 2005, in forum: C Programming
    Replies:
    1
    Views:
    940
    Alexei A. Frounze
    Oct 6, 2005
  5. Replies:
    0
    Views:
    1,227
Loading...

Share This Page