Boost logo

Boost :

From: Björn Karlsson (bjorn.karlsson_at_[hidden])
Date: 2002-01-25 06:17:31


The reasoning below is the background for the proposal of adding a
nothrowing relative to lexical_cast, called lexical_convert.

There are two main reasons why lexical_cast isn't always a perfect fit (as I
see it):
0) In code that doesn't otherwise use exceptions, the error handling can
become awkward/messy/inconsistent when using lexical_cast.
1) When a failed conversion is not considered an error, the exception
handling stands in the way of code clarity.

I seem to recall an earlier proposal for a lexical_cast, something like
"template<typename Target, typename Source> bool lexical_cast(Source arg,
Target* result)", but obviously, that doesn't look like a cast anymore.
It's still useful though, so why don't we give it another name?

I think lexical_convert states the meaning quite clearly:
template<typename Target, typename Source> bool lexical_convert(Source arg,
Target* result)

The implementation would be "borrowed" from lexical_cast:

   template<typename Target, typename Source>
   bool lexical_convert(Source arg, Target* result)
   {
# ifdef BOOST_LEXICAL_CAST_USE_STRSTREAM
      std::strstream interpreter; // for out-of-the-box g++ 2.95.2
# else
      std::stringstream interpreter;
# endif
      if(!result || !(interpreter << arg) || !(interpreter >> *result) ||
      !(interpreter >> std::ws).eof())
         return false;
      return true;
   }

Passing the target by pointer or reference is a tough call - the intention
is made clear by using a pointer, but that allows for code like: int* p;
boost::lexical_convert<int>(s, p);. On the other hand, passing by reference
solves the problem but makes the code less clear. I hesitate to claim that
one version is better than the other, so I'll leave it at that and a test
for !result (in the end, I think this one boils down to personal
preference).

There's not much code, so duplication probably doesn't matter, but the
throwing lexical_cast could obviously be implemented in terms of
lexical_convert:

template <typename Target, typename Source> Target lexical_cast(Source arg)
{
   Target result;
   if (!lexical_convert<Target>(arg, &result))
      throw bad_lexical_cast();
   return result;
}

Sample client code:

void lexical_cast_example(const std::string& s) {
   int i=0;

   if (boost::lexical_convert<int>(s, &i)) {
      std::cout << i << "\n";
   }
   else {
      std::cout << "bad lexical conversion: source type value could not be
interpreted as target";
   }

   try {
      i=boost::lexical_cast<int>(s);
      std::cout << i << "\n";
   }
   catch(boost::bad_lexical_cast& e) {
      std::cout << e.what();
   }
}

The point of the example (for the case where a failed conversion is not
considered an error) is that the else could simply be omitted, and that (in
this case) there's no reason to throw if noone wants to catch :-).

An open issue would be where the stuff should go - lexical_cast.hpp is very
specific, but maybe the right place anyway?

Bjorn



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