Boost logo

Boost :

From: Michael Kenniston (Msk_at_[hidden])
Date: 2001-07-18 14:31:51

Paul A. Bristow wrote:

> The same sort of inconclusive discussion took place over
> the presentation of math constants.

> The (naive) users are VERY keen to avoid extraneous (),
> but the gurus maintain that providing functions is much
> more efficient.

Thanks for pointing me to that thread, and please forgive me for being
late to the party; I was away from the boost list for a while, and
Murphy saw to it that I missed the whole thing.

Going back to the archives and reading the entire "math constants"
discussion all at once kind of super-saturated my brain, and the
following combination of other people's ideas precipitated out.
Since there does not yet appear to be a solid consensus on the
interface, perhaps it's not too late to toss another proposal into
the ring for consideration. This combines the syntax of a const
variable with the semantics of an inline function, and I think it
/might/ be an acceptable compromise to satisfy almost everyone:

- Paul only has to write one interface (not counting macros-for-C,
    which is really a separate library from the C++ stuff)
- no macros, all names are in namespaces
- naive users can just say "pi" instead of "pi()"
- average users can switch "pi" between float and double by changing
    one "using" directive
- no "double rounding" (i.e. no multiple conversions)
- platform implementers can use whatever evil incantations they desire
    for maximum accuracy
- everything is implemented with inline functions to ease constant
- it is (relatively) easy to extend with new constants and
- purists can access all constants via templated functions
- there are no "static-initialization-order" issues, because the static
    variables contain no data to initialize
- there is no partial template specialization, so it works on MSVC
- there are no non-type template parameters, so it should? work on
- the same basic technique will probably work for converting read access

    to any statically allocated class object to an inline function call,

    including in particular physical constants with units/dimensions

Here's the general idea (tested under MSVC6 and g++2.95.3-4):


namespace boost { namespace math { // or whatever

// The "constant" class provides a place to put the actual definition
// of each constant. It also turns an implicit conversion operation
// (which does not need any parentheses) into an explicit call to the
// correct function that actually knows the right value.

template< typename Rep, typename Tag >
struct constant
    constant() {}
    operator Rep() const; // fully specialized for each Rep/Tag pair

// For each constant, define a tag type and define the actual values.

struct pi_tag {};
inline constant< float, pi_tag >::operator float() const { return 3.14F;
inline constant< double, pi_tag >::operator double() const { return
3.14159; }

struct e_tag {};
inline constant< float, e_tag >::operator float() const { return 2.72F;
inline constant< double, e_tag >::operator double() const { return
2.71828; }

// Define a namespace for each representation type. You can put each
one in
// a separate file if you like. Might also need to "extern" them and
// a .cpp file for definitions to avoid linker problems?
// *** These are the names the typical user actually sees. ***

namespace float_constants
    // Define dataless variables to proxy for actual values.

    constant< float, pi_tag > const pi;
    constant< float, e_tag > const e;

namespace double_constants
    constant< double, pi_tag > const pi;
    constant< double, e_tag > const e;

} } // math // boost


#include <iostream>
using std::cout;

template< typename T >
void show_all( const T & dummy, const char * label )
    // The namespaces are not templated, but gurus can bypass the
    // namespaces and access all constants directly via template
    // parameters. Code like this would normally only appear in
    // libraries written by non-novices.

    using namespace boost::math;
    cout << label << ": pi = " << constant< T, pi_tag >() <<
        ", e = " << constant< T, e_tag >() << "\n";

int main( int, char *[] )
    // Select an entire set of constants with a single using directive
    // (on a per-function basis if desired).

    using namespace boost::math::double_constants;

    // Naive users can just say "pi",

    cout << "e = " << e << ", pi = " << pi << "\n";
    double r = 2.0;
    cout << "area = " << pi * r * r << "\n";

    // or they can explicitly access any constant at any time.

    cout << "float e = " << boost::math::float_constants::e <<
        ", pi = " << boost::math::float_constants::pi << "\n";

    // Exercise guru code.

    show_all( double(), "double" );
    show_all( float(), "float" );

    cout << "End of test\n";
    return 0;

Implicit conversions are usually to be avoided, but in this case I
think it's safe because there is no doubt about which conversion
is used (there is only one), or about when a conversion
occurs (anytime you use "pi", since there are /no/ other useful
operations defined for the "pi" variable's type).

One thing you do /not/ get with the above scheme is a free set
of values when you add a new type, e.g. "rational< int >".
Perhaps this is as it should be, though, since there isn't
any point in having something like rational_int_constants::pi
defined unless some human has thought about what the right
value should be. If you rely on automatic conversion you might
just as well say "rational< int >( double_constants::pi )" and
have it be clearer what you're actually getting.

Well, ok, if you /really/ want defaults, I guess you could replace
the master declaration of the conversion operator with something like:

    operator Rep() const { return Rep( constant< long double, Tag >() );

You could use a macro to reduce the verbosity coefficient of the
actual definition of each constant, but I'm not at all convinced
that's a good idea because it would tend to obfuscate the simple but
non-obvious magic involved.

- Michael Kenniston

Boost list run by bdawes at, gregod at, cpdaniel at, john at