Commit c2c66129 authored by Tudor Bosman's avatar Tudor Bosman Committed by Dave Watson

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
parent db0922d9
......@@ -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<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
......@@ -991,6 +1001,28 @@ class FormatValue<
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
......@@ -1032,6 +1064,11 @@ template <class T> struct KeyableTraitsAssoc : public FormatTraitsBase {
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
......@@ -1076,6 +1113,28 @@ class FormatValue<
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>> {
......
......@@ -257,6 +257,31 @@ Formatter<true, Container> 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 <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.
*
......
......@@ -260,6 +260,12 @@ inline dynamic::dynamic(ObjectMaker (*)())
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)
{
......@@ -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<dynamic> il)
: type_(ARRAY)
{
......@@ -907,7 +925,52 @@ class FormatValue<dynamic> {
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
......
......@@ -63,19 +63,21 @@
#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 {
......@@ -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.
......
......@@ -135,6 +135,12 @@ TEST(Format, Simple) {
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;
......@@ -154,10 +160,22 @@ TEST(Format, Simple) {
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"));
......@@ -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)));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment