Nested _Generic selections

Discussion in 'C Programming' started by Joel C. Salomon, Jan 13, 2012.

  1. I'm attempting to use C11's _Generic selection expressions to enforce a
    kind of type consistency, and it's failing in an odd way.

    [The _Generic keyword is described in <http://bit.ly/c11-n1404+> and
    <http://bit.ly/c11-n1441+> by Intel's Clark Nelson, whom I have CC'd on
    this message. Clark, can I ask you to respond on <comp.lang.c>?]

    Motivation: In the revised IEEE 754 (2008, aka IEC 60559:2011) there is
    the function totalOrder(x, y) which is meaningful for each
    floating-point type, but meaningless for mixed types. I was wondering
    whether this was a candidate for _Generic selection: not simply for
    convenience, but for type safety as well.

    Consider a simpler case, the functions funcf() & funcd which I want to
    wrap in a macro func(x, y) so that
    * func(1.0f, 2.0f) calls funcf(),
    * func(1.0, 2.0) calls funcd(), and
    * func(1.0f, 2.0) & func(1.0, 2.0f) are invalid.

    My first effort looked like this:

    #include <stdio.h>
    void funcf(float x, float y) {
    printf("floats %f, %f\n", x, y);
    }
    void funcd(double x, double y) {
    printf("doubles %f, %f\n", x, y);
    }
    #define func(x, y) _Generic((x), \
    float: _Generic((y), float: funcf), \
    double: _Generic((y), double: funcd) ) (x, y)
    int main(void) {
    func(1.0f, 2.0f); // should work, but doesn't
    // func(1.0f, 2.0); // shouldn't work
    // func(1.0, 2.0f); // shouldn't work
    func(1.0, 2.0); // should work, but doesn't
    }

    I used LLVM Clang version 3.0, which complained about the lines which
    I'd intended to have work:

    gen.c:12:13: error: controlling expression type 'float' not compatible
    with any generic association type
    func(1.0f, 2.0f);
    ~~~~~~~~~~~^~~~~
    gen.c:10:20: note: expanded from:
    double: _Generic((y), double: funcd) ) (x, y)
    ^
    [And a similar error for the `func(1.0, 2.0)` line.]

    First question: It seems that all "legs" of the selection expression
    are evaluated; is that correct behavior?

    Second question: Assuming that is correct, what might be the best way
    to accomplish what I'm trying to? The best method I've come up with is
    this:

    #define func(x, y) _Generic((x), \
    float: _Generic((y), float: funcf, double: 0 ), \
    double: _Generic((y), double: funcd, float: 0 ) ) (x, y)
    int main(void) {
    func(1.0f, 2.0f);
    func(1.0f, 2.0);
    func(1.0, 2.0f);
    func(1.0, 2.0);
    }

    This "works"; the correct forms compile and the incorrect ones cause
    compile errors -- but they obscure the issue:

    gen.c:13:2: error: called object type 'int' is not a function or
    function pointer
    func(1.0f, 2.0);
    ^~~~~~~~~~~~~~~
    gen.c:10:53: note: expanded from:
    double: _Generic((y), double: funcd, float: 0 ) ) (x, y)
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ^

    Can this be improved, while working within what the Standard provides?
    Joel C. Salomon, Jan 13, 2012
    #1
    1. Advertising

  2. Joel C. Salomon

    Jens Gustedt Guest

    Hello Joel,

    Am 01/13/2012 03:27 AM, schrieb Joel C. Salomon:
    > I'm attempting to use C11's _Generic selection expressions to enforce a
    > kind of type consistency, and it's failing in an odd way.
    > ...
    >
    > First question: It seems that all "legs" of the selection expression
    > are evaluated; is that correct behavior?


    Yes, I think that it is correct. It is not the only possible behavior,
    the standard is unspecific about this. So for your coding you are in
    any case better off in assuming the restricted version.

    > Second question: Assuming that is correct, what might be the best way
    > to accomplish what I'm trying to?
    > ...
    > Can this be improved, while working within what the Standard provides?


    perhaps something like

    #define func(x, y) _Generic((x),
    \
    float: _Generic((y), float: funcf, double: _Static_assert(0, "float
    and double")), \
    double: _Generic((y), double: funcd, float: _Static_assert(0, "double
    and float"))) (x, y)

    would do what you want.

    Jens
    Jens Gustedt, Jan 13, 2012
    #2
    1. Advertising

  3. On 01/13/2012 01:03 PM, Jens Gustedt wrote:
    > perhaps something like
    >
    > #define func(x, y) _Generic((x), \
    > float: _Generic((y), float: funcf, double: _Static_assert(0, "float and double")), \
    > double: _Generic((y), double: funcd, float: _Static_assert(0, "double and float"))) (x, y)
    >
    > would do what you want.


    Trouble is that _Static_assert is not an expression.

    Before you suggest it, `sizeof(int[-1])` won't work either, because of
    the "evaluate everything" behavior.

    The best I've come up with so far is

    #define func(x, y) _Generic((x), \
    float: _Generic((y), float: funcf, double: "** float and double **"), \
    double: _Generic((y), double: funcd, float: "** double and float **")
    ) (x, y)

    -- which is still buried in the error message.

    --Joel
    Joel C. Salomon, Jan 13, 2012
    #3
  4. Joel C. Salomon

    Jens Gustedt Guest

    Am 01/13/2012 07:03 PM, schrieb Jens Gustedt:
    > perhaps something like
    >
    > #define func(x, y) _Generic((x),
    > \
    > float: _Generic((y), float: funcf, double: _Static_assert(0, "float
    > and double")), \
    > double: _Generic((y), double: funcd, float: _Static_assert(0, "double
    > and float"))) (x, y)
    >
    > would do what you want.


    I notice now, that this doesn't work since _Static_assert is a
    statement, which is not allowed in that place.

    Something like that might give a bit better diagnostic:

    float funcf(float, float);
    void funcf_2nd_arg_error(void);
    double funcd(double, double);
    void funcd_2nd_arg_error(void);

    #define check(x, y) \
    (*(_Generic((x), float: (float*){ 0 }, double: (double*){ 0 }) \
    = &_Generic((y), float: (float){ y }, double: (double){ y })))

    #define func(x, y) \
    _Generic((x), \
    float: _Generic((y), float: funcf, double:
    funcf_2nd_arg_error), \
    double: _Generic((y), double: funcd, float:
    funcd_2nd_arg_error) \
    )(x, check(x, y))


    Jens
    Jens Gustedt, Jan 13, 2012
    #4
  5. Joel C. Salomon

    ImpalerCore Guest

    On Jan 12, 9:27 pm, "Joel C. Salomon" <> wrote:
    > I'm attempting to use C11's _Generic selection expressions to enforce a
    > kind of type consistency, and it's failing in an odd way.
    >
    > [The _Generic keyword is described in <http://bit.ly/c11-n1404+> and
    > <http://bit.ly/c11-n1441+> by Intel's Clark Nelson, whom I have CC'd on
    > this message.  Clark, can I ask you to respond on <comp.lang.c>?]
    >
    > Motivation:  In the revised IEEE 754 (2008, aka IEC 60559:2011) there is
    > the function totalOrder(x, y) which is meaningful for each
    > floating-point type, but meaningless for mixed types.  I was wondering
    > whether this was a candidate for _Generic selection: not simply for
    > convenience, but for type safety as well.
    >
    > Consider a simpler case, the functions funcf() & funcd which I want to
    > wrap in a macro func(x, y) so that
    > * func(1.0f, 2.0f) calls funcf(),
    > * func(1.0, 2.0) calls funcd(), and
    > * func(1.0f, 2.0) & func(1.0, 2.0f) are invalid.
    >
    > My first effort looked like this:
    >
    > #include <stdio.h>
    > void funcf(float x, float y) {
    >         printf("floats %f, %f\n", x, y);}
    >
    > void funcd(double x, double y) {
    >         printf("doubles %f, %f\n", x, y);}
    >
    > #define func(x, y) _Generic((x), \
    >         float:  _Generic((y), float:  funcf), \
    >         double: _Generic((y), double: funcd) ) (x, y)
    > int main(void) {
    >         func(1.0f, 2.0f);       // should work, but doesn't
    > //      func(1.0f, 2.0);        // shouldn't work
    > //      func(1.0,  2.0f);       // shouldn't work
    >         func(1.0,  2.0);        // should work, but doesn't
    >
    > }
    >
    > I used LLVM Clang version 3.0, which complained about the lines which
    > I'd intended to have work:
    >
    > gen.c:12:13: error: controlling expression type 'float' not compatible
    > with any generic association type
    >         func(1.0f, 2.0f);
    >         ~~~~~~~~~~~^~~~~
    > gen.c:10:20: note: expanded from:
    >         double: _Generic((y), double: funcd) ) (x, y)
    >                           ^
    > [And a similar error for the `func(1.0,  2.0)` line.]
    >
    > First question:  It seems that all "legs" of the selection expression
    > are evaluated; is that correct behavior?
    >
    > Second question:  Assuming that is correct, what might be the best way
    > to accomplish what I'm trying to?  The best method I've come up with is
    > this:
    >
    > #define func(x, y) _Generic((x), \
    >         float:  _Generic((y), float:  funcf, double: 0 ), \
    >         double: _Generic((y), double: funcd, float:  0 ) ) (x, y)
    > int main(void) {
    >         func(1.0f, 2.0f);
    >         func(1.0f, 2.0);
    >         func(1.0,  2.0f);
    >         func(1.0,  2.0);
    >
    > }
    >
    > This "works"; the correct forms compile and the incorrect ones cause
    > compile errors -- but they obscure the issue:
    >
    > gen.c:13:2: error: called object type 'int' is not a function or
    > function pointer
    >         func(1.0f, 2.0);
    >         ^~~~~~~~~~~~~~~
    > gen.c:10:53: note: expanded from:
    >         double: _Generic((y), double: funcd, float:  0 ) ) (x, y)
    >         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ^
    >
    > Can this be improved, while working within what the Standard provides?


    Well, if you're willing to party like it's 1999, you can look at this
    alternative.

    \code
    #include <stdio.h>

    #define STATIC_ASSERT(name, expr) extern char (name)[(expr) ? 1 : -1]

    void func_float( float x, float y )
    {
    printf( "floats %f, %f\n", x, y );
    }

    void func_double( double x, double y )
    {
    printf( "doubles %f, %f\n", x, y );
    }

    #define func(x, y, type) \
    do \
    { \
    STATIC_ASSERT( func_type_mismatch, sizeof (x) == sizeof (y) ); \
    func_##type( (x), (y) ); \
    } while (0)

    int main( void )
    {
    func( 1.0f, 2.0f, float );
    //func( 3.0f, 4.0, float );
    //func( 5.0, 6.0f, double );
    func( 7.0, 8.0, double );

    return 0;
    }
    \endcode

    If I uncomment one of those lines out, I get an error message.

    test.c: In function 'main':
    test.c:25:3: error: size of array 'func_type_mismatch' is negative

    Of course, 'sizeof' will not detect differences between types that
    happen to have the same size. If there was a 'typeid' operator that
    the compiler could use to assign a unique integer identifier for each
    unique type, the static assert could wrangle out any type differences
    for types that have the same size.

    You may disapprove of the extra manual 'type' argument in the macro.
    If there was a 'typeof' operator that could determine the type of an
    variable, i.e. typeof (2.0f) --> float, you might not need '_Generic'
    at all.

    \code idea
    void func_float( float x, float y );
    void func_double( double x, double y );

    #define func(x, y) g_func( (x), (y), typeof (x) )
    #define g_func(x, y, type) \
    do \
    { \
    STATIC_ASSERT( func_type_mismatch, typeid (x) == typeid (y) ); \
    func_##type( (x), (y) ); \
    } while (0)
    \endcode

    Just some ideas I've been pondering.

    Best regards,
    John D.
    ImpalerCore, Jan 13, 2012
    #5
  6. Joel C. Salomon

    Jens Gustedt Guest

    Am 01/13/2012 09:23 PM, schrieb ImpalerCore:
    > On Jan 12, 9:27 pm, "Joel C. Salomon" <> wrote:
    >> I'm attempting to use C11's _Generic selection expressions to enforce a
    >> kind of type consistency, and it's failing in an odd way.

    > Well, if you're willing to party like it's 1999, you can look at this
    > alternative.
    >
    > \code
    > #include <stdio.h>
    >
    > #define STATIC_ASSERT(name, expr) extern char (name)[(expr) ? 1 : -1]


    The problem is as Joel stated in another post that _Static_Assert and
    Co are not expressions but statements.

    Your Idea of putting things inside a do { ... } while only works for
    a functionlike macro that leads to a void expression. If you want the
    beast to return a value i.e the end result of the macro to be an
    expression and not only a statement, that doesn't work.

    Jens
    Jens Gustedt, Jan 13, 2012
    #6
  7. Joel C. Salomon

    Shao Miller Guest

    On 1/13/2012 15:38, Jens Gustedt wrote:
    > Am 01/13/2012 09:23 PM, schrieb ImpalerCore:
    >> On Jan 12, 9:27 pm, "Joel C. Salomon"<> wrote:
    >>> I'm attempting to use C11's _Generic selection expressions to enforce a
    >>> kind of type consistency, and it's failing in an odd way.

    >> Well, if you're willing to party like it's 1999, you can look at this
    >> alternative.
    >>
    >> \code
    >> #include<stdio.h>
    >>
    >> #define STATIC_ASSERT(name, expr) extern char (name)[(expr) ? 1 : -1]

    >
    > The problem is as Joel stated in another post that _Static_Assert and
    > Co are not expressions but statements.
    >


    Party like it's C89:

    #define STATIC_ASSERT(name_, expr_) \
    (sizeof (struct { char (name_)[(expr_) ? 1 : -1]; }))

    int main(void) {
    int x;

    x = STATIC_ASSERT(is_true, 1);
    x = STATIC_ASSERT(is_false, 0);
    return 0;
    }
    Shao Miller, Jan 15, 2012
    #7
  8. Joel C. Salomon

    Shao Miller Guest

    On 1/12/2012 21:27, Joel C. Salomon wrote:
    > I'm attempting to use C11's _Generic selection expressions to enforce a
    > kind of type consistency, and it's failing in an odd way.
    >
    > [...]
    >
    > This "works"; the correct forms compile and the incorrect ones cause
    > compile errors -- but they obscure the issue:
    >
    > gen.c:13:2: error: called object type 'int' is not a function or
    > function pointer
    > func(1.0f, 2.0);
    > ^~~~~~~~~~~~~~~
    > gen.c:10:53: note: expanded from:
    > double: _Generic((y), double: funcd, float: 0 ) ) (x, y)
    > ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ^
    >
    > Can this be improved, while working within what the Standard provides?


    What about:

    #define M_STATIC_ASSERT(name_, expr_) \
    (sizeof (struct { char (name_)[(expr_) ? 1 : -1]; }))

    #define M_IS_FLOAT(x_) \
    (_Generic((x_), float: 1, default: 0))

    #define M_BOTH_FLOAT(x_, y_) \
    (M_IS_FLOAT(x_) && M_IS_FLOAT(y_))

    #define M_IS_DOUBLE(x_) \
    (_Generic((x_), double: 1, default: 0))

    #define M_BOTH_DOUBLE(x_, y_) \
    (M_IS_DOUBLE(x_) && M_IS_DOUBLE(y_))

    #define M_BOTH_FLOAT_OR_BOTH_DOUBLE(x_, y_) \
    (M_BOTH_FLOAT((x_), (y_)) || M_BOTH_DOUBLE((x_), (y_)))

    static int funcf(float x, float y) {
    (void) x;
    (void) y;
    return 0;
    }

    static int funcd(double x, double y) {
    (void) x;
    (void) y;
    return 0;
    }

    #define func(x_, y_) ( \
    (void) M_STATIC_ASSERT( \
    func_args_not_both_float_nor_both_double, \
    M_BOTH_FLOAT_OR_BOTH_DOUBLE((x_), (y_)) \
    ), \
    _Generic( \
    (x_), \
    float: funcf, \
    double: funcd, \
    default: 0 \
    )((x_), (y_)) \
    )

    int main(void) {
    int x;

    x = func(1.0f, 2.0f);
    x = func(1.0f, 2.0);
    x = func(1.0, 2.0f);
    x = func(1.0, 2.0);
    return 0;
    }

    - Shao Miller
    Shao Miller, Jan 15, 2012
    #8
  9. On 01/15/2012 03:32 PM, Shao Miller wrote:
    > What about:
    >
    > #define M_STATIC_ASSERT(name_, expr_) \
    > (sizeof (struct { char (name_)[(expr_) ? 1 : -1]; }))
    >
    > #define M_IS_FLOAT(x_) \
    > (_Generic((x_), float: 1, default: 0))
    >
    > #define M_BOTH_FLOAT(x_, y_) \
    > (M_IS_FLOAT(x_) && M_IS_FLOAT(y_))
    >
    > #define M_IS_DOUBLE(x_) \
    > (_Generic((x_), double: 1, default: 0))
    >
    > #define M_BOTH_DOUBLE(x_, y_) \
    > (M_IS_DOUBLE(x_) && M_IS_DOUBLE(y_))
    >
    > #define M_BOTH_FLOAT_OR_BOTH_DOUBLE(x_, y_) \
    > (M_BOTH_FLOAT((x_), (y_)) || M_BOTH_DOUBLE((x_), (y_)))
    >
    > #define func(x_, y_) ( \
    > (void) M_STATIC_ASSERT( \
    > func_args_not_both_float_nor_both_double, \
    > M_BOTH_FLOAT_OR_BOTH_DOUBLE((x_), (y_)) \
    > ), \
    > _Generic( \
    > (x_), \
    > float: funcf, \
    > double: funcd, \
    > default: 0 \
    > )((x_), (y_)) \
    > )


    That works; thanks!

    I particularly like this technique:

    #define M_IS_DOUBLE(x_) \
    (_Generic((x_), double: 1, default: 0))

    I think I can see a way to make more general use of this.

    --Joel
    Joel C. Salomon, Jan 16, 2012
    #9
  10. Joel C. Salomon

    Shao Miller Guest

    On 1/16/2012 12:08, Joel C. Salomon wrote:
    > On 01/15/2012 03:32 PM, Shao Miller wrote:
    >> What about:
    >>
    >> [...code...]
    >>

    > That works; thanks!
    >
    > I particularly like this technique:
    >
    > #define M_IS_DOUBLE(x_) \
    > (_Generic((x_), double: 1, default: 0))
    >
    > I think I can see a way to make more general use of this.


    Are you thinking of something like this?:

    #define M_STATIC_ASSERT(name_, expr_) \
    (sizeof (struct { char (name_)[(expr_) ? 1 : -1]; }))

    #define M_OBJ_IS_TYPE(obj_, type_) \
    (_Generic((obj_), type_: 1, default: 0))

    #define M_BOTH_ARE_TYPE(x_, y_, type_) \
    (M_OBJ_IS_TYPE((x_), type_) && M_OBJ_IS_TYPE((y_), type_))

    #define M_REMOVE_PARENS2_OR_MORE(arg_, ...) arg_, __VA_ARGS__

    #define M_TYPE_MATCH2_CASE_(type_, m_) \
    (M_REMOVE_PARENS2_OR_MORE m_, type_)

    #define M_TYPE_MATCH2_CASE(type_, case_, m_) \
    (M_BOTH_ARE_TYPE M_TYPE_MATCH2_CASE_(type_, m_)) ||

    #define M_TYPE_MATCH2(cases_, x_, y_) ( \
    cases_(M_TYPE_MATCH2_CASE, ((x_), (y_))) \
    0 \
    )

    #define M_TYPE_SELECTION_CASE(type_, case_, m_) \
    type_: (case_),

    #define M_TYPE_SELECTION(subject_, cases_) ( \
    _Generic( \
    (subject_), \
    cases_(M_TYPE_SELECTION_CASE, 0) \
    default: 0 \
    ) \
    )

    #define M_MAKE_TYPEPAIR_FUNC(func_, x_, y_) ( \
    (void) M_STATIC_ASSERT( \
    func_args_not_same_type_or_unsupported_types, \
    M_TYPE_MATCH2(func_, (x_), (y_)) \
    ), \
    M_TYPE_SELECTION((x_), func_) \
    )

    static int myfuncf(float x, float y) {
    (void) x;
    (void) y;
    return 0;
    }

    static int myfuncd(double x, double y) {
    (void) x;
    (void) y;
    return 0;
    }

    static int myfunci(int x, int y) {
    (void) x;
    (void) y;
    return 0;
    }

    #define myfunc_(unit_, m_) \
    unit_(float, myfuncf, m_) \
    unit_(double, myfuncd, m_) \
    unit_(int, myfunci, m_)

    #define myfunc(x_, y_) \
    (M_MAKE_TYPEPAIR_FUNC(myfunc_, (x_), (y_))((x_), (y_)))

    int main(void) {
    int x;

    x = myfunc(1.0f, 2.0f);
    x = myfunc(1.0f, 2.0);
    x = myfunc(1.0, 2.0f);
    x = myfunc(1.0, 2.0);
    x = myfunc(13, 42);
    x = myfunc(13.3, 42);
    return 0;
    }

    - Shao Miller
    Shao Miller, Jan 17, 2012
    #10
    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. brad
    Replies:
    2
    Views:
    401
  2. Joel C. Salomon

    Silly _Generic trick: const_cast(T, exp)

    Joel C. Salomon, May 20, 2011, in forum: C Programming
    Replies:
    7
    Views:
    840
    Keith Thompson
    May 21, 2011
  3. Jens Gustedt

    Re: Thoughts on the _Generic feature?

    Jens Gustedt, Oct 6, 2012, in forum: C Programming
    Replies:
    0
    Views:
    328
    Jens Gustedt
    Oct 6, 2012
  4. Prathamesh Kulkarni
    Replies:
    8
    Views:
    150
    Keith Thompson
    Dec 10, 2013
  5. David Librik

    Does C11's _Generic further complicate parsing?

    David Librik, May 25, 2014, in forum: C Programming
    Replies:
    1
    Views:
    34
    Ben Bacarisse
    May 25, 2014
Loading...

Share This Page