From 6a7ee8aa35cde364801f1b2b79d28eb4389cab29 Mon Sep 17 00:00:00 2001 From: Nicholas Ormrod Date: Tue, 10 Sep 2013 09:59:04 -0700 Subject: [PATCH] range checks in DynamicConverter Summary: Add range-checking to convertTo for small numeric types. Internally, dynamics represent a numeric with an int64_t or a double. When converting to a smaller numeric type, DynamicConverter uses a static_cast. This causes some confusion (re D936940). The code now uses folly::to, which throws a std::range_error on overflow. While working on this I also added some light comments to the new toDynamic section, for consistency with the original convertTo commenting. I also renamed the internal trait is_associative_container to is_map, since is_associative_container is looking for a mapped_type typedef and hence excludes such associative containers as sets. While adding the overflow tests, I also augmented the typetraits test to include the is_map and is_range traits, which hitherto had no test coverage. Test Plan: build and run tests, both in dbg and opt Reviewed By: cberner@fb.com FB internal diff: D961605 --- folly/DynamicConverter.h | 32 +++++++++++++++++-------- folly/test/DynamicConverterTest.cpp | 37 +++++++++++++++++++++++++---- 2 files changed, 55 insertions(+), 14 deletions(-) diff --git a/folly/DynamicConverter.h b/folly/DynamicConverter.h index 2c3d2c1d..444d1f79 100644 --- a/folly/DynamicConverter.h +++ b/folly/DynamicConverter.h @@ -81,7 +81,7 @@ template struct is_range std::false_type >::type {}; -template struct is_associative_container +template struct is_map : std::integral_constant< bool, is_range::value && has_mapped_type::value @@ -178,13 +178,14 @@ conversionIterator(const It& it) { /////////////////////////////////////////////////////////////////////////////// // DynamicConverter specializations -template struct DynamicConverter; - /** * Each specialization of DynamicConverter has the function - * 'static T convert(const dynamic& d);' + * 'static T convert(const dynamic&);' */ +// default - intentionally unimplemented +template struct DynamicConverter; + // boolean template <> struct DynamicConverter { @@ -199,7 +200,7 @@ struct DynamicConverter::value && !std::is_same::value>::type> { static T convert(const dynamic& d) { - return static_cast(d.asInt()); + return folly::to(d.asInt()); } }; @@ -208,7 +209,7 @@ template struct DynamicConverter::value>::type> { static T convert(const dynamic& d) { - return static_cast(d.asDouble()); + return folly::to(d.asDouble()); } }; @@ -261,9 +262,17 @@ struct DynamicConverter struct DynamicConstructor { static dynamic construct(const C& x) { @@ -271,10 +280,11 @@ struct DynamicConstructor { } }; +// maps template struct DynamicConstructor::value>::type> { + dynamicconverter_detail::is_map::value>::type> { static dynamic construct(const C& x) { dynamic d = dynamic::object; for (auto& pair : x) { @@ -284,10 +294,11 @@ struct DynamicConstructor struct DynamicConstructor::value && + !dynamicconverter_detail::is_map::value && !std::is_constructible::value && dynamicconverter_detail::is_range::value>::type> { static dynamic construct(const C& x) { @@ -299,6 +310,7 @@ struct DynamicConstructor struct DynamicConstructor, void> { static dynamic construct(const std::pair& x) { @@ -310,7 +322,7 @@ struct DynamicConstructor, void> { }; /////////////////////////////////////////////////////////////////////////////// -// convertTo implementation +// implementation template T convertTo(const dynamic& d) { diff --git a/folly/test/DynamicConverterTest.cpp b/folly/test/DynamicConverterTest.cpp index c94a00c6..fe79b2bd 100644 --- a/folly/test/DynamicConverterTest.cpp +++ b/folly/test/DynamicConverterTest.cpp @@ -46,6 +46,26 @@ TEST(DynamicConverter, template_metaprogramming) { EXPECT_EQ(c1t, true); EXPECT_EQ(c2t, true); EXPECT_EQ(c3t, true); + + + bool m1f = is_map::value; + bool m2f = is_map>::value; + + bool m1t = is_map>::value; + + EXPECT_EQ(m1f, false); + EXPECT_EQ(m2f, false); + EXPECT_EQ(m1t, true); + + + bool r1f = is_range::value; + + bool r1t = is_range>::value; + bool r2t = is_range>::value; + + EXPECT_EQ(r1f, false); + EXPECT_EQ(r1t, true); + EXPECT_EQ(r2t, true); } TEST(DynamicConverter, arithmetic_types) { @@ -57,10 +77,6 @@ TEST(DynamicConverter, arithmetic_types) { auto i2 = convertTo(d2); EXPECT_EQ(i2, 123456789012345); - dynamic d3 = 123456789012345; - auto i3 = convertTo(d3); - EXPECT_EQ(i3, 121); - dynamic d4 = 3.141; auto i4 = convertTo(d4); EXPECT_EQ((int)(i4*100), 314); @@ -332,6 +348,19 @@ TEST(DynamicConverter, construct) { } } +TEST(DynamicConverter, errors) { + const auto int32Over = + static_cast(std::numeric_limits().max()) + 1; + const auto floatOver = + static_cast(std::numeric_limits().max()) * 2; + + dynamic d1 = int32Over; + EXPECT_THROW(convertTo(d1), std::range_error); + + dynamic d2 = floatOver; + EXPECT_THROW(convertTo(d2), std::range_error); +} + int main(int argc, char ** argv) { testing::InitGoogleTest(&argc, argv); google::ParseCommandLineFlags(&argc, &argv, true); -- 2.34.1