Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When does boost::lexical_cast to std::string fail?

I'm writing unit tests and trying to have all my code covered.

I have in my code something like this:

template<typename ValueType>
std::string ConvertToStringUsingBoost(ValueType const& v)
{
    try 
    {
        return boost::lexical_cast<std::string, ValueType>(v);
    }
    catch(boost::bad_lexical_cast const& e)
    {
        LOG_ERR("Fail to cast %s to string", e.source_type().name);
        return std::string();
    }
}

I was reading these docs and couldn't find any information about when boost::lexical_cast to std::string can throw an exception.

Can you please help me with that?

If it's impossible I'll simply delete this try-catch. If it's possible, I'd prefer to cover this in unit testing.

like image 343
Grigorii Alekseev Avatar asked May 01 '19 11:05

Grigorii Alekseev


People also ask

How do I cast a string to a number in boost?

Boost.LexicalCast which is defined in the Library “boost/lexical_cast.hpp” provides a cast operator, boost::lexical_cast, that can convert numbers from strings to numeric types like int or double and vice versa.

Why lexical_cast is not special-cased?

Where non-stream-based conversions are required, lexical_cast is the wrong tool for the job and is not special-cased for such scenarios. Exception used to indicate runtime lexical_cast failure.

What's new in the current version of lexical_cast?

The current version fixes this error for std::string and, where supported, std::wstring: lexical_cast<std::string> ("Hello, World") succeeds instead of failing with a bad_lexical_cast exception. The previous version of lexical_cast allowed unsafe and meaningless conversions to pointers.

What is a bad_lexical_cast exception?

If the conversion is unsuccessful, a bad_lexical_cast exception is thrown. Source is OutputStreamable, meaning that an operator<< is defined that takes a std::ostream or std::wostream object on the left hand side and an instance of the argument type on the right.


3 Answers

It can fail for example if a user-defined conversion throws:

enum class MyType {};

std::ostream& operator<<( std::ostream&, MyType const& )
{
    throw "error";
}

int main()
{
    try 
    {
        boost::lexical_cast< std::string >( MyType{} );
    }
    catch(...)
    {
        std::cout << "lexical_cast exception";
    }
}

As you have no control about the type of exceptions thrown by user-defined conversions, catching boost::bad_lexical_cast won't even be enough. Your unit test has to catch all exceptions.

Live Demo

like image 104
zett42 Avatar answered Oct 17 '22 22:10

zett42


I can't think of any reason for lexical cast to string to throw bad_lexical_cast, except with user-defined types. If the ValueType stream insertion operator can set an error flag on the stream then that's going to result in a bad_lexical_cast. Otherwise, not.

Personally I'd keep the catch in, even if you're just converting built-ins like ints; it doesn't hurt, and may catch bugs if you change the lexical_cast in some manner, or if there's some edge case that neither you nor I has considered; if you're not handling the resulting exception, you'll get an abort at runtime!

If you're concerned about the overhead of an exception, you can use try_lexical_cast instead and check that it returns true rather than catching. However, if the ValueType stream insertion operator can throw then you'd still need to be able to catch that exception anyway.

like image 38
Lightness Races in Orbit Avatar answered Oct 17 '22 21:10

Lightness Races in Orbit


The only safe and futureproof (eg. no nasty surprises after an update of boost) is to impair your code with something (ugly) like this:

template<typename ValueType>
std::string ConvertToStringUsingBoost(ValueType const& v)
{
    try 
    {

#ifdef UNITTEST
      if (unittest == case_fail) {
        throw boost::bad_lexical_cast();
      }
#endif
        return boost::lexical_cast<std::string, ValueType>(v);
    }
    catch(boost::bad_lexical_cast const& e)
    {
        LOG_ERR("Fail to cast %s to string", e.source_type().name);
        return std::string();
    }
}

Now you should be able to get to that ~100% code coverage !

like image 20
darune Avatar answered Oct 17 '22 22:10

darune