• Casting the return value of ...

    From Kenny McCormack@3:633/280.2 to All on Fri Mar 29 02:09:17 2024
    I think this is a variation on that old CLC standard "Why you should not
    cast the return value of malloc()".

    Caution: POSIX (non-strict ISO) stuff coming up. If this bothers you,
    please hit "next" right now.

    I frequently write "interposer" functions as shared libraries (on Linux). Generally, this requires using dlsym() and RTLD_NEXT to get a pointer to the "real" function, so that you can call that as part of your interposer
    routine. I have always done it like this (e.g., for a function returning
    char *, taking 2 size_t args - just to pick an arbitrary example to make
    things more concrete):

    char *someFunction(size_t,size_t) {
    static char * (*real_someFunction) (size_t,size_t);

    if (!real_someFunction)
    real_someFunction = (char * (*real_someFunction) (size_t,size_t)) dlsym(RTLD_NEXT,"someFunction");
    ....
    }

    This works fine (and compiles clean with the usual -W -Wall -Werror).

    But here's the thing. Is the casting of the result of dlsym() necessary?
    Isn't this the same as "casting the return value of malloc()", where you
    don't need to cast it since it auto-magically gets casted by being assigned
    to the thing on the left of the assignment? Note that it is duplicative, having to specify the type info for the function pointer twice - once in
    the declaration of the pointer and then again in the dlsym() call. It'd be better (in terms of maintenance) if you only had to do it once.

    But here's where it gets interesting. In the man page for dlopen(), we
    find this example code and a long detailed comment:

    double (*cosine)(double);
    ....
    cosine = (double (*)(double)) dlsym(handle, "cos");

    /* According to the ISO C standard, casting between function
    pointers and 'void *', as done above, produces undefined results.
    POSIX.1-2003 and POSIX.1-2008 accepted this state of affairs and
    proposed the following workaround:

    *(void **) (&cosine) = dlsym(handle, "cos");

    This (clumsy) cast conforms with the ISO C standard and will
    avoid any compiler warnings.

    The 2013 Technical Corrigendum to POSIX.1-2008 (a.k.a.
    POSIX.1-2013) improved matters by requiring that conforming
    implementations support casting 'void *' to a function pointer.
    Nevertheless, some compilers (e.g., gcc with the '-pedantic'
    option) may complain about the cast used in this program. */

    So, they seem to think the cast is necessary - or at least a good idea,
    Are they right?

    And why do we even need the "clumsy" cast? Why not just:

    cosine = dlsym(handle, "cos");

    --
    You are a dreadful man, Kenny, for all your ways are the ways of death.
    - Rick C Hodgin -

    (P.S. -> https://www.youtube.com/watch?v=sMmTkKz60W8)

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: The official candy of the new Millennium (3:633/280.2@fidonet)
  • From Kaz Kylheku@3:633/280.2 to All on Fri Mar 29 05:16:03 2024
    On 2024-03-28, Kenny McCormack <gazelle@shell.xmission.com> wrote:
    But here's the thing. Is the casting of the result of dlsym()
    [to a function pointer type] necessary?
    Isn't this the same as "casting the return value of malloc()", where you

    Conversions between function pointers and data pointers are an
    extension; it is not well-defined behavior in ISO C.

    Therefore we can neither say that ISO C doesn't require a cast there (it imposes no requirements at all), nor that the conversion is fine with a
    cast.

    The cast is /likely/ necessary, in order to correctly trigger the
    extension.

    But here's where it gets interesting. In the man page for dlopen(), we
    find this example code and a long detailed comment:

    The "Linux Programmer's Manual" man pages are often lacking in
    technical precision, and even contain rants (see regex(7)).


    *(void **) (&cosine) = dlsym(handle, "cos");

    This (clumsy) cast conforms with the ISO C standard and will
    avoid any compiler warnings.

    It does not conform! The address of a pointer-to-function object is
    type punned to look like a "void *", and assigned.

    It breaks aliasing rules.

    It does not use the extension of converting between function and object pointers; it's relying on punning them.

    For it to be 100% correct, there has to be a compiler extension to
    support the punning that it is doing, and function and object pointers
    have to have the same representation.

    The 2013 Technical Corrigendum to POSIX.1-2008 (a.k.a.
    POSIX.1-2013) improved matters by requiring that conforming
    implementations support casting 'void *' to a function pointer.

    I also seem to remember something like this, but I cannot trust this documentation without a chapter-and-verse citation.

    Assuming it is true, there you have it; if you're on POSIX, the compiler
    is required to have the extension and it is connected to casting,
    in which case the cast is required.

    And why do we even need the "clumsy" cast? Why not just:

    cosine = dlsym(handle, "cos");

    Because of the man page author's goal of eliminating a warning
    from GCC.

    I think there was a time in the development of GCC when there was
    a warning even with the cast. I don't think it's enabled by default
    now?

    (I seem to recall that, a number of years ago, the GCC goofballs, in
    fact, merged a change that caused the compiler to generate a run-time
    abort in code that converted function/object pointers. It was turned on
    by default. It had to be backpedaled due to all the screaming from
    downstream distros. Am I hallucinating that?)

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Keith Thompson@3:633/280.2 to All on Fri Mar 29 06:38:21 2024
    Kaz Kylheku <433-929-6894@kylheku.com> writes:
    [...]
    Conversions between function pointers and data pointers are an
    extension; it is not well-defined behavior in ISO C.

    Therefore we can neither say that ISO C doesn't require a cast there (it imposes no requirements at all), nor that the conversion is fine with a
    cast.

    The cast is /likely/ necessary, in order to correctly trigger the
    extension.

    ISO C does require a cast. The cast is necessary to avoid a constraint violation and a mandatory diagnostic. The resulting behavior is
    undefined in ISO C, but defined by POSIX.

    Assigning a void* value to a pointer-to-function object without a cast
    violates the constraint for simple assignment (N1570 6.5.16.1p1).

    [...]

    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    Working, but not speaking, for Medtronic
    void Void(void) { Void(); } /* The recursive call of the void */

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: None to speak of (3:633/280.2@fidonet)
  • From bart@3:633/280.2 to All on Fri Mar 29 07:30:37 2024
    On 28/03/2024 19:38, Keith Thompson wrote:
    Kaz Kylheku <433-929-6894@kylheku.com> writes:
    [...]
    Conversions between function pointers and data pointers are an
    extension; it is not well-defined behavior in ISO C.

    Therefore we can neither say that ISO C doesn't require a cast there (it
    imposes no requirements at all), nor that the conversion is fine with a
    cast.

    The cast is /likely/ necessary, in order to correctly trigger the
    extension.

    ISO C does require a cast. The cast is necessary to avoid a constraint violation and a mandatory diagnostic. The resulting behavior is
    undefined in ISO C, but defined by POSIX.

    Assigning a void* value to a pointer-to-function object without a cast violates the constraint for simple assignment (N1570 6.5.16.1p1).

    What would such a cast look like? Since this gives a warning with
    -Wpedantic even with a cast:

    void* p;
    void(*q)(void);

    p=(void*)q;
    q=(void(*)(void))p;



    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Keith Thompson@3:633/280.2 to All on Fri Mar 29 08:07:44 2024
    bart <bc@freeuk.com> writes:
    On 28/03/2024 19:38, Keith Thompson wrote:
    Kaz Kylheku <433-929-6894@kylheku.com> writes:
    [...]
    Conversions between function pointers and data pointers are an
    extension; it is not well-defined behavior in ISO C.

    Therefore we can neither say that ISO C doesn't require a cast there (it >>> imposes no requirements at all), nor that the conversion is fine with a
    cast.

    The cast is /likely/ necessary, in order to correctly trigger the
    extension.
    ISO C does require a cast. The cast is necessary to avoid a
    constraint violation and a mandatory diagnostic. The resulting
    behavior is undefined in ISO C, but defined by POSIX. Assigning a
    void* value to a pointer-to-function object without a cast violates
    the constraint for simple assignment (N1570 6.5.16.1p1).

    What would such a cast look like? Since this gives a warning with
    -Wpedantic even with a cast:

    void* p;
    void(*q)(void);

    p=(void*)q;
    q=(void(*)(void))p;

    The warnings I get from gcc are:

    warning: ISO C forbids conversion of function pointer to object pointer type [-Wpedantic]
    warning: ISO C forbids conversion of object pointer to function pointer type [-Wpedantic]

    With -pedantic-errors, these become fatal errors.

    I disagree with gcc. ISO C doesn't define the behavior, but it doesn't
    forbid the conversion. (Anyone who disagrees is invited to cite the
    constraint that it violates.)

    Note that clang doesn't issue this diagnostic.

    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    Working, but not speaking, for Medtronic
    void Void(void) { Void(); } /* The recursive call of the void */

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: None to speak of (3:633/280.2@fidonet)
  • From Chris M. Thomasson@3:633/280.2 to All on Fri Mar 29 08:15:54 2024
    On 3/28/2024 2:07 PM, Keith Thompson wrote:
    bart <bc@freeuk.com> writes:
    On 28/03/2024 19:38, Keith Thompson wrote:
    Kaz Kylheku <433-929-6894@kylheku.com> writes:
    [...]
    Conversions between function pointers and data pointers are an
    extension; it is not well-defined behavior in ISO C.

    Therefore we can neither say that ISO C doesn't require a cast there (it >>>> imposes no requirements at all), nor that the conversion is fine with a >>>> cast.

    The cast is /likely/ necessary, in order to correctly trigger the
    extension.
    ISO C does require a cast. The cast is necessary to avoid a
    constraint violation and a mandatory diagnostic. The resulting
    behavior is undefined in ISO C, but defined by POSIX. Assigning a
    void* value to a pointer-to-function object without a cast violates
    the constraint for simple assignment (N1570 6.5.16.1p1).

    What would such a cast look like? Since this gives a warning with
    -Wpedantic even with a cast:

    void* p;
    void(*q)(void);

    p=(void*)q;
    q=(void(*)(void))p;

    The warnings I get from gcc are:

    warning: ISO C forbids conversion of function pointer to object pointer type [-Wpedantic]
    warning: ISO C forbids conversion of object pointer to function pointer type [-Wpedantic]

    With -pedantic-errors, these become fatal errors.

    I disagree with gcc. ISO C doesn't define the behavior, but it doesn't forbid the conversion. (Anyone who disagrees is invited to cite the constraint that it violates.)

    Note that clang doesn't issue this diagnostic.


    Casting might prevent a warning in certain compilers...

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Kaz Kylheku@3:633/280.2 to All on Fri Mar 29 08:44:28 2024
    On 2024-03-28, Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
    The warnings I get from gcc are:

    warning: ISO C forbids conversion of function pointer to object pointer type [-Wpedantic]
    warning: ISO C forbids conversion of object pointer to function pointer type [-Wpedantic]

    With -pedantic-errors, these become fatal errors.

    I disagree with gcc. ISO C doesn't define the behavior, but it doesn't forbid the conversion.

    It's never good for a diagnostic to be stating a blatant falsehood,
    regardless of whether the presence of the diagnostic is a good idea
    or not.

    There is a problem in the man page also (haven't checked the main doc):

    Issue all the warnings demanded by strict ISO C and ISO C++;
    reject all programs that use forbidden extensions, and some
    other programs that do not follow ISO C and ISO C++. For ISO C,
    follows the version of the ISO C standard specified by any -std
    option used.

    There are no "forbidden extensions"! There are extensions we have,
    and extensions we don't have. Extensions we don't have a undefined
    behavior.

    The misconception is repeated in the GNU Conding Conventions. It might
    have come from the same person.

    https://www.gnu.org/prep/standards/standards.html

    But we do not follow either of these specifications rigidly, and
    there are specific points on which we decided not to follow them, so
    as to make the GNU system better for users.

    For instance, Standard C says that nearly all extensions to C are
    prohibited. How silly! GCC implements many extensions, some of which
    were later adopted as part of the standard. If you want these
    constructs to give an error message as “required” by the standard,
    you must specify ‘--pedantic’, which was implemented only so that we
    can say “GCC is a 100% implementation of the standard”, not because
    there is any reason to actually use it.

    Standard C does not say that any extensions are prohibited.
    How silly to think so, and write about it, and code a facet of the
    compiler diagnostic system that way!

    I'm actually opening a GCC bugzilla about this right now.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Kaz Kylheku@3:633/280.2 to All on Fri Mar 29 09:01:03 2024
    On 2024-03-28, Kaz Kylheku <433-929-6894@kylheku.com> wrote:
    Standard C does not say that any extensions are prohibited.
    How silly to think so, and write about it, and code a facet of the
    compiler diagnostic system that way!

    I'm actually opening a GCC bugzilla about this right now.

    https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114526

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Kaz Kylheku@3:633/280.2 to All on Fri Mar 29 09:33:56 2024
    On 2024-03-28, Kaz Kylheku <433-929-6894@kylheku.com> wrote:
    On 2024-03-28, Kaz Kylheku <433-929-6894@kylheku.com> wrote:
    Standard C does not say that any extensions are prohibited.
    How silly to think so, and write about it, and code a facet of the
    compiler diagnostic system that way!

    I'm actually opening a GCC bugzilla about this right now.

    https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114526

    Hilarious pushback ensues.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Keith Thompson@3:633/280.2 to All on Fri Mar 29 09:37:22 2024
    Kaz Kylheku <433-929-6894@kylheku.com> writes:
    On 2024-03-28, Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
    The warnings I get from gcc are:

    warning: ISO C forbids conversion of function pointer to object pointer type [-Wpedantic]
    warning: ISO C forbids conversion of object pointer to function pointer type [-Wpedantic]

    With -pedantic-errors, these become fatal errors.

    I disagree with gcc. ISO C doesn't define the behavior, but it doesn't
    forbid the conversion.

    It's never good for a diagnostic to be stating a blatant falsehood, regardless of whether the presence of the diagnostic is a good idea
    or not.
    [...]
    I'm actually opening a GCC bugzilla about this right now.

    I should have remembered that I reported this as a bug in 2017:

    https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83584

    That bug report was closed (inappropriately IMHO) as a duplicate of

    https://gcc.gnu.org/bugzilla/show_bug.cgi?id=11234

    A commenter on bug #83584 cites the C99 Rationale:
    "Even with an explicit cast, it is invalid to convert a function pointer
    to an object pointer or a pointer to void, or vice versa."

    It's not clear what "invalid" means in that context, and in any case the Rationale is not normative.

    N1570 Annex J 5.7 mentions casting between object pointers and function pointers as a common extension.

    Kaz, I see you've submitted
    https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114526
    which has (inappropriately IMHO) been closed as a duplicate of #11234.

    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    Working, but not speaking, for Medtronic
    void Void(void) { Void(); } /* The recursive call of the void */

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: None to speak of (3:633/280.2@fidonet)
  • From Andrey Tarasevich@3:633/280.2 to All on Fri Mar 29 15:41:11 2024
    On 03/28/24 8:09 AM, Kenny McCormack wrote:

    But here's the thing. Is the casting of the result of dlsym() necessary? Isn't this the same as "casting the return value of malloc()", where you don't need to cast it since it auto-magically gets casted by being assigned to the thing on the left of the assignment?

    No, it isn't even remotely the same. C language allows implicit casts
    from any object pointer type to `void *` and vice-versa. Which is why
    casting the result of `malloc` is unnecessary.

    C language does not support implicit casts between object pointer types
    (like `void *`) and function pointer types. Moreover, C language still
    does not support _explicit_ casts between `void *` and function pointer
    types either. POSIX guarantees/requirements are not C.

    And why do we even need the "clumsy" cast? Why not just:

    cosine = dlsym(handle, "cos");

    Because this is a constraint violation, aka a "compile error".

    --
    Best regards,
    Andrey


    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Keith Thompson@3:633/280.2 to All on Fri Mar 29 16:02:35 2024
    Andrey Tarasevich <andreytarasevich@hotmail.com> writes:
    [...]
    No, it isn't even remotely the same. C language allows implicit casts
    from any object pointer type to `void *` and vice-versa. Which is why casting the result of `malloc` is unnecessary.

    To be picky, there are no "implicit casts". There are implicit
    conversions. A cast is an explicit conversion.

    C language does not support implicit casts between object pointer
    types (like `void *`) and function pointer types. Moreover, C language
    still does not support _explicit_ casts between `void *` and function
    pointer types either. POSIX guarantees/requirements are not C.

    It "does not support" them in the sense that it does not define their
    behavior. Such a cast does not violate any constraint, and an
    implementation or third-party standard is free to define the semantics.

    By contrast, conversions between floating types and pointer types are constraint violations.

    [...]

    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    Working, but not speaking, for Medtronic
    void Void(void) { Void(); } /* The recursive call of the void */

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: None to speak of (3:633/280.2@fidonet)
  • From David Brown@3:633/280.2 to All on Fri Mar 29 23:58:27 2024
    On 28/03/2024 21:30, bart wrote:
    On 28/03/2024 19:38, Keith Thompson wrote:
    Kaz Kylheku <433-929-6894@kylheku.com> writes:
    [...]
    Conversions between function pointers and data pointers are an
    extension; it is not well-defined behavior in ISO C.

    Therefore we can neither say that ISO C doesn't require a cast there (it >>> imposes no requirements at all), nor that the conversion is fine with a
    cast.

    The cast is /likely/ necessary, in order to correctly trigger the
    extension.

    ISO C does require a cast. The cast is necessary to avoid a constraint
    violation and a mandatory diagnostic. The resulting behavior is
    undefined in ISO C, but defined by POSIX.

    Assigning a void* value to a pointer-to-function object without a cast
    violates the constraint for simple assignment (N1570 6.5.16.1p1).

    What would such a cast look like? Since this gives a warning with
    -Wpedantic even with a cast:

    void* p;
    void(*q)(void);

    p=(void*)q;
    q=(void(*)(void))p;



    One method that silences all gcc warnings here is to cast via uintptr_t:

    #include <stdint.h>

    void* p;
    void(*q)(void);

    typedef void(*FVoid)(void);

    void foo(void) {
    p = (void*) (uintptr_t) q;
    }

    void bar(void) {
    q = (FVoid) (uintptr_t) p;
    }


    I think it is good to have the gcc warnings enabled to help catch
    mistakes, and you then need to make special effort when doing something
    odd - hence the casts. (On any POSIX system where casting between pointer-to-function and pointer-to-object types can work, uintptr_t will
    exist and the cast here will not affect the value of the pointer.)


    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From David Brown@3:633/280.2 to All on Sat Mar 30 00:06:58 2024
    On 28/03/2024 22:07, Keith Thompson wrote:
    bart <bc@freeuk.com> writes:
    On 28/03/2024 19:38, Keith Thompson wrote:
    Kaz Kylheku <433-929-6894@kylheku.com> writes:
    [...]
    Conversions between function pointers and data pointers are an
    extension; it is not well-defined behavior in ISO C.

    Therefore we can neither say that ISO C doesn't require a cast there (it >>>> imposes no requirements at all), nor that the conversion is fine with a >>>> cast.

    The cast is /likely/ necessary, in order to correctly trigger the
    extension.
    ISO C does require a cast. The cast is necessary to avoid a
    constraint violation and a mandatory diagnostic. The resulting
    behavior is undefined in ISO C, but defined by POSIX. Assigning a
    void* value to a pointer-to-function object without a cast violates
    the constraint for simple assignment (N1570 6.5.16.1p1).

    What would such a cast look like? Since this gives a warning with
    -Wpedantic even with a cast:

    void* p;
    void(*q)(void);

    p=(void*)q;
    q=(void(*)(void))p;

    The warnings I get from gcc are:

    warning: ISO C forbids conversion of function pointer to object pointer type [-Wpedantic]
    warning: ISO C forbids conversion of object pointer to function pointer type [-Wpedantic]

    With -pedantic-errors, these become fatal errors.

    I disagree with gcc. ISO C doesn't define the behavior, but it doesn't forbid the conversion. (Anyone who disagrees is invited to cite the constraint that it violates.)


    I think that the C standards don't forbid the conversion, but the
    description of pointer conversions (6.3.2.3) does not describe such conversions. That makes it, AFAICS, undefined behaviour rather than "forbidden" (which I would define as something that mandates a diagnostic).

    Dereferencing such converted pointers might be undefined behaviour (if
    you haven't converted back to the original type), or
    implementation-dependent behaviour (if the conversions change the
    bitwise representation of the pointer).

    I personally think it's good that gcc has this diagnostic, even if the
    message text is not strictly accurate.


    Note that clang doesn't issue this diagnostic.



    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From bart@3:633/280.2 to All on Sat Mar 30 00:32:13 2024
    On 29/03/2024 12:58, David Brown wrote:
    On 28/03/2024 21:30, bart wrote:
    On 28/03/2024 19:38, Keith Thompson wrote:
    Kaz Kylheku <433-929-6894@kylheku.com> writes:
    [...]
    Conversions between function pointers and data pointers are an
    extension; it is not well-defined behavior in ISO C.

    Therefore we can neither say that ISO C doesn't require a cast there
    (it
    imposes no requirements at all), nor that the conversion is fine with a >>>> cast.

    The cast is /likely/ necessary, in order to correctly trigger the
    extension.

    ISO C does require a cast. The cast is necessary to avoid a constraint
    violation and a mandatory diagnostic. The resulting behavior is
    undefined in ISO C, but defined by POSIX.

    Assigning a void* value to a pointer-to-function object without a cast
    violates the constraint for simple assignment (N1570 6.5.16.1p1).

    What would such a cast look like? Since this gives a warning with
    -Wpedantic even with a cast:

    void* p;
    void(*q)(void);

    p=(void*)q;
    q=(void(*)(void))p;



    One method that silences all gcc warnings here is to cast via uintptr_t:

    #include <stdint.h>

    void* p;
    void(*q)(void);

    typedef void(*FVoid)(void);

    void foo(void) {
    p = (void*) (uintptr_t) q;
    }

    void bar(void) {
    q = (FVoid) (uintptr_t) p;
    }

    I was aware of the double conversion but KT used 'a cast' so I wondered
    if there was a single cast that could be used.

    It is odd however that function and object pointers can be considered so different that even an explicit conversion between them is deemed to be meaningless.

    Yet converting either to and from an integer type is perfectly fine,
    even though it isn't even a pointer type!

    I wonder then why a conversion between function and object couldn't be implemented, internally, via such an intermediate integer type anyway.



    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Michael S@3:633/280.2 to All on Sat Mar 30 00:53:10 2024
    On Thu, 28 Mar 2024 22:33:56 -0000 (UTC)
    Kaz Kylheku <433-929-6894@kylheku.com> wrote:

    On 2024-03-28, Kaz Kylheku <433-929-6894@kylheku.com> wrote:
    On 2024-03-28, Kaz Kylheku <433-929-6894@kylheku.com> wrote:
    Standard C does not say that any extensions are prohibited.
    How silly to think so, and write about it, and code a facet of the
    compiler diagnostic system that way!

    I'm actually opening a GCC bugzilla about this right now.

    https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114526

    Hilarious pushback ensues.


    You got one commentator on your side.


    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Michael S@3:633/280.2 to All on Sat Mar 30 01:01:52 2024
    On Fri, 29 Mar 2024 15:53:10 +0200
    Michael S <already5chosen@yahoo.com> wrote:

    On Thu, 28 Mar 2024 22:33:56 -0000 (UTC)
    Kaz Kylheku <433-929-6894@kylheku.com> wrote:

    On 2024-03-28, Kaz Kylheku <433-929-6894@kylheku.com> wrote:
    On 2024-03-28, Kaz Kylheku <433-929-6894@kylheku.com> wrote:
    Standard C does not say that any extensions are prohibited.
    How silly to think so, and write about it, and code a facet of
    the compiler diagnostic system that way!

    I'm actually opening a GCC bugzilla about this right now.

    https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114526

    Hilarious pushback ensues.


    You got one commentator on your side.


    BTW, I tried to find out who are Harald van Dijk and Joseph S. Myers
    (the post of the later sounds like one of insider) but failed.
    Did gcc Bugzilla silently disabled "Search By People" option?
    Or I am holding it wrong?


    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From David Brown@3:633/280.2 to All on Sat Mar 30 03:00:56 2024
    On 29/03/2024 15:01, Michael S wrote:
    On Fri, 29 Mar 2024 15:53:10 +0200
    Michael S <already5chosen@yahoo.com> wrote:

    On Thu, 28 Mar 2024 22:33:56 -0000 (UTC)
    Kaz Kylheku <433-929-6894@kylheku.com> wrote:

    On 2024-03-28, Kaz Kylheku <433-929-6894@kylheku.com> wrote:
    On 2024-03-28, Kaz Kylheku <433-929-6894@kylheku.com> wrote:
    Standard C does not say that any extensions are prohibited.
    How silly to think so, and write about it, and code a facet of
    the compiler diagnostic system that way!

    I'm actually opening a GCC bugzilla about this right now.

    https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114526

    Hilarious pushback ensues.


    You got one commentator on your side.


    BTW, I tried to find out who are Harald van Dijk and Joseph S. Myers
    (the post of the later sounds like one of insider) but failed.
    Did gcc Bugzilla silently disabled "Search By People" option?
    Or I am holding it wrong?


    Joseph S. Myers is one of the major developers of GCC. I believe he is employed by Red Hat at the moment, and is the co-maintainer of the C
    front-end for GCC as well as a Release Manager. He is also involved in
    the C standards themselves and has represented the UK at C standards
    meetings. He is regularly on the gcc mailing lists, and in discussions
    on bugzilla. Basically, he is one of the "big guns" in GCC.


    (I don't know Harald van Dijk.)


    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From David Brown@3:633/280.2 to All on Sat Mar 30 03:10:05 2024
    On 29/03/2024 14:32, bart wrote:
    On 29/03/2024 12:58, David Brown wrote:
    On 28/03/2024 21:30, bart wrote:
    On 28/03/2024 19:38, Keith Thompson wrote:
    Kaz Kylheku <433-929-6894@kylheku.com> writes:
    [...]
    Conversions between function pointers and data pointers are an
    extension; it is not well-defined behavior in ISO C.

    Therefore we can neither say that ISO C doesn't require a cast
    there (it
    imposes no requirements at all), nor that the conversion is fine
    with a
    cast.

    The cast is /likely/ necessary, in order to correctly trigger the
    extension.

    ISO C does require a cast. The cast is necessary to avoid a constraint >>>> violation and a mandatory diagnostic. The resulting behavior is
    undefined in ISO C, but defined by POSIX.

    Assigning a void* value to a pointer-to-function object without a cast >>>> violates the constraint for simple assignment (N1570 6.5.16.1p1).

    What would such a cast look like? Since this gives a warning with
    -Wpedantic even with a cast:

    void* p;
    void(*q)(void);

    p=(void*)q;
    q=(void(*)(void))p;



    One method that silences all gcc warnings here is to cast via uintptr_t:

    #include <stdint.h>

    void* p;
    void(*q)(void);

    typedef void(*FVoid)(void);

    void foo(void) {
    p = (void*) (uintptr_t) q;
    }

    void bar(void) {
    q = (FVoid) (uintptr_t) p;
    }

    I was aware of the double conversion but KT used 'a cast' so I wondered
    if there was a single cast that could be used.

    A single cast is all that is needed as far as the C standards are
    concerned, but the double cast is helpful to silence the gcc warning.

    (I agree with Keith that a diagnostic is not required by the C
    standards, and thus the warning message and the choice of flag to
    control it are inaccurate, but I think it is a useful warning to have
    enabled in general.)


    It is odd however that function and object pointers can be considered so different that even an explicit conversion between them is deemed to be meaningless.


    Why? They are such totally different concepts in C. It does not make
    sense, within the semantics of C, to consider functions as data or data
    as functions.

    It can make sense in a wider context, outside of the things defined in
    the C standards, and it is useful that you can make such conversions
    with real C compilers (since the C standards don't cover /everything/ of interest to programmers).

    But it seems very reasonable to me to say that programmers have to go
    out of their way to mix functions and data like this - it is a good
    thing that awkward, rare and inherently risky things are harder to write
    and come with warnings.

    Yet converting either to and from an integer type is perfectly fine,
    even though it isn't even a pointer type!

    I'd be happy if C required a bit more effort for converting back and
    forth between pointers and integer types - the ease here is for historic reasons, I think. But at least it needs a cast.


    I wonder then why a conversion between function and object couldn't be implemented, internally, via such an intermediate integer type anyway.


    I'm sure it could.

    I don't see such pointer conversions having wide-spread use, and thus
    don't see any reason to make them easier. Cases such as "dlsym" are
    rare, and any required ugly conversions can be neatly contained within
    the function.



    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Chris M. Thomasson@3:633/280.2 to All on Sat Mar 30 09:46:45 2024
    On 3/29/2024 6:06 AM, David Brown wrote:
    On 28/03/2024 22:07, Keith Thompson wrote:
    bart <bc@freeuk.com> writes:
    On 28/03/2024 19:38, Keith Thompson wrote:
    Kaz Kylheku <433-929-6894@kylheku.com> writes:
    [...]
    Conversions between function pointers and data pointers are an
    extension; it is not well-defined behavior in ISO C.

    Therefore we can neither say that ISO C doesn't require a cast
    there (it
    imposes no requirements at all), nor that the conversion is fine
    with a
    cast.

    The cast is /likely/ necessary, in order to correctly trigger the
    extension.
    ISO C does require a cast. The cast is necessary to avoid a
    constraint violation and a mandatory diagnostic. The resulting
    behavior is undefined in ISO C, but defined by POSIX. Assigning a
    void* value to a pointer-to-function object without a cast violates
    the constraint for simple assignment (N1570 6.5.16.1p1).

    What would such a cast look like? Since this gives a warning with
    -Wpedantic even with a cast:

    void* p;
    void(*q)(void);

    p=(void*)q;
    q=(void(*)(void))p;

    The warnings I get from gcc are:

    warning: ISO C forbids conversion of function pointer to object
    pointer type [-Wpedantic]
    warning: ISO C forbids conversion of object pointer to function
    pointer type [-Wpedantic]

    With -pedantic-errors, these become fatal errors.

    I disagree with gcc. ISO C doesn't define the behavior, but it doesn't
    forbid the conversion. (Anyone who disagrees is invited to cite the
    constraint that it violates.)


    I think that the C standards don't forbid the conversion, but the description of pointer conversions (6.3.2.3) does not describe such conversions. That makes it, AFAICS, undefined behaviour rather than "forbidden" (which I would define as something that mandates a diagnostic).

    Agreed. Its undefined behavior wrt std C, however, _not_ when it comes
    to POSIX.





    Dereferencing such converted pointers might be undefined behaviour (if
    you haven't converted back to the original type), or implementation-dependent behaviour (if the conversions change the
    bitwise representation of the pointer).

    I personally think it's good that gcc has this diagnostic, even if the message text is not strictly accurate.


    Note that clang doesn't issue this diagnostic.




    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Tim Rentsch@3:633/280.2 to All on Sat Mar 30 09:52:06 2024
    gazelle@shell.xmission.com (Kenny McCormack) writes:

    [some white space added or deleted]

    I think this is a variation on that old CLC standard "Why you should not
    cast the return value of malloc()".

    Caution: POSIX (non-strict ISO) stuff coming up. If this bothers you, please hit "next" right now.

    I frequently write "interposer" functions as shared libraries (on Linux). Generally, this requires using dlsym() and RTLD_NEXT to get a pointer to the "real" function, so that you can call that as part of your interposer routine. I have always done it like this (e.g., for a function returning char *, taking 2 size_t args - just to pick an arbitrary example to make things more concrete):

    char *someFunction(size_t,size_t) {
    static char * (*real_someFunction) (size_t,size_t);

    if (!real_someFunction)
    real_someFunction =
    (char * (*real_someFunction) (size_t,size_t))
    dlsym(RTLD_NEXT,"someFunction");
    ...
    }

    Presumably you meant (char * (*)(size_t,size_t)) for the type used
    in the cast.

    This works fine (and compiles clean with the usual -W -Wall -Werror).

    Did you also try with -pedantic or -pedantic-errors? If not then
    it's a good idea to include it. Even if you don't use -pedantic all
    the time, it's a good idea to include it occasionally, especially in
    cases like this where you want to understand more about what is
    going on.


    But here's the thing. Is the casting of the result of dlsym() necessary? Isn't this the same as "casting the return value of malloc()", where you don't need to cast it since it auto-magically gets casted by being assigned to the thing on the left of the assignment?

    There are a couple of reasons why this doesn't work, and also why
    it isn't a good idea. More explanation below.

    Note that it is duplicative, having to specify the type info for the
    function pointer twice - once in the declaration of the pointer and
    then again in the dlsym() call. It'd be better (in terms of
    maintenance) if you only had to do it once.

    Certainly it is inconvenient to give a long and complicated type (or
    type name) multiple times. Typically most of the inconvenience is
    avoided by using a typedef, as for example

    typedef char *SomeF( size_t, size_t );
    static SomeF *f_pointer;
    ...
    f_pointer = (SomeF *)( ... );

    I should add that this construction doesn't help in the dlsym()
    situation, but it illustrates the principle of using typedef.


    But here's where it gets interesting. In the man page for dlopen(), we
    find this example code and a long detailed comment:

    double (*cosine)(double);
    ...
    cosine = (double (*)(double)) dlsym(handle, "cos");

    /* According to the ISO C standard, casting between function
    pointers and 'void *', as done above, produces undefined results.
    POSIX.1-2003 and POSIX.1-2008 accepted this state of affairs and
    proposed the following workaround:

    *(void **) (&cosine) = dlsym(handle, "cos");

    This (clumsy) cast conforms with the ISO C standard and will
    avoid any compiler warnings.

    The 2013 Technical Corrigendum to POSIX.1-2008 (a.k.a.
    POSIX.1-2013) improved matters by requiring that conforming
    implementations support casting 'void *' to a function pointer.
    Nevertheless, some compilers (e.g., gcc with the '-pedantic'
    option) may complain about the cast used in this program. */

    So, they seem to think the cast is necessary - or at least a good idea,
    Are they right?

    They are right in the important sense that using a straightforward
    cast is the wrong way to tackle this problem. The "solution" given
    above is ill advised because it strays into the realm of undefined
    behavior. But the key point is correct. A fix for how to address
    the problem is given below.

    And why do we even need the "clumsy" cast? Why not just:

    cosine = dlsym(handle, "cos");

    The C standard does not consider function pointers and object
    pointers to be inter-convertible. Because of that, there is no
    reason to allow a void * pointer to be assigned to a variable
    that has a function pointer type.

    Returning to your original scenario, here is a way to do what you
    want that stays inside the ISO C limits, and so avoids the problem
    mentioned in the dlopen() man page:

    /* presume there is a declaration for dlsym() available */

    char *
    outerFunction( size_t a, size_t b ){
    typedef char *SF_f( size_t, size_t ); // an actual function type
    extern SF_f outerFunction; // verify type compatibility
    static SF_f *dynamic_f; // pointer to dynamically loaded f

    if( ! dynamic_f ){
    union { void *v; SF_f *f; } it = { dlsym( 0, "someFunction" ) };
    dynamic_f = it.f;
    // we might want to check that dynamic_f is not null now
    }
    return dynamic_f( a, b );
    }

    This code compiles cleanly as C99 or C11, under both gcc and clang,
    with a full set of restrictive diagnostics: -Wall -Wextra -Werror
    and -pedantic-errors. (A simple change will allow it to compile,
    and compile cleanly, as C90 if that is desired.)

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Tim Rentsch@3:633/280.2 to All on Sat Mar 30 20:32:04 2024
    bart <bc@freeuk.com> writes:

    On 29/03/2024 12:58, David Brown wrote:

    On 28/03/2024 21:30, bart wrote:

    On 28/03/2024 19:38, Keith Thompson wrote:

    Kaz Kylheku <433-929-6894@kylheku.com> writes:
    [...]

    Conversions between function pointers and data pointers are an
    extension; it is not well-defined behavior in ISO C.

    Therefore we can neither say that ISO C doesn't require a cast
    there (it imposes no requirements at all), nor that the
    conversion is fine with a cast.

    The cast is /likely/ necessary, in order to correctly trigger
    the extension.

    ISO C does require a cast. The cast is necessary to avoid a
    constraint violation and a mandatory diagnostic. The resulting
    behavior is undefined in ISO C, but defined by POSIX.

    Assigning a void* value to a pointer-to-function object without a
    cast violates the constraint for simple assignment (N1570
    6.5.16.1p1).

    What would such a cast look like? Since this gives a warning with
    -Wpedantic even with a cast:

    void* p;
    void(*q)(void);

    p=(void*)q;
    q=(void(*)(void))p;

    One method that silences all gcc warnings here is to cast via
    uintptr_t:

    #include <stdint.h>

    void* p;
    void(*q)(void);

    typedef void(*FVoid)(void);

    void foo(void) {
    p = (void*) (uintptr_t) q;
    }

    void bar(void) {
    q = (FVoid) (uintptr_t) p;
    }

    I was aware of the double conversion but KT used 'a cast' so I
    wondered if there was a single cast that could be used.

    There is not, if it's important that it work reliably across
    different compilers and different platforms.

    It is odd however that function and object pointers can be
    considered so different that even an explicit conversion
    between them is deemed to be meaningless.

    Function pointers and object pointers don't have to be the same
    size, or use the same form of representation. The C standard
    allows implementations where code and data live in completely
    separate memories. In such cases there is no sensible way to
    convert between the two kinds of pointers, because the two kinds
    of addresses have no relationship to each other.

    Yet converting either to and from an integer type is perfectly
    fine, even though it isn't even a pointer type!

    Converting to an integer type might be okay but it doesn't have
    to be. The C standard says this explicitly:

    Any pointer type may be converted to an integer type. Except
    as previously specified, the result is implementation-defined.
    If the result cannot be represented in the integer type, the
    behavior is undefined. The result need not be in the range of
    values of any integer type.

    Note in particular the last sentence.

    I wonder then why a conversion between function and object
    couldn't be implemented, internally, via such an intermediate
    integer type anyway.

    On platforms where object pointers and function pointers both
    point into the same address space, converting between the two
    kinds of pointers could be implemented internally just by
    copying the bits, without having to go through some integer
    intermediate. Indeed I expect that implementations that
    support the extension of converting between the two kinds
    of pointers do just that. But there is no guarantee this
    kind of approach will work on other platforms. Because the
    C standard is meant to apply to platforms where function
    pointers and object pointers have no relationship to each
    other, the C standard does not give permission to convert
    between them.

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From bart@3:633/280.2 to All on Sat Mar 30 22:14:32 2024
    On 30/03/2024 09:32, Tim Rentsch wrote:
    bart <bc@freeuk.com> writes:

    I was aware of the double conversion but KT used 'a cast' so I
    wondered if there was a single cast that could be used.

    There is not, if it's important that it work reliably across
    different compilers and different platforms.

    It is odd however that function and object pointers can be
    considered so different that even an explicit conversion
    between them is deemed to be meaningless.

    Function pointers and object pointers don't have to be the same
    size, or use the same form of representation. The C standard
    allows implementations where code and data live in completely
    separate memories. In such cases there is no sensible way to
    convert between the two kinds of pointers, because the two kinds
    of addresses have no relationship to each other.

    Suppose a object pointer is 32 bits, and a function pointer is a 32-byte descriptor.

    An implementation could choose to present a function pointer as a 32-bit object pointer, which points to the full 32-byte descriptor in data memory.

    The simplest way of doing that is to have, for each function (or each
    one whose address is taken), a fixed corresponding descriptor in data
    memory. So here function and object pointers can be exactly the same
    size, and can both refer to data memory, as far as the programmer is concerned.

    Dereferencing such a function pointer, to call the function, will
    involve an extra bit of indirection. It would need something extra
    anyway to deal with those 32 bytes.



    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Tim Rentsch@3:633/280.2 to All on Sun Mar 31 04:33:31 2024
    scott@slp53.sl.home (Scott Lurndal) writes:

    Kaz Kylheku <433-929-6894@kylheku.com> writes:

    On 2024-03-28, Kenny McCormack <gazelle@shell.xmission.com> wrote:

    But here's the thing. Is the casting of the result of dlsym()
    [to a function pointer type] necessary?
    Isn't this the same as "casting the return value of malloc()", where you

    Conversions between function pointers and data pointers are an
    extension; it is not well-defined behavior in ISO C.

    I also seem to remember something like this, but I cannot trust this
    documentation without a chapter-and-verse citation.

    https://pubs.opengroup.org/onlinepubs/9699919799/functions/dlsym.html

    Assuming it is true, there you have it; if you're on POSIX, the compiler
    is required to have the extension and it is connected to casting,
    in which case the cast is required.

    I've used this form for the last two decades, with gcc, with no
    issues:

    /**
    * Each shared object that simulates a Data Link Processor (DLP), will
    * contain a single namespace-scope function <b>get_dlp</b> which constructs
    * a DLP object of the specified type (for example, a #c_uniline_dlp,
    * #c_card_reader_dlp, et alia). <b>get_dlp</b> returns the constructed
    * object as a #c_dlp object to the #channel function, which is then used
    * by the I/O subsystem to request services of the DLP at the MLI level.
    */
    typedef c_dlp* (*get_dlp_t)(const char *, uint64_t, c_logger *);
    ...
    get_dlp_t sym;
    ...

    sym = (get_dlp_t)dlsym(handle, "get_dlp");
    if (sym == NULL) {
    lp->log("Invalid DLP shared object format: %s\n", dlerror());
    unregister_handle(channel);
    dlclose(handle);
    return 1;
    }


    I think there was a time in the development of GCC when there was
    a warning even with the cast. I don't think it's enabled by default
    now?

    We compile with -Wall -Werror and have never seen any warnings related to casting the result of dlsym(), and we build with GCC[4..13].

    Do you use -pedantic? Compiling with -pedantic using gcc 8.4.0
    gives a warning diagnostic (and a fatal error if -pedantic-errors
    is specified in place of -pedantic).

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Scott Lurndal@3:633/280.2 to All on Sun Mar 31 06:29:50 2024
    Reply-To: slp53@pacbell.net

    Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
    scott@slp53.sl.home (Scott Lurndal) writes:

    Kaz Kylheku <433-929-6894@kylheku.com> writes:

    On 2024-03-28, Kenny McCormack <gazelle@shell.xmission.com> wrote:

    * Each shared object that simulates a Data Link Processor (DLP), will
    * contain a single namespace-scope function <b>get_dlp</b> which constructs >> * a DLP object of the specified type (for example, a #c_uniline_dlp,
    * #c_card_reader_dlp, et alia). <b>get_dlp</b> returns the constructed
    * object as a #c_dlp object to the #channel function, which is then used
    * by the I/O subsystem to request services of the DLP at the MLI level.
    */
    typedef c_dlp* (*get_dlp_t)(const char *, uint64_t, c_logger *);
    ...
    get_dlp_t sym;
    ...

    sym = (get_dlp_t)dlsym(handle, "get_dlp");
    if (sym == NULL) {
    lp->log("Invalid DLP shared object format: %s\n", dlerror());
    unregister_handle(channel);
    dlclose(handle);
    return 1;
    }


    I think there was a time in the development of GCC when there was
    a warning even with the cast. I don't think it's enabled by default
    now?

    We compile with -Wall -Werror and have never seen any warnings related to
    casting the result of dlsym(), and we build with GCC[4..13].

    Do you use -pedantic? Compiling with -pedantic using gcc 8.4.0
    gives a warning diagnostic (and a fatal error if -pedantic-errors
    is specified in place of -pedantic).

    Of course not. We write production code not standard C (or in this
    case, C++) code. Portability to compilers other than gcc is
    not a requirement for the several million line codebase.

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: UsenetServer - www.usenetserver.com (3:633/280.2@fidonet)
  • From Lawrence D'Oliveiro@3:633/280.2 to All on Sun Mar 31 10:05:14 2024
    On Sat, 30 Mar 2024 19:29:50 GMT, Scott Lurndal wrote:

    We write production code not standard C (or in this
    case, C++) code. Portability to compilers other than gcc is
    not a requirement for the several million line codebase.

    Interesting. Has GCC become the de-facto-standard C compiler?

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From David Brown@3:633/280.2 to All on Mon Apr 1 00:44:00 2024
    On 31/03/2024 00:05, Lawrence D'Oliveiro wrote:
    On Sat, 30 Mar 2024 19:29:50 GMT, Scott Lurndal wrote:

    We write production code not standard C (or in this
    case, C++) code. Portability to compilers other than gcc is
    not a requirement for the several million line codebase.

    Interesting. Has GCC become the de-facto-standard C compiler?

    gcc is the most commonly used compiler on Linux and in embedded systems.
    It is not the most common on Windows, AFAIK - that honour goes to
    MSVC. A number of gcc extensions, practices and flags have been copied
    by other compilers (such as clang, icc, and others). A number of
    features that started off as gcc extensions ended up in the C standards
    - more, I think, than for extensions of any other compiler.

    If you consider that to mean gcc is the "de-facto-standard C compiler",
    that's up to you - it is not the standard C compiler in the context of comp.lang.c, where "standard" refers to the C standards, since there is
    no C standard reference compiler. But the term "de-facto standard" is
    so vague and poorly defined that you can use it any way you like,
    without conveying any useful information.

    I can't speak for Scott, but in my work I happily use gcc extensions
    (though I don't use them when standard C does as good a job). I don't
    do that because it is a "de-facto standard" or anything of the sort - I
    use gcc extensions when they let me write better code or give better
    results, since my code does not normally have to be portable to other compilers. If I happen to be working on a project that uses a different compiler, I'll happily use extensions for that compiler too.


    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Chris M. Thomasson@3:633/280.2 to All on Mon Apr 1 07:15:37 2024
    On 3/30/2024 4:05 PM, Lawrence D'Oliveiro wrote:
    On Sat, 30 Mar 2024 19:29:50 GMT, Scott Lurndal wrote:

    We write production code not standard C (or in this
    case, C++) code. Portability to compilers other than gcc is
    not a requirement for the several million line codebase.

    Interesting. Has GCC become the de-facto-standard C compiler?

    I don't think so.

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Tim Rentsch@3:633/280.2 to All on Tue Apr 9 16:01:17 2024
    scott@slp53.sl.home (Scott Lurndal) writes:

    Tim Rentsch <tr.17687@z991.linuxsc.com> writes:

    scott@slp53.sl.home (Scott Lurndal) writes:

    Kaz Kylheku <433-929-6894@kylheku.com> writes:

    On 2024-03-28, Kenny McCormack <gazelle@shell.xmission.com> wrote:

    * Each shared object that simulates a Data Link Processor (DLP),
    * will contain a single namespace-scope function <b>get_dlp</b>
    * which constructs a DLP object of the specified type (for example,
    * a #c_uniline_dlp, #c_card_reader_dlp, et alia). <b>get_dlp</b>
    * returns the constructed object as a #c_dlp object to the #channel
    * function, which is then used by the I/O subsystem to request
    * services of the DLP at the MLI level.
    */
    typedef c_dlp* (*get_dlp_t)(const char *, uint64_t, c_logger *);
    ...
    get_dlp_t sym;
    ...

    sym = (get_dlp_t)dlsym(handle, "get_dlp");
    if (sym == NULL) {
    lp->log("Invalid DLP shared object format: %s\n", dlerror());
    unregister_handle(channel);
    dlclose(handle);
    return 1;
    }


    I think there was a time in the development of GCC when there was
    a warning even with the cast. I don't think it's enabled by default
    now?

    We compile with -Wall -Werror and have never seen any warnings related
    to casting the result of dlsym(), and we build with GCC[4..13].

    Do you use -pedantic? Compiling with -pedantic using gcc 8.4.0
    gives a warning diagnostic (and a fatal error if -pedantic-errors
    is specified in place of -pedantic).

    Of course not. We write production code not standard C (or in this
    case, C++) code. Portability to compilers other than gcc is
    not a requirement for the several million line codebase.

    It's quite amusing to hear of a team that insists on -Wall but
    avoids -pedantic, saving a tiny drizzle of cases (all of which
    are easy to remedy) while enduring the ever-changing force 5
    blizzard of conditions tested by -Wall.

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Tim Rentsch@3:633/280.2 to All on Tue Apr 9 16:41:47 2024
    bart <bc@freeuk.com> writes:

    On 30/03/2024 09:32, Tim Rentsch wrote:

    bart <bc@freeuk.com> writes:

    I was aware of the double conversion but KT used 'a cast' so I
    wondered if there was a single cast that could be used.

    There is not, if it's important that it work reliably across
    different compilers and different platforms.

    It is odd however that function and object pointers can be
    considered so different that even an explicit conversion
    between them is deemed to be meaningless.

    Function pointers and object pointers don't have to be the same
    size, or use the same form of representation. The C standard
    allows implementations where code and data live in completely
    separate memories. In such cases there is no sensible way to
    convert between the two kinds of pointers, because the two kinds
    of addresses have no relationship to each other.

    Suppose a object pointer is 32 bits, and a function pointer is a
    32-byte descriptor.

    An implementation could choose to present a function pointer as a
    32-bit object pointer, which points to the full 32-byte descriptor in
    data memory.

    The simplest way of doing that is to have, for each function (or each
    one whose address is taken), a fixed corresponding descriptor in data
    memory. So here function and object pointers can be exactly the same
    size, and can both refer to data memory, as far as the programmer is concerned.

    Dereferencing such a function pointer, to call the function, will
    involve an extra bit of indirection. It would need something extra
    anyway to deal with those 32 bytes.

    Two problems. One, even if the proposed scheme is workable in some
    cases that doesn't mean it will be in all cases. Two, it imposes
    what may be a significant cost but offers essentially no benefit.
    The only useful thing that can be done with a converted function
    pointer is cast it to an appropriate function pointer type so that
    the function can be called. If someone wants to have values and
    variables that can hold both object pointers and function pointers
    it is easy enough to do that by using a union:

    typedef union { void *pv; void (*pf)(); } VorF;

    with no hidden implementation machinery needed. There is no good
    reason to gussy up the language or have implementations jump
    through hoops when the needed capability is already present in
    the language as it is now (and has been for more than 30 years).

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From David Brown@3:633/280.2 to All on Tue Apr 9 18:03:40 2024
    On 09/04/2024 08:01, Tim Rentsch wrote:
    scott@slp53.sl.home (Scott Lurndal) writes:

    Tim Rentsch <tr.17687@z991.linuxsc.com> writes:

    scott@slp53.sl.home (Scott Lurndal) writes:

    We compile with -Wall -Werror and have never seen any warnings related >>>> to casting the result of dlsym(), and we build with GCC[4..13].

    Do you use -pedantic? Compiling with -pedantic using gcc 8.4.0
    gives a warning diagnostic (and a fatal error if -pedantic-errors
    is specified in place of -pedantic).

    Of course not. We write production code not standard C (or in this
    case, C++) code. Portability to compilers other than gcc is
    not a requirement for the several million line codebase.

    It's quite amusing to hear of a team that insists on -Wall but
    avoids -pedantic, saving a tiny drizzle of cases (all of which
    are easy to remedy) while enduring the ever-changing force 5
    blizzard of conditions tested by -Wall.

    I guess that's because his team writes real-world code using real-world
    tools, rather than living in some sort of fantasy realm or ivory tower.

    gcc's -Wall does not change much from version to version, and not
    without a lot of debate and consideration. A new warning does not get
    added to -Wall unless there is strong confidence and agreement that it
    will have a very low rate of false positives, and the things it catches
    have a high rate of being real problems (not just stylistic choices).
    And part of the beta test procedure involves complete rebuilds of the
    entire Debian and Red Hat source bases - many packages would fail to
    build if changes to -Wall triggered new warnings.

    And most real-world C programs are not strictly limited to fully
    portable standard C. If portability is not an issue, then compiler
    extensions give you a better language - better error checking, more
    efficient results, easier coding, clearer coding, and of course features
    that are tied to the target platform. "-pedantic" is not helpful for
    such code.

    Of course there are also people whose C coding must be strictly
    according to the standards, and there are endless other variants in how
    people use the language, tools and flags. But I expect the use of
    "-Wall" without "-Wpedantic" to be very much more common than the opposite.




    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)