C99 added the header <tgmath.h>
, which
defines a number of generic math functions, à la Fortran.
Thus, if you include this header and call:
and so forth. But there is no portable way to implement this header, and hence no way for programmers to add generic functions. With the advent of fixed-point and decimal floating-point math functions, the problem continues to grow. Hence the call, at recent WG14 meetings, to provide portable support for writing generic functions.sin(1)
, you effectively callsin(1.0)
,
sin(1.0F)
, you effectively callsinf(1.0F)
,
sin(1.0L)
, you effectively callsinl(1.0L)
,
Edison Design Group (EDG) provided one kind of compiler magic for
<tgmath.h>
. It effectively lets you write
generic sin
as:
#define sin(x) __generic(x,,, sin, sinf, sinl, csin,
csinf, csinl)(x)
The __generic
pseudo function determines the
desired argument type from up to three arguments (here x,,,) by
the usual widening and balancing rules of C, without generating
any code that evaluates these arguments. It then picks which of
up to six functions to call. It evaluates the function-call
expression only for the function actually called. As a further
example, atan2
can be written as:
#define atan2(y, x) __generic(x,y,, atan2, atan2f,
atan2l, ,,)(x)
and cimag
as:
#define cimag(x) __generic(x,,, ,,, cimag, cimagf,
cimagl)(x)
This mechanism is obviously limited to functions involving floating-point and complex arguments only, but it has filled the needs of C99 library writers for years.
Steve Adamczyk, President of EDG, has suggested a way to
extend this mechanism. Basically, it replaces the fixed-length
sequence of function names, with presumed associated types, to a
varying-length sequence of {type, function name}
pairs. Thus, the examples above could be written:
#define sin(x) __tgmath(x,,, float, sinf, long double,
sinl, \
float complex, csinf, double complex, csin, \
long double complex, csinl, , sin)(x)
#define atan2(y, x) __tgmath(x,y,, float, atan2f, \
long double, atan2l, , atan2)(x)
#define cimag(x) __tgmath(x,,, float complex, cimagf, \
double complex, cimag, long double complex, cimagl)(x)
Omitting the last type means that __tgmath
will
always choose the last function name if the argument type matches
none of the earlier function types.
Extending these functions to include fixed-point and decimal
floating-point arguments is straightforward:. Here, for example,
is one way to rewrite sin
:
#define sin(x) __tgmath(x,,, \
float, sinf, long double, sinl, \
decimal float, sinfd, decimal double, sindd, \
decimal long double, sinld, \
short accumulator sin_hk, accumulator sin_k, \
long accumulator sin_lk, float complex, csinf, double complex,
csin, \
long double complex, csinl, , sin)(x)
The double functions are defined by TR24732, Decimal Floating-Point. The fixed-point functions are not required by TR18037 Embedded C, but are a part of the Dinkumware Embedded C Fixed-Point Library.
No implementation for __tgmath
yet exists, but it
is close enough to a proven design, and it has been put forth by
an experienced compiler writer, so I feel it is very much worthy
of consideration for the next revision of Standard C.