Boost logo

Boost :

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/


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk