|
Boost : |
From: Tobias Schwinger (tschwinger_at_[hidden])
Date: 2005-05-07 17:18:45
Hi Paul,
I just took a look at your library because I sometimes need certain
constants (like zero, one, pi or e) and was missing the rather special
ones (as the 12th root of two, integral constants for well-distributed
binary permutation or some magic numbers for linear congruental random.
However, this is not the point of this post but made me look at the library
design from another perspective:
A constants-library needs to be extensible in many directions.
Paul A Bristow wrote:
> I have recently posted to the Boost Sandbox vault an update of my collection
> of mathematical constants, previously reviewed as a Boost library and ultimately
> not accepted because of lack of agreement on how to present the constants,
> especially for User Defined Types, and an outbreak of macrophobia.
"Lack of agreement" sounds pretty sad. Such a library is most useful,
especially for generic programming.
Will there never ever be a set of predefined math constants in Boost (in
fact, I would prefer the most common constants to be part of the
standard library) ?
However, macros which can be avoided and do not significantly simplify things
_do_ suck (please excuse my straightforwardness), unless you are programming in
plain-C (well, they still suck but there often is no better way then ;-) ). They
are only acceptable as an interface "#ifndef __cplusplus", IMHO and do not
belong in a C++ library.
Further macros come especially unhandy when trying to build an
extensible framework for constant definition because there is no way to automate
macro definition with the preprocessor (see below).
Static constants in "<type>_constants"-namespaces are also undesirable, because
there is no way to select a namespace named "<T>_constants" from within a template.
What I have mentioned so far are facts - not a matter of agreement !
The problem with the current design is there are three implementations, two of
them are very disadvantageous and the third not optimal, yet (I'll explain why
below). I would not give a positive vote for the library as-is because of that,
when writing a review.
[...]
> (and also as the functions allowing pi to call a function pi()
> proposed by Michael Kenniston, even though I am not entirely convinced
> of their usefulness).
What do you need to be convinced ?
Did you ever take a look at 'numeric_limits' [ ref. 18.2.1.1 ] ?
This is C++ style ;-) and the empty parentheses hurt much less than an ugly
'M_'-prefix and heavy namespace pollution !
From what I see in the archive (I did not read up the original proposal) this
can still be refined. I'ld prefer:
pi<float>() // constructor invocation of a "convertible to float"
or
pi<T>()
in a template.
Probably it's even worth thinking about static functions for the sake of
runtime efficiency:
pi<float>::value() // needs disassembly-reading on several compilers
(the functor semantics could be achieved with a wrapper template in this case)
The current state also lacks extensibility. It is not acceptable to rely
on an external code generator. We have a great preprocessor library for
jobs like this one. It is desirable to have user-defined constants for arbitrary
types, which can be defined from within user code on-the-fly, using a
self-contained code generator.
Typically a constant applies to a group of types:
Constants like 'zero', 'one' or 'two' exist for pretty much all types.
Constants like 'one_half', 'pi' or 'e' exist for real types.
Constants for bit-twiddling purposes only make sense for integral types.
There might even be a constant, e.g. 'molecules in the ocean', which is only
encodable with a special type.
There is another orthogonal dimension:
Different problem domains require different constants for the math involved.
I made a 10-minutes-hack using the Boost.Preprocessor to clarify the one
possible direction I see (some parts mentioned above are left open,
but the skeleton works and has been tested with the GCC preprocessor):
--- [snip] sample invocation
// There can be any number of groups of any number of types.
// This example group's name is 'real'
// (See Preprocessor library documentation Datatype SEQUENCE)
#define BOOST_CONSTANT_GROUP_real (float)(double)
// Every type needs a "generator"
// The generator is macro that forms an expression the specified
// type when passed input from the table below
#define BOOST_CONSTANT_GENERATOR_float(value) BOOST_PP_CAT(value,f)
#define BOOST_CONSTANT_GENERATOR_double(value) value
// This tells us we want two constants, 'pi' and 'e' both for
// all types in the group 'real' which can be constructed from
// the value in the next column by using the "generator" for the
// appropriate type
// (See Preprocessor library documentation Datatype SEQUENCE)
// (See Preprocessor library documentation Datatype TUPLE)
#define BOOST_CONSTANTS \
((real,pi,3.141592653589793238462643383279502884197)) \
((real,e,2.718281828459045235360287471352662497757))
// Generate code
#include <generate_constants.hpp>
--- [snap]
--- [snip] header generate_constants.hpp
// This header is intended for multiple inclusion.
#if !defined(BOOST_PP_IS_ITERATING)
//------------------------------------------------------------------------------
// Dependecies
//------------------------------------------------------------------------------
#ifndef BOOST_GENERATE_CONSTANTS_HPP_INCLUDED
#define BOOST_GENERATE_CONSTANTS_HPP_INCLUDED
# include <boost/preprocessor/cat.hpp>
# include <boost/preprocessor/seq/elem.hpp>
# include <boost/preprocessor/seq/size.hpp>
# include <boost/preprocessor/tuple/elem.hpp>
# include <boost/preprocessor/iteration/iterate.hpp>
#endif
//------------------------------------------------------------------------------
// Kick off outer iteration
//------------------------------------------------------------------------------
#ifndef BOOST_CONSTANTS
# error "BOOST_CONSTANTS is not defined"
#endif
#define BOOST_PP_ITERATION_LIMITS (0,BOOST_PP_SEQ_SIZE(BOOST_CONSTANTS)-1)
#define BOOST_PP_FILENAME_1 "generate_constants.hpp"
#include BOOST_PP_ITERATE()
//==============================================================================
#elif !defined(BOOST_CONSTANT_entry) // For each constant:
//==============================================================================
#define BOOST_CONSTANT_entry BOOST_PP_SEQ_ELEM( BOOST_PP_FRAME_ITERATION(1) \
, BOOST_CONSTANTS )
#define BOOST_CONSTANT_group \
BOOST_PP_CAT( BOOST_CONSTANT_GROUP_ \
, BOOST_PP_TUPLE_ELEM(3,0,BOOST_CONSTANT_entry) )
#define BOOST_CONSTANT_name BOOST_PP_TUPLE_ELEM(3,1,BOOST_CONSTANT_entry)
#define BOOST_CONSTANT_value BOOST_PP_TUPLE_ELEM(3,2,BOOST_CONSTANT_entry)
//------------------------------------------------------------------------------
template<typename T> struct BOOST_CONSTANT_name ;
#define BOOST_PP_ITERATION_LIMITS \
(0,BOOST_PP_SEQ_SIZE(BOOST_CONSTANT_group)-2)
#define BOOST_PP_FILENAME_2 "generate_constants.hpp"
#include BOOST_PP_ITERATE()
//------------------------------------------------------------------------------
#undef BOOST_CONSTANT_value
#undef BOOST_CONSTANT_name
#undef BOOST_CONSTANT_group
#undef BOOST_CONSTANT_entry
//==============================================================================
#else // For each target type in constant type group
//==============================================================================
#define BOOST_CONSTANT_type BOOST_PP_SEQ_ELEM( BOOST_PP_FRAME_ITERATION(2) \
, BOOST_CONSTANT_group )
// Specialization comes here
// [...]
return BOOST_PP_CAT( BOOST_CONSTANT_GENERATOR_ \
, BOOST_CONSTANT_type )(BOOST_CONSTANT_value) ;
// [...]
}
//==============================================================================
#endif
--- [snap]
Just a hack - there is still plenty of room for improvement and more ideas.
Done for fun and inspiration...
Regards,
Tobias
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk