$include_dir="/home/hyper-archives/boost/include"; include("$include_dir/msg-header.inc") ?>
From: Paul A. Bristow (boost_at_[hidden])
Date: 2001-11-26 14:22:57
Thanks for your brainpower on this.
I am trying to evaluate the cunningness/convenience balance on this
proposal,
and to try it out a bit.
What about a complex constant - will it fit?
Paul
Dr Paul A Bristow, hetp Chromatography
Prizet Farmhouse
Kendal, Cumbria
LA8 8AB      UK
+44 1539 561830
Mobile +44 7714 33 02 04
mailto:pbristow_at_[hidden]
> -----Original Message-----
> From: mkenniston_at_[hidden] [mailto:mkenniston_at_[hidden]]
> Sent: Saturday, November 24, 2001 7:02 PM
> To: boost_at_[hidden]
> Subject: [boost] Re: Math constants for naive and gurus? - which
> constants do you want?
>
>
> I'd like to take another crack at this.
>
> --- Topic 1
>
> There was a question as to what's wrong with
> (paraphrasing slightly):
>
>   namespace boost {
>   namespace math {
>   namespace float_constant {
>     double pi( 3.14159 );
>   } } }
>
> It has already been pointed out that this doesn't
> mesh well with generic programming, but it's actually
> worse than that - this isn't easily extensible either,
> even without bringing templates into it.  Assume that
> someone defines an arbitrary precision bigfloat
> class.  Then certainly users will want a corresponding
> bigfloat pi.  Even neglecting the mess that results
> if bigfloat is itself a template class, we'll
> end up with something like:
>
>   namespace boost {
>   namespace math {
>   namespace bigfloat_constant
>     bigfloat pi( appropriate_arguments );
>   } } }
>
> The problem here is that since bigfloat may have a
> non-trival constructor, that constructor may have to be
> executed at runtime, and that opens up the whole
> order-of-initialization can of worms.  Let's regard
> this as Yet Another Reason Why Globals Are Evil.
>
> --- Topic 2
>
> Peter Dimov pointed out that my previous idea, or any
> similar scheme using convertible proxy objects that
> are hidden from the user, has a problem with templates:
>
>   template<class Numeric> void foo(Numeric n);
>   foo(pi()); // calls void foo<some_type_that_is_not_numeric>
>
> and Ed Brey raised a similar issue with:
>
>   double triple(double const* x); // Triples x, or returns 0 if x is
> null.
>
> The problem in each case is that "convertible to X" can
> usually be substituted for "X", but not always.  The really
> nasty part is that the user interface pretends that they
> are the same, so when the pretense fails (as it does in
> these examples), the nature of the problem will often
> be quite obscure to the end user and thus will result in
> long and difficult debugging.
>
> The subtlety (from the naive user's point of view) of this
> kind of issue has been of growing concern to me.  Now perhaps
> one shouldn't argue against his own design like this, but
> I've had to maintain existing systems and have learned to
> be wary of "subtle".  Then when Paul Bristow described
> my design as "cunning" I got _really_ worried. :-)
>
> --- Topic 3
>
>
>     "Once more unto the breach, dear friends, once more"
>         - Henry V, Act III, Scene I
>
>
> Let's throw everything away (figuratively, not literally!)
> and go back to first principles.  The key observations are:
>
> - Inline functions are a big win for the designer,
> as they are very flexible and maximally efficient.
> But (barring cunning tricks) function calls require
> parentheses.
>
> - Users refuse to write "pi()".  Personally I
> think this is silly, but WEB's users made this
> point quite clear to him, so I must reluctantly
> accept it.  However, if I read his original post
> http://groups.yahoo.com/group/boost/message/11442
> correctly, the users didn't so much mind the extra
> two characters of typing; instead they were objecting
> to the "unnatural" notation.  (Please correct me
> if I'm wrong on this, since it's a crucial point.)
>
> With this in mind, I'll propose another approach,
> focusing on the fact that the constants of interest
> are generally irrationals, and we'll handle the
> parentheses problem by making a virtue of necessity.
>
> In this design, we only have _one_ object called pi.
> (Well, technically we can have lots of them, because
> they're degenerate monostates, but the point is
> that we do _not_ have separate "pi" objects or types
> for float, double, long double, etc.)
>
> Since this object is called "pi", we define it to
> represent pi -- exactly.  I mean that literally;
> the object called "pi" will represent the precise,
> 100% accurate value of the irrational number,
> with no rounding, approximation, or compromise.
> Noah Stein was undoubtedly joking when he suggested
> this, but I intend to show that it's actually quite
> easy to do.  Pretty cool, huh?
>
> Now let's say a user who wants to calculate an
> area says:
>
>   double radius;
>   cin >> radius;
>   double area;
>   area = pi * radius * radius;
>
> This won't compile, because "pi" is an exact
> representation of an irrational number, and we don't
> have a CPU that can calculate with those.  You have
> to cast it to something you can compute with, like this:
>
>   area = double( pi ) * radius * radius;
>
> Of course, we've re-introduced the parentheses, but
> now we can explain what they do and why you need them in
> terms that will make sense to a user.  Furthermore, the
> expression is essentially what it appears to be (a conversion
> constructor), so any problems should be easy to diagnose.
> The type of the expression is also explicit, so templates
> and overload resolution and such will all work as one
> would expect.  Filling in the details (and assuming we
> put everything in the appropriate namespace) we have:
>
>   // math.hpp
>
>   template< typename Tag >
>   struct irrational
>   {
>     template< typename Target >
>     operator Target() const;
>   };
>
>   struct pi_tag {};
>   typedef irrational< pi_tag > pi_t;
>
>   template<> template<>
>   inline irrational< pi_tag >::operator double() const
>   {
>     return 3.14159;
>   }
>
>   // math_constants.hpp
>
>   extern pi_t pi;
>
>   // math_constants.cpp
>
>   pi_t pi;
>
>
> Comments:
>
> - You can hack up a serviceable (but less extensible)
> implementation with #ifdefs for template-challenged
> compilers without messing up the user-visible interface.
>
> - The "pi" identifier is provided for user convenience.
> There should be no efficiency hit, because the actual
> references to that identifier should be optimized
> away, but it does require linking to a library binary.
> If you are writing a library, you can use "pi_t()"
> instead of "pi" to avoid introducing the link dependency.
>
> - Because the conversions "double( pi )" are true
> conversions that the compiler understands, I don't
> think there will be any problems with using class names,
> typedefs, or templates.
>
> - There appears to be an order-of-initialization issue,
> but since objects like pi don't have any state and their
> (trivial default) constructors don't do anything, it doesn't
> matter when, or even if, they get initialized.
>
> - I got rid of the annoying null constructor by dropping
> the "const" part of the declaration for the "pi" object.
> Technically it should be a const, but since you can't do
> anything to such an object anyway I don't see any harm
> in leaving off the const, and it makes the compiler happy.
>
> - I haven't yet pushed this to the example of a templated
> user-defined type (like bigfloat), which would involve the
> definition of a template class with a template conversion
> operator member function whose template parameter is a
> template class - but it sounds like a good test for
> compiler compliance!
>
>
> A further refinement:
>
> The main drawback of this approach is that an explicit
> conversion is required at every point of use of every
> constant.  I would expect that users will object to this,
> but I'd also expect that the vast majority of uses are
> inside arithmetic expressions.  That means if we define
> operators for + - * / like this:
>
>   template< typename Rep, typename Tag >
>   inline Rep operator*( irrational< Tag > lhs, Rep rhs )
>   {
>     return Rep( lhs ) * rhs;
>   }
>
> then the user really can say:
>
>   area = pi * radius * radius;
>
> There will still be a few places where an explicit conversion
> will be required, but those should be sufficiently
> few as to not be too annoying, and they can be explained
> easily and clearly.
>
>   double radius, circumference;
>   cin >> radius;
>   circumference = 2.0 * pi * radius; // ok
>   circumference = 2 * ( pi * radius ); // ok
>   circumference = double( 2 ) * pi * radius; // ok
>   circumference = 2 * double( pi ) * radius; // ok
>   circumference = radius * pi * 2; // ok
>   circumference = 2 * radius * pi; // ok
>   circumference = 2 * pi * radius; // ERROR: int * irrational
>
> or generically:
>
>   template< typename Rep >
>   Rep circumference( Rep radius )
>   {
>     return 2 * Rep( pi ) * radius;
>   }
>
> If users really demand it, maybe something could be done to
> make "2 * pi" work, but I fear that might get too cunning ...
>
>
> Pros:
>
> - very flexible for generic programming.
>
> - allows arbitrary efficiency/accuracy tweaks.
>
> - syntax of use is simple and transparent
> (_how_ things are done can be hidden under
> the covers, but _what_ is being done is
> right out in the open).
>
> - eliminates the need for a separate namespace
> for each type.
>
> - easy to extend to new constants (even if
> incorporated into std::, since specialization
> of classes in std:: is allowed).
>
> - easy to extend to to new types (although I'm
> a little hazy about whether the standard technically
> allows a user to use template specialization to
> overload a conversion operator member function of
> a template class in std::).
>
>
> Cons:
>
> - occasional uses of constants must be explicitly
> converted to double or whatever.
>
>
> So, what do folks think?  Will users buy this notation
> and the rationale behind it, or will they just dismiss
> it all as the wild rantings of a crazed designer?  :-O
>
> - Michael Kenniston
>
>
> P.S.  In situations like this, I often find I want to
> specify "explicit" for conversion operators as well as
> for conversion constructors.  This seems logical to me,
> since a conversion constructor in class A or a conversion
> operator in class B both convert from B to A, yet the
> language clearly allows only the constructor to be
> declared "explicit".  Does anyone know if this asymmetry
> was deliberate, and if so what the reasoning behind it was?
>
>
>
> Info: http://www.boost.org  Unsubscribe:
<mailto:boost-unsubscribe_at_[hidden]>
Your use of Yahoo! Groups is subject to http://docs.yahoo.com/info/terms/