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)?
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);
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.
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++.
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
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.
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)();
typedef void (*UniversalFunctionPointer)();
Adding an intermediate reinterpret_cast to this type indeed seems to get
rid of the warning! Thanks a lot!
Am 23.04.24 um 20:50 schrieb Paavo Helde:
typedef void (*UniversalFunctionPointer)();
Adding an intermediate reinterpret_cast to this type indeed seems to get
rid of the warning! Thanks a lot!
Wouldn't it be better to change the storage type of these function
pointers in your registry to above type, making the intention clear to
those you recognize its special property? Instead of introducing obscure
cast chains everywhere.
intention clear to those you recognize its special property?
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.
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
Am 23.04.2024 um 16:44 schrieb David Brown:
The compiler is warning here about casting incompatible function types
because usually such casts are a bad idea. In particular, casting to
a different function type, then calling through this new type, is
undefined behaviour. About the only useful thing you can do with your
cast pointer is cast it back to the original type before calling it.
So g++ with -Wextra warns you that you have either made a mistake in
your code, or are trying to do something that is potentially dangerous.
He needs to store different function-pointers with a common type;
why not as a void-pointer ?
On 24/04/2024 11:27, Bonita Montero wrote:
Am 23.04.2024 um 16:44 schrieb David Brown:
The compiler is warning here about casting incompatible function
types because usually such casts are a bad idea. In particular,
casting to
a different function type, then calling through this new type, is
undefined behaviour. About the only useful thing you can do with
your cast pointer is cast it back to the original type before calling
it.
So g++ with -Wextra warns you that you have either made a mistake in
your code, or are trying to do something that is potentially dangerous.
He needs to store different function-pointers with a common type;
why not as a void-pointer ?
As far as I know, the standards do not define the behaviour of casting between function pointers and void pointers (or any object pointers).
The C standards are clear about the matter - it is not defined
behaviour. But I don't know the C++ standards well enough to say.
Am 23.04.2024 um 16:44 schrieb David Brown:
You /could/, if you don't mind the undefined behaviour - type-punning
unions are not defined behaviour in C++.
Actually all compiler support that.
But MSVC++ isn't that efficient
with that like clang or g++; MSVC often does a store and load round-
trip. But there's bit_cast with C++20, which also works efficient
with MSVC++.
But actually he neither needs bit_cast nor unions for
his purpose, but just a reinterpret_cast or a C-style cast, which
I prefer for readability.
Am 23.04.2024 um 17:00 schrieb Markus Schaaf:
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.
A union actually isn't needed for the discusses purpose.
Just do a reinterpret_cast or a C-style cast.
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<>.
On 2024-04-24 at 13:14, David Brown wrote:
On 24/04/2024 11:27, Bonita Montero wrote:
Am 23.04.2024 um 16:44 schrieb David Brown:
The compiler is warning here about casting incompatible functionHe needs to store different function-pointers with a common type;
types because usually such casts are a bad idea. In particular,
casting to
a different function type, then calling through this new type, is
undefined behaviour. About the only useful thing you can do with
your cast pointer is cast it back to the original type before
calling it.
So g++ with -Wextra warns you that you have either made a mistake in >>>> your code, or are trying to do something that is potentially dangerous. >>>
why not as a void-pointer ?
As far as I know, the standards do not define the behaviour of casting
between function pointers and void pointers (or any object pointers).
The C standards are clear about the matter - it is not defined
behaviour. But I don't know the C++ standards well enough to say.
This is something about theory and practice. A function pointer is
allowed to be larger than a void*.
In practice both Linux/Posix and Windows will use void* for returning function pointers into dynamic libraries. So on those systems it will obviously work.
Am 24.04.2024 um 13:14 schrieb David Brown:
As far as I know, the standards do not define the behaviour of casting
between function pointers and void pointers (or any object pointers).
The C standards are clear about the matter - it is not defined
behaviour. But I don't know the C++ standards well enough to say.
There's for sure no compiler which doesn't support that.
Am 24.04.2024 um 13:23 schrieb David Brown:
That may be true - but I am entirely confident that you don't know it
is true. You live in a little world where the only compiler is MSVC++
- you know nothing about the hundred other C++ compilers out there.
(I know more of them than you - and more importantly, I know my
knowledge is limited.)
Any C++ compiler supports that since this is very common
and you
coudln't port a lot of code to compilers who wouldn't understand
that. I've no problem using a union since this feature is required
for any compiler to be conformant with a lot of software.
Am 24.04.2024 um 18:02 schrieb David Brown:
On the AVR (which is supported by gcc), code pointers can haveNo one aliases code through a union.
different sizes from data pointers. ...
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.
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},
//...
}
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.
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.
C:\Test\ConsoleTestVS2022\ConsoleTest2022\main.cpp(97,18): errorC2124: divide or mod by zero
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; } }
};
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
C:\Test\ConsoleTestVS2022\ConsoleTest2022\main.cpp(97,18): errorC2124: divide or mod by zero
But that's fully another topic.
Thanks for the demo code!
Sysop: | Keyop |
---|---|
Location: | Huddersfield, West Yorkshire, UK |
Users: | 547 |
Nodes: | 16 (2 / 14) |
Uptime: | 60:20:48 |
Calls: | 10,398 |
Calls today: | 6 |
Files: | 14,067 |
Messages: | 6,417,476 |
Posted today: | 1 |