Boost logo

Boost :

From: David B. Held (dheld_at_[hidden])
Date: 2004-07-14 19:55:23


Pavol Droba wrote:
> [...]
> I have put some info into the docs, but I'm not sure if it is
> correct and sufficient. Could some of you, that had lead the
> discussion about this topic (namely David Abrahams or David B.
> Held) check it out and tell me your opinion?
> [...]

Looks fine to me. However, instead of saying that the library makes
assumptions about the exception safety of template and function
arguments, I would say that providing the basic guarantee is a
precondition for using the library. Also, I think it would be helpful
to have a table of exactly which functions give a stronger guarantee,
unless to_lower_copy() is the only such function, in which case you
should just mention that in the original note. Note that it is
operations which give exception safety guarantees, not types. For a
given type, there may be functions (member or free) which offer the
basic, strong or nothrow guarantee.

Technically, you want to look at which member/user-defined functions
of the argument types get called, and require those to provide the basic
guarantee. Really, all functions should provide the basic guarantee, so
this should really go without saying, but it's better to say it to
remind users. This is probably how I would word that section:

   Exception Safety

   The library requires that all operations on types used as template or
   function arguments provide the basic guarantee. In turn, all
   functions and algorithms in this library, except where stated
   otherwise, will provide the basic guarantee.

Now, here is where it gets tricky. You mention "const operations",
which should probably read "pure operations", possibly with a note
specifying that "pure" means "without side effects". But really, in
order to provide the strong guarantee, you need to state exactly which
user functions need to be pure. Let's analyze to_lower_copy() to see
what we should say about it:

     template< typename ContainerT >
     inline ContainerT to_lower_copy(
         const ContainerT& Input,
         const std::locale& Loc=std::locale() )
     {
         ContainerT Output;
         std::transform(
             string_algo::begin(Input),
             string_algo::end(Input),
             std::back_inserter<ContainerT>( Output ),
             string_algo::detail::to_lowerF(Loc) );

         return Output;
     }

Now, let's look at what functions are being called. First,
std::locale() is called, but I'm pretty sure we can assume that does not
modify global state. Thus, it is a pure function. Next, ContainerT()
is called. If it provides the basic guarantee but modifies global state
before throwing, then this function will not provide the strong
guarantee. Next, string_algo::begin() and ::end() are called, which I
assume are pure functions. Since std::back_inserter() is called on
local data, it is pure with respect to to_lower_copy(). Since Loc is
passed by const&, we assume that string_algo::detail::to_lowerF(Loc)
does not modify Loc nor global state. Thus, it too is pure w.r.t.
to_lower_copy(). Finally, since std::transform() is called on local or
const data, it too is pure w.r.t. to_lower_copy(). So this function can
give the strong guarantee if it has the signature

    { pure, strong, pure* }

Where the first one is std::locale(), the second is ContainerT(), and
the rest is std::transform et al. So what we really mean is that
to_lower_copy() gives the strong guarantee when ContainerT's default
constructor gives the strong guarantee. Note that none of the other
c'tors of ContainerT need give any special guarantees. So be careful
not to overspecify your client requirements for exception safety.
Exception safety is a property of operations, not types. Hope that helps.

Dave


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