After building gcc/DJGPP, the installation requires the following hack: sed -i '/{ return __builtin_tanh(__x); }/a\ \ using ::trunc;\ \ #ifndef __CORRECT_ISO_CPP_MATH_H_PROTO_FP\ constexpr float\ trunc(float __x)\ { return __builtin_truncf(__x); }\ \ constexpr long double\ trunc(long double __x)\ { return __builtin_truncl(__x); }\ #endif\ \ template<typename _Tp>\ constexpr typename __gnu_cxx::__enable_if<__is_integer<_Tp>::__value, \ double>::__type\ trunc(_Tp __x)\ { return __builtin_trunc(__x); }' $pkgdir/usr/$_target/include/c++/$pkgver/cmath The hack can be observed in action if you install/checkout: https://aur.archlinux.org/packages/dosbox-gcc If we don't apply the hack, gcc will complain about std::trunc() being unavailable (compile error), if we try to compile an application requiring std::trunc(), but not if it requires plain C trunc(). At least part of the blame probably lies with DJGPP, not with libstdc++, but libstdc++ manages to hack many other math.h functions (i.e. it seems to contain a template for the hack, that it applies to many different math.h functions, just not to trunc()). Perhaps a simple fix of adding trunc() to a list of functions, that need to be hacked, is possible. Other math.h functions, not just trunc(), may also need to be hacked into <cmath>. Like most DJGPP-related bugs, this one is very old.
This means djgpp is doing this: dnl To use the obsolete 'c_std' headers use --enable-cheaders-obsolete as dnl well as --enable-cheaders=c_std, otherwise configure will fail. Any idea why? As it says, the include/c_std/cmath header is obsolete, and is unmaintained and so has hardly any of the C99 <math.h> functions. The supported include/c_global/cmath header has trunc, and all the other math functions.
Also: if test $enable_cheaders = c_std ; then AC_MSG_WARN([the --enable-cheaders=c_std configuration is obsolete, c_global should be used instead]) AC_MSG_WARN([if you are unable to use c_global please report a bug or inform libstdc++@gcc.gnu.org]) if test $enable_cheaders_obsolete != yes ; then AC_MSG_ERROR(use --enable-cheaders-obsolete to use c_std "C" headers) fi fi So somebody is ignoring that when configuring djgpp. I think it would be much better to figure out how to get djgpp using the new header instead.
I am the "maintainer" of this dosbox-gcc aur (mostly I just borrow from other builds). I have told the DJGPP community about this issue 2 times and they shrugged it off, since they mostly compile .c source files. I don't think most of them even know of these 2 configure options you mentioned and I failed to spot them when I looked into configure. My impression is that configure itself figured it needed this obsolete header and the build also finishes just fine without having to specify anything extra, just that std::trunc() is later unavailable (which doesn't bother anyone since they are mostly C users). I experimented with various env and configure options, but found nothing that worked, so I resolved to just hack <cmath>. Now I'll take another look.
Hmm, I don't see anything in the configure scripts that would cause c_std to be used implicitly for djgpp (or any other target). Maybe there's a local patch applied to the djgpp copy of GCC sources? It might set C_INCLUDE_DIR explicitly, or adjust the default chosen for GLIBCXX_ENABLE_CHEADERS in libstdc++-v3/acinclude.m4
And the $build-dir/$target/libstdc++-v3/config.log file should show the output from this: AC_MSG_NOTICE("C" header strategy set to $enable_cheaders) That would confirm if it's being set to c_std
There are a lot of patches and I never went over all of them. I'll take another look and update my bug report, if necessary (I think you're probably right). But, should anyone else want to, the build script is accessible to everyone.
Some of the libstdc++ changes in https://aur.archlinux.org/cgit/aur.git/tree/gcc-djgpp.diff?h=dosbox-gcc would be great to upstream.
I took a look and I believe the c_global is already being selected and I believe the hack I presented should perhaps be applied to libstdc++-v3/include/c_global/cmath - the hack template is in there. As for the other hacks/patches - I am not the author of any of them. The author(s) never bothered to report any bugs and I doubt they ever will. The patches are very old and they change incrementally, whenever something in gcc changes.
I'd also like to mention, that the hack is already present in the c_global cmath file, but is perhaps in a wrong section of the file, i.e. it is in the section protected by: #if defined(__STDCPP_FLOAT64_T__) && defined(_GLIBCXX_DOUBLE_IS_IEEE_BINARY64) while I can see _GLIBCXX_DOUBLE_IS_IEEE_BINARY64 is defined in c++config.h, I don't see __STDCPP_FLOAT64_T__ defined anywhere.
(In reply to Janez Zemva from comment #8) > I took a look and I believe the c_global is already being selected and I > believe the hack I presented should perhaps be applied to > libstdc++-v3/include/c_global/cmath No, absolutely not, std::trunc is already defined in that file, see line 2023: using ::trunc; using ::truncf; using ::truncl; And then at line 2642: #ifndef __CORRECT_ISO_CPP11_MATH_H_PROTO_FP constexpr float trunc(float __x) { return __builtin_truncf(__x); } constexpr long double trunc(long double __x) { return __builtin_truncl(__x); } #endif #ifndef __CORRECT_ISO_CPP11_MATH_H_PROTO_INT template<typename _Tp> constexpr typename __gnu_cxx::__enable_if<__is_integer<_Tp>::__value, double>::__type trunc(_Tp __x) { return __builtin_trunc(__x); } #endif (In reply to Janez Zemva from comment #9) > I'd also like to mention, that the hack is already present in the c_global > cmath file, but is perhaps in a wrong section of the file, i.e. it is in the > section protected by: > > #if defined(__STDCPP_FLOAT64_T__) && > defined(_GLIBCXX_DOUBLE_IS_IEEE_BINARY64) > > while I can see _GLIBCXX_DOUBLE_IS_IEEE_BINARY64 is defined in c++config.h, > I don't see __STDCPP_FLOAT64_T__ defined anywhere. No, you're looking at the definition for trunc(_Float64) which is a completely different overload.
If djgpp is already using c_global/cmath then your change is completely wrong. The problem is more likely to be that this macro is not defined for djgpp: #ifdef _GLIBCXX_USE_C99_MATH_TR1 And if djgpp doesn't have a full C99 math library then that would explain why you don't have std::trunc (or any of the other C99 math functions). Adding ad-hoc declarations for a single function in arbitrary places is certainly not going to happen. You should figure out why the existing declarations aren't being used and fix *that*, not just propose ad-hoc kluges without understanding the context. At this point there is no clear description of the problem or the status quo for djgpp. "This change is needed" is not sufficient, sorry.
I doubt DJGPP will change their C library, at least this workaround exists now.
The workaround is wrong and will not be accepted upstream.
Certainly, but users will still be able to make use of the kludge.
The reason that std::trunc is not provided is because (In reply to Jonathan Wakely from comment #11) > The problem is more likely to be that this macro is not defined for djgpp: > > #ifdef _GLIBCXX_USE_C99_MATH_TR1 > > And if djgpp doesn't have a full C99 math library then that would explain > why you don't have std::trunc (or any of the other C99 math functions). I've confirmed this is the problem. The configure test that defines that macro fails because these are missing: config.cc7:9: error: 'double_t' does not name a type; did you mean 'double'? config.cc8:27: error: 'float_t' does not name a type; did you mean 'float'? config.cc1:19: error: 'acoshl' was not declared in this scope; did you mean 'acoshf'? config.cc4:19: error: 'asinhl' was not declared in this scope; did you mean 'asinhf'? config.cc7:19: error: 'atanhl' was not declared in this scope; did you mean 'atanhf'? config.cc0:19: error: 'cbrtl' was not declared in this scope; did you mean 'cbrtf'? config.cc3:19: error: 'copysignl' was not declared in this scope; did you mean 'copysignf'? config.cc6:19: error: 'erfl' was not declared in this scope; did you mean 'erff'? config.cc9:19: error: 'erfcl' was not declared in this scope; did you mean 'erfcf'? config.cc1:19: error: 'exp2f' was not declared in this scope; did you mean 'expf'? config.cc2:19: error: 'exp2l' was not declared in this scope; did you mean 'exp2'? config.cc5:19: error: 'expm1l' was not declared in this scope; did you mean 'expm1f'? config.cc6:19: error: 'fdim' was not declared in this scope config.cc7:19: error: 'fdimf' was not declared in this scope config.cc8:19: error: 'fdiml' was not declared in this scope config.cc9:19: error: 'fma' was not declared in this scope config.cc0:19: error: 'fmaf' was not declared in this scope config.cc1:19: error: 'fmal' was not declared in this scope config.cc2:19: error: 'fmax' was not declared in this scope config.cc3:19: error: 'fmaxf' was not declared in this scope config.cc4:19: error: 'fmaxl' was not declared in this scope config.cc5:19: error: 'fmin' was not declared in this scope config.cc6:19: error: 'fminf' was not declared in this scope config.cc7:19: error: 'fminl' was not declared in this scope config.cc0:19: error: 'hypotl' was not declared in this scope; did you mean 'hypotf'? config.cc3:19: error: 'ilogbl' was not declared in this scope; did you mean 'ilogbf'? config.cc6:19: error: 'lgammal' was not declared in this scope; did you mean 'lgammaf'? config.cc7:19: error: 'log1pl' was not declared in this scope; did you mean 'log1pf'? config.cc0:19: error: 'log2l' was not declared in this scope; did you mean 'log2f'? config.cc3:19: error: 'logbl' was not declared in this scope; did you mean 'logbf'? config.cc3:19: error: 'nearbyint' was not declared in this scope config.cc4:19: error: 'nearbyintf' was not declared in this scope config.cc5:19: error: 'nearbyintl' was not declared in this scope config.cc8:19: error: 'nextafterl' was not declared in this scope; did you mean 'nextafterf'? config.cc9:19: error: 'nexttoward' was not declared in this scope config.cc0:19: error: 'nexttowardf' was not declared in this scope config.cc1:19: error: 'nexttowardl' was not declared in this scope config.cc4:19: error: 'remainderl' was not declared in this scope; did you mean 'remainderf'? config.cc5:19: error: 'remquo' was not declared in this scope config.cc6:19: error: 'remquof' was not declared in this scope config.cc7:19: error: 'remquol' was not declared in this scope config.cc4:19: error: 'scalbln' was not declared in this scope; did you mean 'scalbn'? config.cc5:19: error: 'scalblnf' was not declared in this scope; did you mean 'scalbnf'? config.cc6:19: error: 'scalblnl' was not declared in this scope; did you mean 'scalbnf'? config.cc9:19: error: 'scalbnl' was not declared in this scope; did you mean 'scalbnf'? config.cc0:19: error: 'tgamma' was not declared in this scope; did you mean 'lgamma'? config.cc1:19: error: 'tgammaf' was not declared in this scope; did you mean 'lgammaf'? config.cc2:19: error: 'tgammal' was not declared in this scope; did you mean 'lgammaf'? A proper fix would be to split up the configure test for USE_C99_MATH_TR1 to be more fine-grained, so that we use the subset of functions that are supported, instead of all or nothing.
Most of the missing functions are the long double variants. We could certainly have a separate macro for long double math, which might help several targets. The other missing ones are fdim, fma, fmin, fmax, nearbyint, nexttoward, remoquo, scalbln and tgamma, which are missing for all of float, double and long double. We could have a separate check for those.
Perhaps missing functionality could also be auto-generated? I am a big fan of generative programming.
No, the functionality belongs in the C library
(In reply to Jonathan Wakely from comment #15) > A proper fix would be to split up the configure test for USE_C99_MATH_TR1 to > be more fine-grained, so that we use the subset of functions that are > supported, instead of all or nothing. Ugh, the set of functions defined by djgpp's <math.h> seems really arbitraty. exp2 only has the double form, not float or long double. expm1 has double and float forms, not long double. trunc has all three forms. Even within a group like the Gamma functions, lgamma and lgammaf are present (but no lgammal) but none of tgamma, tgammaf and tgammal are present. So there's no clear way to split them up into groups that can be tested together, it ends up just being incredibly djgpp-specific (and we might as well just manually add #ifndef __DJGPP__ in very specific places).
Created attachment 55070 [details] Patch to make cmath configure tests more granular Something like this might work, but it's horrible.
Oh, checking FLT_EVAL_METHOD doesn't work, because that's defined in <float.h> not <math.h>. So another djgpp-specific "NO_FLOAT_TYPES" macro will be needed. Sigh.
They are very sloppy, so I doubt even what they provide is working 100%. This is why I suggested the generative approach. gcc has many built-in functions, surely a rudimentary <cmath> could be cobbled out of them.
No, the built-in functions just call the functions defined in libm. GCC doesn't implement them. Look at the code for a call to __builtin_acosh: https://godbolt.org/z/dPf46sKxx
I'll go libm shopping them, I know just the thing to try out first: https://www.ibiblio.org/pub/micro/pc-stuff/freedos/files/devel/libs/libm/
I think the simplest solution for this bug is just use <math.h> and call trunc instead of trying to use std::trunc. If you use the <math.h> functions in the global namespace then you get exactly the set that is supported by djgpp, not the incomplete set that gets imported into namespace std. It's not ideal, but there's only so much we can do when the underlying <math.h> is incomplete.
I am a c++ user, so I don't like using c header files if at all possible. I am pleased with how things are: I now compile/link a replacement libm and replace the sloppy djgpp header files, but I haven't tested this arrangement yet. Maybe it works.
forcing glibcxx_cv_c99_math_tr1=yes solved this issue for me. The C99 compliance test is really strict, even openlibm fails the test (it fails to define float_t and double_t, but this can be hacked around).
(In reply to Janez Zemva from comment #26) > I am a c++ user, so I don't like using c header files if at all possible. <math.h> is a C++ header file, if you doubt that, you can check the path where it's included from. You'll see that libstdc++ installs its own <math.h> in the same location as <cmath>. If you prefer, include <cmath>, you'll get the same contents. What I'm saying is to use ::trunc not std::trunc, because that is present whether or not libstdc++ adds the math functions to namespace std.
(In reply to Janez Zemva from comment #27) > forcing glibcxx_cv_c99_math_tr1=yes solved this issue for me. The C99 > compliance test is really strict, even openlibm fails the test (it fails to > define float_t and double_t, but this can be hacked around). It's precisely as strict as it needs to be: it checks that every name that will be subject to a using-declaration is present. Because otherwise compiling <cmath> will give errors. But supporting openlibm would be nice, so I'll separate the checks for those typedefs.
The master branch has been updated by Jonathan Wakely <redi@gcc.gnu.org>: https://gcc.gnu.org/g:49f59826c66bcaa3531429381b4aed944c332e5b commit r14-1454-g49f59826c66bcaa3531429381b4aed944c332e5b Author: Jonathan Wakely <jwakely@redhat.com> Date: Wed May 31 12:22:06 2023 +0100 libstdc++: Add separate autoconf macro for std::float_t and std::double_t [PR109818] This should make it possible to use openlibm with djgpp (and other targets with missing C99 <math.h> functions). The <math.h> from openlibm provides all the functions, but not the float_t and double_t typedefs. By separating the autoconf checks for the functionsand the typedefs, we don't disable support for all the functions just because those typedefs are not present. libstdc++-v3/ChangeLog: PR libstdc++/109818 * acinclude.m4 (GLIBCXX_ENABLE_C99): Add separate check for float_t and double_t and define HAVE_C99_FLT_EVAL_TYPES. * config.h.in: Regenerate. * configure: Regenerate. * include/c_global/cmath (float_t, double_t): Guard using new _GLIBCXX_HAVE_C99_FLT_EVAL_TYPES macro.
Using openlibm should work now. I'm inclined to close this as WONTFIX, since the problem is in djgpp. I don't think it's worth doing more work here. If there is interest in making <cmath> work better for djgpp then improving djgpp's <math.h> would be the place to start.
I resolved this issue by porting openlibm over to djgpp (some hacks and typedef float float_t;, ... were necessary). The fix on the side of gcc might have been a more thorough analysis of what is available in math.h and then making a suitable <cmath> based on that analysis. For me, this issue is resolved, but the problem of how gcc handles non-compliant standard c library implementations will probably pop up again.
Created attachment 55232 [details] Patch to make cmath configure tests more granular Rebased patch. Attaching it here as I don't intend to keep it in my local git repo.