Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.c++ > #118835 > unrolled thread

Proper cast of function pointers

Started byPaavo Helde <eesnimi@osa.pri.ee>
First post2024-04-23 14:31 +0300
Last post2024-04-25 09:50 +0200
Articles 20 on this page of 41 — 7 participants

Back to article view | Back to comp.lang.c++


Contents

  Proper cast of function pointers Paavo Helde <eesnimi@osa.pri.ee> - 2024-04-23 14:31 +0300
    Re: Proper cast of function pointers Bonita Montero <Bonita.Montero@gmail.com> - 2024-04-23 13:44 +0200
      Re: Proper cast of function pointers Paavo Helde <eesnimi@osa.pri.ee> - 2024-04-23 21:33 +0300
        Re: Proper cast of function pointers David Brown <david.brown@hesbynett.no> - 2024-04-24 09:33 +0200
        Re: Proper cast of function pointers Bonita Montero <Bonita.Montero@gmail.com> - 2024-04-24 09:36 +0200
          Re: Proper cast of function pointers Paavo Helde <eesnimi@osa.pri.ee> - 2024-04-24 15:32 +0300
            Re: Proper cast of function pointers Tim Rentsch <tr.17687@z991.linuxsc.com> - 2024-04-24 15:10 -0700
              Re: Proper cast of function pointers "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> - 2024-04-24 15:14 -0700
              Re: Proper cast of function pointers Paavo Helde <eesnimi@osa.pri.ee> - 2024-04-25 08:37 +0300
                Re: Proper cast of function pointers Tim Rentsch <tr.17687@z991.linuxsc.com> - 2024-04-25 15:04 -0700
            Re: Proper cast of function pointers Bonita Montero <Bonita.Montero@gmail.com> - 2024-04-25 11:19 +0200
              Re: Proper cast of function pointers Bonita Montero <Bonita.Montero@gmail.com> - 2024-04-25 11:39 +0200
                Re: Proper cast of function pointers Bonita Montero <Bonita.Montero@gmail.com> - 2024-04-25 11:48 +0200
                  Re: Proper cast of function pointers Paavo Helde <eesnimi@osa.pri.ee> - 2024-04-25 22:22 +0300
                    Re: Proper cast of function pointers Bonita Montero <Bonita.Montero@gmail.com> - 2024-04-26 07:52 +0200
        Re: Proper cast of function pointers Tim Rentsch <tr.17687@z991.linuxsc.com> - 2024-04-24 14:40 -0700
    Re: Proper cast of function pointers Markus Schaaf <mschaaf@elaboris.de> - 2024-04-23 14:23 +0200
      Re: Proper cast of function pointers David Brown <david.brown@hesbynett.no> - 2024-04-23 16:44 +0200
        Re: Proper cast of function pointers Markus Schaaf <mschaaf@elaboris.de> - 2024-04-23 17:00 +0200
          Re: Proper cast of function pointers David Brown <david.brown@hesbynett.no> - 2024-04-24 09:55 +0200
          Re: Proper cast of function pointers Bonita Montero <Bonita.Montero@gmail.com> - 2024-04-24 13:11 +0200
            Re: Proper cast of function pointers David Brown <david.brown@hesbynett.no> - 2024-04-24 13:15 +0200
        Re: Proper cast of function pointers Bonita Montero <Bonita.Montero@gmail.com> - 2024-04-24 13:10 +0200
          Re: Proper cast of function pointers David Brown <david.brown@hesbynett.no> - 2024-04-24 13:23 +0200
            Re: Proper cast of function pointers Bonita Montero <Bonita.Montero@gmail.com> - 2024-04-24 17:06 +0200
              Re: Proper cast of function pointers David Brown <david.brown@hesbynett.no> - 2024-04-24 17:59 +0200
                Re: Proper cast of function pointers Bonita Montero <Bonita.Montero@gmail.com> - 2024-04-24 19:36 +0200
      Re: Proper cast of function pointers Paavo Helde <eesnimi@osa.pri.ee> - 2024-04-23 21:50 +0300
        Re: Proper cast of function pointers Markus Schaaf <mschaaf@elaboris.de> - 2024-04-23 22:18 +0200
          Re: Proper cast of function pointers Markus Schaaf <mschaaf@elaboris.de> - 2024-04-23 22:22 +0200
          Re: Proper cast of function pointers Paavo Helde <eesnimi@osa.pri.ee> - 2024-04-23 23:33 +0300
    Re: Proper cast of function pointers David Brown <david.brown@hesbynett.no> - 2024-04-23 16:44 +0200
      Re: Proper cast of function pointers Bonita Montero <Bonita.Montero@gmail.com> - 2024-04-24 11:27 +0200
        Re: Proper cast of function pointers David Brown <david.brown@hesbynett.no> - 2024-04-24 13:14 +0200
          Re: Proper cast of function pointers Bo Persson <bo@bo-persson.se> - 2024-04-24 14:00 +0200
            Re: Proper cast of function pointers David Brown <david.brown@hesbynett.no> - 2024-04-24 16:41 +0200
          Re: Proper cast of function pointers Bonita Montero <Bonita.Montero@gmail.com> - 2024-04-24 17:07 +0200
            Re: Proper cast of function pointers David Brown <david.brown@hesbynett.no> - 2024-04-24 18:02 +0200
              Re: Proper cast of function pointers Bonita Montero <Bonita.Montero@gmail.com> - 2024-04-24 19:36 +0200
                Re: Proper cast of function pointers David Brown <david.brown@hesbynett.no> - 2024-04-24 21:18 +0200
                  Re: Proper cast of function pointers Bonita Montero <Bonita.Montero@gmail.com> - 2024-04-25 09:50 +0200

Page 1 of 3  [1] 2 3  Next page →


#118835 — Proper cast of function pointers

FromPaavo Helde <eesnimi@osa.pri.ee>
Date2024-04-23 14:31 +0300
SubjectProper cast of function pointers
Message-ID<v08672$1jh9c$1@dont-email.me>
There is an old third-party library where some function pointers are 
casted to another type, then back to the original type before use. C++ 
standard says this is kosher, and there have never been any problems 
with actual behavior. Alas, different versions and compile modes of g++ 
still produce warnings. What would be the best way to silence them 
(without just switching off warnings)?


Simplified example:

typedef double (*Func)(double);
typedef double (*DoubleFunc_2_args)(double, double);

Func FuncCast2(DoubleFunc_2_args fp) {
	return reinterpret_cast<Func>(fp);
}

$ g++ test1.cpp -Wall -Wextra
test1.cpp: In function ‘double (* FuncCast2(DoubleFunc_2_args))(double)’:
test1.cpp:26:34: warning: cast between incompatible function types from 
‘DoubleFunc_2_args’ {aka ‘double (*)(double, double)’} to ‘Func’ {aka 
‘double (*)(double)’} [-Wcast-function-type]
   return reinterpret_cast<Func>(fp);


If I change the function to use more indirection, then there is a 
warning with -O2 only:

Func FuncCast2(DoubleFunc_2_args fp) {
     return *reinterpret_cast<Func*>(&fp);
}

$ g++ test1.cpp -Wall -Wextra -O2
test1.cpp: In function ‘double (* FuncCast2(DoubleFunc_2_args))(double)’:
test1.cpp:22:10: warning: dereferencing type-punned pointer will break 
strict-aliasing rules [-Wstrict-aliasing]
   return *reinterpret_cast<Func*>(&fp);
           ^~~~~~~~~~~~~~~~~~~~~~~~~~~~

$ g++ --version
g++ (Debian 10.2.1-6) 10.2.1 20210110

[toc] | [next] | [standalone]


#118836

FromBonita Montero <Bonita.Montero@gmail.com>
Date2024-04-23 13:44 +0200
Message-ID<v086ut$1jkn4$1@raubtier-asyl.eternal-september.org>
In reply to#118835
Am 23.04.2024 um 13:31 schrieb Paavo Helde:
> 
> There is an old third-party library where some function pointers are 
> casted to another type, then back to the original type before use.
> C++  standard says this is kosher, and there have never been any
> problems  with actual behavior. Alas, different versions and compile
> modes of g++ still produce warnings. ...

That's because of the danger that someone calls the function-pointer
to which you cast. Maybe you can cast through a void-pointer to sup-
press this warning. But for me casting to a function pointer of a
differnt type doesn't make sense at at..
I think you confront yourself to uncertainties which actually never
happen.

[toc] | [prev] | [next] | [standalone]


#118843

FromPaavo Helde <eesnimi@osa.pri.ee>
Date2024-04-23 21:33 +0300
Message-ID<v08ut6$1p6m1$1@dont-email.me>
In reply to#118836
23.04.2024 14:44 Bonita Montero kirjutas:
> Am 23.04.2024 um 13:31 schrieb Paavo Helde:
>>
>> There is an old third-party library where some function pointers are 
>> casted to another type, then back to the original type before use.
>> C++  standard says this is kosher, and there have never been any
>> problems  with actual behavior. Alas, different versions and compile
>> modes of g++ still produce warnings. ...
> 
> That's because of the danger that someone calls the function-pointer
> to which you cast. Maybe you can cast through a void-pointer to sup-
> press this warning. But for me casting to a function pointer of a
> differnt type doesn't make sense at at..
> I think you confront yourself to uncertainties which actually never
> happen.
> 

The function pointers are cast to a single type so that they can be 
stored in a common lookup array. I could use a union there, but this 
would mean additional work with no real benefit, as the hypothetical 
"someone" could just as easily mess up the unions than the casting.

[toc] | [prev] | [next] | [standalone]


#118852

FromDavid Brown <david.brown@hesbynett.no>
Date2024-04-24 09:33 +0200
Message-ID<v0acjo$26ofk$1@dont-email.me>
In reply to#118843
On 23/04/2024 20:33, Paavo Helde wrote:
> 23.04.2024 14:44 Bonita Montero kirjutas:
>> Am 23.04.2024 um 13:31 schrieb Paavo Helde:
>>>
>>> There is an old third-party library where some function pointers are 
>>> casted to another type, then back to the original type before use.
>>> C++  standard says this is kosher, and there have never been any
>>> problems  with actual behavior. Alas, different versions and compile
>>> modes of g++ still produce warnings. ...
>>
>> That's because of the danger that someone calls the function-pointer
>> to which you cast. Maybe you can cast through a void-pointer to sup-
>> press this warning. But for me casting to a function pointer of a
>> differnt type doesn't make sense at at..
>> I think you confront yourself to uncertainties which actually never
>> happen.
>>
> 
> The function pointers are cast to a single type so that they can be 
> stored in a common lookup array. I could use a union there, but this 
> would mean additional work with no real benefit, as the hypothetical 
> "someone" could just as easily mess up the unions than the casting.

You could consider using std::variant<>, but you need to know the 
possible function types in advance (that may or may not be an issue for 
you) and it stores an index to in the variant object.  Again, that may 
or may not be a useful thing for you.

Otherwise I agree with you that casting to a common "void (*)(void)" 
pointer type seems reasonable here.  Just disable the warning around the 
casts.

[toc] | [prev] | [next] | [standalone]


#118853

FromBonita Montero <Bonita.Montero@gmail.com>
Date2024-04-24 09:36 +0200
Message-ID<v0acqb$26rt3$1@raubtier-asyl.eternal-september.org>
In reply to#118843
Am 23.04.2024 um 20:33 schrieb Paavo Helde:

> The function pointers are cast to a single type so that they can be 
> stored in a common lookup array. ...

Then you'd need additional information to distinguish the different
types. If you have sth. like that you could take a variant<>.

[toc] | [prev] | [next] | [standalone]


#118862

FromPaavo Helde <eesnimi@osa.pri.ee>
Date2024-04-24 15:32 +0300
Message-ID<v0au56$2amfo$1@dont-email.me>
In reply to#118853
24.04.2024 10:36 Bonita Montero kirjutas:
> Am 23.04.2024 um 20:33 schrieb Paavo Helde:
> 
>> The function pointers are cast to a single type so that they can be 
>> stored in a common lookup array. ...
> 
> Then you'd need additional information to distinguish the different
> types. If you have sth. like that you could take a variant<>.

Right, the varying part is the number of arguments, which is explicitly 
declared and stored in the array as well (n_pars below). If you are 
interested, the current code (no warnings any more here, thanks to 
Markus!) looks like below. Not sure if changing to a variant or 
void(*)() would make the code better, looks like then I would need to 
add extra casts to all the lines in the table which currently do not 
need any casts.

#include <math.h>

typedef double (*Func)(double);

struct formu_item {
   const char *name;
   Func f;    /* pointer to function*/
   int n_pars; /* number of parameters (0, 1, 2 or 3) */
   int varying; /* Does the result of the function vary
		  even when the parameters stay the same?
		  varying=1 for e.g. random-number generators. */
};

typedef void (*VoidFunc)();
typedef double (*DoubleFunc_0_args)();
typedef double (*DoubleFunc_2_args)(double, double);

Func FuncCast2(DoubleFunc_2_args fp) {
	return reinterpret_cast<Func>(reinterpret_cast<VoidFunc>(fp));
}
Func FuncCast0(DoubleFunc_0_args fp) {
	return reinterpret_cast<Func>(reinterpret_cast<VoidFunc>(fp));
}
double pi() {
  return 3.1415926535897932384626433832795029;
}

static const formu_item ftable_static[TABLESIZE]=
{
   {"exp", exp,1,0},
   {"ln",  log,1,0},
   {"sin", sin,1,0},
   {"cos", cos,1,0},
   {"tan", tan,1,0},
   {"asin", asin,1,0},
   {"acos", acos,1,0},
   {"atan", atan,1,0},
   {"atan2", FuncCast2(atan2),2,0},
   {"abs",  fabs,1,0},
   {"sqrt",  sqrt,1,0},
   {"pi", FuncCast0(pi),0,0},
//...
}

[toc] | [prev] | [next] | [standalone]


#118879

FromTim Rentsch <tr.17687@z991.linuxsc.com>
Date2024-04-24 15:10 -0700
Message-ID<86frvawgwj.fsf@linuxsc.com>
In reply to#118862
Paavo Helde <eesnimi@osa.pri.ee> writes:

> 24.04.2024 10:36 Bonita Montero kirjutas:
>
>> Am 23.04.2024 um 20:33 schrieb Paavo Helde:
>>
>>> The function pointers are cast to a single type so that they can be
>>> stored in a common lookup array. ...
>>
>> Then you'd need additional information to distinguish the different
>> types.  If you have sth.  like that you could take a variant<>.
>
> Right, the varying part is the number of arguments, which is
> explicitly declared and stored in the array as well (n_pars below).  If
> you are interested, the current code (no warnings any more here,
> thanks to Markus!) looks like below.  Not sure if changing to a variant
> or void(*)() would make the code better, looks like then I would need
> to add extra casts to all the lines in the table which currently do
> not need any casts.
>
> #include <math.h>
>
> typedef double (*Func)(double);
>
> struct formu_item {
>   const char *name;
>   Func f;    /* pointer to function*/
>   int n_pars; /* number of parameters (0, 1, 2 or 3) */
>   int varying; /* Does the result of the function vary
>           even when the parameters stay the same?
>           varying=1 for e.g. random-number generators. */
> };
>
> typedef void (*VoidFunc)();
> typedef double (*DoubleFunc_0_args)();
> typedef double (*DoubleFunc_2_args)(double, double);
>
> Func FuncCast2(DoubleFunc_2_args fp) {
>     return reinterpret_cast<Func>(reinterpret_cast<VoidFunc>(fp));
> }
> Func FuncCast0(DoubleFunc_0_args fp) {
>     return reinterpret_cast<Func>(reinterpret_cast<VoidFunc>(fp));
> }
> double pi() {
>  return 3.1415926535897932384626433832795029;
> }
>
> static const formu_item ftable_static[TABLESIZE]=
> {
>   {"exp", exp,1,0},
>   {"ln",  log,1,0},
>   {"sin", sin,1,0},
>   {"cos", cos,1,0},
>   {"tan", tan,1,0},
>   {"asin", asin,1,0},
>   {"acos", acos,1,0},
>   {"atan", atan,1,0},
>   {"atan2", FuncCast2(atan2),2,0},
>   {"abs",  fabs,1,0},
>   {"sqrt",  sqrt,1,0},
>   {"pi", FuncCast0(pi),0,0},
> //...
> }

The code below uses no casting, and encapsulates the constructors
for 'formu_item's so that the functions are guaranteed to be in
sync with the discriminating member of the struct.  The names and
types of members in formu_item have been changed slightly in some
cases, but except for that it should drop in to the existing code
pretty easily.  The final function shows how to invoke a function
in the table safely.

I have added a few bits of running commentary.

Code compiles cleanly (if I haven't made any editing mistakes)
with -pedantic -Wall -Wextra, under c++11, c++14, and c++17.


union FuncU {
    double (*zero)();
    double (*one)( double );
    double (*two)( double, double );
    double (*three)( double, double, double );

    constexpr FuncU( double (*f)() ) : zero( f ) {}
    constexpr FuncU( double (*f)( double ) ) : one( f ) {}
    constexpr FuncU( double (*f)( double, double ) ) : two( f ) {}
    constexpr FuncU( double (*f)( double, double, double ) ) : three( f ) {}

 // a member for each kind of function, and
 // a constructor for each of the different function kinds

};


typedef enum {
    A_ZERO, A_ONE, A_TWO, A_THREE,

 // I use an enum rather than an int

} FKind;


struct formu_item {
    const char  *name;
    FKind        n_pars;
    bool         varying;
    FuncU        fu;
};


// next is the core idea - have a type-safe constructor
// for each of the different kinds of functions

constexpr formu_item
zero_f( const char *name, bool varying, double (*f)() ){
    return  { name, A_ZERO, varying, FuncU( f ) };
}

constexpr formu_item
one_f( const char *name, bool varying, double (*f)( double ) ){
    return  { name, A_ONE, varying, FuncU( f ) };
}

constexpr formu_item
two_f( const char *name, bool varying, double (*f)( double, double ) ){
    return  { name, A_TWO, varying, FuncU( f ) };
}

typedef double (*Fdouble3)( double, double, double );

constexpr formu_item
three_f( const char *name, bool varying, Fdouble3 f ){
    return  { name, A_THREE, varying, FuncU( f ) };
}

#include <math.h>

static double pi(){
    return  3.1415926535897932384626433832795029;
}

static const formu_item ftable_static[]= {
    one_f(  "exp",   0, exp   ),
    one_f(  "ln",    0, log   ),
    one_f(  "sin",   0, sin   ),
    one_f(  "cos",   0, cos   ),
    one_f(  "tan",   0, tan   ),
    one_f(  "asin",  0, asin  ),
    one_f(  "acos",  0, acos  ),
    one_f(  "atan",  0, atan  ),
    two_f(  "atan2", 0, atan2 ),
    one_f(  "abs",   0, fabs  ),
    one_f(  "sqrt",  0, sqrt  ),
    zero_f( "pi",    0, pi    ),

 // the function table.  Note that if the supplied function
 // doesn't match the associated constructor then there will
 // be a compilation error

};


double
invoke_formula( unsigned k, double a, double b, double c ){
    unsigned n = sizeof ftable_static / sizeof ftable_static[0];
    if(  k >= n  )  return  0. / 0.;  // k too large => NaN

    switch(  ftable_static[k].n_pars  ){
        case  A_ZERO:   return  ftable_static[k].fu.zero();
        case  A_ONE:    return  ftable_static[k].fu.one(   a );
        case  A_TWO:    return  ftable_static[k].fu.two(   a, b );
        case  A_THREE:  return  ftable_static[k].fu.three( a, b, c );
    }

    return  -1. / 0.;  // table messed up => infinity
}

[toc] | [prev] | [next] | [standalone]


#118880

From"Chris M. Thomasson" <chris.m.thomasson.1@gmail.com>
Date2024-04-24 15:14 -0700
Message-ID<v0c07i$2i9oo$1@dont-email.me>
In reply to#118879
On 4/24/2024 3:10 PM, Tim Rentsch wrote:
> Paavo Helde <eesnimi@osa.pri.ee> writes:
> 
>> 24.04.2024 10:36 Bonita Montero kirjutas:
>>
>>> Am 23.04.2024 um 20:33 schrieb Paavo Helde:
>>>
>>>> The function pointers are cast to a single type so that they can be
>>>> stored in a common lookup array. ...
>>>
>>> Then you'd need additional information to distinguish the different
>>> types.  If you have sth.  like that you could take a variant<>.
>>
>> Right, the varying part is the number of arguments, which is
>> explicitly declared and stored in the array as well (n_pars below).  If
>> you are interested, the current code (no warnings any more here,
>> thanks to Markus!) looks like below.  Not sure if changing to a variant
>> or void(*)() would make the code better, looks like then I would need
>> to add extra casts to all the lines in the table which currently do
>> not need any casts.
>>
>> #include <math.h>
>>
>> typedef double (*Func)(double);
>>
>> struct formu_item {
>>    const char *name;
>>    Func f;    /* pointer to function*/
>>    int n_pars; /* number of parameters (0, 1, 2 or 3) */
>>    int varying; /* Does the result of the function vary
>>            even when the parameters stay the same?
>>            varying=1 for e.g. random-number generators. */
>> };
>>
>> typedef void (*VoidFunc)();
>> typedef double (*DoubleFunc_0_args)();
>> typedef double (*DoubleFunc_2_args)(double, double);
>>
>> Func FuncCast2(DoubleFunc_2_args fp) {
>>      return reinterpret_cast<Func>(reinterpret_cast<VoidFunc>(fp));
>> }
>> Func FuncCast0(DoubleFunc_0_args fp) {
>>      return reinterpret_cast<Func>(reinterpret_cast<VoidFunc>(fp));
>> }
>> double pi() {
>>   return 3.1415926535897932384626433832795029;
>> }
>>
>> static const formu_item ftable_static[TABLESIZE]=
[...]

For some damn reason this makes me think of this older code:

https://groups.google.com/g/comp.lang.c/c/8X_an3MpDYQ/m/TYw70DcnBQAJ

https://pastebin.com/raw/f52a443b1

[toc] | [prev] | [next] | [standalone]


#118882

FromPaavo Helde <eesnimi@osa.pri.ee>
Date2024-04-25 08:37 +0300
Message-ID<v0cq63$2r80s$1@dont-email.me>
In reply to#118879
25.04.2024 01:10 Tim Rentsch kirjutas:
> Paavo Helde <eesnimi@osa.pri.ee> writes:
> 
[...]
>> static const formu_item ftable_static[TABLESIZE]=
>> {
>>    {"exp", exp,1,0},
>>    {"ln",  log,1,0},
>>    {"sin", sin,1,0},
>>    {"cos", cos,1,0},
>>    {"tan", tan,1,0},
>>    {"asin", asin,1,0},
>>    {"acos", acos,1,0},
>>    {"atan", atan,1,0},
>>    {"atan2", FuncCast2(atan2),2,0},
>>    {"abs",  fabs,1,0},
>>    {"sqrt",  sqrt,1,0},
>>    {"pi", FuncCast0(pi),0,0},
>> //...
>> }
> 
> The code below uses no casting, and encapsulates the constructors
> for 'formu_item's so that the functions are guaranteed to be in
> sync with the discriminating member of the struct.  The names and
> types of members in formu_item have been changed slightly in some
> cases, but except for that it should drop in to the existing code
> pretty easily.  The final function shows how to invoke a function
> in the table safely.


Indeed. Somehow I was convinced that when providing multiple 
constructors, the compiler would fail with ambiguity errors because all 
of these functions like sin() are overloaded in C++. But it seems the 
compiler figures it out nicely.

> 
> I have added a few bits of running commentary.
> 
> Code compiles cleanly (if I haven't made any editing mistakes)
> with -pedantic -Wall -Wextra, under c++11, c++14, and c++17.

This is not exactly true, when compiling with VS2022 I get two compile
errors:

    if (k >= n)  return  0. / 0.;  // k too large => NaN

1>C:\Test\ConsoleTestVS2022\ConsoleTest2022\main.cpp(97,18): error 
C2124: divide or mod by zero

But that's fully another topic.

Thanks for the demo code!
BR

[toc] | [prev] | [next] | [standalone]


#118890

FromTim Rentsch <tr.17687@z991.linuxsc.com>
Date2024-04-25 15:04 -0700
Message-ID<8634r9w139.fsf@linuxsc.com>
In reply to#118882
Paavo Helde <eesnimi@osa.pri.ee> writes:

> 25.04.2024 01:10 Tim Rentsch kirjutas:
>
>> Paavo Helde <eesnimi@osa.pri.ee> writes:
>
> [...]
>
>>> static const formu_item ftable_static[TABLESIZE]=
>>> {
>>>    {"exp", exp,1,0},
>>>    {"ln",  log,1,0},
>>>    {"sin", sin,1,0},
>>>    {"cos", cos,1,0},
>>>    {"tan", tan,1,0},
>>>    {"asin", asin,1,0},
>>>    {"acos", acos,1,0},
>>>    {"atan", atan,1,0},
>>>    {"atan2", FuncCast2(atan2),2,0},
>>>    {"abs",  fabs,1,0},
>>>    {"sqrt",  sqrt,1,0},
>>>    {"pi", FuncCast0(pi),0,0},
>>> //...
>>> }
>>
>> The code below uses no casting, and encapsulates the constructors
>> for 'formu_item's so that the functions are guaranteed to be in
>> sync with the discriminating member of the struct.  The names and
>> types of members in formu_item have been changed slightly in some
>> cases, but except for that it should drop in to the existing code
>> pretty easily.  The final function shows how to invoke a function
>> in the table safely.
>
> Indeed.  Somehow I was convinced that when providing multiple
> constructors, the compiler would fail with ambiguity errors because
> all of these functions like sin() are overloaded in C++.  But it seems
> the compiler figures it out nicely.

As it turns out I may have complicated the question by using
<math.h> rather than <cmath>.  However, upon trying again with
<cmath> and with both <math.h> and <cmath> I learned that
overload resolution is indeed smart enough to figure out which
function matches.  I expect this works because exact matches
always take precedence.

>> I have added a few bits of running commentary.
>>
>> Code compiles cleanly (if I haven't made any editing mistakes)
>> with -pedantic -Wall -Wextra, under c++11, c++14, and c++17.
>
> This is not exactly true, when compiling with VS2022 I get two compile
> errors:
>
>    if (k >= n)  return  0. / 0.;  // k too large => NaN
>
> 1>C:\Test\ConsoleTestVS2022\ConsoleTest2022\main.cpp(97,18): error
> C2124: divide or mod by zero

How strange.  In C it would work (and it does work under gcc
and clang).  Apparently the C++ standard is fuzzier about what
is required for floating-point constant expressions.

> But that's fully another topic.

Right.  Also I expect the limitation is easy to get around, if
that is important (and here it really wasn't).

> Thanks for the demo code!

You are most welcome.  I am definitely a proponent of avoiding
casts whenever possible.

[toc] | [prev] | [next] | [standalone]


#118884

FromBonita Montero <Bonita.Montero@gmail.com>
Date2024-04-25 11:19 +0200
Message-ID<v0d767$2u00j$1@raubtier-asyl.eternal-september.org>
In reply to#118862
Wouldn't that be more convenient ?

     static vector<pair<char const *, function<double ( double, double, 
double )>>> fns;
     fns.reserve( 12 );
     fns.emplace_back( "exp", +[]( double num, double, double ) -> 
double { return exp( num ); } );
     fns.emplace_back( "ln", +[]( double num, double, double ) -> double 
{ return log( num ); } );
     fns.emplace_back( "sin", +[]( double num, double, double ) -> 
double { return sin( num ); } );
     fns.emplace_back( "cos", +[]( double num, double, double ) -> 
double { return cos( num ); } );
     fns.emplace_back( "tan", +[]( double num, double, double ) -> 
double { return tan( num ); } );
     fns.emplace_back( "asin", +[]( double num, double, double ) -> 
double { return asin( num ); } );
     fns.emplace_back( "acos", +[]( double num, double, double ) -> 
double { return acos( num ); } );
     fns.emplace_back( "atan", +[]( double num, double, double ) -> 
double { return atan( num ); } );
     fns.emplace_back( "atan2", +[]( double x, double y, double ) -> 
double { return atan2( x, y ); } );
     fns.emplace_back( "abs", +[]( double num, double, double ) -> 
double { return abs( num ); } );
     fns.emplace_back( "sqrt", +[]( double num, double, double ) -> 
double { return sqrt( num ); } );
     fns.emplace_back( "pi", +[]( double, double, double ) -> double { 
return 3.14; } );

[toc] | [prev] | [next] | [standalone]


#118885

FromBonita Montero <Bonita.Montero@gmail.com>
Date2024-04-25 11:39 +0200
Message-ID<v0d8cl$2u75r$1@raubtier-asyl.eternal-september.org>
In reply to#118884
Am 25.04.2024 um 11:19 schrieb Bonita Montero:
> Wouldn't that be more convenient ?
> 
>      static vector<pair<char const *, function<double ( double, double, 
> double )>>> fns;
>      fns.reserve( 12 );
>      fns.emplace_back( "exp", +[]( double num, double, double ) -> 
> double { return exp( num ); } );
>      fns.emplace_back( "ln", +[]( double num, double, double ) -> double 
> { return log( num ); } );
>      fns.emplace_back( "sin", +[]( double num, double, double ) -> 
> double { return sin( num ); } );
>      fns.emplace_back( "cos", +[]( double num, double, double ) -> 
> double { return cos( num ); } );
>      fns.emplace_back( "tan", +[]( double num, double, double ) -> 
> double { return tan( num ); } );
>      fns.emplace_back( "asin", +[]( double num, double, double ) -> 
> double { return asin( num ); } );
>      fns.emplace_back( "acos", +[]( double num, double, double ) -> 
> double { return acos( num ); } );
>      fns.emplace_back( "atan", +[]( double num, double, double ) -> 
> double { return atan( num ); } );
>      fns.emplace_back( "atan2", +[]( double x, double y, double ) -> 
> double { return atan2( x, y ); } );
>      fns.emplace_back( "abs", +[]( double num, double, double ) -> 
> double { return abs( num ); } );
>      fns.emplace_back( "sqrt", +[]( double num, double, double ) -> 
> double { return sqrt( num ); } );
>      fns.emplace_back( "pi", +[]( double, double, double ) -> double { 
> return 3.14; } );

Or somewhat simpler:

     vector<pair<char const *, double (*)( double, double, double )>> fns;
     fns.reserve( 12 );
     fns.emplace_back( "exp", +[]( double num, double, double ) -> 
double { return exp( num ); } );
     fns.emplace_back( "ln", +[]( double num, double, double ) -> double 
{ return log( num ); } );
     fns.emplace_back( "sin", +[]( double num, double, double ) -> 
double { return sin( num ); } );
     fns.emplace_back( "cos", +[]( double num, double, double ) -> 
double { return cos( num ); } );
     fns.emplace_back( "tan", +[]( double num, double, double ) -> 
double { return tan( num ); } );
     fns.emplace_back( "asin", +[]( double num, double, double ) -> 
double { return asin( num ); } );
     fns.emplace_back( "acos", +[]( double num, double, double ) -> 
double { return acos( num ); } );
     fns.emplace_back( "atan", +[]( double num, double, double ) -> 
double { return atan( num ); } );
     fns.emplace_back( "atan2", +[]( double x, double y, double ) -> 
double { return atan2( x, y ); } );
     fns.emplace_back( "abs", +[]( double num, double, double ) -> 
double { return abs( num ); } );
     fns.emplace_back( "sqrt", +[]( double num, double, double ) -> 
double { return sqrt( num ); } );
     fns.emplace_back( "pi", +[]( double, double, double ) -> double { 
return 3.14; } );

[toc] | [prev] | [next] | [standalone]


#118886

FromBonita Montero <Bonita.Montero@gmail.com>
Date2024-04-25 11:48 +0200
Message-ID<v0d8ss$2ube3$1@raubtier-asyl.eternal-september.org>
In reply to#118885
So this takes no CPU-time at all:

     static pair<char const *, double (*)( double, double, double )> 
const fns[] =
     {
         { "exp", []( double num, double, double ) -> double { return 
exp( num ); } },
         { "ln", []( double num, double, double ) -> double { return 
log( num ); } },
         { "sin", []( double num, double, double ) -> double { return 
sin( num ); } },
         { "cos", []( double num, double, double ) -> double { return 
cos( num ); } },
         { "tan", []( double num, double, double ) -> double { return 
tan( num ); } },
         { "asin", []( double num, double, double ) -> double { return 
asin( num ); } },
         { "acos", []( double num, double, double ) -> double { return 
acos( num ); } },
         { "atan", []( double num, double, double ) -> double { return 
atan( num ); } },
         { "atan2", []( double x, double y, double ) -> double { return 
atan2( x, y ); } },
         { "abs", []( double num, double, double ) -> double { return 
abs( num ); } },
         { "sqrt", []( double num, double, double ) -> double { return 
sqrt( num ); } },
         { "pi", []( double, double, double ) -> double { return 3.14; } }
     };

[toc] | [prev] | [next] | [standalone]


#118887

FromPaavo Helde <eesnimi@osa.pri.ee>
Date2024-04-25 22:22 +0300
Message-ID<v0eahr$35tsn$1@dont-email.me>
In reply to#118886
25.04.2024 12:48 Bonita Montero kirjutas:
> So this takes no CPU-time at all:
> 
>      static pair<char const *, double (*)( double, double, double )> 
> const fns[] =
>      {
>          { "exp", []( double num, double, double ) -> double { return 
> exp( num ); } },
>          { "ln", []( double num, double, double ) -> double { return 
> log( num ); } },
>          { "sin", []( double num, double, double ) -> double { return 
> sin( num ); } },
>          { "cos", []( double num, double, double ) -> double { return 
> cos( num ); } },
>          { "tan", []( double num, double, double ) -> double { return 
> tan( num ); } },
>          { "asin", []( double num, double, double ) -> double { return 
> asin( num ); } },
>          { "acos", []( double num, double, double ) -> double { return 
> acos( num ); } },
>          { "atan", []( double num, double, double ) -> double { return 
> atan( num ); } },
>          { "atan2", []( double x, double y, double ) -> double { return 
> atan2( x, y ); } },
>          { "abs", []( double num, double, double ) -> double { return 
> abs( num ); } },
>          { "sqrt", []( double num, double, double ) -> double { return 
> sqrt( num ); } },
>          { "pi", []( double, double, double ) -> double { return 3.14; } }
>      };

Looks cleaner, but for using with the current code the correct number of 
arguments would still be needed to stored separately, as this is the 
only thing which tells the expression evaluator how many arguments to 
pop out of the operand stack.

Current usage goes about like this:

      switch(ftable[*rcode].n_pars) {
      case 0:
		 *bufp++ = (*BackCast0(ftable[*rcode++].f))();
		 break;
      case 1:
		 x = *--bufp;
		 *bufp++ = ftable[*rcode++].f(x);
		 break;
      case 2:
		 y = *--bufp;
		 x = *--bufp;
		 *bufp++ = (*BackCast2(ftable[*rcode++].f))(x,y);
		 break;
      case 3:
		 z = *--bufp;
		 y = *--bufp;
		 x = *--bufp;
		 *bufp++ = (*BackCast3(ftable[*rcode++].f))(x,y,z);
		 break;
      }

BackCast* are just another function pointer casts, e.g.

DoubleFunc_2_args BackCast2(Func fp) {
	return reinterpret_cast<DoubleFunc_2_args>(reinterpret_cast<VoidFunc>(fp));
}

With your proposal this code could be indeed shortened and there would 
be no need for casts ;-)

switch(ftable[*rcode].n_pars) {
case 3:
     z = *--bufp;
     [[fallthrough]];
case 2:
     y = *--bufp;
     [[fallthrough]];
case 1:
     x = *--bufp;
}
*bufp++ = ftable[*rcode++].f(x,y,z);


[toc] | [prev] | [next] | [standalone]


#118893

FromBonita Montero <Bonita.Montero@gmail.com>
Date2024-04-26 07:52 +0200
Message-ID<v0ffed$3h1d8$1@raubtier-asyl.eternal-september.org>
In reply to#118887
Am 25.04.2024 um 21:22 schrieb Paavo Helde:
> 25.04.2024 12:48 Bonita Montero kirjutas:
>> So this takes no CPU-time at all:
>>
>>      static pair<char const *, double (*)( double, double, double )> 
>> const fns[] =
>>      {
>>          { "exp", []( double num, double, double ) -> double { return 
>> exp( num ); } },
>>          { "ln", []( double num, double, double ) -> double { return 
>> log( num ); } },
>>          { "sin", []( double num, double, double ) -> double { return 
>> sin( num ); } },
>>          { "cos", []( double num, double, double ) -> double { return 
>> cos( num ); } },
>>          { "tan", []( double num, double, double ) -> double { return 
>> tan( num ); } },
>>          { "asin", []( double num, double, double ) -> double { return 
>> asin( num ); } },
>>          { "acos", []( double num, double, double ) -> double { return 
>> acos( num ); } },
>>          { "atan", []( double num, double, double ) -> double { return 
>> atan( num ); } },
>>          { "atan2", []( double x, double y, double ) -> double { 
>> return atan2( x, y ); } },
>>          { "abs", []( double num, double, double ) -> double { return 
>> abs( num ); } },
>>          { "sqrt", []( double num, double, double ) -> double { return 
>> sqrt( num ); } },
>>          { "pi", []( double, double, double ) -> double { return 3.14; 
>> } }
>>      };
> 
> Looks cleaner, but for using with the current code the correct number of 
> arguments would still be needed to stored separately, ...

And how about that:

	struct fn_t
	{
		char const *what;
		size_t nParams;
		double (*fn)( double, double, double );
	};
	static fn_t const fns[] =
	{
		{ "exp", 1, []( double num, double, double ) -> double { return exp( 
num ); } },
		{ "ln", 1, []( double num, double, double ) -> double { return log( 
num ); } },
		{ "sin", 1, []( double num, double, double ) -> double { return sin( 
num ); } },
		{ "cos", 1, []( double num, double, double ) -> double { return cos( 
num ); } },
		{ "tan", 1, []( double num, double, double ) -> double { return tan( 
num ); } },
		{ "asin", 1, []( double num, double, double ) -> double { return asin( 
num ); } },
		{ "acos", 1, []( double num, double, double ) -> double { return acos( 
num ); } },
		{ "atan", 1, []( double num, double, double ) -> double { return atan( 
num ); } },
		{ "atan2", 2, []( double x, double y, double ) -> double { return 
atan2( x, y ); } },
		{ "abs", 1, []( double num, double, double ) -> double { return abs( 
num ); } },
		{ "sqrt", 1, []( double num, double, double ) -> double { return sqrt( 
num ); } },
		{ "pi", 1, []( double, double, double ) -> double { return 3.14; } }
	};

[toc] | [prev] | [next] | [standalone]


#118878

FromTim Rentsch <tr.17687@z991.linuxsc.com>
Date2024-04-24 14:40 -0700
Message-ID<86jzkmwib2.fsf@linuxsc.com>
In reply to#118843
Paavo Helde <eesnimi@osa.pri.ee> writes:

> 23.04.2024 14:44 Bonita Montero kirjutas:
>
>> Am 23.04.2024 um 13:31 schrieb Paavo Helde:
>>
>>> There is an old third-party library where some function pointers
>>> are casted to another type, then back to the original type before
>>> use.
>>> C++ standard says this is kosher, and there have never been any
>>> problems with actual behavior.  Alas, different versions and compile
>>> modes of g++ still produce warnings. ...
>>
>> That's because of the danger that someone calls the function-pointer
>> to which you cast.  Maybe you can cast through a void-pointer to sup-
>> press this warning.  But for me casting to a function pointer of a
>> differnt type doesn't make sense at at..
>> I think you confront yourself to uncertainties which actually never
>> happen.
>
> The function pointers are cast to a single type so that they can be
> stored in a common lookup array.  I could use a union there, but this
> would mean additional work with no real benefit, as the hypothetical
> "someone" could just as easily mess up the unions than the casting.

That depends on how the code is written.  The code can be written
so that making a mistake with the unions is both difficult and
unlikely.  To be continued downthread...

[toc] | [prev] | [next] | [standalone]


#118837

FromMarkus Schaaf <mschaaf@elaboris.de>
Date2024-04-23 14:23 +0200
Message-ID<v08985$1j0ue$3@dont-email.me>
In reply to#118835
Am 23.04.24 um 13:31 schrieb Paavo Helde:
> 
> There is an old third-party library where some function pointers are
> casted to another type, then back to the original type before use. C++
> standard says this is kosher, and there have never been any problems
> with actual behavior. Alas, different versions and compile modes of g++
> still produce warnings. What would be the best way to silence them
> (without just switching off warnings)?

You could use a union, if you know all possible function types 
beforehand.

> typedef double (*Func)(double);
> typedef double (*DoubleFunc_2_args)(double, double);
> 
> Func FuncCast2(DoubleFunc_2_args fp) {
> 	return reinterpret_cast<Func>(fp);
> }
> 
> $ g++ test1.cpp -Wall -Wextra
> test1.cpp: In function ‘double (* FuncCast2(DoubleFunc_2_args))(double)’:
> test1.cpp:26:34: warning: cast between incompatible function types from
> ‘DoubleFunc_2_args’ {aka ‘double (*)(double, double)’} to ‘Func’ {aka
> ‘double (*)(double)’} [-Wcast-function-type]
>     return reinterpret_cast<Func>(fp);

You could wonder why you are asking for non-standard (extra) 
warnings in the first place.

Out of curiosity I have fiddled with g++, asking myself if there 
is a type like (void*) for objects, that the compiler is happy 
converting function pointers into. And alas, there is! And it is 
the type one would guess:

typedef void (*UniversalFunctionPointer)();

Of course it's a cat-and-mouse play with these compiler warnings.

BR

[toc] | [prev] | [next] | [standalone]


#118839

FromDavid Brown <david.brown@hesbynett.no>
Date2024-04-23 16:44 +0200
Message-ID<v08hg4$1m0ss$2@dont-email.me>
In reply to#118837
On 23/04/2024 14:23, Markus Schaaf wrote:
> Am 23.04.24 um 13:31 schrieb Paavo Helde:
>>
>> There is an old third-party library where some function pointers are
>> casted to another type, then back to the original type before use. C++
>> standard says this is kosher, and there have never been any problems
>> with actual behavior. Alas, different versions and compile modes of g++
>> still produce warnings. What would be the best way to silence them
>> (without just switching off warnings)?
> 
> You could use a union, if you know all possible function types beforehand.
> 

You /could/, if you don't mind the undefined behaviour - type-punning 
unions are not defined behaviour in C++.


>> typedef double (*Func)(double);
>> typedef double (*DoubleFunc_2_args)(double, double);
>>
>> Func FuncCast2(DoubleFunc_2_args fp) {
>>     return reinterpret_cast<Func>(fp);
>> }
>>
>> $ g++ test1.cpp -Wall -Wextra
>> test1.cpp: In function ‘double (* FuncCast2(DoubleFunc_2_args))(double)’:
>> test1.cpp:26:34: warning: cast between incompatible function types from
>> ‘DoubleFunc_2_args’ {aka ‘double (*)(double, double)’} to ‘Func’ {aka
>> ‘double (*)(double)’} [-Wcast-function-type]
>>     return reinterpret_cast<Func>(fp);
> 
> You could wonder why you are asking for non-standard (extra) warnings in 
> the first place.
> 

I can't answer for the OP, but I know why /I/ use lots of extra warnings 
in my code.  (What do you mean by "non-standard warnings" anyway?  There 
are standards-required diagnostics, but AFAIK the standard does not 
distinguish between errors and warnings, except for #error directives.)

[toc] | [prev] | [next] | [standalone]


#118840

FromMarkus Schaaf <mschaaf@elaboris.de>
Date2024-04-23 17:00 +0200
Message-ID<v08ien$1ke8k$2@dont-email.me>
In reply to#118839
Am 23.04.24 um 16:44 schrieb David Brown:
> On 23/04/2024 14:23, Markus Schaaf wrote:
>> Am 23.04.24 um 13:31 schrieb Paavo Helde:
>>>
>>> There is an old third-party library where some function pointers are
>>> casted to another type, then back to the original type before use. C++
>>> standard says this is kosher, and there have never been any problems
>>> with actual behavior. Alas, different versions and compile modes of g++
>>> still produce warnings. What would be the best way to silence them
>>> (without just switching off warnings)?
>>
>> You could use a union, if you know all possible function types beforehand.
>>
> 
> You /could/, if you don't mind the undefined behaviour - type-punning
> unions are not defined behaviour in C++.

I have no idea what you are writing about. Of course one would 
read the exact same member of the union one had written to 
before. That is what unions are for.

BR

[toc] | [prev] | [next] | [standalone]


#118854

FromDavid Brown <david.brown@hesbynett.no>
Date2024-04-24 09:55 +0200
Message-ID<v0adto$273oo$1@dont-email.me>
In reply to#118840
On 23/04/2024 17:00, Markus Schaaf wrote:
> Am 23.04.24 um 16:44 schrieb David Brown:
>> On 23/04/2024 14:23, Markus Schaaf wrote:
>>> Am 23.04.24 um 13:31 schrieb Paavo Helde:
>>>>
>>>> There is an old third-party library where some function pointers are
>>>> casted to another type, then back to the original type before use. C++
>>>> standard says this is kosher, and there have never been any problems
>>>> with actual behavior. Alas, different versions and compile modes of g++
>>>> still produce warnings. What would be the best way to silence them
>>>> (without just switching off warnings)?
>>>
>>> You could use a union, if you know all possible function types 
>>> beforehand.
>>>
>>
>> You /could/, if you don't mind the undefined behaviour - type-punning
>> unions are not defined behaviour in C++.
> 
> I have no idea what you are writing about. Of course one would read the 
> exact same member of the union one had written to before. That is what 
> unions are for.
> 
> BR

Type-punning unions are defined behaviour in C, undefined in C++.  A 
typical example (written to be compilable as C and C++) would be :

typedef union RawFloat {
	float f;
	unsigned int u;
} RawFloat;

unsigned int float_to_raw(float f) {
	RawFloat r;
	r.f = f;
	return r.u;
}

In C, writing to one union member and then reading via a different union 
member is defined behaviour - you get the same underlying 
representation, re-interpreted as the new type.  This is known as "type 
punning", and was explicitly given defined behaviour in C99.  (Prior to 
C99, the standard was vague on the topic.)

In C++, this is undefined behaviour - you may not read a union member 
that was not the last written member.

Many - perhaps most - C++ compilers allow type-punning via unions in 
C++, as long as they are standard layout unions (simple types with no 
constructors, virtual functions, or anything beyond plain C).  But this 
is not defined behaviour according to the C++ standards.  In C++, the 
defined ways to achieve type punning are std::memcpy and std::bit_cast<> 
(in C++20).


Unions were /not/ intended for type-punning.  They were designed for 
saving memory and to support "sum" types (compared to structs which are 
"product" types).  But they are also sometimes used for type-punning, 
and the definition of this behaviour was added to C99 because some 
people non-portably relied on that behaviour in existing C code.  It was 
/not/ added to C++ because specifying such a rule would be complicated 
in the face of more advanced types.


<https://en.cppreference.com/w/c/language/union>
(See the first "since C99" box)

<https://en.cppreference.com/w/cpp/language/union>
(See the first paragraph of "Explanation")



[toc] | [prev] | [next] | [standalone]


Page 1 of 3  [1] 2 3  Next page →

Back to top | Article view | comp.lang.c++


csiph-web