Boost logo

Boost :

From: Fernando Cacciola (fernando_cacciola_at_[hidden])
Date: 2003-12-19 09:37:44

Thorsten Ottosen <nesotto_at_[hidden]> wrote in message news:brtrc5$f6k$
> "Fernando Cacciola" <fernando_cacciola_at_[hidden]> wrote in message
> news:brt1qg$peg$
> > Some traits are free-standing:
> >
> > int_float_mixture<>
> > sign_mixture<>
> > udt_builtin_mixture<>
> > is_subranged<>
> >
> > And there's also the bigger traits:
> >
> > conversion_traits<> that includes all of the above plus some aliases and
> > additional useful types.
> Ok, I guess I thought you would make all traits free standing, so when I
> used it, I could
> say
> using namespace numeric::conversion;
> target_type_of<T>::type
> source_type_of<S>::type
> etc.
Traits can be (or should be) isolated only when they are actually independent.
The big "conversion_traits" is such (big) because most of its defintions depends on many common
Isolating the parts of it would actually duplicate the common parts.

target/source type is a bit of a corner case though,

target_type_of<T,S> (notice that both T and S are needed) uses is_same<T,S>

source_type_of<S> uses numeric_limits<S>::is_specialized

These alone could be separated; however, is_same<T,S> is also used inside "conversion_traits" to
define "is_trivial",
which in turn is _needed_ by other stuff in that big traits.
Similarly, is_specialized is used to determine which types are UDTs which is _needed_ in many
places inside the big traits.
Thus, these two traits could be isolated, by they will necesarily be defined in terms of
subtraits used in other places, so separation will lead to duplicate metacode.
Similar arguments justify why no more "pars" of conversion_traits were separated.

> BTW, argument_type is the same as call_traits<T>::param_type, is it not? If
> so, why is it provided?
Because call_traits<> is dead broken in so many compilers....
call_traits is context independent, so it uses a rather complex logic. In contrast,
argument:type is directly given by "::is_specialized".

> > > The section "Exact, Correctly Rounded and Out-Of-Range Representations"
> > > mentions 1 ulp, but that has not been defined anywhere.
> > >
> > oh, OK.
> btw, what is it?

ulp = unit in the last place

It's the number formed by a single 1 in the mantissa.
This definiton is independent of the exponent, so the actual "magnitude" of 1 ulp depends on the
exponent. Its a relative quantity.

> > > Numeric Cast.
> > >
> > > what is improved in the new version?
> > >
> > The range checking is performed only when needed and only at the extent
> > actually needed.
> > In the case of T==S, the return value is a reference allowing most
> compilers
> > to effectively transform the "conversion" into nothing.
> > In the case S is a UDT the argument is taken by reference and nbot by
> value.
> >
> > > Why is the return type not just Target?
> > To allow a reference to be returned when T==S
> aha, maybe you could add a small sentence to the docs here.

> > "The documentation specifies exceptions like this:
> > > virtual const char *what() const // throw()
> > > { return "bad numeric conversion: overflow"; }
> > > the comment after 'const' is bogus.
> > > We need the throw() to override the virtual function.
> >
> > Why? This form of documentation is used all over boost.
> Also when the implementation is actually using the throw() specs?
> My thought was that when a novice sees the docs, he thinks he can override
> what() in the same way, which he can't.
OK you're right here..
the "throw()" is effectively used in the implementation so it must appear as such in the docs.

> > > can "nearbyint()" be renamed to nearby_int() ?
> > "nearbyint" is the name of a C99 function.
> so ? :-) just because C99 is hard to read it does not mean your code has
> to be. In the string lib, Pavol
> consistently avoided this.

Good point... but consider that most probably, "nearbyint" will be as usual as "scanf" in the
future, so familiarity will be important.

> > > About the traits, then I
> > > would prefer the freestanding conventiontemplate<class Traits>
> > > struct YourRawConverterPolicy
> > > {
> > > typedef typename Traits::result_type result_type ;
> > > typedef typename Traits::argument_type argument_type ;
> >
> > Hmmm... I don't get this.... can you explain?
> As I mentioned above, instead of one big trait class, then individual
> free-standing
> traits is prefereed in my oppinion. Each time you just need one type from
> your traits,
> you pay the (compile time) price of the them all.
In the particular case of the RawConverterPolicy the whole conversion_traits is passed in order
to allow the policy implementation to _efficiently_ use all the definitions there.
An instance of "conversion_traits" passed to a policiy is a complete type which means that all
metacode was exceuted already.
If each policy were to use separated traits, such as "argument_type_of<S>::type" then a lot of
meta-code will be duplicated and "meta-executed" again and again; unncesesarilly.
All of the stuff defined in the conversion_traits is needed by the converter so it is already
"pre-meta-compiled" and can be readibly used by the policies.

> > > This would also mean some design changes, right? Didn't you talk about
> > this
> > > before the review?
> >
> > I don't know :-)
> >
> > > Also, can the two nested typedefs somehow be avoided.
> >
> > Do you mean "result_type" and "argument_type"?
> > Remember that these are not necessarily just T and S, but can be "T
> const&"
> > and/or "S const&"
> If you did have free standing traits, then the converter policy could not
> take a traits
> class, but rather source and target paramters and you would only use
> result_type_of<T>::type and argument_type_of<T>::type and only pay for those
> two typedefs.
Same comments as above.


Fernando Cacciola

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