diff --git a/circle.yml b/circle.yml index 51b0022f..761c0bc3 100644 --- a/circle.yml +++ b/circle.yml @@ -142,6 +142,17 @@ jobs: - build_and_test - benchmark + no-exceptions: + environment: + - BUILD_TYPE: Release + - CMAKE_OPTIONS: -DEXCEPTIONS=OFF -DRTTI=OFF + docker: + - image: ethereum/cpp-build-env:14-gcc-10 + steps: + - install_deps + - build_and_test + - benchmark + linux-32bit: environment: - BUILD_PARALLEL_JOBS: 4 @@ -211,6 +222,7 @@ workflows: - linux-clang-coverage - linux-clang-asan - linux-clang-ubsan + - no-exceptions - linux-32bit - fuzzing - macos diff --git a/cmake/CableCompilerSettings.cmake b/cmake/CableCompilerSettings.cmake index 4e735c3a..f62f6d3c 100644 --- a/cmake/CableCompilerSettings.cmake +++ b/cmake/CableCompilerSettings.cmake @@ -9,8 +9,10 @@ # # CHANGELOG # -# 1.1.0 - 2020-06-18 -# - Allow unknown C++ attributes in MSVC compiler +# 1.1.0 - 2020-06-20 +# - Allow unknown C++ attributes in MSVC compiler. +# - Option -DEXCEPTIONS=OFF to disable C++ exceptions support (GCC, clang). +# - Option -DRTTI=OFF to disable RTTI support (GCC, clang). # # 1.0.1 - 2020-01-30 # - Do not explicitly set -mtune=generic, this is default anyway. @@ -135,6 +137,16 @@ macro(cable_configure_compiler) endif() + option(EXCEPTIONS "Build with exceptions support" ON) + if(NOT EXCEPTIONS) + add_compile_options(-fno-exceptions) + endif() + + option(RTTI "Build with RTTI support" ON) + if(NOT RTTI) + add_compile_options(-fno-rtti) + endif() + # Option for arch=native. option(NATIVE "Build for native CPU" OFF) if(NATIVE) diff --git a/include/intx/int128.hpp b/include/intx/int128.hpp index 108ecb91..16c8e47a 100644 --- a/include/intx/int128.hpp +++ b/include/intx/int128.hpp @@ -798,10 +798,22 @@ struct numeric_limits> namespace intx { +template +[[noreturn]] inline void throw_(const char* what) +{ +#if __cpp_exceptions + throw T{what}; +#else + std::fputs(what, stderr); + std::abort(); +#endif +} + constexpr inline int from_dec_digit(char c) { - return (c >= '0' && c <= '9') ? c - '0' : - throw std::invalid_argument{std::string{"Invalid digit: "} + c}; + if (c < '0' || c > '9') + throw_("invalid digit"); + return c - '0'; } constexpr inline int from_hex_digit(char c) @@ -814,8 +826,9 @@ constexpr inline int from_hex_digit(char c) } template -constexpr Int from_string(const char* s) +constexpr Int from_string(const char* str) { + auto s = str; auto x = Int{}; int num_digits = 0; @@ -825,7 +838,7 @@ constexpr Int from_string(const char* s) while (const auto c = *s++) { if (++num_digits > int{sizeof(x) * 2}) - throw std::overflow_error{"Integer overflow"}; + throw_(str); x = (x << 4) | from_hex_digit(c); } return x; @@ -834,12 +847,12 @@ constexpr Int from_string(const char* s) while (const auto c = *s++) { if (num_digits++ > std::numeric_limits::digits10) - throw std::overflow_error{"Integer overflow"}; + throw_(str); const auto d = from_dec_digit(c); x = constexpr_mul(x, Int{10}) + d; if (x < d) - throw std::overflow_error{"Integer overflow"}; + throw_(str); } return x; } @@ -859,7 +872,7 @@ template inline std::string to_string(uint x, int base = 10) { if (base < 2 || base > 36) - throw std::invalid_argument{"invalid base: " + std::to_string(base)}; + throw_("invalid base"); if (x == 0) return "0"; diff --git a/test/unittests/test_int128.cpp b/test/unittests/test_int128.cpp index cf2a8cb7..ea9cee62 100644 --- a/test/unittests/test_int128.cpp +++ b/test/unittests/test_int128.cpp @@ -2,6 +2,7 @@ // Copyright 2019-2020 Pawel Bylica. // Licensed under the Apache License, Version 2.0. +#include "test_utils.hpp" #include #include @@ -355,21 +356,48 @@ TEST(int128, literals) auto a = 340282366920938463463374607431768211455_u128; EXPECT_EQ(a, (uint128{0xffffffffffffffff, 0xffffffffffffffff})); - EXPECT_THROW(340282366920938463463374607431768211456_u128, std::overflow_error); - EXPECT_THROW(3402823669209384634633746074317682114550_u128, std::overflow_error); - a = 0xffffffffffffffffffffffffffffffff_u128; EXPECT_EQ(a, (uint128{0xffffffffffffffff, 0xffffffffffffffff})); - EXPECT_THROW(0x100000000000000000000000000000000_u128, std::overflow_error); + EXPECT_EQ(0xaBc123eFd_u128, 0xAbC123EfD_u128); +} + +TEST(int128, from_string) +{ + constexpr auto ka = from_string("18446744073709551617"); + static_assert(ka == uint128{1, 1}, ""); + const auto sa = "18446744073709551617"; + const auto a = from_string(sa); + EXPECT_EQ(a, uint128(1, 1)); + + constexpr auto kb = from_string("0x300aabbccddeeff99"); + static_assert(kb == uint128{3, 0xaabbccddeeff99}, ""); + const auto sb = "0x300aabbccddeeff99"; + EXPECT_EQ(from_string(sb), uint128(3, 0xaabbccddeeff99)); +} + +TEST(int128, literals_exceptions) +{ + EXPECT_THROW_MESSAGE(340282366920938463463374607431768211456_u128, std::out_of_range, + "340282366920938463463374607431768211456"); + EXPECT_THROW_MESSAGE(3402823669209384634633746074317682114550_u128, std::out_of_range, + "3402823669209384634633746074317682114550"); + + EXPECT_THROW_MESSAGE(0x100000000000000000000000000000000_u128, std::out_of_range, + "0x100000000000000000000000000000000"); // Binary literals 0xb... are not supported yet. - EXPECT_THROW(operator""_u128("0b1"), std::invalid_argument); + EXPECT_THROW_MESSAGE(operator""_u128("0b1"), std::invalid_argument, "invalid digit"); + EXPECT_THROW_MESSAGE(0b1010_u128, std::invalid_argument, "invalid digit"); - EXPECT_THROW(operator""_u128("123x456"), std::invalid_argument); - EXPECT_THROW(operator""_u128("0xabcxdef"), std::invalid_argument); + EXPECT_THROW_MESSAGE(operator""_u128("123x456"), std::invalid_argument, "invalid digit"); + EXPECT_THROW_MESSAGE(operator""_u128("0xabcxdef"), std::invalid_argument, "invalid digit"); +} - EXPECT_EQ(0xaBc123eFd_u128, 0xAbC123EfD_u128); +TEST(int128, from_string_exceptions) +{ + EXPECT_THROW_MESSAGE(from_string("123a"), std::invalid_argument, "invalid digit"); + EXPECT_THROW_MESSAGE(from_string("0xcdefg"), std::invalid_argument, "invalid digit"); } TEST(int128, to_string) diff --git a/test/unittests/test_intx.cpp b/test/unittests/test_intx.cpp index 99d51404..3c73891f 100644 --- a/test/unittests/test_intx.cpp +++ b/test/unittests/test_intx.cpp @@ -560,8 +560,8 @@ TYPED_TEST(uint_test, string_conversions) TYPED_TEST(uint_test, to_string_base) { auto x = TypeParam{1024}; - EXPECT_THROW(to_string(x, 1), std::invalid_argument); - EXPECT_THROW(to_string(x, 37), std::invalid_argument); + EXPECT_THROW_MESSAGE(to_string(x, 1), std::invalid_argument, "invalid base"); + EXPECT_THROW_MESSAGE(to_string(x, 37), std::invalid_argument, "invalid base"); EXPECT_EQ(to_string(x, 10), "1024"); EXPECT_EQ(to_string(x, 16), "400"); EXPECT_EQ(to_string(x, 36), "sg"); diff --git a/test/unittests/test_utils.hpp b/test/unittests/test_utils.hpp index 7792824c..38737bdb 100644 --- a/test/unittests/test_utils.hpp +++ b/test/unittests/test_utils.hpp @@ -5,6 +5,23 @@ #include +#if __cpp_exceptions + #define EXPECT_THROW_MESSAGE(stmt, ex_type, expected) \ + try \ + { \ + stmt; \ + ADD_FAILURE() << "Exception of type " #ex_type " is expected, but none was thrown."; \ + } \ + catch (const ex_type& exception) \ + { \ + EXPECT_STREQ(exception.what(), expected); \ + } \ + (void)0 +#else + #define EXPECT_THROW_MESSAGE(stmt, ex_type, expected) EXPECT_DEATH(stmt, expected) +#endif + + struct type_to_name { template