gpt4 book ai didi

c++ - 如何仅使用 C++ 14 实现 std::from_chars()

转载 作者:行者123 更新时间:2023-12-01 14:15:56 24 4
gpt4 key购买 nike

我正在尝试将 boost::string_view 转换为整数。 This post讨论了使用 from_chars(),但这在 C++17 中可用,我正在寻找 C++14 解决方案。

这里最好的选择是什么?

最佳答案

本着 @t-niese's now-deleted answer 的 spirit [原文如此],我建议采用 spirit 方法。由于 C++14 已经摆在桌面上,让我们使用 X3:

template <typename Int = int, unsigned Radix=10>
Int parse_int(std::string_view sv) {
static constexpr boost::spirit::x3::int_parser<Int, Radix> p{};
Int val;
if (!parse(begin(sv), end(sv), p, val))
throw std::runtime_error("parse_int");
return val;
}

这个令人惊讶的小东西却能做很多令人惊讶的事情。它可以解析为任何整数类型,包括非标准类型(如 Boost Multiprecision、GMP 或 MPFR)。

You can even make it parse integers into non-integral types if you really want although it will not parse non-integer formats, see How to parse space-separated floats in C++ quickly? for that if you're interested in more.

Also see there to learn just how performant these routines are in practice.

测试用例

int main() {
expect("0", 0);
expect("+0", 0);
expect("-0", 0);
expect("+1", 1);
expect("-1", -1);
expect<int8_t>("-127", -127);
expect<int8_t>("-128", -128);

// edge case
expect<uint8_t>("-1", -1); // surprising?
expect<unsigned long>("-1", std::stoul("-1")); // Nope, matches stoul!

auto std_roundtrip = [](auto value) { expect(std::to_string(value), value); };
std_roundtrip(std::numeric_limits<intmax_t>::min());
std_roundtrip(std::numeric_limits<intmax_t>::max());
std_roundtrip(std::numeric_limits<uintmax_t>::min());
std_roundtrip(std::numeric_limits<uintmax_t>::max());

// radix
expect<int, 2>("-01011", -11);
expect<int, 8>("-01011", -521);
expect<int, 16>("a0", 160);

// invalids
should_fail(""); // empty
should_fail("+"); // lone sign
should_fail("+ 9999"); // space

// extensibility:
using Large = boost::multiprecision::int1024_t;
for (auto huge : { Large(42) << 700, -(Large(42) << 701) })
expect(boost::lexical_cast<std::string>(huge), huge);

// doesn't require the target type to be integral either
using Decimal = boost::multiprecision::cpp_dec_float_50;
expect<Decimal>("123456789", 123456789);
// but it's still an integer parser:
should_fail<Decimal>("1e10");
should_fail<Decimal>("1.0");
}

打印

"0" -> 0 OK
"+0" -> 0 OK
"-0" -> 0 OK
"+1" -> 1 OK
"-1" -> -1 OK
"-127" -> OK
"-128" -> € OK
"-1" -> ÿ OK
"-1" -> 18446744073709551615 OK
"-9223372036854775808" -> -9223372036854775808 OK
"9223372036854775807" -> 9223372036854775807 OK
"0" -> 0 OK
"18446744073709551615" -> 18446744073709551615 OK
"-01011" -> -11 OK
"-01011" -> -521 OK
"a0" -> 160 OK
OK (should not parse)
OK (should not parse)
OK (should not parse)
"220925707865031687304121575080965403953114271718573302098927797928886750480477394528773994523658714951533284691959485143946816154507719762251368220367378995698119394187394673124049877831141554125394316572902817792" -> 220925707865031687304121575080965403953114271718573302098927797928886750480477394528773994523658714951533284691959485143946816154507719762251368220367378995698119394187394673124049877831141554125394316572902817792 OK
"-441851415730063374608243150161930807906228543437146604197855595857773500960954789057547989047317429903066569383918970287893632309015439524502736440734757991396238788374789346248099755662283108250788633145805635584" -> -441851415730063374608243150161930807906228543437146604197855595857773500960954789057547989047317429903066569383918970287893632309015439524502736440734757991396238788374789346248099755662283108250788633145805635584 OK
"123456789" -> 1.23457e+08 OK
OK (should not parse)
OK (should not parse)

完整 list

Live On Coliru

NOTE UPDATED to the better non-throwing interface which updates the string_view to reflect what part of input was consumed (see below in BONUS TOPICS). Last two tests now print:

"3e10" -> 3 OK
-> trailing "e10"
"-7.0" -> -7 OK
-> trailing ".0"
#include <boost/spirit/home/x3.hpp>

template <typename Int = int, unsigned Radix=10>
static inline std::optional<Int> parse_int(std::string_view& remain) {
static constexpr boost::spirit::x3::int_parser<Int, Radix> p{};
Int val;
auto f = begin(remain), l = end(remain);
if (!parse(f, l, p, val))
return std::nullopt;
remain = remain.substr(f - begin(remain));
return val;
}

#include <iostream>
#include <iomanip>
#include <boost/lexical_cast.hpp>

template <typename Int>
std::string to_string(Int const& value) {
using widen = std::common_type_t<int, Int>; // pesky chars keep showing as non-numbers
return boost::lexical_cast<std::string>(static_cast<widen>(value));
}

template <typename Int = int, unsigned Radix = 10>
void expect(std::string_view input, Int expected) {
std::cout << std::quoted(input);
if (auto actual = parse_int<Int, Radix>(input)) {
if (expected == actual.value())
std::cout << " -> " << to_string(actual.value()) << " OK\n";
else
std::cout << " -> " << to_string(actual.value()) << " MISMATCH (" << to_string(expected) << " expected instead)\n";

if (!input.empty())
std::cout << " -> trailing " << std::quoted(input) << "\n";
} else {
std::cout << " FAILED (" << to_string(expected) << " expected instead)\n";
}
}

template <typename Int = int, unsigned Radix = 10>
void should_fail(std::string_view input) {
std::cout << std::quoted(input);
if (auto actual = parse_int<Int, Radix>(input)) {
std::cout << " -> " << to_string(actual.value())
<< " MISMATCH (expected to fail parse instead)\n";
if (!input.empty())
std::cout << " -> trailing " << std::quoted(input) << "\n";
} else {
std::cout << " OK (should not parse)\n";
}
}

#include <boost/multiprecision/cpp_dec_float.hpp>
#include <boost/multiprecision/cpp_int.hpp>

int main() {
expect("0", 0);
expect("+0", 0);
expect("-0", 0);
expect("+1", 1);
expect("-1", -1);
expect<int8_t>("-127", -127);
expect<int8_t>("-128", -128);

// edge case
expect<uint8_t>("-1", -1); // surprising?
expect<unsigned long>("-1", std::stoul("-1")); // Nope, matches stoul!

auto std_roundtrip = [](auto value) { expect(std::to_string(value), value); };
std_roundtrip(std::numeric_limits<intmax_t>::min());
std_roundtrip(std::numeric_limits<intmax_t>::max());
std_roundtrip(std::numeric_limits<uintmax_t>::min());
std_roundtrip(std::numeric_limits<uintmax_t>::max());

// radix
expect<int, 2>("-01011", -11);
expect<int, 8>("-01011", -521);
expect<int, 16>("a0", 160);

// invalids
should_fail(""); // empty
should_fail("+"); // lone sign
should_fail("+ 9999"); // space

// extensibility:
using Large = boost::multiprecision::int1024_t;
for (auto huge : { Large(42) << 700, -(Large(42) << 701) })
expect(boost::lexical_cast<std::string>(huge), huge);

// doesn't require the target type to be integral either
using Decimal = boost::multiprecision::cpp_dec_float_50;
expect<Decimal>("123456789", 123456789);
// but it's still an integer parser:
expect<Decimal>("3e10", 3);
expect<Decimal>("-7.0", -7);
}

奖励主题

  • 要严格解析无符号整数(因此 -1 变为无效输入),请将 x3::int_parser 替换为 x3::uint_parser 。 (请注意,无论允许的输入格式如何,都可以对目标类型进行签名)。

    template <typename Int = int, unsigned Radix=10>
    Int parse_uint(std::string_view sv) {
    static constexpr boost::spirit::x3::uint_parser<Int, Radix> p{};
    Int val;
    if (!parse(begin(sv), end(sv), p >> boost::spirit::x3::eoi, val))
    throw std::runtime_error("parse_int");
    return val;
    }
  • 要获得未解析下一个字符的 from_chars 行为,只需删除 x3::eoi 表达式:

    template <typename Int = int, unsigned Radix=10>
    Int parse_int(std::string_view sv, std::string_view& remain) {
    static constexpr boost::spirit::x3::int_parser<Int, Radix> p{};
    Int val;
    auto f = begin(sv), l = end(sv);
    if (!parse(f, l, p, val))
    throw std::runtime_error("parse_int");
    remain = { &*f, size_t(std::distance(f,l)) };
    return val;
    }

    查看其行为Live On Coliru

    std::string_view input = "123bogus", remain;

    std::cout
    << "input: " << std::quoted(input) << " -> "
    << parse_int(input, remain)
    << " remaining: " << std::quoted(remain)
    << std::endl;

    打印

     input: "123bogus" -> 123 remaining: "bogus"
  • 实际上,事后想想,optional<> 可能比信号失败的异常好得多:

      template <typename Int = int, unsigned Radix=10>
    static inline std::optional<Int> parse_int(std::string_view& remain) {
    static constexpr boost::spirit::x3::int_parser<Int, Radix> p{};
    Int val;
    auto f = begin(remain), l = end(remain);
    if (!parse(f, l, p, val))
    return std::nullopt;
    remain = remain.substr(f - begin(remain));
    return val;
    }

    结合了两全其美。例如这个:

    int main() {
    auto input = "123bogus";
    std::string_view remain = input;

    if (auto parsed = parse_int(remain))
    printf("input: '%s' -> %d remaining: '%s'\n",
    input, parsed.value(), remain.data());
    }

    编译一直到:Compiler Explorer

    .LC0:
    .string "123bogus"
    .LC1:
    .string "input: '%s' -> %d remaining: '%s'\n"
    main:
    sub rsp, 8
    mov ecx, OFFSET FLAT:.LC0+3
    mov edx, 123
    xor eax, eax
    mov esi, OFFSET FLAT:.LC0
    mov edi, OFFSET FLAT:.LC1
    call printf
    xor eax, eax
    add rsp, 8
    ret

关于c++ - 如何仅使用 C++ 14 实现 std::from_chars(),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62052259/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com