[Math.Constants] Help needed with the preprocessor

Folks we have a problem: rather late in the day I've discovered that our Math.Constants code doesn't compile on anything except VC++, and the problem is preprocessor token pasting. So I'm hoping we have some experts around here that can help! The issue is we currently construct the constants from a macro invocation such as: BOOST_DEFINE_MATH_CONSTANT(half, 5.000000000000000000000000000000000000, 00000000000000000000000000000000000000000000000000000000000000000000000000, -01); But the "-01" part (the exponent) isn't a valid pp-token, so trying to token-paste it fails on GCC (compiler error). Does anyone know if there's a way to overcome this? As an alternative, I've considered something like: BOOST_DEFINE_MATH_CONSTANT(ln_two, 6.931471805599453094172321214581765680e-001, "6.93147180559945309417232121458176568075500134360255254120680009493393621969694715605863326996418687542001481021e-001"); But this would still require token pasting of the 6.931471805599453094172321214581765680e-001 part. But is this now a valid pp-token if the "-" is internal to the token? It works on the compilers I've tried, but I don't want to be skewered by this again down the line.... Truly hating the preprocessor yours, John.

On Fri, Dec 16, 2011 at 10:23 AM, John Maddock <boost.regex@virgin.net>wrote:
Folks we have a problem: rather late in the day I've discovered that our Math.Constants code doesn't compile on anything except VC++, and the problem is preprocessor token pasting. So I'm hoping we have some experts around here that can help!
The issue is we currently construct the constants from a macro invocation such as:
BOOST_DEFINE_MATH_CONSTANT(**half, 5.**000000000000000000000000000000**000000, 000000000000000000000000000000**000000000000000000000000000000**00000000000000, -01);
But the "-01" part (the exponent) isn't a valid pp-token, so trying to token-paste it fails on GCC (compiler error).
Does anyone know if there's a way to overcome this?
Maybe include the "e" in that 4th argument? E.g., "e-01". If you actually need to recover the "-01" without the e, I'm pretty sure you can do something like #define remove_e( X ) BOOST_PP_CAT( remove_e_, X ) #define remove_e_e #define remove_e_E // prefer "E" instead? Weak explanation for why I believe this will work below... As an alternative, I've considered something like:
BOOST_DEFINE_MATH_CONSTANT(ln_**two, 6.**931471805599453094172321214581**765680e-001, "6.**931471805599453094172321214581**765680755001343602552541206800** 094933936219696947156058633269**96418687542001481021e-001");
But this would still require token pasting of the
6.**931471805599453094172321214581**765680e-001
part. But is this now a valid pp-token if the "-" is internal to the token? It works on the compilers I've tried, but I don't want to be skewered by this again down the line....
Truly hating the preprocessor yours, John.
From my experience, only the characters on which the preprocessor is slabbing glue for the token pasting matters, and anything you can use in a C++ identifier is fine there. I know Lorenzo's done some crazy stuff with token pasting with things that looks like "const bind & identifier", e.g., detecting the presence of the "const" and "bind" keywords, and I presume
the internal "&" doesn't mess things up. I'm not an expert in the technicalities of the preprocessor, however. - Jeff

On Fri, Dec 16, 2011 at 3:26 PM, Jeffrey Lee Hellrung, Jr. <jeffrey.hellrung@gmail.com> wrote:
On Fri, Dec 16, 2011 at 10:23 AM, John Maddock <boost.regex@virgin.net>wrote:
Folks we have a problem: rather late in the day I've discovered that our Math.Constants code doesn't compile on anything except VC++, and the problem is preprocessor token pasting. So I'm hoping we have some experts around here that can help!
The issue is we currently construct the constants from a macro invocation such as:
BOOST_DEFINE_MATH_CONSTANT(**half, 5.**000000000000000000000000000000**000000, 000000000000000000000000000000**000000000000000000000000000000**00000000000000, -01);
But the "-01" part (the exponent) isn't a valid pp-token, so trying to token-paste it fails on GCC (compiler error).
Does anyone know if there's a way to overcome this?
Maybe include the "e" in that 4th argument? E.g., "e-01". If you actually need to recover the "-01" without the e, I'm pretty sure you can do something like
#define BOOST_DEFINE_MATH_CONSTANT(name, x, y, exp) Yes, as indicated by Jeff, you can have your users to always prefix exp with e (I looked at your macro and you always CAT(e, exp)). BTW, can x ever start with a symbol (-)? BOOST_DEFINE_MATH_CONSTANT(half, 5.000000000000000000000000000000000000, 00000000000000000000000000000000000000000000000000000000000000000000000000, e-01); // OK Otherwise it seems you can use PP_SEQ_CAT: #include <boost/preprocessor/seq/cat.hpp> #include <boost/preprocessor/stringize.hpp> #define BOOST_DEFINE_MATH_CONSTANT(name, x, y, exp)\ template <class T> inline T name(BOOST_MATH_EXPLICIT_TEMPLATE_TYPE(T))\ {\ static const T result = ::boost::lexical_cast<T>(BOOST_PP_STRINGIZE( \ BOOST_PP_SEQ_CAT( (x) (y) (e) (exp) )));\ return result;\ }\ template <> inline float name<float>(BOOST_MATH_EXPLICIT_TEMPLATE_TYPE_SPEC(float))\ { return BOOST_PP_SEQ_CAT( (x) (e) (exp) (F)); }\ template <> inline double name<double>(BOOST_MATH_EXPLICIT_TEMPLATE_TYPE_SPEC(double))\ { return BOOST_PP_SEQ_CAT( (x) (e) (exp) ); }\ template <> inline long double name<long double>(BOOST_MATH_EXPLICIT_TEMPLATE_TYPE_SPEC(long double))\ { return BOOST_PP_SEQ_CAT( (x) (e) (exp) (L) ); } BOOST_DEFINE_MATH_CONSTANT(half, 5.000000000000000000000000000000000000, 00000000000000000000000000000000000000000000000000000000000000000000000000, -01); // ok but no e This seems to work even with x = -1.0: BOOST_PP_SEQ_CAT( (-1.0) (123) (e) (-10) ) // -1.0123e- 10 But I'm not sure how this actually works inside Boost.Preprocessor... HTH, --Lorenzo

This seems to work even with x = -1.0:
BOOST_PP_SEQ_CAT( (-1.0) (123) (e) (-10) ) // -1.0123e- 10
But I'm not sure how this actually works inside Boost.Preprocessor...
Unfortunately neither of the ideas offered work for me, I tried: #define C_(x, y) BOOST_JOIN(x, y) #define C2_(x, y) BOOST_PP_SEQ_CAT((x)(e)(y)) int main() { float f = C_(5.0, e-001); float g = C2_(5.0, -001); } And the preprocessor produced: int main() { float f = 5.0e -001; float g = 5.0e- 001; } Note the whitespace inserted before or after the "-". The strange thing is, if I invoke: C_(5.0, 0e-001) Then I get: 5.00e-001 And no whitespace anymore! This leads to me to wonder if GCC is doing something special when it recognizes that the token might be a valid number.... or else that this is just blind luck? Either way, I'd really like to find something that's actually guaranteed to work, rather than just happens to :-( Thanks, John.

Hi, On Sat, Dec 17, 2011 at 12:52 PM, John Maddock
Either way, I'd really like to find something that's actually guaranteed to work, rather than just happens to :-(
I don't know if this help or is accepted Boost practice, but as a last resort, couldn't you generate the necessary header files through some scripting language? Add the script to the boost distribution, so folks can generate their own constants. Best, Christoph

Either way, I'd really like to find something that's actually guaranteed to work, rather than just happens to :-(
I don't know if this help or is accepted Boost practice, but as a last resort, couldn't you generate the necessary header files through some scripting language?
Nod. That's the last resort! ;-) John.

-----Original Message----- From: boost-bounces@lists.boost.org [mailto:boost-bounces@lists.boost.org] On Behalf Of John Maddock Sent: Saturday, December 17, 2011 4:35 PM To: boost@lists.boost.org Subject: Re: [boost] [Math.Constants] Help needed with the preprocessor
Either way, I'd really like to find something that's actually guaranteed to work, rather than just happens to :-(
I don't know if this help or is accepted Boost practice, but as a last resort, couldn't you generate the necessary header files through some scripting language?
Nod. That's the last resort! ;-)
Since the net effect is to generate C++ class (struct) definitions for each constant, we could also write a C++ program that would generate them to fully write all or part of the constants.hpp file. This method was used for the previous math constants submission. It was tedious to write. We would have to fix the precision (but that's done already - currently 100 decimal digits). This would write a much larger constants.hpp file, but users would not be pre-processing at compile time (something that might become a concern if too many constants are being generated, forcing us to split into multiple files). I have no idea about the relative speed of making the pre-processor work hard versus reading a larger C++ header file. However, since the docs and everything is almost ready-to-go, it would be *very* nice to find a quick fix and have to refactor big-time! So useful suggestions still most welcome :-) Paul --- Paul A. Bristow, Prizet Farmhouse, Kendal LA8 8AB UK +44 1539 561830 07714330204 pbristow@hetp.u-net.com

On Sat, Dec 17, 2011 at 6:22 PM, Paul A. Bristow <pbristow@hetp.u->
I have no idea about the relative speed of making the pre-processor work hard versus reading a larger C++ header file.
I'd guess that preprocessing is faster, since it can be done in memory. I wonder whether someone has accurate figures for different compilers. Best, Christoph

John Maddock wrote: [...]
The strange thing is, if I invoke:
C_(5.0, 0e-001)
Then I get:
5.00e-001
And no whitespace anymore!
This leads to me to wonder if GCC is doing something special when it recognizes that the token might be a valid number.... or else that this is just blind luck?
No, that is the way a standard compatible preprocessor works. 0e-001 is parsed as a single pp-token, whereas e-100 is broken down into three pp-token 'e', '-' and '100'. And 1.23e is recognized as a single token, but invalid, because it is not a complete number. The concatenation operator # in BOOST_JOIN concatenates only the two tokens adjacent to it, and the result must be a valid token again. This is why -1234.56 # e-100 does not work: - 1234.56e - 100 are the 4 resulting tokens here, and one of it is invalid. Your idea of using representations of 0 is clever enough to work around this problem. The downside is, that the mantissa receives an additional zero at the end, which is visible in stringizations. It might lead to confusion, when this string is displayed to the user, or an implied precision is derived from it. Cheers Wolf Lammen -- NEU: FreePhone - 0ct/min Handyspartarif mit Geld-zurück-Garantie! Jetzt informieren: http://www.gmx.net/de/go/freephone
participants (6)
-
Christoph Heindl
-
Jeffrey Lee Hellrung, Jr.
-
John Maddock
-
Lorenzo Caminiti
-
Paul A. Bristow
-
Wolf Lammen