Skip to content

Commit

Permalink
Merge pull request #68 from mapbox/value_shared_ptr
Browse files Browse the repository at this point in the history
Recursive feature values through shared ptrs
  • Loading branch information
flippmoke committed Jul 15, 2020
2 parents a5571a3 + b5ee34c commit e2a8e0a
Show file tree
Hide file tree
Showing 4 changed files with 338 additions and 21 deletions.
74 changes: 68 additions & 6 deletions include/mapbox/feature.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,36 @@
#include <cstdint>
#include <string>
#include <vector>
#include <memory>
#include <unordered_map>

namespace mapbox {
namespace feature {

// comparator functors
struct equal_comp_shared_ptr
{

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wfloat-equal"
template <typename T>
bool operator()(T const& lhs, T const& rhs) const
{
return lhs == rhs;
}
#pragma GCC diagnostic pop

template <typename T>
bool operator()(std::shared_ptr<T> const& lhs, std::shared_ptr<T> const& rhs) const
{
if (lhs == rhs)
{
return true;
}
return *lhs == *rhs;
}
};

struct value;

struct null_value_t
Expand Down Expand Up @@ -41,13 +66,17 @@ constexpr null_value_t null_value = null_value_t();
// using uint64_t for positive integers, int64_t for negative integers, and double
// for non-integers and integers outside the range of 64 bits.
using value_base = mapbox::util::variant<null_value_t, bool, uint64_t, int64_t, double, std::string,
mapbox::util::recursive_wrapper<std::vector<value>>,
mapbox::util::recursive_wrapper<std::unordered_map<std::string, value>>>;
std::shared_ptr<std::vector<value>>,
std::shared_ptr<std::unordered_map<std::string, value>>>;

struct value : public value_base
{
using array_type = std::vector<value>;
using array_ptr_type = std::shared_ptr<std::vector<value>>;
using const_array_ptr_type = std::shared_ptr<const std::vector<value>>;
using object_type = std::unordered_map<std::string, value>;
using object_ptr_type = std::shared_ptr<std::unordered_map<std::string, value>>;
using const_object_ptr_type = std::shared_ptr<const std::unordered_map<std::string, value>>;

value() : value_base(null_value) {}
value(null_value_t) : value_base(null_value) {}
Expand All @@ -71,18 +100,51 @@ struct value : public value_base
value(T t) : value_base(double(t))
{
}
value(array_type array) : value_base(std::move(array)) {}
value(object_type object) : value_base(std::move(object)) {}
value(array_type array) : value_base(std::make_shared<array_type>(std::forward<array_type>(array))) {}
value(array_ptr_type array) : value_base(array) {}
value(object_type object) : value_base(std::make_shared<object_type>(std::forward<object_type>(object))) {}
value(object_ptr_type object) : value_base(object) {}

bool operator==(value const& rhs) const
{
assert(valid() && rhs.valid());
if (this->which() != rhs.which())
{
return false;
}
mapbox::util::detail::comparer<value, equal_comp_shared_ptr> visitor(*this);
return visit(rhs, visitor);
}

explicit operator bool() const { return !is<null_value_t>(); }

DECLARE_VALUE_TYPE_ACCESOR(Int, int64_t)
DECLARE_VALUE_TYPE_ACCESOR(Uint, uint64_t)
DECLARE_VALUE_TYPE_ACCESOR(Bool, bool)
DECLARE_VALUE_TYPE_ACCESOR(Double, double)
DECLARE_VALUE_TYPE_ACCESOR(Array, array_type)
DECLARE_VALUE_TYPE_ACCESOR(Object, object_type)
DECLARE_VALUE_TYPE_ACCESOR(String, std::string)

array_ptr_type getArray() noexcept
{
return match(
[](array_ptr_type& val) -> array_ptr_type { return val; },
[](auto&) -> array_ptr_type { return nullptr; });
}
const_array_ptr_type getArray() const noexcept
{
return const_cast<value*>(this)->getArray();
}

object_ptr_type getObject() noexcept
{
return match(
[](object_ptr_type& val) -> object_ptr_type { return val; },
[](auto&) -> object_ptr_type { return nullptr; });
}
const_object_ptr_type getObject() const noexcept
{
return const_cast<value*>(this)->getObject();
}
};

#undef DECLARE_VALUE_TYPE_ACCESOR
Expand Down
162 changes: 155 additions & 7 deletions include/mapbox/geometry_io.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,31 +9,31 @@
namespace mapbox {
namespace geometry {

std::ostream& operator<<(std::ostream& os, const empty&)
inline std::ostream& operator<<(std::ostream& os, const empty&)
{
return os << "[]";
}

template <typename T>
std::ostream& operator<<(std::ostream& os, const point<T>& point)
{
return os << "[" << point.x << "," << point.y << "]";
return os << '[' << point.x << ',' << point.y << ']';
}

template <typename T, template <class, class...> class C, class... Args>
std::ostream& operator<<(std::ostream& os, const C<T, Args...>& cont)
{
os << "[";
os << '[';
for (auto it = cont.cbegin();;)
{
os << *it;
if (++it == cont.cend())
{
break;
}
os << ",";
os << ',';
}
return os << "]";
return os << ']';
}

template <typename T>
Expand Down Expand Up @@ -89,9 +89,157 @@ std::ostream& operator<<(std::ostream& os, const geometry_collection<T>& geom)

namespace feature {

std::ostream& operator<<(std::ostream& os, const null_value_t&)
inline std::ostream& operator<<(std::ostream& os, const null_value_t&)
{
return os << "[]";
return os << "null";
}

void to_stream(mapbox::feature::property_map const&, std::ostream& dest);

void to_stream(std::vector<mapbox::feature::value> const&, std::ostream& dest);

void quote_string(std::string const& in, std::ostream& dest)
{
dest << '\"';
for (char c : in)
{
if (c == '"' || c == '\\')
{
dest << '\\';
}
dest << c;
}
dest << '\"';
}

struct value_to_stream_visitor
{

std::ostream& out;
bool in;

value_to_stream_visitor(std::ostream& out_)
: out(out_), in(false) {}

template <typename T>
void operator()(T val)
{
out << val;
}

void operator()(std::string const& val)
{
if (in)
{
quote_string(val, out);
}
else
{
out << val;
}
}

void operator()(bool val)
{
out << (val ? "true" : "false");
}

void operator()(std::vector<mapbox::feature::value> const& vec)
{
out << '[';
bool first = true;
bool set_in = false;
if (!in)
{
in = true;
set_in = true;
}
for (auto const& item : vec)
{
if (first)
{
first = false;
}
else
{
out << ',';
}
mapbox::util::apply_visitor(*this, item);
}
if (set_in)
{
in = false;
}
out << ']';
}

void operator()(std::shared_ptr<std::vector<mapbox::feature::value>> const& vec)
{
(*this)(*vec);
}

void operator()(std::unordered_map<std::string, mapbox::feature::value> const& map)
{
out << '{';
std::vector<std::string> keys;
bool set_in = false;
if (!in)
{
in = true;
set_in = true;
}
for (auto const& p : map)
{
keys.push_back(p.first);
}
std::sort(keys.begin(), keys.end());
bool first = true;
for (auto const& k : keys)
{
if (first)
{
first = false;
}
else
{
out << ',';
}
auto const val = map.find(k);
quote_string(k, out);
out << ':';
mapbox::util::apply_visitor(*this, val->second);
}
if (set_in)
{
in = false;
}
out << '}';
}

void operator()(std::shared_ptr<std::unordered_map<std::string, mapbox::feature::value>> const& map)
{
(*this)(*map);
}
};

inline std::ostream& operator<<(std::ostream& os, std::unordered_map<std::string, mapbox::feature::value> const& map)
{
value_to_stream_visitor vis(os);
vis(map);
return os;
}

inline std::ostream& operator<<(std::ostream& os, std::vector<mapbox::feature::value> const& vec)
{
value_to_stream_visitor vis(os);
vis(vec);
return os;
}

inline std::ostream& operator<<(std::ostream& os, mapbox::feature::value const& val)
{
mapbox::util::apply_visitor(value_to_stream_visitor(os), val);
return os;
}

} // namespace feature
Expand Down
39 changes: 37 additions & 2 deletions test/feature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ using mapbox::feature::value;
namespace {

template <typename T, typename U>
void checkType(U&& arg) try
void checkType(U arg) try
{
value v{std::forward<U>(arg)};
value v{arg};
CHECK(v);
CHECK(v.template is<T>());
CHECK(v.template get<T>() == arg);
Expand All @@ -24,6 +24,32 @@ catch (...)
FAIL();
}

template <typename T, typename U>
void checkPtrType(U arg) try
{
value v{arg};
CHECK(v);
CHECK(v.template is<T>());
CHECK(*(v.template get<T>()) == *arg);
}
catch (...)
{
FAIL();
}

template <typename T, typename U>
void checkPtrType2(U arg) try
{
value v{arg};
CHECK(v);
CHECK(v.template is<T>());
CHECK(*(v.template get<T>()) == arg);
}
catch (...)
{
FAIL();
}

} // namespace

TEST_CASE("test value")
Expand All @@ -34,6 +60,15 @@ TEST_CASE("test value")
checkType<bool>(false);
checkType<std::string>("hello");

value::array_type vec;
vec.emplace_back(value(32));
checkPtrType<value::array_ptr_type>(std::make_shared<value::array_type>(vec));
checkPtrType2<value::array_ptr_type>(vec);
value::object_type map;
map.emplace("a", value(33));
checkPtrType<value::object_ptr_type>(std::make_shared<value::object_type>(map));
checkPtrType2<value::object_ptr_type>(map);

value intV{32};
CHECK_THROWS(intV.get<uint64_t>());

Expand Down
Loading

0 comments on commit e2a8e0a

Please sign in to comment.