Commit 9eef9f86 authored by Yedidya Feldblum's avatar Yedidya Feldblum Committed by Facebook GitHub Bot

operator symmetry for folly::dynamic

Summary: Symmetric operators are done using free or non-member operators. This ensures that whatever conversions could happen to the RHS can also happen to the LHS, symmetrically.

Reviewed By: philippv

Differential Revision: D31582089

fbshipit-source-id: ecae61bc810ee9538b1c2b738c58d4e9e01f1483
parent e2504d85
......@@ -67,25 +67,25 @@ TypeError::TypeError(
#define FB_DYNAMIC_APPLY(type, apply) \
do { \
switch ((type)) { \
case NULLT: \
case dynamic::NULLT: \
apply(std::nullptr_t); \
break; \
case ARRAY: \
apply(Array); \
case dynamic::ARRAY: \
apply(dynamic::Array); \
break; \
case BOOL: \
case dynamic::BOOL: \
apply(bool); \
break; \
case DOUBLE: \
case dynamic::DOUBLE: \
apply(double); \
break; \
case INT64: \
case dynamic::INT64: \
apply(int64_t); \
break; \
case OBJECT: \
apply(ObjectImpl); \
case dynamic::OBJECT: \
apply(dynamic::ObjectImpl); \
break; \
case STRING: \
case dynamic::STRING: \
apply(std::string); \
break; \
default: \
......@@ -94,31 +94,34 @@ TypeError::TypeError(
} \
} while (0)
bool dynamic::operator<(dynamic const& o) const {
if (UNLIKELY(type_ == OBJECT || o.type_ == OBJECT)) {
throw_exception<TypeError>("object", type_);
bool operator<(dynamic const& a, dynamic const& b) {
constexpr auto obj = dynamic::OBJECT;
if (UNLIKELY(a.type_ == obj || b.type_ == obj)) {
auto type = a.type_ == obj ? b.type_ : b.type_ == obj ? a.type_ : obj;
throw_exception<TypeError>("object", type);
}
if (type_ != o.type_) {
return type_ < o.type_;
if (a.type_ != b.type_) {
return a.type_ < b.type_;
}
#define FB_X(T) return CompareOp<T>::comp(*getAddress<T>(), *o.getAddress<T>())
FB_DYNAMIC_APPLY(type_, FB_X);
#define FB_X(T) \
return dynamic::CompareOp<T>::comp(*a.getAddress<T>(), *b.getAddress<T>())
FB_DYNAMIC_APPLY(a.type_, FB_X);
#undef FB_X
}
bool dynamic::operator==(dynamic const& o) const {
if (type() != o.type()) {
if (isNumber() && o.isNumber()) {
auto& integ = isInt() ? *this : o;
auto& doubl = isInt() ? o : *this;
bool operator==(dynamic const& a, dynamic const& b) {
if (a.type() != b.type()) {
if (a.isNumber() && b.isNumber()) {
auto& integ = a.isInt() ? a : b;
auto& doubl = a.isInt() ? b : a;
return integ.asInt() == doubl.asDouble();
}
return false;
}
#define FB_X(T) return *getAddress<T>() == *o.getAddress<T>();
FB_DYNAMIC_APPLY(type_, FB_X);
#define FB_X(T) return *a.getAddress<T>() == *b.getAddress<T>();
FB_DYNAMIC_APPLY(a.type_, FB_X);
#undef FB_X
}
......
......@@ -60,8 +60,6 @@
#include <utility>
#include <vector>
#include <boost/operators.hpp>
#include <folly/Expected.h>
#include <folly/Range.h>
#include <folly/Traits.h>
......@@ -85,7 +83,7 @@ using detect_construct_string = decltype(std::string(
FOLLY_DECLVAL(T const&).data(), FOLLY_DECLVAL(T const&).size()));
}
struct dynamic : private boost::operators<dynamic> {
struct dynamic {
enum Type {
NULLT,
ARRAY,
......@@ -223,13 +221,23 @@ struct dynamic : private boost::operators<dynamic> {
* "Deep" equality comparison. This will compare all the way down
* an object or array, and is potentially expensive.
*/
bool operator==(dynamic const& o) const;
friend bool operator==(dynamic const& a, dynamic const& b);
friend bool operator!=(dynamic const& a, dynamic const& b) {
return !(a == b);
}
/*
* For all types except object this returns the natural ordering on
* those types. For objects, we throw TypeError.
*/
bool operator<(dynamic const& o) const;
friend bool operator<(dynamic const& a, dynamic const& b);
friend bool operator>(dynamic const& a, dynamic const& b) { return b < a; }
friend bool operator<=(dynamic const& a, dynamic const& b) {
return !(b < a);
}
friend bool operator>=(dynamic const& a, dynamic const& b) {
return !(a < b);
}
/*
* General operators.
......@@ -251,6 +259,44 @@ struct dynamic : private boost::operators<dynamic> {
dynamic& operator++();
dynamic& operator--();
friend dynamic operator+(dynamic const& a, dynamic const& b) {
return std::move(copy(a) += b);
}
friend dynamic operator-(dynamic const& a, dynamic const& b) {
return std::move(copy(a) -= b);
}
friend dynamic operator*(dynamic const& a, dynamic const& b) {
return std::move(copy(a) *= b);
}
friend dynamic operator/(dynamic const& a, dynamic const& b) {
return std::move(copy(a) /= b);
}
friend dynamic operator%(dynamic const& a, dynamic const& b) {
return std::move(copy(a) %= b);
}
friend dynamic operator|(dynamic const& a, dynamic const& b) {
return std::move(copy(a) |= b);
}
friend dynamic operator&(dynamic const& a, dynamic const& b) {
return std::move(copy(a) &= b);
}
friend dynamic operator^(dynamic const& a, dynamic const& b) {
return std::move(copy(a) ^= b);
}
friend dynamic operator+(dynamic&& a, dynamic const& b) {
return std::move(a += b);
}
dynamic operator++(int) {
auto self = *this;
return ++*this, self;
}
dynamic operator-(int) {
auto self = *this;
return --*this, self;
}
/*
* Assignment from other dynamics. Because of the implicit conversion
* to dynamic from its potential types, you can use this to change the
......
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