From ad2f228ab1359952d37acdc4f3032e1405cca221 Mon Sep 17 00:00:00 2001 From: Eric Niebler Date: Mon, 15 Aug 2016 15:08:57 -0700 Subject: [PATCH] Reverted commit D3557832 Summary: This change adds a non-throwing interface for folly::to: tryTo, which returns an Expected. Here is how the non-throwing interface compares to the regular interface in terms of performance. On the successful path, there's generally not much difference between using the throwing and non-throwing interfaces. For the error path, tryTo<> is about three orders of magnitude faster than to<>. Reviewed By: mhx Differential Revision: D3557832 fbshipit-source-id: 21f06b3c1a72b06dcf867ed3a3410f51e0fdaf45 --- folly/Conv.cpp | 263 ++++++------- folly/Conv.h | 718 +++++++++++++++-------------------- folly/Portability.h | 9 - folly/String-inl.h | 4 +- folly/String.h | 3 +- folly/docs/Conv.md | 28 -- folly/test/ConvBenchmark.cpp | 68 +--- folly/test/ConvTest.cpp | 115 +----- folly/test/StringTest.cpp | 19 +- folly/test/TraitsTest.cpp | 2 +- 10 files changed, 438 insertions(+), 791 deletions(-) diff --git a/folly/Conv.cpp b/folly/Conv.cpp index 0ab6dcf5..e11d8693 100644 --- a/folly/Conv.cpp +++ b/folly/Conv.cpp @@ -207,10 +207,8 @@ struct ErrorString { bool quote; }; -// Keep this in sync with ConversionCode in Conv.h -constexpr const std::array< - ErrorString, - static_cast(ConversionCode::NUM_ERROR_CODES)> +// Keep this in sync with ConversionError::Code in Conv.h +constexpr const std::array kErrorStrings{{ {"Success", true}, {"Empty input string", true}, @@ -257,11 +255,34 @@ inline bool bool_str_cmp(const char** b, size_t len, const char* value) { } // anonymous namespace -Expected str_to_bool(StringPiece* src) noexcept { +ConversionError makeConversionError( + ConversionError::Code code, + const char* input, + size_t inputLen) { + assert(code >= 0 && code < kErrorStrings.size()); + const ErrorString& err = kErrorStrings[code]; + if (code == ConversionError::EMPTY_INPUT_STRING && inputLen == 0) { + return ConversionError(err.string, code); + } + std::string tmp(err.string); + tmp.append(": "); + if (err.quote) { + tmp.append(1, '"'); + } + if (input && inputLen > 0) { + tmp.append(input, inputLen); + } + if (err.quote) { + tmp.append(1, '"'); + } + return ConversionError(tmp, code); +} + +ConversionResult str_to_bool(StringPiece* src) { auto b = src->begin(), e = src->end(); for (;; ++b) { if (b >= e) { - return makeUnexpected(ConversionCode::EMPTY_INPUT_STRING); + return ConversionResult(ConversionError::EMPTY_INPUT_STRING); } if (!std::isspace(*b)) { break; @@ -276,7 +297,7 @@ Expected str_to_bool(StringPiece* src) noexcept { result = false; for (; b < e && isdigit(*b); ++b) { if (result || (*b != '0' && *b != '1')) { - return makeUnexpected(ConversionCode::BOOL_OVERFLOW); + return ConversionResult(ConversionError::BOOL_OVERFLOW); } result = (*b == '1'); } @@ -317,16 +338,16 @@ Expected str_to_bool(StringPiece* src) noexcept { } else if (bool_str_cmp(&b, len, "off")) { result = false; } else { - return makeUnexpected(ConversionCode::BOOL_INVALID_VALUE); + return ConversionResult(ConversionError::BOOL_INVALID_VALUE); } break; default: - return makeUnexpected(ConversionCode::BOOL_INVALID_VALUE); + return ConversionResult(ConversionError::BOOL_INVALID_VALUE); } src->assign(b, e); - return result; + return ConversionResult(result); } /** @@ -334,7 +355,7 @@ Expected str_to_bool(StringPiece* src) noexcept { * StringPiece parameter to munch the already-parsed characters. */ template -Expected str_to_floating(StringPiece* src) noexcept { +ConversionResult str_to_floating(StringPiece* src) { using namespace double_conversion; static StringToDoubleConverter conv(StringToDoubleConverter::ALLOW_TRAILING_JUNK @@ -345,7 +366,7 @@ Expected str_to_floating(StringPiece* src) noexcept { nullptr, nullptr); if (src->empty()) { - return makeUnexpected(ConversionCode::EMPTY_INPUT_STRING); + return ConversionResult(ConversionError::EMPTY_INPUT_STRING); } int length; @@ -362,10 +383,10 @@ Expected str_to_floating(StringPiece* src) noexcept { // that was processed, so we need to check if that character was // whitespace or not. if (length == 0 || (result == 0.0 && std::isspace((*src)[length - 1]))) { - return makeUnexpected(ConversionCode::EMPTY_INPUT_STRING); + return ConversionResult(ConversionError::EMPTY_INPUT_STRING); } src->advance(length); - return result; + return ConversionResult(result); } auto* e = src->end(); @@ -414,7 +435,7 @@ Expected str_to_floating(StringPiece* src) noexcept { if (result == 0.0) { // All bets are off - return makeUnexpected(ConversionCode::STRING_TO_FLOAT_ERROR); + return ConversionResult(ConversionError::STRING_TO_FLOAT_ERROR); } if (negative) { @@ -423,13 +444,11 @@ Expected str_to_floating(StringPiece* src) noexcept { src->assign(b, e); - return result; + return ConversionResult(result); } -template Expected str_to_floating( - StringPiece* src) noexcept; -template Expected str_to_floating( - StringPiece* src) noexcept; +template ConversionResult str_to_floating(StringPiece* src); +template ConversionResult str_to_floating(StringPiece* src); /** * This class takes care of additional processing needed for signed values, @@ -441,39 +460,39 @@ class SignedValueHandler; template class SignedValueHandler { public: - ConversionCode init(const char*& b) { + ConversionError::Code init(const char*& b) { negative_ = false; if (!std::isdigit(*b)) { if (*b == '-') { negative_ = true; } else if (UNLIKELY(*b != '+')) { - return ConversionCode::INVALID_LEADING_CHAR; + return ConversionError::INVALID_LEADING_CHAR; } ++b; } - return ConversionCode::SUCCESS; + return ConversionError::SUCCESS; } - ConversionCode overflow() { - return negative_ ? ConversionCode::NEGATIVE_OVERFLOW - : ConversionCode::POSITIVE_OVERFLOW; + ConversionError::Code overflow() { + return negative_ ? ConversionError::NEGATIVE_OVERFLOW + : ConversionError::POSITIVE_OVERFLOW; } template - Expected finalize(U value) { + ConversionResult finalize(U value) { T rv; if (negative_) { rv = -value; if (UNLIKELY(rv > 0)) { - return makeUnexpected(ConversionCode::NEGATIVE_OVERFLOW); + return ConversionResult(ConversionError::NEGATIVE_OVERFLOW); } } else { rv = value; if (UNLIKELY(rv < 0)) { - return makeUnexpected(ConversionCode::POSITIVE_OVERFLOW); + return ConversionResult(ConversionError::POSITIVE_OVERFLOW); } } - return rv; + return ConversionResult(rv); } private: @@ -484,16 +503,16 @@ class SignedValueHandler { template class SignedValueHandler { public: - ConversionCode init(const char*&) { - return ConversionCode::SUCCESS; + ConversionError::Code init(const char*&) { + return ConversionError::SUCCESS; } - ConversionCode overflow() { - return ConversionCode::POSITIVE_OVERFLOW; + ConversionError::Code overflow() { + return ConversionError::POSITIVE_OVERFLOW; } - Expected finalize(T value) { - return value; + ConversionResult finalize(T value) { + return ConversionResult(value); } }; @@ -505,17 +524,15 @@ class SignedValueHandler { * an appropriate error. */ template -inline Expected digits_to( - const char* b, - const char* const e) noexcept { +inline ConversionResult digits_to(const char* b, const char* const e) { using UT = typename std::make_unsigned::type; assert(b <= e); SignedValueHandler sgn; auto err = sgn.init(b); - if (UNLIKELY(err != ConversionCode::SUCCESS)) { - return makeUnexpected(err); + if (UNLIKELY(err != ConversionError::SUCCESS)) { + return ConversionResult(err); } size_t size = e - b; @@ -528,7 +545,7 @@ inline Expected digits_to( if (b < e && *b == '0') { for (++b;; ++b) { if (b == e) { - return Tgt(0); // just zeros, e.g. "0000" + return ConversionResult(Tgt(0)); // just zeros, e.g. "0000" } if (*b != '0') { size = e - b; @@ -539,7 +556,7 @@ inline Expected digits_to( if (size > std::numeric_limits::digits10 && (size != std::numeric_limits::digits10 + 1 || strncmp(b, MaxString::value, size) > 0)) { - return makeUnexpected(sgn.overflow()); + return ConversionResult(sgn.overflow()); } } @@ -594,7 +611,7 @@ inline Expected digits_to( default: assert(b == e); if (size == 0) { - return makeUnexpected(ConversionCode::NO_DIGITS); + return ConversionResult(ConversionError::NO_DIGITS); } break; } @@ -602,52 +619,46 @@ inline Expected digits_to( return sgn.finalize(result); outOfRange: - return makeUnexpected(ConversionCode::NON_DIGIT_CHAR); + return ConversionResult(ConversionError::NON_DIGIT_CHAR); } -template Expected digits_to( - const char*, - const char*) noexcept; -template Expected digits_to( +template ConversionResult digits_to(const char*, const char*); +template ConversionResult digits_to( const char*, - const char*) noexcept; -template Expected digits_to( + const char*); +template ConversionResult digits_to( const char*, - const char*) noexcept; + const char*); -template Expected digits_to( - const char*, - const char*) noexcept; -template Expected digits_to( +template ConversionResult digits_to(const char*, const char*); +template ConversionResult digits_to( const char*, - const char*) noexcept; + const char*); -template Expected digits_to( +template ConversionResult digits_to(const char*, const char*); +template ConversionResult digits_to( const char*, - const char*) noexcept; -template Expected digits_to( - const char*, - const char*) noexcept; + const char*); -template Expected digits_to( +template ConversionResult digits_to(const char*, const char*); +template ConversionResult digits_to( const char*, const char*); -template Expected digits_to( - const char*, - const char*) noexcept; -template Expected digits_to( +template ConversionResult digits_to( + const char*, + const char*); +template ConversionResult digits_to( const char*, - const char*) noexcept; -template Expected -digits_to(const char*, const char*) noexcept; + const char*); #if FOLLY_HAVE_INT128_T -template Expected<__int128, ConversionCode> digits_to<__int128>( +template ConversionResult<__int128> digits_to<__int128>( + const char*, + const char*); +template ConversionResult digits_to( const char*, - const char*) noexcept; -template Expected -digits_to(const char*, const char*) noexcept; + const char*); #endif /** @@ -655,14 +666,14 @@ digits_to(const char*, const char*) noexcept; * StringPiece parameter to munch the already-parsed characters. */ template -Expected str_to_integral(StringPiece* src) noexcept { +ConversionResult str_to_integral(StringPiece* src) { using UT = typename std::make_unsigned::type; auto b = src->data(), past = src->data() + src->size(); for (;; ++b) { if (UNLIKELY(b >= past)) { - return makeUnexpected(ConversionCode::EMPTY_INPUT_STRING); + return ConversionResult(ConversionError::EMPTY_INPUT_STRING); } if (!std::isspace(*b)) { break; @@ -672,93 +683,63 @@ Expected str_to_integral(StringPiece* src) noexcept { SignedValueHandler sgn; auto err = sgn.init(b); - if (UNLIKELY(err != ConversionCode::SUCCESS)) { - return makeUnexpected(err); + if (UNLIKELY(err != ConversionError::SUCCESS)) { + return ConversionResult(err); } if (std::is_signed::value && UNLIKELY(b >= past)) { - return makeUnexpected(ConversionCode::NO_DIGITS); + return ConversionResult(ConversionError::NO_DIGITS); } if (UNLIKELY(!isdigit(*b))) { - return makeUnexpected(ConversionCode::NON_DIGIT_CHAR); + return ConversionResult(ConversionError::NON_DIGIT_CHAR); } auto m = findFirstNonDigit(b + 1, past); auto tmp = digits_to(b, m); - if (UNLIKELY(!tmp.hasValue())) { - return makeUnexpected( - tmp.error() == ConversionCode::POSITIVE_OVERFLOW ? sgn.overflow() - : tmp.error()); + if (UNLIKELY(!tmp.success())) { + return ConversionResult( + tmp.error == ConversionError::POSITIVE_OVERFLOW ? sgn.overflow() + : tmp.error); } - auto res = sgn.finalize(tmp.value()); + auto res = sgn.finalize(tmp.value); - if (res.hasValue()) { + if (res.success()) { src->advance(m - src->data()); } return res; } -template Expected str_to_integral( - StringPiece* src) noexcept; -template Expected str_to_integral( - StringPiece* src) noexcept; -template Expected str_to_integral( - StringPiece* src) noexcept; - -template Expected str_to_integral( - StringPiece* src) noexcept; -template Expected -str_to_integral(StringPiece* src) noexcept; - -template Expected str_to_integral( - StringPiece* src) noexcept; -template Expected str_to_integral( - StringPiece* src) noexcept; - -template Expected str_to_integral( - StringPiece* src) noexcept; -template Expected str_to_integral( - StringPiece* src) noexcept; - -template Expected str_to_integral( - StringPiece* src) noexcept; -template Expected -str_to_integral(StringPiece* src) noexcept; +template ConversionResult str_to_integral(StringPiece* src); +template ConversionResult str_to_integral( + StringPiece* src); +template ConversionResult str_to_integral( + StringPiece* src); + +template ConversionResult str_to_integral(StringPiece* src); +template ConversionResult str_to_integral( + StringPiece* src); + +template ConversionResult str_to_integral(StringPiece* src); +template ConversionResult str_to_integral( + StringPiece* src); + +template ConversionResult str_to_integral(StringPiece* src); +template ConversionResult str_to_integral( + StringPiece* src); + +template ConversionResult str_to_integral( + StringPiece* src); +template ConversionResult +str_to_integral(StringPiece* src); #if FOLLY_HAVE_INT128_T -template Expected<__int128, ConversionCode> str_to_integral<__int128>( - StringPiece* src) noexcept; -template Expected -str_to_integral(StringPiece* src) noexcept; +template ConversionResult<__int128> str_to_integral<__int128>(StringPiece* src); +template ConversionResult str_to_integral( + StringPiece* src); #endif } // namespace detail - -ConversionError makeConversionError(ConversionCode code, StringPiece input) { - using namespace detail; - static_assert( - std::is_unsigned::type>::value, - "ConversionCode should be unsigned"); - assert((std::size_t)code < kErrorStrings.size()); - const ErrorString& err = kErrorStrings[(std::size_t)code]; - if (code == ConversionCode::EMPTY_INPUT_STRING && input.empty()) { - return {err.string, code}; - } - std::string tmp(err.string); - tmp.append(": "); - if (err.quote) { - tmp.append(1, '"'); - } - if (input.size() > 0) { - tmp.append(input.data(), input.size()); - } - if (err.quote) { - tmp.append(1, '"'); - } - return {tmp, code}; -} - } // namespace folly diff --git a/folly/Conv.h b/folly/Conv.h index 9fc19696..df34f2fa 100644 --- a/folly/Conv.h +++ b/folly/Conv.h @@ -39,88 +39,75 @@ #include // V8 JavaScript implementation #include -#include #include #include #include -#include #include namespace folly { -// Keep this in sync with kErrorStrings in Conv.cpp -enum class ConversionCode : unsigned char { - SUCCESS, - EMPTY_INPUT_STRING, - NO_DIGITS, - BOOL_OVERFLOW, - BOOL_INVALID_VALUE, - NON_DIGIT_CHAR, - INVALID_LEADING_CHAR, - POSITIVE_OVERFLOW, - NEGATIVE_OVERFLOW, - STRING_TO_FLOAT_ERROR, - NON_WHITESPACE_AFTER_END, - ARITH_POSITIVE_OVERFLOW, - ARITH_NEGATIVE_OVERFLOW, - ARITH_LOSS_OF_PRECISION, - NUM_ERROR_CODES, // has to be the last entry -}; - -struct ConversionErrorBase : std::range_error { - using std::range_error::range_error; -}; - -class ConversionError : public ConversionErrorBase { +class ConversionError : public std::range_error { public: - ConversionError(const std::string& str, ConversionCode code) - : ConversionErrorBase(str), code_(code) {} + // Keep this in sync with kErrorStrings in Conv.cpp + enum Code { + SUCCESS, + EMPTY_INPUT_STRING, + NO_DIGITS, + BOOL_OVERFLOW, + BOOL_INVALID_VALUE, + NON_DIGIT_CHAR, + INVALID_LEADING_CHAR, + POSITIVE_OVERFLOW, + NEGATIVE_OVERFLOW, + STRING_TO_FLOAT_ERROR, + NON_WHITESPACE_AFTER_END, + ARITH_POSITIVE_OVERFLOW, + ARITH_NEGATIVE_OVERFLOW, + ARITH_LOSS_OF_PRECISION, + NUM_ERROR_CODES, // has to be the last entry + }; - ConversionError(const char* str, ConversionCode code) - : ConversionErrorBase(str), code_(code) {} + ConversionError(const std::string& str, Code code) + : std::range_error(str), code_(code) {} - ConversionCode errorCode() const { - return code_; - } + ConversionError(const char* str, Code code) + : std::range_error(str), code_(code) {} + + Code errorCode() const { return code_; } private: - ConversionCode code_; + Code code_; }; -/******************************************************************************* - * Custom Error Translation - * - * Your overloaded parseTo() function can return a custom error code on failure. - * ::folly::to() will call makeConversionError to translate that error code into - * an object to throw. makeConversionError is found by argument-dependent - * lookup. It should have this signature: - * - * namespace other_namespace { - * enum YourErrorCode { BAD_ERROR, WORSE_ERROR }; - * - * struct YourConversionError : ConversionErrorBase { - * YourConversionError(const char* what) : ConversionErrorBase(what) {} - * }; - * - * YourConversionError - * makeConversionError(YourErrorCode code, ::folly::StringPiece sp) { - * ... - * return YourConversionError(messageString); - * } - ******************************************************************************/ -ConversionError makeConversionError(ConversionCode code, StringPiece sp); - namespace detail { + +ConversionError makeConversionError( + ConversionError::Code code, + const char* input, + size_t inputLen); + +inline ConversionError makeConversionError( + ConversionError::Code code, + const std::string& str) { + return makeConversionError(code, str.data(), str.size()); +} + +inline ConversionError makeConversionError( + ConversionError::Code code, + StringPiece sp) { + return makeConversionError(code, sp.data(), sp.size()); +} + /** * Enforce that the suffix following a number is made up only of whitespace. */ -inline ConversionCode enforceWhitespaceErr(StringPiece sp) { +inline ConversionError::Code enforceWhitespaceErr(StringPiece sp) { for (auto c : sp) { - if (UNLIKELY(!std::isspace(c))) { - return ConversionCode::NON_WHITESPACE_AFTER_END; + if (!std::isspace(c)) { + return ConversionError::NON_WHITESPACE_AFTER_END; } } - return ConversionCode::SUCCESS; + return ConversionError::SUCCESS; } /** @@ -128,57 +115,42 @@ inline ConversionCode enforceWhitespaceErr(StringPiece sp) { */ inline void enforceWhitespace(StringPiece sp) { auto err = enforceWhitespaceErr(sp); - if (err != ConversionCode::SUCCESS) { - throw makeConversionError(err, sp); + if (err != ConversionError::SUCCESS) { + throw detail::makeConversionError(err, sp); } } -} /** - * The identity conversion function. - * tryTo(T) returns itself for all types T. + * A simple std::pair-like wrapper to wrap both a value and an error */ -template -typename std::enable_if< - std::is_same::type>::value, - Expected>::type -tryTo(Src&& value) { - return std::forward(value); -} +template +struct ConversionResult { + explicit ConversionResult(T v) : value(v) {} + explicit ConversionResult(ConversionError::Code e) : error(e) {} -template -typename std::enable_if< - std::is_same::type>::value, - Tgt>::type -to(Src&& value) { - return std::forward(value); -} + bool success() const { + return error == ConversionError::SUCCESS; + } -/******************************************************************************* - * Arithmetic to boolean - ******************************************************************************/ + T value; + ConversionError::Code error{ConversionError::SUCCESS}; +}; +} /** - * Unchecked conversion from arithmetic to boolean. This is different from the - * other arithmetic conversions because we use the C convention of treating any - * non-zero value as true, instead of range checking. + * The identity conversion function. + * to(T) returns itself for all types T. */ template -typename std::enable_if< - std::is_arithmetic::value && !std::is_same::value && - std::is_same::value, - Expected>::type -tryTo(const Src& value) { - return value != Src(); +typename std::enable_if::value, Tgt>::type +to(const Src & value) { + return value; } template -typename std::enable_if< - std::is_arithmetic::value && !std::is_same::value && - std::is_same::value, - Tgt>::type -to(const Src& value) { - return value != Src(); +typename std::enable_if::value, Tgt>::type +to(Src && value) { + return std::forward(value); } /******************************************************************************* @@ -187,28 +159,34 @@ to(const Src& value) { namespace detail { -template -struct LastElementImpl { - static void call(Ignored...) {} -}; - -template -struct LastElementImpl { - template - static Last call(Ignored..., Last&& last) { - return std::forward(last); - } -}; +template +const T& getLastElement(const T & v) { + return v; +} -template -auto getLastElement(const Ts&... ts) - -> decltype(LastElementImpl::call(ts...)) { - return LastElementImpl::call(ts...); +template +typename std::tuple_element< + sizeof...(Ts), + std::tuple >::type const& + getLastElement(const T&, const Ts&... vs) { + return getLastElement(vs...); } +// This class exists to specialize away std::tuple_element in the case where we +// have 0 template arguments. Without this, Clang/libc++ will blow a +// static_assert even if tuple_element is protected by an enable_if. template -struct LastElement : std::decay::call(std::declval()...))> { +struct last_element { + typedef typename std::enable_if< + sizeof...(Ts) >= 1, + typename std::tuple_element< + sizeof...(Ts) - 1, std::tuple + >::type>::type type; +}; + +template <> +struct last_element<> { + typedef void type; }; } // namespace detail @@ -395,10 +373,11 @@ toAppend(Src value, Tgt * result) { } } -template -typename std::enable_if::value, size_t>:: - type - estimateSpaceNeeded(Src value) { +template +typename std::enable_if< + std::is_convertible::value, + size_t>::type +estimateSpaceNeeded(Src value) { const char *c = value; if (c) { return folly::StringPiece(value).size(); @@ -768,10 +747,11 @@ toAppendStrImpl(const T& v, Tgt result) { } template -typename std::enable_if< - sizeof...(Ts) >= 2 && - IsSomeString::type>::type>::value>::type +typename std::enable_if= 2 + && IsSomeString< + typename std::remove_pointer< + typename detail::last_element::type + >::type>::value>::type toAppendStrImpl(const T& v, const Ts&... vs) { toAppend(v, getLastElement(vs...)); toAppendStrImpl(vs...); @@ -785,10 +765,11 @@ toAppendDelimStrImpl(const Delimiter& /* delim */, const T& v, Tgt result) { } template -typename std::enable_if< - sizeof...(Ts) >= 2 && - IsSomeString::type>::type>::value>::type +typename std::enable_if= 2 + && IsSomeString< + typename std::remove_pointer< + typename detail::last_element::type + >::type>::value>::type toAppendDelimStrImpl(const Delimiter& delim, const T& v, const Ts&... vs) { // we are really careful here, calling toAppend with just one element does // not try to estimate space needed (as we already did that). If we call @@ -822,10 +803,11 @@ toAppendDelimStrImpl(const Delimiter& delim, const T& v, const Ts&... vs) { * } */ template -typename std::enable_if< - sizeof...(Ts) >= 3 && - IsSomeString::type>::type>::value>::type +typename std::enable_if= 3 + && IsSomeString< + typename std::remove_pointer< + typename detail::last_element::type + >::type>::value>::type toAppend(const Ts&... vs) { ::folly::detail::toAppendStrImpl(vs...); } @@ -851,8 +833,11 @@ void toAppend(const pid_t a, Tgt* res) { * will probably save a few calls to malloc. */ template -typename std::enable_if::type>::type>::value>::type +typename std::enable_if< + IsSomeString< + typename std::remove_pointer< + typename detail::last_element::type + >::type>::value>::type toAppendFit(const Ts&... vs) { ::folly::detail::reserveInTarget(vs...); toAppend(vs...); @@ -889,10 +874,11 @@ typename std::enable_if::value>::type toAppendDelim( * comments for toAppend for details about memory allocation. */ template -typename std::enable_if< - sizeof...(Ts) >= 3 && - IsSomeString::type>::type>::value>::type +typename std::enable_if= 3 + && IsSomeString< + typename std::remove_pointer< + typename detail::last_element::type + >::type>::value>::type toAppendDelim(const Delimiter& delim, const Ts&... vs) { detail::toAppendDelimStrImpl(delim, vs...); } @@ -901,8 +887,11 @@ toAppendDelim(const Delimiter& delim, const Ts&... vs) { * Detail in comment for toAppendFit */ template -typename std::enable_if::type>::type>::value>::type +typename std::enable_if< + IsSomeString< + typename std::remove_pointer< + typename detail::last_element::type + >::type>::value>::type toAppendDelimFit(const Delimiter& delim, const Ts&... vs) { detail::reserveInTargetDelim(delim, vs...); toAppendDelim(delim, vs...); @@ -917,11 +906,10 @@ void toAppendDelimFit(const De&, const Ts&) {} */ template typename std::enable_if< - IsSomeString::value && - (sizeof...(Ts) != 1 || - !std::is_same::type>:: - value), - Tgt>::type + IsSomeString::value && ( + sizeof...(Ts) != 1 || + !std::is_same::type>::value), + Tgt>::type to(const Ts&... vs) { Tgt result; toAppendFit(vs..., &result); @@ -945,11 +933,10 @@ toDelim(const Delim& /* delim */, const Src& value) { */ template typename std::enable_if< - IsSomeString::value && - (sizeof...(Ts) != 1 || - !std::is_same::type>:: - value), - Tgt>::type + IsSomeString::value && ( + sizeof...(Ts) != 1 || + !std::is_same::type>::value), + Tgt>::type toDelim(const Delim& delim, const Ts&... vs) { Tgt result; toAppendDelimFit(delim, vs..., &result); @@ -962,121 +949,130 @@ toDelim(const Delim& delim, const Ts&... vs) { namespace detail { -Expected str_to_bool(StringPiece* src) noexcept; +ConversionResult str_to_bool(StringPiece* src); template -Expected str_to_floating(StringPiece* src) noexcept; +ConversionResult str_to_floating(StringPiece* src); -extern template Expected str_to_floating( - StringPiece* src) noexcept; -extern template Expected str_to_floating( - StringPiece* src) noexcept; +extern template ConversionResult str_to_floating( + StringPiece* src); +extern template ConversionResult str_to_floating( + StringPiece* src); template -Expected digits_to(const char* b, const char* e) noexcept; +ConversionResult digits_to(const char* b, const char* e); -extern template Expected digits_to( +extern template ConversionResult digits_to( const char*, - const char*) noexcept; -extern template Expected digits_to( + const char*); +extern template ConversionResult digits_to( const char*, - const char*) noexcept; -extern template Expected -digits_to(const char*, const char*) noexcept; - -extern template Expected digits_to( + const char*); +extern template ConversionResult digits_to( const char*, - const char*) noexcept; -extern template Expected -digits_to(const char*, const char*) noexcept; + const char*); -extern template Expected digits_to( +extern template ConversionResult digits_to( const char*, - const char*) noexcept; -extern template Expected digits_to( + const char*); +extern template ConversionResult digits_to( const char*, - const char*) noexcept; + const char*); -extern template Expected digits_to( +extern template ConversionResult digits_to(const char*, const char*); +extern template ConversionResult digits_to( const char*, - const char*) noexcept; -extern template Expected -digits_to(const char*, const char*) noexcept; + const char*); -extern template Expected digits_to( +extern template ConversionResult digits_to( const char*, - const char*) noexcept; -extern template Expected -digits_to(const char*, const char*) noexcept; + const char*); +extern template ConversionResult digits_to( + const char*, + const char*); + +extern template ConversionResult digits_to( + const char*, + const char*); +extern template ConversionResult +digits_to(const char*, const char*); #if FOLLY_HAVE_INT128_T -extern template Expected<__int128, ConversionCode> digits_to<__int128>( +extern template ConversionResult<__int128> digits_to<__int128>( const char*, - const char*) noexcept; -extern template Expected -digits_to(const char*, const char*) noexcept; + const char*); +extern template ConversionResult +digits_to(const char*, const char*); #endif template -Expected str_to_integral(StringPiece* src) noexcept; - -extern template Expected str_to_integral( - StringPiece* src) noexcept; -extern template Expected -str_to_integral(StringPiece* src) noexcept; -extern template Expected -str_to_integral(StringPiece* src) noexcept; - -extern template Expected str_to_integral( - StringPiece* src) noexcept; -extern template Expected -str_to_integral(StringPiece* src) noexcept; - -extern template Expected str_to_integral( - StringPiece* src) noexcept; -extern template Expected -str_to_integral(StringPiece* src) noexcept; - -extern template Expected str_to_integral( - StringPiece* src) noexcept; -extern template Expected -str_to_integral(StringPiece* src) noexcept; - -extern template Expected str_to_integral( - StringPiece* src) noexcept; -extern template Expected -str_to_integral(StringPiece* src) noexcept; +ConversionResult str_to_integral(StringPiece* src); + +extern template ConversionResult str_to_integral(StringPiece* src); +extern template ConversionResult str_to_integral( + StringPiece* src); +extern template ConversionResult str_to_integral( + StringPiece* src); + +extern template ConversionResult str_to_integral( + StringPiece* src); +extern template ConversionResult +str_to_integral(StringPiece* src); + +extern template ConversionResult str_to_integral(StringPiece* src); +extern template ConversionResult str_to_integral( + StringPiece* src); + +extern template ConversionResult str_to_integral(StringPiece* src); +extern template ConversionResult str_to_integral( + StringPiece* src); + +extern template ConversionResult str_to_integral( + StringPiece* src); +extern template ConversionResult +str_to_integral(StringPiece* src); #if FOLLY_HAVE_INT128_T -extern template Expected<__int128, ConversionCode> str_to_integral<__int128>( - StringPiece* src) noexcept; -extern template Expected -str_to_integral(StringPiece* src) noexcept; +extern template ConversionResult<__int128> str_to_integral<__int128>( + StringPiece* src); +extern template ConversionResult +str_to_integral(StringPiece* src); #endif template -typename std:: - enable_if::value, Expected>::type - convertTo(StringPiece* src) noexcept { +typename std::enable_if::value, ConversionResult>::type +convertTo(StringPiece* src) { return str_to_bool(src); } template typename std::enable_if< - std::is_floating_point::value, - Expected>::type -convertTo(StringPiece* src) noexcept { + std::is_floating_point::value, ConversionResult>::type +convertTo(StringPiece* src) { return str_to_floating(src); } template typename std::enable_if< std::is_integral::value && !std::is_same::value, - Expected>::type -convertTo(StringPiece* src) noexcept { + ConversionResult>::type +convertTo(StringPiece* src) { return str_to_integral(src); } +template +struct WrapperInfo { using type = T; }; + +template +typename std::enable_if< + std::is_same::type, T>::value, T>::type +inline wrap(ConversionResult::type> res, Gen&& gen) { + if (LIKELY(res.success())) { + return res.value; + } + throw detail::makeConversionError(res.error, gen()); +} + } // namespace detail /** @@ -1085,22 +1081,12 @@ convertTo(StringPiece* src) noexcept { */ template typename std::enable_if< - std::is_integral::value && !std::is_same::value, - Expected>::type -tryTo(const char* b, const char* e) { - return detail::digits_to(b, e); -} - -template -typename std::enable_if< - std::is_integral::value && !std::is_same::value, + std::is_integral::type>::value && + !std::is_same::type, bool>::value, Tgt>::type to(const char* b, const char* e) { - return tryTo(b, e).thenOrThrow( - [](Tgt res) { return res; }, - [=](ConversionCode code) { - return makeConversionError(code, StringPiece(b, e)); - }); + auto res = detail::digits_to::type>(b, e); + return detail::wrap(res, [&] { return StringPiece(b, e); }); } /******************************************************************************* @@ -1108,21 +1094,47 @@ to(const char* b, const char* e) { ******************************************************************************/ /** - * Parsing strings to numeric types. + * Parsing strings to numeric types. These routines differ from + * parseTo(str, numeric) routines in that they take a POINTER TO a StringPiece + * and alter that StringPiece to reflect progress information. */ template -FOLLY_WARN_UNUSED_RESULT inline typename std::enable_if< - std::is_arithmetic::value, - Expected>::type +typename std::enable_if< + std::is_arithmetic::type>::value>::type +parseTo(StringPiece* src, Tgt& out) { + auto res = detail::convertTo::type>(src); + out = detail::wrap(res, [&] { return *src; }); +} + +template +typename std::enable_if< + std::is_arithmetic::type>::value>::type parseTo(StringPiece src, Tgt& out) { - return detail::convertTo(&src).then( - [&](Tgt res) { return void(out = res), src; }); + auto res = detail::convertTo::type>(&src); + if (LIKELY(res.success())) { + res.error = detail::enforceWhitespaceErr(src); + } + out = detail::wrap(res, [&] { return src; }); } /******************************************************************************* * Integral / Floating Point to integral / Floating Point ******************************************************************************/ +/** + * Unchecked conversion from arithmetic to boolean. This is different from the + * other arithmetic conversions because we use the C convention of treating any + * non-zero value as true, instead of range checking. + */ +template +typename std::enable_if< + std::is_arithmetic::value && !std::is_same::value && + std::is_same::value, + Tgt>::type +to(const Src& value) { + return value != Src(); +} + namespace detail { /** @@ -1135,22 +1147,22 @@ typename std::enable_if< std::is_integral::value && !std::is_same::value && !std::is_same::value && std::is_integral::value, - Expected>::type -convertTo(const Src& value) noexcept { + ConversionResult>::type +convertTo(const Src& value) { /* static */ if ( std::numeric_limits::max() < std::numeric_limits::max()) { if (greater_than::max()>(value)) { - return makeUnexpected(ConversionCode::ARITH_POSITIVE_OVERFLOW); + return ConversionResult(ConversionError::ARITH_POSITIVE_OVERFLOW); } } /* static */ if ( std::is_signed::value && (!std::is_signed::value || sizeof(Src) > sizeof(Tgt))) { if (less_than::min()>(value)) { - return makeUnexpected(ConversionCode::ARITH_NEGATIVE_OVERFLOW); + return ConversionResult(ConversionError::ARITH_NEGATIVE_OVERFLOW); } } - return static_cast(value); + return ConversionResult(static_cast(value)); } /** @@ -1162,18 +1174,18 @@ template typename std::enable_if< std::is_floating_point::value && std::is_floating_point::value && !std::is_same::value, - Expected>::type -convertTo(const Src& value) noexcept { + ConversionResult>::type +convertTo(const Src& value) { /* static */ if ( std::numeric_limits::max() < std::numeric_limits::max()) { if (value > std::numeric_limits::max()) { - return makeUnexpected(ConversionCode::ARITH_POSITIVE_OVERFLOW); + return ConversionResult(ConversionError::ARITH_POSITIVE_OVERFLOW); } if (value < std::numeric_limits::lowest()) { - return makeUnexpected(ConversionCode::ARITH_NEGATIVE_OVERFLOW); + return ConversionResult(ConversionError::ARITH_NEGATIVE_OVERFLOW); } } - return boost::implicit_cast(value); + return ConversionResult(boost::implicit_cast(value)); } /** @@ -1240,18 +1252,18 @@ template typename std::enable_if< (std::is_integral::value && std::is_floating_point::value) || (std::is_floating_point::value && std::is_integral::value), - Expected>::type -convertTo(const Src& value) noexcept { + ConversionResult>::type +convertTo(const Src& value) { if (LIKELY(checkConversion(value))) { Tgt result = static_cast(value); if (LIKELY(checkConversion(result))) { Src witness = static_cast(result); if (LIKELY(value == witness)) { - return result; + return ConversionResult(result); } } } - return makeUnexpected(ConversionCode::ARITH_LOSS_OF_PRECISION); + return ConversionResult(ConversionError::ARITH_LOSS_OF_PRECISION); } template @@ -1274,20 +1286,13 @@ using IsArithToArith = std::integral_constant< template typename std::enable_if< - detail::IsArithToArith::value, - Expected>::type -tryTo(const Src& value) noexcept { - return detail::convertTo(value); -} - -template -typename std::enable_if::value, Tgt>::type to( - const Src& value) { - return tryTo(value).thenOrThrow( - [](Tgt res) { return res; }, - [&](ConversionCode e) { - return makeConversionError(e, detail::errorValue(value)); - }); + detail::IsArithToArith< + typename detail::WrapperInfo::type, Src>::value, Tgt>::type +to(const Src& value) { + auto res = detail::convertTo::type>(value); + return detail::wrap(res, [&] { + return detail::errorValue::type>(value); + }); } /******************************************************************************* @@ -1298,157 +1303,49 @@ typename std::enable_if::value, Tgt>::type to( * argument-dependent lookup: * * namespace other_namespace { - * ::folly::Expected<::folly::StringPiece, SomeErrorCode> - * parseTo(::folly::StringPiece, OtherType&) noexcept; + * void parseTo(::folly::StringPiece, OtherType&); * } ******************************************************************************/ template -FOLLY_WARN_UNUSED_RESULT typename std::enable_if< - std::is_enum::value, - Expected>::type -parseTo(StringPiece in, T& out) noexcept { - typename std::underlying_type::type tmp{}; - auto restOrError = parseTo(in, tmp); - out = static_cast(tmp); // Harmless if parseTo fails - return restOrError; -} - -FOLLY_WARN_UNUSED_RESULT -inline Expected parseTo( - StringPiece in, - StringPiece& out) noexcept { - out = in; - return StringPiece{in.end(), in.end()}; +typename std::enable_if::value>::type +parseTo(StringPiece in, T& out) { + typename std::underlying_type::type tmp; + parseTo(in, tmp); + out = static_cast(tmp); } -FOLLY_WARN_UNUSED_RESULT -inline Expected parseTo( - StringPiece in, - std::string& out) { - out.clear(); - out.append(in.data(), in.size()); // TODO try/catch? - return StringPiece{in.end(), in.end()}; +inline void parseTo(StringPiece in, StringPiece& out) { + out = in; } -FOLLY_WARN_UNUSED_RESULT -inline Expected parseTo( - StringPiece in, - fbstring& out) { +inline void parseTo(StringPiece in, std::string& out) { out.clear(); - out.append(in.data(), in.size()); // TODO try/catch? - return StringPiece{in.end(), in.end()}; + out.append(in.data(), in.size()); } -namespace detail { -template -using ParseToResult = decltype(parseTo(StringPiece{}, std::declval())); - -struct CheckTrailingSpace { - Expected operator()(StringPiece sp) const { - auto e = enforceWhitespaceErr(sp); - if (UNLIKELY(e != ConversionCode::SUCCESS)) - return makeUnexpected(e); - return unit; - } -}; - -template -struct ReturnUnit { - template - constexpr Expected operator()(T&&) const { - return unit; - } -}; - -// Older versions of the parseTo customization point threw on error and -// returned void. Handle that. -template -inline typename std::enable_if< - std::is_void>::value, - Expected>::type -parseToWrap(StringPiece sp, Tgt& out) { - parseTo(sp, out); - return StringPiece(sp.end(), sp.end()); -} - -template -inline typename std::enable_if< - !std::is_void>::value, - ParseToResult>::type -parseToWrap(StringPiece sp, Tgt& out) { - return parseTo(sp, out); +inline void parseTo(StringPiece in, fbstring& out) { + out.clear(); + out.append(in.data(), in.size()); } -template -using ParseToError = ExpectedErrorType()))>; - -} // namespace detail - /** * String or StringPiece to target conversion. Accepts leading and trailing * whitespace, but no non-space trailing characters. */ template -inline typename std::enable_if< - !std::is_same::value, - Expected>>::type -tryTo(StringPiece src) { - Tgt result{}; - using Error = detail::ParseToError; - using Check = typename std::conditional< - std::is_arithmetic::value, - detail::CheckTrailingSpace, - detail::ReturnUnit>::type; - return parseTo(src, result).then(Check(), [&](Unit) { - return std::move(result); - }); -} - -template -inline - typename std::enable_if::value, Tgt>::type - to(StringPiece src) { - Tgt result{}; - using Error = detail::ParseToError; - using Check = typename std::conditional< - std::is_arithmetic::value, - detail::CheckTrailingSpace, - detail::ReturnUnit>::type; - auto tmp = detail::parseToWrap(src, result); - return tmp - .thenOrThrow(Check(), [&](Error e) { throw makeConversionError(e, src); }) - .thenOrThrow( - [&](Unit) { return std::move(result); }, - [&](Error e) { throw makeConversionError(e, tmp.value()); }); -} - -/** - * tryTo/to that take the strings by pointer so the caller gets information - * about how much of the string was consumed by the conversion. These do not - * check for trailing whitepsace. - */ -template -Expected> tryTo(StringPiece* src) { +typename std::enable_if::value, Tgt>::type +to(StringPiece src) { Tgt result; - return parseTo(*src, result).then([&, src](StringPiece sp) -> Tgt { - *src = sp; - return std::move(result); - }); + parseTo(src, result); + return result; } template Tgt to(StringPiece* src) { Tgt result; - using Error = detail::ParseToError; - return parseTo(*src, result) - .thenOrThrow( - [&, src](StringPiece sp) -> Tgt { - *src = sp; - return std::move(result); - }, - [=](Error e) { return makeConversionError(e, *src); }); + parseTo(src, result); + return result; } /******************************************************************************* @@ -1457,27 +1354,8 @@ Tgt to(StringPiece* src) { template typename std::enable_if< - std::is_enum::value && !std::is_same::value, - Expected>::type -tryTo(const Src& value) { - using I = typename std::underlying_type::type; - return tryTo(static_cast(value)); -} - -template -typename std::enable_if< - std::is_enum::value && !std::is_same::value, - Tgt>::type -tryTo(const Src& value) { - using I = typename std::underlying_type::type; - return tryTo(value).then([](I i) { return static_cast(i); }); -} - -template -typename std::enable_if< - std::is_enum::value && !std::is_same::value, - Tgt>::type -to(const Src& value) { + std::is_enum::value && !std::is_same::value, Tgt>::type +to(const Src & value) { return to(static_cast::type>(value)); } diff --git a/folly/Portability.h b/folly/Portability.h index e6d06a4d..56dc7c95 100644 --- a/folly/Portability.h +++ b/folly/Portability.h @@ -92,15 +92,6 @@ constexpr bool kHasUnalignedAccess = false; # define FOLLY_ALWAYS_INLINE inline #endif -// warn unused result -#if defined(_MSC_VER) && (_MSC_VER >= 1700) -#define FOLLY_WARN_UNUSED_RESULT _Check_return_ -#elif defined(__clang__) || defined(__GNUC__) -#define FOLLY_WARN_UNUSED_RESULT __attribute__((__warn_unused_result__)) -#else -#define FOLLY_WARN_UNUSED_RESULT -#endif - // target #ifdef _MSC_VER # define FOLLY_TARGET_ATTRIBUTE(target) diff --git a/folly/String-inl.h b/folly/String-inl.h index 2bf0104a..1d613e9c 100644 --- a/folly/String-inl.h +++ b/folly/String-inl.h @@ -328,7 +328,7 @@ bool splitFixed(const Delim& delimiter, StringPiece input, OutputType& output) { if (exact && UNLIKELY(std::string::npos != input.find(delimiter))) { return false; } - output = folly::to(input); + parseTo(input, output); return true; } @@ -346,7 +346,7 @@ bool splitFixed( StringPiece tail(input.begin() + cut + detail::delimSize(delimiter), input.end()); if (LIKELY(splitFixed(delimiter, tail, outTail...))) { - outHead = folly::to(head); + parseTo(head, outHead); return true; } return false; diff --git a/folly/String.h b/folly/String.h index 5d631340..fc552f11 100644 --- a/folly/String.h +++ b/folly/String.h @@ -491,8 +491,7 @@ struct IsConvertible { template struct IsConvertible< T, - decltype(static_cast( - parseTo(std::declval(), std::declval())))> { + decltype(parseTo(std::declval(), std::declval()))> { enum { value = true }; }; diff --git a/folly/docs/Conv.md b/folly/docs/Conv.md index 7156c116..8d285e9d 100644 --- a/folly/docs/Conv.md +++ b/folly/docs/Conv.md @@ -215,31 +215,3 @@ is returned, which can be tested for as follows: // string could not be parsed } ``` - -#### Non-throwing interfaces - -`tryTo` is the non-throwing variant of `to`. It returns -an `Expected`. You can think of `Expected` -as like an `Optional`, but if the conversion failed, `Expected` -stores an error code instead of a `T`. - -`tryTo` has similar performance as `to` when the -conversion is successful. On the error path, you can expect -`tryTo` to be roughly three orders of magnitude faster than -the throwing `to` and to completely avoid any lock contention -arising from stack unwinding. - -Here is how to use non-throwing conversions: - -``` Cpp - auto t1 = tryTo(str); - if (t1.hasValue()) { - use(t1.value()); - } -``` - -`Expected` has a composability feature to make the above pattern simpler. - -``` Cpp - tryTo(str).then([](int i) { use(i); }); -``` diff --git a/folly/test/ConvBenchmark.cpp b/folly/test/ConvBenchmark.cpp index ec975f3e..0464822b 100644 --- a/folly/test/ConvBenchmark.cpp +++ b/folly/test/ConvBenchmark.cpp @@ -911,16 +911,6 @@ inline void stringToTypeClassic(const char* str, uint32_t n) { } } -template -inline void stringToTypeOptional(const char* str, uint32_t n) { - for (uint32_t i = 0; i < n; ++i) { - auto val = tryTo(str); - if (val.hasValue()) { - doNotOptimizeAway(val.value()); - } - } -} - template inline void ptrPairToIntClassic(StringPiece sp, uint32_t n) { for (uint32_t i = 0; i < n; ++i) { @@ -934,16 +924,6 @@ inline void ptrPairToIntClassic(StringPiece sp, uint32_t n) { } } -template -inline void ptrPairToIntOptional(StringPiece sp, uint32_t n) { - for (uint32_t i = 0; i < n; ++i) { - auto val = tryTo(sp.begin(), sp.end()); - if (val.hasValue()) { - doNotOptimizeAway(val.value()); - } - } -} - constexpr uint32_t kArithNumIter = 10000; template @@ -964,24 +944,6 @@ inline size_t arithToArithClassic(const U* in, uint32_t numItems) { return kArithNumIter * numItems; } -template -inline size_t arithToArithOptional(const U* in, uint32_t numItems) { - for (uint32_t i = 0; i < kArithNumIter; ++i) { - for (uint32_t j = 0; j < numItems; ++j) { - auto val = tryTo(*in); - doNotOptimizeAway(val.hasValue()); - if (val.hasValue()) { - auto v2 = val.value(); - doNotOptimizeAway(v2); - } - doNotOptimizeAway(j); - } - doNotOptimizeAway(i); - } - - return kArithNumIter * numItems; -} - } // namespace namespace folly { @@ -1018,12 +980,6 @@ std::array double2IntBad{{1e100, 1.25, 2.5, 100.00001}}; } \ BENCHMARK(stringTo##name##ClassicError, n) { \ stringToTypeClassic(fail, n); \ - } \ - BENCHMARK(stringTo##name##Optional, n) { \ - stringToTypeOptional(pass, n); \ - } \ - BENCHMARK(stringTo##name##OptionalError, n) { \ - stringToTypeOptional(fail, n); \ } #define PTR_PAIR_TO_INT_BENCHMARK(type, name, pass, fail) \ @@ -1032,26 +988,14 @@ std::array double2IntBad{{1e100, 1.25, 2.5, 100.00001}}; } \ BENCHMARK(ptrPairTo##name##ClassicError, n) { \ ptrPairToIntClassic(fail, n); \ - } \ - BENCHMARK(ptrPairTo##name##Optional, n) { \ - ptrPairToIntOptional(pass, n); \ - } \ - BENCHMARK(ptrPairTo##name##OptionalError, n) { \ - ptrPairToIntOptional(fail, n); \ } -#define ARITH_TO_ARITH_BENCHMARK(type, name, pass, fail) \ - BENCHMARK_MULTI(name##Classic) { \ - return arithToArithClassic(pass.data(), pass.size()); \ - } \ - BENCHMARK_MULTI(name##ClassicError) { \ - return arithToArithClassic(fail.data(), fail.size()); \ - } \ - BENCHMARK_MULTI(name##Optional) { \ - return arithToArithOptional(pass.data(), pass.size()); \ - } \ - BENCHMARK_MULTI(name##OptionalError) { \ - return arithToArithOptional(fail.data(), fail.size()); \ +#define ARITH_TO_ARITH_BENCHMARK(type, name, pass, fail) \ + BENCHMARK_MULTI(name##Classic) { \ + return arithToArithClassic(pass.data(), pass.size()); \ + } \ + BENCHMARK_MULTI(name##ClassicError) { \ + return arithToArithClassic(fail.data(), fail.size()); \ } #define INT_TO_ARITH_BENCHMARK(type, name, pass, fail) \ diff --git a/folly/test/ConvTest.cpp b/folly/test/ConvTest.cpp index 2ff1ab08..0ac0ed5a 100644 --- a/folly/test/ConvTest.cpp +++ b/folly/test/ConvTest.cpp @@ -18,7 +18,6 @@ #include #include #include -#include #include #include #include @@ -821,14 +820,6 @@ TEST(Conv, StringToBool) { EXPECT_EQ(buf5, sp5.begin()); } -TEST(Conv, Transform) { - const std::vector in{1, 2, 3}; - std::vector out(in.size()); - std::transform(in.begin(), in.end(), out.begin(), to); - const std::vector ref{"1", "2", "3"}; - EXPECT_EQ(ref, out); -} - TEST(Conv, FloatToInt) { EXPECT_EQ(to(42.0f), 42); EXPECT_EQ(to(-128.0f), int8_t(-128)); @@ -883,7 +874,7 @@ template void testConvError( F&& expr, const char* exprStr, - ConversionCode code, + ConversionError::Code code, const char* value, bool quotedValue, int line) { @@ -916,7 +907,7 @@ void testConvError( testConvError( \ [&] { return expr; }, \ #expr, \ - ConversionCode::code, \ + ConversionError::code, \ value, \ quoted, \ __LINE__) @@ -1035,91 +1026,6 @@ TEST(Conv, ConversionErrorFloatToInt) { EXPECT_CONV_ERROR_ARITH(int8_t, 65.5, ARITH_LOSS_OF_PRECISION); } -TEST(Conv, TryStringToBool) { - auto rv1 = folly::tryTo("xxxx"); - EXPECT_FALSE(rv1.hasValue()); - auto rv2 = folly::tryTo("false"); - EXPECT_TRUE(rv2.hasValue()); - EXPECT_FALSE(rv2.value()); - auto rv3 = folly::tryTo("yes"); - EXPECT_TRUE(rv3.hasValue()); - EXPECT_TRUE(rv3.value()); -} - -TEST(Conv, TryStringToInt) { - auto rv1 = folly::tryTo("1000000000000000000000000000000"); - EXPECT_FALSE(rv1.hasValue()); - auto rv2 = folly::tryTo("4711"); - EXPECT_TRUE(rv2.hasValue()); - EXPECT_EQ(rv2.value(), 4711); -} - -TEST(Conv, TryStringToFloat) { - auto rv1 = folly::tryTo(""); - EXPECT_FALSE(rv1.hasValue()); - auto rv2 = folly::tryTo("3.14"); - EXPECT_TRUE(rv2.hasValue()); - EXPECT_NEAR(rv2.value(), 3.14, 1e-5); -} - -TEST(Conv, TryStringToDouble) { - auto rv1 = folly::tryTo(""); - EXPECT_FALSE(rv1.hasValue()); - auto rv2 = folly::tryTo("3.14"); - EXPECT_TRUE(rv2.hasValue()); - EXPECT_NEAR(rv2.value(), 3.14, 1e-10); -} - -TEST(Conv, TryIntToInt) { - auto rv1 = folly::tryTo(256); - EXPECT_FALSE(rv1.hasValue()); - auto rv2 = folly::tryTo(255); - EXPECT_TRUE(rv2.hasValue()); - EXPECT_EQ(rv2.value(), 255); -} - -TEST(Conv, TryFloatToFloat) { - auto rv1 = folly::tryTo(1e100); - EXPECT_FALSE(rv1.hasValue()); - auto rv2 = folly::tryTo(25.5f); - EXPECT_TRUE(rv2.hasValue()); - EXPECT_NEAR(rv2.value(), 25.5, 1e-10); -} - -TEST(Conv, TryFloatToInt) { - auto rv1 = folly::tryTo(100.001); - EXPECT_FALSE(rv1.hasValue()); - auto rv2 = folly::tryTo(100.0); - EXPECT_TRUE(rv2.hasValue()); - EXPECT_EQ(rv2.value(), 100); -} - -TEST(Conv, TryIntToFloat) { - auto rv1 = folly::tryTo(std::numeric_limits::max()); - EXPECT_FALSE(rv1.hasValue()); - auto rv2 = folly::tryTo(1000ULL); - EXPECT_TRUE(rv2.hasValue()); - EXPECT_EQ(rv2.value(), 1000.0f); -} - -TEST(Conv, TryPtrPairToInt) { - StringPiece sp1("1000000000000000000000000000000"); - auto rv1 = folly::tryTo(sp1.begin(), sp1.end()); - EXPECT_FALSE(rv1.hasValue()); - StringPiece sp2("4711"); - auto rv2 = folly::tryTo(sp2.begin(), sp2.end()); - EXPECT_TRUE(rv2.hasValue()); - EXPECT_EQ(rv2.value(), 4711); - StringPiece sp3("-4711"); - auto rv3 = folly::tryTo(sp3.begin(), sp3.end()); - EXPECT_TRUE(rv3.hasValue()); - EXPECT_EQ(rv3.value(), -4711); - StringPiece sp4("4711"); - auto rv4 = folly::tryTo(sp4.begin(), sp4.end()); - EXPECT_TRUE(rv4.hasValue()); - EXPECT_EQ(rv4.value(), 4711); -} - TEST(Conv, NewUint64ToString) { char buf[21]; @@ -1185,12 +1091,10 @@ struct Dimensions { } }; -Expected parseTo( - folly::StringPiece in, - Dimensions& out) { - return parseTo(in, out.w) - .then([](StringPiece sp) { return sp.removePrefix("x"), sp; }) - .then([&](StringPiece sp) { return parseTo(sp, out.h); }); +void parseTo(folly::StringPiece in, Dimensions& out) { + out.w = folly::to(&in); + in.removePrefix("x"); + out.h = folly::to(&in); } template @@ -1213,10 +1117,3 @@ TEST(Conv, custom_kkproviders) { EXPECT_GT(str.capacity(), 2000); EXPECT_LT(str.capacity(), 2500); } - -TEST(Conv, TryToThenWithVoid) { - auto x = tryTo("42").then([](int) {}); - EXPECT_TRUE(x.hasValue()); - Unit u = x.value(); - (void)u; -} diff --git a/folly/test/StringTest.cpp b/folly/test/StringTest.cpp index c01cca5d..76cf6ac4 100644 --- a/folly/test/StringTest.cpp +++ b/folly/test/StringTest.cpp @@ -921,27 +921,14 @@ enum class Color { Blue, }; -enum class ColorErrorCode { INVALID_COLOR }; - -struct ColorError : std::runtime_error { - using std::runtime_error::runtime_error; -}; - -ColorError makeConversionError(ColorErrorCode, StringPiece sp) { - return ColorError("Invalid my::Color representation : " + sp.str()); -} - -Expected parseTo( - StringPiece in, - Color& out) noexcept { +void parseTo(folly::StringPiece in, Color& out) { if (in == "R") { out = Color::Red; } else if (in == "B") { out = Color::Blue; } else { - return makeUnexpected(ColorErrorCode::INVALID_COLOR); + throw runtime_error(""); } - return StringPiece(in.end(), in.end()); } } @@ -951,8 +938,6 @@ TEST(Split, fixed_convert_custom) { EXPECT_TRUE(folly::split(',', "R,B", c1, c2)); EXPECT_EQ(c1, my::Color::Red); EXPECT_EQ(c2, my::Color::Blue); - - EXPECT_THROW(folly::split(',', "B,G", c1, c2), my::ColorError); } TEST(String, join) { diff --git a/folly/test/TraitsTest.cpp b/folly/test/TraitsTest.cpp index 402d1d2c..23541b8e 100644 --- a/folly/test/TraitsTest.cpp +++ b/folly/test/TraitsTest.cpp @@ -52,7 +52,7 @@ TEST(Traits, scalars) { TEST(Traits, containers) { EXPECT_TRUE (IsRelocatable>::value); - EXPECT_TRUE((IsRelocatable>::value)); + EXPECT_TRUE ((IsRelocatable>::value)); EXPECT_TRUE ((IsRelocatable>::value)); } -- 2.34.1