6
\$\begingroup\$

I love boost::lexical_cast but its usage of exceptions there is not preferred in most situations I use it.

I have implemented a version and it seems to work but I want to share it here to hopefully find any defects and/or simply find improvements.

template<typename T1, typename T2>
T1 lexical_cast(T2 const& value, T1 const& defaultValue = T1{})
{
    T1 convertedValue;
    std::stringstream ss;
    if (!(ss << value) || !(ss >> convertedValue))
    {
        return defaultValue;
    }

    return convertedValue;
}

Here is the code on Coliru where I've been testing.

\$\endgroup\$
1
  • \$\begingroup\$ Have you looked at Boost.Convert? ⟪choice of immediate or delayed exception-throwing and non-throwing conversion-failure processing⟫ \$\endgroup\$ Commented Jun 22, 2018 at 8:33

3 Answers 3

2
\$\begingroup\$

I know I am quite late, but try_lexical_convert() can be utilized:

using boost::conversion::try_lexical_convert;
if(try_lexical_convert(source, target)) {
   // ...
}
\$\endgroup\$
4
  • 5
    \$\begingroup\$ Could you explain to the OP how this code could be incorporated with his/her code? Should that function be used instead of lexical_cast or before/after it? \$\endgroup\$ Commented Jun 21, 2018 at 15:47
  • 1
    \$\begingroup\$ The OP can figure it out by self, because it's enough to know the function name. I add answer there so as google searching for 'lexical_cast without exceptions' will give this sweet short answer. \$\endgroup\$ Commented Jun 22, 2018 at 9:53
  • 2
    \$\begingroup\$ You have presented an alternative solution, but haven't reviewed the code. Please explain your reasoning (how your solution works and why it is better than the original) so that the author and other readers can learn from your thought process. Please read Why are alternative solutions not welcome? \$\endgroup\$ Commented Jun 22, 2018 at 17:18
  • 1
    \$\begingroup\$ Upvoted. This is a fantastic answer. According to Boost docs, lexical_cast is imlemented internally using try_lexical_convert. \$\endgroup\$ Commented Sep 22, 2019 at 19:31
2
\$\begingroup\$

If you are on C++17, you can use std::from_chars to convert your string into numerical values. It is much faster than boost::lexical_cast.

A sample usage can be:

int parsed_num{};
std::string to_parse{"123"};
std::from_chars(to_parse.begin(), to_parse.end(), parsed_num);

The function returns a struct with the error code and a pointer to the rest of the unparsed string. If the usage seems a little involved, you can always wrap it in a templated wrapper function that can define the variable for you, pass it to from_chars, and then return the result. One implementation can be:

#include <charconv>
#include <string_view>
#include <stdexcept>
#include <optional>

template<typename T, typename SAFE_PARSE_T=unsafe_parse>
[[nodiscard]] inline constexpr T parse(std::string_view sv, 
                                       const std::optional<T> default_val={}, 
                                       const char** next = nullptr)   
                                       noexcept(SAFE_PARSE_T::value)
{
  if constexpr(not SAFE_PARSE_T::value) {
    sv.remove_prefix(sv.find_first_not_of("+ ")); 
  }
  T var {default_val.value_or(T{})};
  auto ec = std::from_chars(sv.cbegin(), sv.cend(), var);
  if (next) *next = ec.ptr;
  if constexpr(not SAFE_PARSE_T::value) {
    if (default_val) {
      return var;
    }
    if(ec.ec!=std::errc{}) {
      throw std::runtime_error{"Parsing error!"};
    }
  }
  return var;
}

source: https://thecodepad.com/cpp/performant-string-parsing-in-cpp/

\$\endgroup\$
1
\$\begingroup\$

Since you have an already working lexical cast. Why not just wrap that?

namespace MyStuff
{
    template<typename T1, typename T2>
    T1 lexical_cast(T2 const& value, T1 const& defaultValue = T1{})
    {
        try
        {
            return boost::lexical_cast<T1,T2>(value);
        }
        catch(...) // Ignore all exceptions and use default.
        {
            return defaultValue;
        }
    }
}

The reason I would not advocate writing your own is the actually complicity of all the edge cases. Though your code embodies what is happening the actual code has not reflected this in a long time (you have basically written the deprecated version of boost::lexical cast).

Also the boost version has a whole bunch of special case optimizations.

\$\endgroup\$
8
  • \$\begingroup\$ The goal is to avoid exceptions. While I agree with your point about not reinventing the wheel, simply hiding the exception does not remove the performance impact. \$\endgroup\$ Commented Feb 5, 2015 at 15:19
  • \$\begingroup\$ I don't see exceptions being a real cost when you consider what we are doing - reading from a stream a slow operation most of the time. But I have not timed it. If you have some real data about the extra cost I would love to see it. Also is the extra cost greater than the speed saved by all the specializations specifically designed to increase the speed. \$\endgroup\$ Commented Feb 5, 2015 at 19:07
  • \$\begingroup\$ Also note: There is no extra cost if the exception is not thrown. So if you have good sanitized input then it will run faster than your code because of the specialization designed to increase speed in the boost::lexical_cast \$\endgroup\$ Commented Feb 7, 2015 at 0:19
  • \$\begingroup\$ You're missing the point. When I'm parsing a large XML file, for example, I don't need thousands of exceptions to occur which would negatively impact parse times. Exceptions are for exceptional situations, and in this case they are not exceptional (some failures are expected and can be recovered without the usage of exceptions). \$\endgroup\$ Commented Feb 8, 2015 at 21:52
  • \$\begingroup\$ @void.pointer: What I actually asked in the last comment do you have any data to support that there is actually a negative impact on parse time? Assuming you know what you have parsed and thus know it is a number already then why is boost::lexical_cast going to throw. \$\endgroup\$ Commented Feb 8, 2015 at 22:41

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.