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)
static const value_type& at(const C& c, int idx) {
return c.at(static_cast<typename C::key_type>(idx));
}
+ static const value_type& at(const C& c, int idx,
+ const value_type& dflt) {
+ auto pos = c.find(static_cast<typename C::key_type>(idx));
+ return pos != c.end() ? pos->second : dflt;
+ }
};
// std::array
const T& val_;
};
+template <class Container, class Value>
+class FormatValue<
+ detail::DefaultValueWrapper<Container, Value>,
+ typename detail::IndexableTraits<Container>::enabled> {
+ public:
+ explicit FormatValue(const detail::DefaultValueWrapper<Container, Value>& val)
+ : val_(val) { }
+
+ template <class FormatCallback>
+ void format(FormatArg& arg, FormatCallback& cb) const {
+ FormatValue<typename std::decay<
+ typename detail::IndexableTraits<Container>::value_type>::type>(
+ detail::IndexableTraits<Container>::at(
+ val_.container,
+ arg.splitIntKey(),
+ val_.defaultValue)).format(arg, cb);
+ }
+
+ private:
+ const detail::DefaultValueWrapper<Container, Value>& val_;
+};
+
namespace detail {
// Define enabled, key_type, convert from StringPiece to the key types
static const value_type& at(const T& map, StringPiece key) {
return map.at(KeyFromStringPiece<key_type>::convert(key));
}
+ static const value_type& at(const T& map, StringPiece key,
+ const value_type& dflt) {
+ auto pos = map.find(KeyFromStringPiece<key_type>::convert(key));
+ return pos != map.end() ? pos->second : dflt;
+ }
};
// Define enabled, key_type, value_type, at() for supported string-keyed
const T& val_;
};
+template <class Container, class Value>
+class FormatValue<
+ detail::DefaultValueWrapper<Container, Value>,
+ typename detail::KeyableTraits<Container>::enabled> {
+ public:
+ explicit FormatValue(const detail::DefaultValueWrapper<Container, Value>& val)
+ : val_(val) { }
+
+ template <class FormatCallback>
+ void format(FormatArg& arg, FormatCallback& cb) const {
+ FormatValue<typename std::decay<
+ typename detail::KeyableTraits<Container>::value_type>::type>(
+ detail::KeyableTraits<Container>::at(
+ val_.container,
+ arg.splitKey(),
+ val_.defaultValue)).format(arg, cb);
+ }
+
+ private:
+ const detail::DefaultValueWrapper<Container, Value>& val_;
+};
+
// Partial specialization of FormatValue for pairs
template <class A, class B>
class FormatValue<std::pair<A, B>> {
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 <class Container, class Value> struct DefaultValueWrapper {
+ DefaultValueWrapper(const Container& container, const Value& defaultValue)
+ : container(container),
+ defaultValue(defaultValue) {
+ }
+
+ const Container& container;
+ const Value& defaultValue;
+};
+} // namespace
+
+template <class Container, class Value>
+detail::DefaultValueWrapper<Container, Value>
+defaulted(const Container& c, const Value& v) {
+ return detail::DefaultValueWrapper<Container, Value>(c, v);
+}
+
/**
* Append formatted output to a string.
*
new (getAddress<ObjectImpl>()) ObjectImpl();
}
+inline dynamic::dynamic(StringPiece s)
+ : type_(STRING)
+{
+ new (&u_.string) fbstring(s.data(), s.size());
+}
+
inline dynamic::dynamic(char const* s)
: type_(STRING)
{
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<dynamic> il)
: type_(ARRAY)
{
const dynamic& val_;
};
-}
+template <class V>
+class FormatValue<detail::DefaultValueWrapper<dynamic, V>> {
+ public:
+ explicit FormatValue(
+ const detail::DefaultValueWrapper<dynamic, V>& val)
+ : val_(val) { }
+
+ template <class FormatCallback>
+ 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<dynamic>(c).format(arg, cb);
+ break;
+ case dynamic::ARRAY:
+ {
+ int key = arg.splitIntKey();
+ if (key >= 0 && key < c.size()) {
+ FormatValue<dynamic>(c.at(key)).format(arg, cb);
+ } else{
+ FormatValue<V>(val_.defaultValue).format(arg, cb);
+ }
+ }
+ break;
+ case dynamic::OBJECT:
+ {
+ auto pos = c.find(arg.splitKey());
+ if (pos != c.items().end()) {
+ FormatValue<dynamic>(pos->second).format(arg, cb);
+ } else {
+ FormatValue<V>(val_.defaultValue).format(arg, cb);
+ }
+ }
+ break;
+ }
+ }
+
+ private:
+ const detail::DefaultValueWrapper<dynamic, V>& val_;
+};
+
+} // namespaces
#undef FB_DYNAMIC_APPLY
#ifndef FOLLY_DYNAMIC_H_
#define FOLLY_DYNAMIC_H_
-#include <unordered_map>
+#include <cstdint>
+#include <initializer_list>
#include <memory>
-#include <string>
-#include <utility>
#include <ostream>
+#include <string>
#include <type_traits>
-#include <initializer_list>
+#include <unordered_map>
+#include <utility>
#include <vector>
-#include <cstdint>
+
#include <boost/operators.hpp>
-#include "folly/Traits.h"
#include "folly/FBString.h"
+#include "folly/Range.h"
+#include "folly/Traits.h"
namespace folly {
/*
* 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
*/
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.
const std::vector<int> 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;
std::map<int, std::string> 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<std::string, std::string> 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"));
"}");
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)));