Remove hand written conversion code and replace with stdlib features.
With C++11 the stdlib gains:
- `std::stoi` function family to convert from `std::string` to integral type
- `std::to_string` to convert from number types to `std::string`
The only reason for hand-writing the conversion code therefore is
performance. I benchmarked an `osrm-extract` with the hand-written code
against one with the stdlib conversion features and could not find any
significant difference (we switch back and forth between C++ and Lua,
shaving off a few us in conversion doesn't gain us much).
Formatting arithmetic types in the default format with given precision
requires streams, but is doable in a few lines of idiomatic stdlib code.
For this, there is now the following function template available:
template <Arithmetic T, int Precision = 6>
inline std::string to_string_with_precision(const T);
that requires integral or floating point types and returns a formatted
string in the defaukt format with the given precision applied.
In addition this completely rips out Boost.Spirit from the `casts.hpp`
header, resulting in faster compile times.
Boom!
References:
- http://en.cppreference.com/w/cpp/string/basic_string/stol
- http://en.cppreference.com/w/cpp/string/basic_string/to_string
- http://www.kumobius.com/2013/08/c-string-to-int/
This commit is contained in:
+17
-147
@@ -28,158 +28,28 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#ifndef CAST_HPP
|
||||
#define CAST_HPP
|
||||
|
||||
#include <boost/spirit/include/karma.hpp>
|
||||
#include <boost/spirit/include/qi.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <type_traits>
|
||||
|
||||
struct cast
|
||||
namespace cast
|
||||
{
|
||||
// convert scoped enums to integers
|
||||
template <typename Enumeration>
|
||||
static auto enum_to_underlying(Enumeration const value) ->
|
||||
typename std::underlying_type<Enumeration>::type
|
||||
{
|
||||
return static_cast<typename std::underlying_type<Enumeration>::type>(value);
|
||||
}
|
||||
template <typename Enumeration>
|
||||
inline auto enum_to_underlying(Enumeration const value) ->
|
||||
typename std::underlying_type<Enumeration>::type
|
||||
{
|
||||
return static_cast<typename std::underlying_type<Enumeration>::type>(value);
|
||||
}
|
||||
|
||||
template <typename Number>
|
||||
static typename std::enable_if<std::is_integral<Number>::value, std::string>::type
|
||||
integral_to_string(const Number value)
|
||||
{
|
||||
std::string output;
|
||||
std::back_insert_iterator<std::string> sink(output);
|
||||
template <typename T, int Precision = 6> inline std::string to_string_with_precision(const T x)
|
||||
{
|
||||
static_assert(std::is_arithmetic<T>::value, "integral or floating point type required");
|
||||
|
||||
if (8 == sizeof(Number))
|
||||
{
|
||||
boost::spirit::karma::generate(sink, boost::spirit::karma::long_long, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (std::is_signed<Number>::value)
|
||||
{
|
||||
boost::spirit::karma::generate(sink, boost::spirit::karma::int_, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
boost::spirit::karma::generate(sink, boost::spirit::karma::uint_, value);
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
static int string_to_int(const std::string &input)
|
||||
{
|
||||
auto first_digit = input.begin();
|
||||
// Delete any trailing white-spaces
|
||||
while (first_digit != input.end() && std::isspace(*first_digit))
|
||||
{
|
||||
++first_digit;
|
||||
}
|
||||
int value = 0;
|
||||
boost::spirit::qi::parse(first_digit, input.end(), boost::spirit::int_, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
static unsigned string_to_uint(const std::string &input)
|
||||
{
|
||||
auto first_digit = input.begin();
|
||||
// Delete any trailing white-spaces
|
||||
while (first_digit != input.end() && (std::isspace(*first_digit) || '-' == *first_digit))
|
||||
{
|
||||
++first_digit;
|
||||
}
|
||||
unsigned value = 0;
|
||||
boost::spirit::qi::parse(first_digit, input.end(), boost::spirit::uint_, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
static uint64_t string_to_uint64(const std::string &input)
|
||||
{
|
||||
auto first_digit = input.begin();
|
||||
// Delete any trailing white-spaces
|
||||
while (first_digit != input.end() && std::isspace(*first_digit))
|
||||
{
|
||||
++first_digit;
|
||||
}
|
||||
uint64_t value = 0;
|
||||
boost::spirit::qi::parse(first_digit, input.end(), boost::spirit::long_long, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
// source: http://tinodidriksen.com/2011/05/28/cpp-convert-string-to-double-speed/
|
||||
static double string_to_double(const char *p) noexcept
|
||||
{
|
||||
double r = 0.0;
|
||||
bool neg = false;
|
||||
if (*p == '-')
|
||||
{
|
||||
neg = true;
|
||||
++p;
|
||||
}
|
||||
while (*p >= '0' && *p <= '9')
|
||||
{
|
||||
r = (r * 10.0) + (*p - '0');
|
||||
++p;
|
||||
}
|
||||
if (*p == '.')
|
||||
{
|
||||
double f = 0.0;
|
||||
int n = 0;
|
||||
++p;
|
||||
while (*p >= '0' && *p <= '9')
|
||||
{
|
||||
f = (f * 10.0) + (*p - '0');
|
||||
++p;
|
||||
++n;
|
||||
}
|
||||
r += f / std::pow(10.0, n);
|
||||
}
|
||||
if (neg)
|
||||
{
|
||||
r = -r;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
template <typename T> struct scientific_policy : boost::spirit::karma::real_policies<T>
|
||||
{
|
||||
// we want the numbers always to be in fixed format
|
||||
static int floatfield(T) { return boost::spirit::karma::real_policies<T>::fmtflags::fixed; }
|
||||
static unsigned int precision(T) { return 6; }
|
||||
};
|
||||
using science_type = boost::spirit::karma::real_generator<double, scientific_policy<double>>;
|
||||
|
||||
static std::string double_fixed_to_string(const double value)
|
||||
{
|
||||
std::string output;
|
||||
std::back_insert_iterator<std::string> sink(output);
|
||||
boost::spirit::karma::generate(sink, science_type(), value);
|
||||
if (output.size() >= 2 && output[output.size() - 2] == '.' &&
|
||||
output[output.size() - 1] == '0')
|
||||
{
|
||||
output.resize(output.size() - 2);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
static std::string double_to_string(const double value)
|
||||
{
|
||||
std::string output;
|
||||
std::back_insert_iterator<std::string> sink(output);
|
||||
boost::spirit::karma::generate(sink, value);
|
||||
return output;
|
||||
}
|
||||
|
||||
static void double_with_two_digits_to_string(const double value, std::string &output)
|
||||
{
|
||||
// The largest 32-bit integer is 4294967295, that is 10 chars
|
||||
// On the safe side, add 1 for sign, and 1 for trailing zero
|
||||
char buffer[12];
|
||||
sprintf(buffer, "%g", value);
|
||||
output = buffer;
|
||||
}
|
||||
};
|
||||
std::ostringstream out;
|
||||
out << std::setprecision(Precision) << x;
|
||||
return out.str();
|
||||
}
|
||||
}
|
||||
|
||||
#endif // CAST_HPP
|
||||
|
||||
@@ -111,7 +111,7 @@ struct ArrayRenderer : mapbox::util::static_visitor<>
|
||||
|
||||
void operator()(const Number &number) const
|
||||
{
|
||||
const std::string number_string = cast::double_fixed_to_string(number.value);
|
||||
const std::string number_string = cast::to_string_with_precision(number.value);
|
||||
out.insert(out.end(), number_string.begin(), number_string.end());
|
||||
}
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ struct XMLToArrayRenderer : mapbox::util::static_visitor<>
|
||||
|
||||
void operator()(const Number &number) const
|
||||
{
|
||||
const std::string number_string = cast::double_fixed_to_string(number.value);
|
||||
const std::string number_string = cast::to_string_with_precision(number.value);
|
||||
out.insert(out.end(), number_string.begin(), number_string.end());
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user