Post by Paavo HeldePost by Bonita MonteroPost by Paavo HeldeThe 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
}