Discussion in 'C Programming' started by Jorgen Grahn, Mar 1, 2014.

  1. Jorgen Grahn

    Ian Collins Guest

    David was one of many who couldn't imagine any "perfectly reasonable
    local uses" and requested an example. I'm adding my name to the list of
    those lacking imagination! Please enlighten us.
    Ian Collins, Mar 10, 2014
    1. Advertisements

  2. #include "fantasticllycomplexexternallib.h"
    /* Note: don't touch this library, it's cleverer that you are !!!"

    int foo(char *program)
    int err;
    bool halts;

    err = solvehaltingproblem(program, &halts);
    return -1;
    else return halts ? 1 : 0;

    Malcolm McLean, Mar 10, 2014
    1. Advertisements

  3. Jorgen Grahn

    David Brown Guest

    The trouble is, all-caps is at best a vague suggestion that the
    "function" is likely to be a macro. It is not a guarantee - in
    particular, just because an identifier does not have all-caps does not
    mean that it is a function and not a macro. So using all-caps for the
    macros does not actually benefit the user significantly, but it still
    has a cost (in terms of ugly code, unwanted emphasis, and difficulty in
    I agree on that. When working on other people's code, or working with
    other people, it is sometimes necessary to compromise on what you know
    yourself to be good style - consistency, local conventions, and
    cooperation can trump the rules you prefer when you have complete
    control. And for this reason I will occasionally use all-caps for
    macros - but it is not by choice.
    I haven't made use of it myself, but I believe _Generic can be nested to
    handle the types of more than one parameter. And I believe (again, this
    is untested) that you could make a limited gcc "typeof" operator using

    #define integer_typeof(x) _Generic((x), \
    _Bool : _Bool, unsigned char : unsigned char, \
    signed char : signed char, short : short, \
    unsigned short : unsigned short, int : int, \
    unsigned int : unsigned int, long : long, \
    unsigned long : unsigned long, long long : long long, \
    unsigned long long : unsigned long long, \
    default : int)

    Still, it's going to be a bit messy - all right for a library header,
    but not something you'd want to write in the application code.

    That's kind of my point. Functions that used to be implemented as
    macros are now often made as static inline functions (with the exception
    of type-generic "max" functions, and similar). Only the implementation
    has changed, not the use - it makes no sense to change the naming
    convention because of the implementation style. Ergo, function-like
    macros should have been named like functions. Similarly for constant
    Yes - and these limitations are quite annoying. In particular (in my
    code at least), it is a pain that you cannot use a "static const int"
    for the size of an array - even though the compiler knows it as a
    compile-time constant and optimises its use just as if it were a literal
    I'll certainly agree that if I see the name in all-caps, I'll assume it
    is constant - but if I see it in small letters and it is relevant to
    know if it is constant or variable (which is not something that happens
    often), I would check its definition.
    David Brown, Mar 11, 2014
  4. Jorgen Grahn

    David Brown Guest

    I agree that different developers have different opinions here - I am
    just offering mine. And it is a little relevant, in that you are
    suggesting using TRUE and FALSE because you think "true" and "false"
    look like they could be variable.
    One can keep habits from old systems after new ones are introduced -
    such as having a habit of writing some things in all-caps even after you
    are able to use small letters. However, it is a very minor issue - I
    mean to say that while all-caps macro names might have made sense long
    ago, I do not like it in modern programming.
    I think it is pretty clear that when I express an opinion, it is my
    opinion - unless you think I have some sort of official status as a
    representative for many other programmers. And yes, I fully understand
    that opinions vary one topics like this - I am giving you /my/ opinion
    and some justification for it. Feel free to ignore it, or give your own
    opinions. I certainly value hearing about other ideas, or other
    justifications for existing ideas, even if I don't agree with them myself.
    The standard always has to consider compatibility with old code, or with
    odd code. <stdbool.h> is optional, and has undefineable macros, in
    order to let people work with old code that defines its own bool type
    and constants. But it /allows/ this sort of thing - it certainly does
    not /recommend/ or /suggest it/. In fact, C11 explicitly says "The
    ability to undefine and perhaps then redefine the macros bool, true, and
    false is an obsolescent feature" in the section on "Future library

    What I take from this is that you can mess around with your own
    definitions of bool, true and false if you have to - but you should not
    do so when working on modern and future-proof code.

    Tell that to people who have to maintain code with TRUE (or "true")
    defined to 0 or -1, or other such joys. People /have/ written such
    code, and telling them that it's a good idea to invent your own versions
    of standard types and standard identifiers is a bad idea. The standard
    libraries and standard headers are there for a reason, and they should
    be used unless you have a very particular reason to do something else
    (such as for working with old code).
    I understand what you are saying here. But what we are missing is any
    good reasons as to when one would want to use the definitions you
    suggest rather than <stdbool.h>. There are occasions why one might need
    an odd definition (such as for weird code that defines TRUE to be 0),
    but I fail to see any serious advantage of your definition over the
    standard one. It's not that I think there is anything /wrong/ with your
    enum type - it is just that I see no reason to use it rather than the
    standard one.

    You /are/ responsible in this case for telling us that there are
    "perfectly reasonable" cases where the <stdbool.h> macros would cause
    conflicts but your typedef enum version would work, and for telling us
    that there are cases where the enum version is significantly better than
    the <stdbool.h> version. So either I - and several other posters here -
    am lacking imagination, or you are making up justification for your
    preferred implementation of bool. Of course, it is absolutely fine for
    you to prefer your own implementation - but I would very much like to
    hear something more concrete about uses where it makes a real
    difference. Unfortunately, with every post in which you fail to give
    examples, you re-enforce the thought that you don't have any good
    examples. Please prove me wrong here!
    David Brown, Mar 11, 2014
  5. By that reasoning, calling a function "print_foo" is at best a
    vague suggestion that it prints a foo. But unless the author is
    deliberately perverse, the name provides extremely useful information
    about what the function does.

    I, and a lot of other programmers, find the convention of using
    all-caps names for macros, useful. It's not pretty, but then this is C
    we're talking about. :cool:}

    Perhaps -- but I think the size of the code would be proportional to the
    square of the number of types it handles, and it couldn't be portably
    written to apply to extended integer types. Perhaps it could be
    generated automatically.
    You'd want plain char as well (which is distinct from both unsigned char
    and signed char).
    I'm certainly not arguing that there are no problems with the
    convention. In my opinion, the benefits outweigh the problems.
    I'm not a big fan of ALL-CAPS in general -- I don't even have
    a working caps-lock key on my keyboard -- but for macro names it
    doesn't particularly bother me for some reason. If I found all-caps
    particularly jarring, I suspect I'd agree with you.

    Keith Thompson, Mar 11, 2014
  6. In a generic expression, what follows the ':' must itself be an
    expression (specifically the syntactic production 'assignment-
    expression') so I don't see how this can work.

    Ben Bacarisse, Mar 11, 2014
  7. The trivial functions you need

    uniform(); - return a uniform random number on 0 - 1-epsilon.
    min / maxN - minimum/maximium of 2, 3, 4 and so on numbers.
    lerp(a, b, t) - linear interpolation between a and b on t = 0-1.
    clamp(x, low, high) - force value into range low - high.
    sign(x) - 0 1 or -1

    Generally it makes sense to implement as macros.
    Malcolm McLean, Mar 11, 2014
  8. Jorgen Grahn

    David Brown Guest

    I see your point, but I don't quite agree with it here. It is true that
    an all-caps "function" will be a macro, assuming a reasonable author -
    but it is perfectly reasonable for a non-caps "function" to be a macro.
    Indeed, the standard headers are full of macros that are not in
    all-caps. And it is the knowledge that you are dealing with a real
    function, and not a function-like macro, that lets you (safely) have
    side effects in your arguments - information that you cannot get from
    the name alone.

    One compromise could be to use all-caps for macros which re-use their
    parameters, and are therefore unsafe for side-effects. Thus:

    #define MAX(x, y) ( (x) >= (y) ? (x) : (y) )
    #define sum(x, y) ((x) + (y))

    That would give you the extra information you want from all-caps when
    you need it, while keeping code neater when you don't need it.
    I put the balance point of ugliness vs. usefulness at a different point
    in the case of all-caps macros. But I understand other people see it
    differently - maybe I am just lucky in that because of the way I work, I
    can choose to avoid all-caps macro names.
    Extended integer types are non-portable anyway.

    But I think you are right that the size of source code would be O(n^2) -
    although the generated code should be optimal.
    Maybe, although I don't think it would make a difference here. It
    depends on what you want to do with such a "typeof" macro - and on how
    _Generic handles chars. For a max() macro, I think it would be
    sufficient to handle "int" and above - integer promotion would mean that
    a max(char, char) would be handled like max(int, int) anyway.
    And if I found all-caps particularly useful, then I'd agree with you :)

    It's just a matter of where we draw the line, and that's partly personal
    opinion, and partly the type of code we deal with (for most of my
    programming, I am the only one who works with the code - and that makes
    a huge difference to how much I can make the rules, and how much I
    follow other people's rules or conventions).
    David Brown, Mar 12, 2014
  9. Jorgen Grahn

    David Brown Guest

    I didn't know that rule - I thought _Generic was more like macro
    expansion. Of course that changes things, and means that such a
    typeof() _Generic could not work. Thanks for point it out.
    David Brown, Mar 12, 2014
  10. It's just a form of expression so as far as the abstract machine goes
    it's a run-time construct. Of course, since the choice is based on
    information that is available at compile time, no sane implementation
    will leave any trace of it in the executable. In that sense it *is*
    like macro expansion.

    Ben Bacarisse, Mar 12, 2014
  11. Jorgen Grahn

    James Kuyper Guest

    But code that is portable can make use of extended integer types, if
    they come into play through standard typedefs such as size_t, int64_t,
    sig_atomic_t, etc. Dealing with this possibility using _Generic() is
    problematic, because "No two generic associations in the same generic
    selection shall specify compatible types.".
    James Kuyper, Mar 12, 2014
  12. _Generic uses the unpromoted type of the controlling expression;
    promotion happens only for operators that explicitly say their
    operands are promoted. For example:

    #include <stdio.h>
    int main(void) {
    char c;
    puts(_Generic(c, char: "char",
    signed char: "signed char",
    unsigned char: "unsigned char",
    int: "int",
    default: "default"));

    The output is "char" (using clang 3.4).
    Keith Thompson, Mar 12, 2014
  13. Based on the description in the C11 standard, I don't think it's a
    run-time construct even in the abstract machine.


    The controlling expression of a generic selection is not
    evaluated. If a generic selection has a generic association with
    a type name that is compatible with the type of the controlling
    expression, then the result expression of the generic selection
    is the expression in that generic association. Otherwise, the
    result expression of the generic selection is the expression in
    the default generic association. None of the expressions from any
    other generic association of the generic selection is evaluated.

    Note that this talks about the result *expression*, not the result
    Keith Thompson, Mar 12, 2014
  14. Ah, yes. I had thought that variably modified types would be permitted,
    but they are excluded explicitly in the preceding paragraph and would be
    useless anyway some the controlling expression is not evaluated.
    Ben Bacarisse, Mar 12, 2014

    Relevant? I think so.

    - Anand
    Anand Hariharan, Mar 13, 2014
  16. Jorgen Grahn

    Eric Sosman Guest

    I don't. The article is about C++ rather than C, and many of
    the drawbacks of the various approaches appear to be self-inflicted
    by the choice of language:

    - The "Option 1" problem doesn't even arise in C.

    - "Option 2" works the same way in C++ and C, but the only
    objection is pure opinion. The opinion may make sense for
    C++ (I "disavow any knowledge"), but doesn't seem especially
    telling in C terms.

    - "Option 3" apparently works better in C than in C++, due to
    a C++ restriction C doesn't suffer from.

    - "Option 4" is simply not in the cards for C, not at all.

    So: While the cases for and against a built-in boolean type
    in C may be plausibly argued, this article doesn't do so. It's
    about as relevant to C as a discussion of boolean types in COBOL.
    Eric Sosman, Mar 13, 2014
  17. Only partly. The linked article (by Herb Sutter) is about whether bool
    could have been emulated in C++ without addingit as a built-in type. In
    C, overloading is not an issue, and conditions are not converted to
    bool/_Bool anyway.

    C99's addition of _Bool does two things that couldn't have been done
    with a user-defined type: it standardizes a single Boolean type for all
    implementations, and it ensures conversions to _Bool can only yield
    false or true (0 or 1). The former could have been accomplished by
    adding <stdbool.h> with something like:

    typedef enum { false, true } bool;

    but the latter required a language change.
    Keith Thompson, Mar 13, 2014
  18. Jorgen Grahn

    David Brown Guest

    OK, that clause makes it quite a lot harder. Perhaps the best way then
    would be to include <stdint.h> and use types intN_t for _Generic -
    the different sizes (assuming the platform supports them, of course)
    will not be compatible with each other.

    Are different standard integer types "compatible" if they are the same
    size? i.e., if both "int" and "long int" are 32-bit, are they
    considered "compatible" for _Generic? I couldn't find a clear statement
    in the C11 standard.

    I guess _Generic will have its uses - but replacing gcc's very nice
    "typeof" operator is not one of them!
    David Brown, Mar 13, 2014
  19. Jorgen Grahn

    David Brown Guest

    Yes, I understand that - but the implementation of "max" is going to use
    a comparison operator which will lead to a promotion at that point.
    David Brown, Mar 13, 2014
  20. Jorgen Grahn

    James Kuyper Guest

    As a practical matter (I'll get to issue of what the standard says
    farther down), same size is insufficient to enable two types to be
    compatible. They must also have the same representation and alignment
    requirements, and must use the same mechanism when passed as a parameter
    and when returned as the value of a function.
    I've argued in the past that the standard guarantees compatibility for
    certain pairs of types (which do not include "int" and "long"), but that
    it does not prohibit other pairs of types from happening to be
    compatible on particular implementations. I was told that the cases
    where the standard specifies that two types are compatible are
    exhaustive - no other pairs of types can be compatible. This is an
    example of a more general rule when interpreting the standard: whenever
    it provides a list of things for which something is true, that list is
    exhaustive unless the standard explicitly says otherwise. I knew of that
    rule, but find that argument less than compelling, because the
    compatibility rules are not provided as a list, but as several
    independent clauses in widely separated parts of the standard.
    However, the official interpretation is that whenever a constraint
    requires that two types be compatible, it is a violation of that
    constraint, requiring a diagnostic, if the two types are not ones
    explicitly specified as being compatible by the standard.

    After issuing the required diagnostic, the implementation is free to
    produce an executable anyway, and if you chose to execute the resulting
    program it might behave in exactly the same fashion as would be
    mandatory if the standard had specified that those two types were
    compatible, and the implementation is free to document this as a fact.
    However, the diagnostic message is still the only mandatory result of
    translating such a program. The implementation's documentation cannot,
    therefore, accurately describe this fact by saying "int and long int are
    compatible"; it must use some other wording to describe that fact.
    James Kuyper, Mar 13, 2014
    1. Advertisements

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.