From: Tudor Bosman Date: Wed, 30 Apr 2014 15:41:45 +0000 (-0700) Subject: Add defaulted() in Format.h to avoid throwing on missing keys; add string-y dynamic... X-Git-Tag: v0.22.0~566 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=c2c66129648524ddb456fa44b35c1531dd103004;p=folly.git Add defaulted() in Format.h to avoid throwing on missing keys; add string-y dynamic constructors Test Plan: folly/test Reviewed By: simpkins@fb.com FB internal diff: D1303911 --- diff --git a/folly/Format-inl.h b/folly/Format-inl.h index 5575151c..79ea1f79 100644 --- a/folly/Format-inl.h +++ b/folly/Format-inl.h @@ -912,6 +912,11 @@ struct IndexableTraitsSeq : public FormatTraitsBase { static const value_type& at(const C& c, int idx) { return c.at(idx); } + + static const value_type& at(const C& c, int idx, + const value_type& dflt) { + return (idx >= 0 && idx < c.size()) ? c.at(idx) : dflt; + } }; // Base class for associative types (maps) @@ -921,6 +926,11 @@ struct IndexableTraitsAssoc : public FormatTraitsBase { static const value_type& at(const C& c, int idx) { return c.at(static_cast(idx)); } + static const value_type& at(const C& c, int idx, + const value_type& dflt) { + auto pos = c.find(static_cast(idx)); + return pos != c.end() ? pos->second : dflt; + } }; // std::array @@ -991,6 +1001,28 @@ class FormatValue< const T& val_; }; +template +class FormatValue< + detail::DefaultValueWrapper, + typename detail::IndexableTraits::enabled> { + public: + explicit FormatValue(const detail::DefaultValueWrapper& val) + : val_(val) { } + + template + void format(FormatArg& arg, FormatCallback& cb) const { + FormatValue::value_type>::type>( + detail::IndexableTraits::at( + val_.container, + arg.splitIntKey(), + val_.defaultValue)).format(arg, cb); + } + + private: + const detail::DefaultValueWrapper& val_; +}; + namespace detail { // Define enabled, key_type, convert from StringPiece to the key types @@ -1032,6 +1064,11 @@ template struct KeyableTraitsAssoc : public FormatTraitsBase { static const value_type& at(const T& map, StringPiece key) { return map.at(KeyFromStringPiece::convert(key)); } + static const value_type& at(const T& map, StringPiece key, + const value_type& dflt) { + auto pos = map.find(KeyFromStringPiece::convert(key)); + return pos != map.end() ? pos->second : dflt; + } }; // Define enabled, key_type, value_type, at() for supported string-keyed @@ -1076,6 +1113,28 @@ class FormatValue< const T& val_; }; +template +class FormatValue< + detail::DefaultValueWrapper, + typename detail::KeyableTraits::enabled> { + public: + explicit FormatValue(const detail::DefaultValueWrapper& val) + : val_(val) { } + + template + void format(FormatArg& arg, FormatCallback& cb) const { + FormatValue::value_type>::type>( + detail::KeyableTraits::at( + val_.container, + arg.splitKey(), + val_.defaultValue)).format(arg, cb); + } + + private: + const detail::DefaultValueWrapper& val_; +}; + // Partial specialization of FormatValue for pairs template class FormatValue> { diff --git a/folly/Format.h b/folly/Format.h index 970ca405..eb8b5f81 100644 --- a/folly/Format.h +++ b/folly/Format.h @@ -257,6 +257,31 @@ Formatter vformatChecked(StringPiece fmt, return f; } +/** + * Wrap a sequence or associative container so that out-of-range lookups + * return a default value rather than throwing an exception. + * + * Usage: + * format("[no_such_key"], defaulted(map, 42)) -> 42 + */ +namespace detail { +template struct DefaultValueWrapper { + DefaultValueWrapper(const Container& container, const Value& defaultValue) + : container(container), + defaultValue(defaultValue) { + } + + const Container& container; + const Value& defaultValue; +}; +} // namespace + +template +detail::DefaultValueWrapper +defaulted(const Container& c, const Value& v) { + return detail::DefaultValueWrapper(c, v); +} + /** * Append formatted output to a string. * diff --git a/folly/dynamic-inl.h b/folly/dynamic-inl.h index 35b8e3ea..204d48f9 100644 --- a/folly/dynamic-inl.h +++ b/folly/dynamic-inl.h @@ -260,6 +260,12 @@ inline dynamic::dynamic(ObjectMaker (*)()) new (getAddress()) ObjectImpl(); } +inline dynamic::dynamic(StringPiece s) + : type_(STRING) +{ + new (&u_.string) fbstring(s.data(), s.size()); +} + inline dynamic::dynamic(char const* s) : type_(STRING) { @@ -272,6 +278,18 @@ inline dynamic::dynamic(std::string const& s) new (&u_.string) fbstring(s); } +inline dynamic::dynamic(fbstring const& s) + : type_(STRING) +{ + new (&u_.string) fbstring(s); +} + +inline dynamic::dynamic(fbstring&& s) + : type_(STRING) +{ + new (&u_.string) fbstring(std::move(s)); +} + inline dynamic::dynamic(std::initializer_list il) : type_(ARRAY) { @@ -907,7 +925,52 @@ class FormatValue { const dynamic& val_; }; -} +template +class FormatValue> { + public: + explicit FormatValue( + const detail::DefaultValueWrapper& val) + : val_(val) { } + + template + void format(FormatArg& arg, FormatCallback& cb) const { + auto& c = val_.container; + switch (c.type()) { + case dynamic::NULLT: + case dynamic::BOOL: + case dynamic::INT64: + case dynamic::STRING: + case dynamic::DOUBLE: + FormatValue(c).format(arg, cb); + break; + case dynamic::ARRAY: + { + int key = arg.splitIntKey(); + if (key >= 0 && key < c.size()) { + FormatValue(c.at(key)).format(arg, cb); + } else{ + FormatValue(val_.defaultValue).format(arg, cb); + } + } + break; + case dynamic::OBJECT: + { + auto pos = c.find(arg.splitKey()); + if (pos != c.items().end()) { + FormatValue(pos->second).format(arg, cb); + } else { + FormatValue(val_.defaultValue).format(arg, cb); + } + } + break; + } + } + + private: + const detail::DefaultValueWrapper& val_; +}; + +} // namespaces #undef FB_DYNAMIC_APPLY diff --git a/folly/dynamic.h b/folly/dynamic.h index 1494a557..d388ce72 100644 --- a/folly/dynamic.h +++ b/folly/dynamic.h @@ -63,19 +63,21 @@ #ifndef FOLLY_DYNAMIC_H_ #define FOLLY_DYNAMIC_H_ -#include +#include +#include #include -#include -#include #include +#include #include -#include +#include +#include #include -#include + #include -#include "folly/Traits.h" #include "folly/FBString.h" +#include "folly/Range.h" +#include "folly/Traits.h" namespace folly { @@ -143,8 +145,11 @@ public: /* * String compatibility constructors. */ + /* implicit */ dynamic(StringPiece val); /* implicit */ dynamic(char const* val); /* implicit */ dynamic(std::string const& val); + /* implicit */ dynamic(fbstring const& val); + /* implicit */ dynamic(fbstring&& val); /* * This is part of the plumbing for object(), above. Used to create @@ -326,7 +331,6 @@ public: */ const_item_iterator find(dynamic const&) const; - /* * If this is an object, returns whether it contains a field with * the given name. Otherwise throws TypeError. diff --git a/folly/test/FormatTest.cpp b/folly/test/FormatTest.cpp index 53e7f658..65d5c29e 100644 --- a/folly/test/FormatTest.cpp +++ b/folly/test/FormatTest.cpp @@ -135,6 +135,12 @@ TEST(Format, Simple) { const std::vector v2 = v1; EXPECT_EQ("0020", fstr("{0[1]:04}", v2)); EXPECT_EQ("0020", vstr("{1:04}", v2)); + EXPECT_THROW(fstr("{0[3]:04}", v2), std::out_of_range); + EXPECT_THROW(vstr("{3:04}", v2), std::out_of_range); + EXPECT_EQ("0020", fstr("{0[1]:04}", defaulted(v2, 42))); + EXPECT_EQ("0020", vstr("{1:04}", defaulted(v2, 42))); + EXPECT_EQ("0042", fstr("{0[3]:04}", defaulted(v2, 42))); + EXPECT_EQ("0042", vstr("{3:04}", defaulted(v2, 42))); const int p[] = {10, 20, 30}; const int* q = p; @@ -154,10 +160,22 @@ TEST(Format, Simple) { std::map m { {10, "hello"}, {20, "world"} }; EXPECT_EQ("worldXX", fstr("{[20]:X<7}", m)); EXPECT_EQ("worldXX", vstr("{20:X<7}", m)); + EXPECT_THROW(fstr("{[42]:X<7}", m), std::out_of_range); + EXPECT_THROW(vstr("{42:X<7}", m), std::out_of_range); + EXPECT_EQ("worldXX", fstr("{[20]:X<7}", defaulted(m, "meow"))); + EXPECT_EQ("worldXX", vstr("{20:X<7}", defaulted(m, "meow"))); + EXPECT_EQ("meowXXX", fstr("{[42]:X<7}", defaulted(m, "meow"))); + EXPECT_EQ("meowXXX", vstr("{42:X<7}", defaulted(m, "meow"))); std::map m2 { {"hello", "world"} }; EXPECT_EQ("worldXX", fstr("{[hello]:X<7}", m2)); EXPECT_EQ("worldXX", vstr("{hello:X<7}", m2)); + EXPECT_THROW(fstr("{[none]:X<7}", m2), std::out_of_range); + EXPECT_THROW(vstr("{none:X<7}", m2), std::out_of_range); + EXPECT_EQ("worldXX", fstr("{[hello]:X<7}", defaulted(m2, "meow"))); + EXPECT_EQ("worldXX", vstr("{hello:X<7}", defaulted(m2, "meow"))); + EXPECT_EQ("meowXXX", fstr("{[none]:X<7}", defaulted(m2, "meow"))); + EXPECT_EQ("meowXXX", vstr("{none:X<7}", defaulted(m2, "meow"))); // Test indexing in strings EXPECT_EQ("61 62", fstr("{0[0]:x} {0[1]:x}", "abcde")); @@ -260,7 +278,20 @@ TEST(Format, dynamic) { "}"); EXPECT_EQ("world", fstr("{0[hello]}", dyn)); + EXPECT_THROW(fstr("{0[none]}", dyn), std::out_of_range); + EXPECT_EQ("world", fstr("{0[hello]}", defaulted(dyn, "meow"))); + EXPECT_EQ("meow", fstr("{0[none]}", defaulted(dyn, "meow"))); + EXPECT_EQ("20", fstr("{0[x.0]}", dyn)); + EXPECT_THROW(fstr("{0[x.2]}", dyn), std::out_of_range); + + // No support for "deep" defaulting (dyn["x"] is not defaulted) + auto v = dyn.at("x"); + EXPECT_EQ("20", fstr("{0[0]}", v)); + EXPECT_THROW(fstr("{0[2]}", v), std::out_of_range); + EXPECT_EQ("20", fstr("{0[0]}", defaulted(v, 42))); + EXPECT_EQ("42", fstr("{0[2]}", defaulted(v, 42))); + EXPECT_EQ("42", fstr("{0[y.a]}", dyn)); EXPECT_EQ("(null)", fstr("{}", dynamic(nullptr)));