Syntax for union parameter

Discussion in 'C Programming' started by Rick C. Hodgin, Jan 29, 2014.

  1. Note: I tried to post this a few times. There were errors. I apologize
    if it is double- or multi-posted. I do not see it presently, which
    is why I am now re-posting it.

    Best regards,
    Rick C. Hodgin

    -----
    It doubles your workload. It introduces places where there can be
    errors / discrepancies. I ran into two last night. I had something
    defined as ** in the prototype, but as * in the function. It had
    been some time since I wrote the code so I had to figure out which was
    correct. It turned out * was correct, which was the function
    definition and the prototype in another file simply was not updated.
    And since I had not used that function yet, it was a silent error
    that cropped through.

    Had I defined it one place it would be right or wrong in one place.
    As it is with prototypes one needs a truth table:

    Prototype | Function
    --------------------
    Right | Right
    Right | Wrong
    Wrong | Right
    Wrong | Wrong

    If I have only the function definition, which serves also as the
    prototype it becomes much easier:

    Function
    --------
    Right
    Wrong

    Easy to track down. Easy to identify the cause. Errors are a constant,
    not a variable. You know things rather than are guessing at things.
    It helps the digestion, rather than causing it to become irritated.
    And so on.

    It's a no brainer, IMHO.

    Best regards,
    Rick C. Hodgin
     
    Rick C. Hodgin, Jan 29, 2014
    #61
    1. Advertisements

  2. Rick C. Hodgin

    Ian Collins Guest

    Then it wouldn't have compiled with a C compiler.
     
    Ian Collins, Jan 29, 2014
    #62
    1. Advertisements

  3. It did compile because I had not referenced the function previously. I
    had written it, and I had it available. However, until that function
    was used, the fact that it had a discrepancy between the forward prototype
    definition and the actual function declaration was of no consequence. No
    C compiler would catch that (at least not automatically, because having
    those discrepancies are not errors).

    However, when I tried to use the function as per its forward prototype
    declaration, it now generated a link-time error because it could not
    find the function with the **.

    Had I only used one declaration, the function declaration, as both the
    prototype and function declaration, then it would never have been an
    error.

    Best regards,
    Rick C. Hodgin
     
    Rick C. Hodgin, Jan 29, 2014
    #63
  4. Rick C. Hodgin

    James Kuyper Guest

    In C, as currently defined, they are both forward declarations and
    prototyped declarations. Forward declarations can be non-prototyped or
    prototyped. It might be better to choose either new terms not already
    defined with a different meaning, or current terms with exactly their
    current meaning.
     
    James Kuyper, Jan 29, 2014
    #64
  5. Rick C. Hodgin

    Seebs Guest

    I know a large number of religious zealots who are more willing to
    consider the possibility that they are wrong. So yes.

    -s
     
    Seebs, Jan 29, 2014
    #65
  6. I can't help how somebody else uses the word "prototype", other than to
    point out the correct definition of the word. The term is defined in
    C11 6.2.1:

    A *function prototype* is a declaration of a function that declares
    the types of its parameters.

    Prototypes are a subset of function declarations.

    A function definition provides a function declaration.

    The distinction I'm making is between function declarations that are
    prototypes and function declarations that are not prototypes. I'm
    suggesting that there is (almost) never any good reason to use the
    latter.
    You can sometimes avoid *separate* prototypes by ordering your function
    definitions, so that each function precedes any function that calls it.
    That's often more trouble than it's worth, and it doesn't work in the
    presence of mutually recursive calls.
    That can be avoided by putting prototypes in header files that can be
    shared by multiple translation units. There's still some duplication,
    but if "foo.c" defines a function foo(), "foo.h" declares the same
    function, and "foo.c" has a #include directive for "foo.h", then any
    inconsistency will be caught by the compiler.
    If you have function definitions (as any C program must), then those
    function definitions provide function declarations. I'm saying, among
    other things, that there is no good reason for those declarations not to
    be prototypes.
     
    Keith Thompson, Jan 29, 2014
    #66
  7. Rick C. Hodgin

    James Kuyper Guest

    On 01/29/2014 02:28 PM, Rick C. Hodgin wrote:
    ....
    I think you're confusing prototypes with separate declarations. Having a
    separate declaration from the one that forms part of your function
    definition does double your workload, if it's a prototyped declaration.

    Writing a prototype declaration for a function rather than a
    non-prototyped declaration for that same function requires very little,
    if any, extra work, if it's the declaration that appears at the top of
    the definition; the main part of the work is the list of parameter names
    and types, and those are present whether or not you use a prototype;
    it's just a slight difference is the syntax surrounding and separating
    that list.

    On the other hand, using a prototyped declaration rather than a
    non-prototyped declaration involves a lot more than twice as much work
    if it's a declaration that's separate from the definition, since the
    non-prototyped declaration doesn't need to have the argument type list.
    You should have #included the separate declaration into the same
    translation unit with the definition. If you had, the error would not
    have been silent.
    I'm suspicious of any decision described as a "no brainer"; far too
    often, I've found that it's a decision that would not have been chosen
    by those who do use their brains.

    In this case, using the function definition as the function prototype
    means that you cannot use the function prototype to validate a call to
    the function until that function has been defined. You could start with
    an empty definition, and more fully define it later. But that's
    essentially what C's separate declarations are: a function definition
    without a function body.
     
    James Kuyper, Jan 29, 2014
    #67
  8. Excellent.
     
    Keith Thompson, Jan 29, 2014
    #68
  9. You may not have the source file and/or it may not be written in
    whatever this language is. Unless you are designing for a walled
    garden with very high walls, you'll want to be able to describe an
    external function in such a way that the calling code can do what is
    needed.
    So, for example, why not implement type inference? Much less typing and no
    possibility of error[1].
    Yes, which is why I was surprised that you wanted to designed a
    language "based on C", and that you seem resistant to looking into
    modern programming language design.
    Must we sign a pledge? Do we line up and salute the IDE? People like
    to use familiar tools that work well for them. The liberal approach is
    to provide tools that inter-work, both with each other and with existing
    tools. If you offer just a fascist slab of an IDE, you won't get
    anywhere with your target users (which seems to be free software
    aficionados). Large companies and governments have the clout to impose
    the One True IDE, but you don't.
    It does, and will continue to do so, but you probably won't be able to
    have any influence on how.
    Old C or C of the 1999 standard or then 2011 one?

    <snip>
    [1] Let's ignore Curry-Howard for the moment.
     
    Ben Bacarisse, Jan 29, 2014
    #69
  10. Rick C. Hodgin

    Eric Sosman Guest

    s/is/was/, for the last fifteen years.
     
    Eric Sosman, Jan 29, 2014
    #70
  11. Why forward? In the brave new world, the compiler is multi-pass. They
    can surely all be at the end of a source file just as well as anywhere
    else.
    This is getting out of hand. We have no idea at all about what RDC is,
    so how can we speculate? If it has run-time types there's no need for
    any compiler-time type checking. We are in the dark.

    The best we can do is consider something "based on C" that is an
    incremental change. If it has compile-time type-checking of function
    calls, then something is needed for those cases when the source of the
    function being called is not available, or when it is in some language
    that RDC can't type-check. That seems to mean that "forward
    declarations" can't be eliminated.

    <snip>
     
    Ben Bacarisse, Jan 29, 2014
    #71
  12. The conventional way to deal with this is for all functions intended
    to be used by multiple translation units to be declared in header
    files. (Functions used only in a single translation unit needn't
    be declared in any header, and should be defined as "static".)

    Any ".c" file that either defines or refers to a function has a
    #include directive for the header that declares it. That means
    writing the declaration twice, but any discrepancy will be caught
    by the compiler. (Writing the duplicate declaration is a simple
    matter of copy-and-paste.)

    Following this convention probably would have prevented the problem
    you're talking about.

    If you have a function call with no visible declaration, and the
    compiler accepts it, it's probably because the compiler is operating
    under pre-C99 rules -- but even if you're using a pre-C99 compiler,
    providing visible declarations for all functions is still good
    practice.

    One can certainly imagine some non-C language in which declarations
    need not precede reference to functions, as long as the declaration
    appears *somewhere*. And you may be able to get away with pretending
    that you're programming in such a language if your pre-C99 compiler
    doesn't enforce the C99 rules. But it will not detect argument
    mismatch errors.

    C certainly allows you to be sloppy. That's why discipline is so
    important.

    As for what you can do in some language other than C, perhaps of
    your own invention, I frankly do not care.
     
    Keith Thompson, Jan 29, 2014
    #72
  13. It was in another file. The problem was not using a shared include
    file, as best as I can make out. Using one is standard in C, but it's
    easy to forget that the language does not enforce good practise in this
    regard. Using a header file, you get an error immediately you change
    something. You do have two copies, but they never get out of sync for
    longer than it takes to type "make".
     
    Ben Bacarisse, Jan 29, 2014
    #73
  14. I am designing systems that interface with C systems, or with something
    to which I have an interface. In those cases where I am writing to
    something which is external, I have no problem manually creating the
    file because that's just the cost of doing business. For everything
    that is inside the C ecosystem, I should never have to do that.
    I mentioned something like that in a previous thread. I see no reason why
    it can't do that, and allow the linker to fixup issues, and report ultimat
    errors. However, there are some considerations for things like
    int k = foo(4) which gets translated to an int foo(float) function. Since
    4 was specified as an integer, was the intended function the float version?
    Or was the intent to use int k = foot(4) which was defined as int foot(int)?

    There are reasons to use definitions. They make sense. But, there are no
    reasons to use forward prototype definitions in C when you have access to
    the source files, except for those cases where you are doing compilation
    to something external which needs the references to create its object file.
    For everything else, the compiler should be able to handle it.
    I like C because it's low level. However, there are aspects of C which
    are not forward-looking. My desires with RDC are to mate those two
    together (forward-looking, and low-level).
    That's why I'm writing something new with RDC and not writing another
    C compiler. I'm beginning with the premise of the integrated IDE which
    knows the system you're working with, and is part of the integrated
    system ecosystem.
    Agreed. :)
    Yes. I would support standards in my C compiler add-on for RDC.

    Best regards,
    Rick C. Hodgin
     
    Rick C. Hodgin, Jan 29, 2014
    #74
  15. That is what I did. However, I had not yet used the file. So, as far
    as the compiler knew they were two separate functions, but since neither
    was referenced it just removed them from the output, or compiled them as
    they were and since nothing called them they were ignored.

    When I used the function in my code my usage corresponded to the forward
    prototype definition. However, the linker could not find that function
    because it did not exist. I had to reconcile the issue between my
    forward declaration and my function definition. And at that point it
    became a research project to figure out which one was correct. It
    didn't take long, but it took a minute. The function definition was
    correct and I changed the prototype and it worked. That occurred on
    four functions actually IIRC, and they were all ** and * variances as
    I had changed the code at one point but didn't change the forward
    prototype.
    Only if the function is used.
    So now I'm spending time copy-and-pasting something an intelligent
    compiler would otherwise be capable of knowing for itself? "Manual
    effort exerted by every use to maintain an outdated standard" is
    what it sounds like to me.
    Not in this case. I did follow that convention, but the compiler had
    no way of reconciling the two because the function was never used.
    You have misunderstood. I do believe by the time you read this line
    that you will have understood my particular case, but I will explain
    it plainly just in case.

    Somewhere I forward declared:

    int foo(char** x);

    And in my code I wrote the function:

    int foo(char* x) { // Code goes here }

    I did not call foo() anywhere in my code. As such, the compiler saw
    the forward prototype and the function body and logically created two
    separate functions internally. But, since none of them were called
    it simply discarded them. An error in code, but no way the compiler
    could catch it. However, had I only had to use the int foo(char* x),
    this never would've been an error because the only place my IDE
    could've found that function was from the body definition, which
    would've been conveyed accurately. Since it is in two places, we are
    again back to the four-action truth table (RR,RW,WR,WW) rather than
    the two-action variety (R,W).
    Any self-respecting C compiler purposed on recognizing those resources
    which exist in computer hardware in the year 2013 should produce the
    same code with or without forward declarations provided you are compiling
    C source files in a C project. There should never be any discrepancies.

    When you are dealing with external things ... that's just the cost of
    doing business (external prototype declarations, not forward prototype
    declarations).
    That, and an IDE which is able to point out potential issues as by
    compiler-generating warnings. The two should be cojoined.
    Well, I appreciate your input on all of this. I realize I am using
    the wrong words a lot with regards to C word usage. I apologize.
    However, the concepts I'm conveying are, to my knowledge, accurate.

    Best regards,
    Rick C. Hodgin
     
    Rick C. Hodgin, Jan 29, 2014
    #75
  16. Rick C. Hodgin

    Ian Collins Guest

    No, read that again: "Any ".c" file that either defines..". Always
    include the header with the prototype declaration in the source file
    with the function definition.
    Alas, compilers aren't psychic yet.
    If it was never used, you wouldn't have had a problem with it, would
    you? Don't you consider defining a function "use"? If you had followed
    the convention, the definition would have cased an error.
    That somewhere should have been in a header included everywhere the
    function was called or defined. End of problem.
     
    Ian Collins, Jan 29, 2014
    #76
  17. Rick C. Hodgin

    BartC Guest

    Not in gcc 4.8.1. I've tried putting those forward declarations at the end
    of the source file, and they don't work down there!
    I think there's some confusion here. I'm trying to separate C as it is,
    which needs these extra declarations, and something else (OK forget RDC
    because I don't know the details).

    That something else does *not* need the programmer to provide those extra
    declarations. It might need them for its internal workings, but it sorts
    that out for itself.

    (In my case, through external tools, and translators of a modified version
    of the language. Example (if you can excuse my introducing some non-C source
    to illustrate, this is quite a thick syntax wrapper around C, but the idea
    would work with C syntax too):

    Input source, defining two functions, one exported and one local:

    global function add(int a,b)int=
    return a+b
    end

    function sub(int a,b)int=
    return a-b
    end

    C source file generated (with some manual tidying):

    #define global

    /* Function Prototypes */

    global int add(int a,int b);
    static int sub(int a,int b);

    /* Local Function Definitions */

    global int add(int a,int b) {
    return (a+b);
    }

    static int sub(int a,int b) {
    return (a-b);
    }

    And the corresponding header file:

    extern int add(int a,int b);

    So three declarations generated from the definitions. The original only
    needs the definitions.)
     
    BartC, Jan 29, 2014
    #77
  18. No; see below.
    It's a way of collecting information needed by client code in one place
    (the header file containing function *declarations*) and information
    needed to implement the interface in another place (the .c file
    containing function *definitions*. There are languages that don't
    separate these things. For that matter, strictly speaking, C doesn't
    require this kind of organization either, but it's a common convention.

    Personally, I find this separation to be useful. I don't need to see,
    or even have a copy of, the file containing the definition of a function
    to be able to call it.

    I'm sure a tool could be written that would take a .c file containing a
    set of function definitions, and automatically generate a corresponding
    ..h file. I'd be quite surprised if such a tool doesn't already exist.
    Were the "forward" declaration and the function definition in two
    separate files?

    Suppose you have the declaration:

    int foo(char** x);

    in foo.h, and the definition:

    int foo(char* x) { /* Code goes here */ }

    in foo.c. Then foo.c should have a line:

    #include "foo.h"

    and the compiler, when compiling foo.c, will see the two conflicting
    declarations and report the error -- regardless of whether you've
    written any calls to foo. (You'd want include guards in foo.h as well.)
    Any conforming C compiler (for C99 or later) must issue a diagnostic
    message for any function call for which there is no visible declaration
    *preceding* the call.

    Prior to C99, calls to functions with no visible declaration were
    permitted -- but they were not required to be checked. The compiler
    would simply *assume*, without reference to any actual definition, that
    the called function returns an int and takes arguments of the (promoted)
    types given by the call.

    This particular change in C99 was IMHO a substantial improvement to the
    language, not in any sense a step backwards (though it did break some
    existing legal, but IMHO poorly styled, code).

    What you seem to be advocating is a different change to the language,
    allowing function declarations and calls to appear out of order and
    still be checked. (Is that about right?) Neither pre-1999 C nor
    post-1999 C supports this feature. (I personally am not thrilled about
    the idea, since we have ways to avoid the need for out-of-order
    declarations.)

    I'm not trying to convince you that this is the best possible language
    design, or that this is the way C *should* be defined. I'm simply
    telling you how C *is* defined.
     
    Keith Thompson, Jan 29, 2014
    #78
  19. Right. I take it the "!" means joke?

    Yes, internals are irrelevant.
    So what has that to do with my point?

    <snip>
     
    Ben Bacarisse, Jan 29, 2014
    #79
  20. Rick C. Hodgin

    James Kuyper Guest

    On 01/29/2014 05:16 PM, Rick C. Hodgin wrote:
    ....
    Keep in mind that the convention Keith describes does not merely require
    that the forward declaration occur "somewhere". It requires specifically
    that it occur at file scope, in a header file, and that the header file
    be #included at file scope, into the same translation unit where foo()
    is defined, and prior to that definition.

    Did you actually follow that convention? Almost certainly not, if you
    didn't get a diagnostic message from the following definition of foo():
    No - the forward function declaration and the function definition both
    declared 'foo' as an identifier with external linkage. The C language
    only allows a single thing to be identified by any given identifier with
    external linkage. If the definition of the function occurred inside the
    scope of the forward declaration (which it would have been, had you
    actually been following the convention that Keith describes), then they
    must identify the same function, and therefore should have compatible
    definitions. They don't, and a conforming implementation of C MUST
    therefore issue at least one diagnostic message. It doesn't matter
    whether or not there are any calls to the function, anywhere in the
    entire program. It's still a constraint violation for which a diagnostic
    is mandatory.
    Any self-respecting C compiler will have a mode fully conforming to the
    requirements of at least one version of the C standard. For all but the
    very first version of that standard, a diagnostic message is mandatory
    for any call to any function for which there is no declaration in scope.
    After issuing the diagnostic, an implementation is free to conform to
    your expectations described above. It's also free to start playing
    "Moonlight Sonata" - since the behavior is undefined. However, I think
    that you'll find most implementations will halt translation of your
    program, so you can fix the problem and restart the process.
    In C as currently defined, "forward" is just a description of the
    position of the declaration in the translation unit, relative to the
    first use of that declaration. Any prototype declaration with external
    linkage that is in the correct position in the translation unit to
    qualify as a solution to this problem IS also a forward declaration. You
    may be thinking of a relevant distinction, but "external prototype
    declaration" and "forward prototype declaration" do not describe
    distinct things; you'll need to find different words to describe that
    distinction.
     
    James Kuyper, Jan 29, 2014
    #80
    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.