|
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
folding
- it is (relatively) easy to extend with new constants and
representation
types
- 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
Borland
- 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):
// LIBRARY CODE
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
create
// 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
// USER CODE
#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 mkenniston_at_[hidden] msk_at_[hidden] http://www.xnet.com/~msk/
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk