[libc++] Should we guard against macro definitions

I have a C library, and I want to build libc++ and libc++abi against it. This C library’s implementation of many of the math.h functions involves code similar to…

#define cos(Val) RealCosImpl(Val)

This interacts poorly with libcxx’s cmath header, as the float and long double overload declarations hit this #define.

If I make a patch that guards the bulk of cmath from this kind of macro, do people feel that this would be useful, or would it get rejected on the grounds that it is unnecessary noise to support an odd C library? This fix would generally take the form of putting the function name in parenthesis to suppress function macro expansion. So code that looks like this…

float cos(float val)

… would turn into this…

float (cos)(float val)

One could argue that this would be a reasonable thing to do across libcxx, but I think it is more important to do so for cmath, as many of those functions must have macro version in the C tgmath.h header (but not the C++ one). It probably wouldn’t hurt to throw some parenthesis at std::min and std::max in as well, considering the unfortunate history with those and the <windows.h> min and max macros.

Employee of Qualcomm Innovation Center, Inc.

Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project

forgot to cc the list on my reply.

– Marshall

Short story:

Instead of putting lots of parenthesis everywhere, I think the better approach is to put lots of #ifdef + #undef blocks everywhere. More rationale follows.

That’s not good enough, because we have to “hoist” the function into namespace std.

Yes and no.

First the good(?) part. This C implementation starts with a real function definition for ‘cos’, then follows it up with the macro version. That means that “using ::cos;” does get the “double cos(double)” signature into namespace std, because it doesn’t invoke the macro (no parenthesis).

The bad part is that the cos macro is still hanging around, and if someone types something reasonable like “float x = cos(1.0f);” they will invoke the double version. If they type “float x = std::cos(1.0f);” they will get a gross compiler error.

This suggests to me that the better answer is to throw around a lot of blocks of the form #ifdef cos, #undef cos.

libc++'s header does this for some of the calls there (look at ‘fpclassify’, for example).

Doing the same for cos (and others) shouldn’t be too hard.

I think C99 fpclassify is specified to be a macro, and C++ fpclassify is specified to be a set of functions. Given that, I think the #undef technique used there is reasonable. I don’t think we can do the same for the math functions though, because if we #undef the old cos, then introduce a new cos overload that takes a double as the only argument, then we will introduce ambiguity. If we just do the #undef though, then I think we’ll be fine, as long as the cos macro isn’t the only way of getting to the C libraries cos implementation.

Can you provide a list of the functions that your library defines as macros?

I’m not sure if I can, but either way, it’s a rather spotty list. For example, sinhf has a macro version, but sinf doesn’t. I think it may make more sense to look at everything in the C version of tgmath, and guard all of those. I’m absolutely willing to be the one to make the patch, I was just trying to gauge interest before going through the motions.

Employee of Qualcomm Innovation Center, Inc.

Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project

Certainly libstdc++'s cmath header has a ton of “#undef abs”, “#undef div”, etc. lines at the beginning of the file.

It seems likely that would be a sensible thing to do in libc++ too.

I have a C library, and I want to build libc++ and libc++abi against it.
This C library’s implementation of many of the math.h functions involves
code similar to…

#define cos(Val) RealCosImpl(Val)

If you're not providing a declaration of a real 'cos' function, you do not
have a conforming C standard library. See C11 7.1.4/1:

"Any function declared in a header may be additionally implemented as a
function-like macro defined in the header, so if a library function is
declared explicitly when its header is included, one of the techniques
shown below can be used to ensure the declaration is not affected by such a
macro. Any macro definition of a function can be suppressed locally by
enclosing the name of the function in parentheses, because the name is then
not followed by the left parenthesis that indicates expansion of a macro
function name. For the same syntactic reason, it is permitted to take the
address of a library function even if it is also defined as a macro.
[Footnote: This means that an implementation shall provide an actual
function for each library function, even if it also provides a macro for
that function.]"

This interacts poorly with libcxx’s cmath header, as the float and long

double overload declarations hit this #define.

If I make a patch that guards the bulk of cmath from this kind of macro,
do people feel that this would be useful, or would it get rejected on the
grounds that it is unnecessary noise to support an odd C library? This fix
would generally take the form of putting the function name in parenthesis
to suppress function macro expansion. So code that looks like this…

float cos(float val)

… would turn into this…

float (cos)(float val)

We should just fix this with

#undef cos

One could argue that this would be a reasonable thing to do across libcxx,