Extensible Generic Math Functions

P.J. Plauger
Dinkumware, Ltd.
Date: 11 August 2008
Document: WG14 N1340

Background

C99 added the header <tgmath.h>, which defines a number of generic math functions, à la Fortran. Thus, if you include this header and call:

sin(1), you effectively call sin(1.0),
sin(1.0F), you effectively call sinf(1.0F),
sin(1.0L), you effectively call sinl(1.0L),
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.

Prior Art

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.

Proposal

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.