diff --git a/include/llmr/style/value.hpp b/include/llmr/style/value.hpp index b90f824e9..2e9d66136 100644 --- a/include/llmr/style/value.hpp +++ b/include/llmr/style/value.hpp @@ -1,12 +1,12 @@ #ifndef LLMR_STYLE_VALUE #define LLMR_STYLE_VALUE -#include +#include #include namespace llmr { -typedef boost::variant Value; +typedef ::util::variant Value; Value parseValue(pbf data); diff --git a/include/llmr/util/variant.hpp b/include/llmr/util/variant.hpp new file mode 100644 index 000000000..410aa8ead --- /dev/null +++ b/include/llmr/util/variant.hpp @@ -0,0 +1,380 @@ +#ifndef UTIL_VARIANT_HPP +#define UTIL_VARIANT_HPP + +#include +#include +#include +#include // std::move/swap +#include // runtime_error +#include // operator new +#include // size_t +#include + +#ifdef NDEBUG + #define VARIANT_INLINE inline __attribute__((always_inline)) +#else + #define VARIANT_INLINE __attribute__((noinline)) +#endif + +namespace util { + +namespace detail { + +static constexpr std::size_t invalid_value = std::size_t(-1); + +template +struct type_traits; + +template +struct type_traits +{ + static constexpr std::size_t id = std::is_same::value ? sizeof...(Types) : type_traits::id; +}; + +template +struct type_traits +{ + static constexpr std::size_t id = invalid_value; +}; + +template +struct is_valid_type; + +template +struct is_valid_type +{ + static constexpr bool value = std::is_same::value + || is_valid_type::value; +}; + +template +struct is_valid_type : std::false_type {}; + +} + +template +struct static_max; + +template +struct static_max +{ + static const std::size_t value = arg; +}; + +template +struct static_max +{ + static const std::size_t value = arg1 >= arg2 ? static_max::value : + static_max::value; +}; + +template +struct variant_helper; + +template +struct variant_helper +{ + VARIANT_INLINE static void destroy(std::size_t id, void * data) + { + if (id == sizeof...(Types)) + { + reinterpret_cast(data)->~T(); + } + else + { + variant_helper::destroy(id, data); + } + } + + VARIANT_INLINE static void move(const std::size_t old_t, void * old_v, void * new_v) + { + if (old_t == sizeof...(Types)) + { + new (new_v) T(std::move(*reinterpret_cast(old_v))); + //std::memcpy(new_v, old_v, sizeof(T)); + // ^^ DANGER: this should only be considered for relocatable types e.g built-in types + // Also, I don't see any measurable performance benefit just yet + } + else + { + variant_helper::move(old_t, old_v, new_v); + } + } + + VARIANT_INLINE static void copy(const std::size_t old_t, const void * old_v, void * new_v) + { + if (old_t == sizeof...(Types)) + { + new (new_v) T(*reinterpret_cast(old_v)); + } + else + { + variant_helper::copy(old_t, old_v, new_v); + } + } +}; + +template<> struct variant_helper<> +{ + VARIANT_INLINE static void destroy(const std::size_t, void *) {} + VARIANT_INLINE static void move(const std::size_t, void *, void *) {} + VARIANT_INLINE static void copy(const std::size_t, const void *, void *) {} +}; + +namespace detail { + +template +struct dispatcher; + +template +struct dispatcher +{ + typedef typename std::result_of::type result_type; + VARIANT_INLINE static result_type apply(V const& v, F f) + { + if (v.get_type_id() == sizeof...(Types)) + { + return f(v. template get()); + } + else + { + return dispatcher::apply(v, f); + } + } +}; + +template +struct dispatcher +{ + typedef typename std::result_of::type result_type; + VARIANT_INLINE static result_type apply(V const&, F) + { + throw std::runtime_error("dispatch: FAIL"); + } +}; + +// comparator functors +struct equal_comp +{ + template + bool operator()(T const& lhs, T const& rhs) const + { + return lhs == rhs; + } +}; + +struct less_comp +{ + template + bool operator()(const T& lhs, const T& rhs) const + { + return lhs < rhs; + } +}; + +template +struct comparer +{ +public: + explicit comparer(Variant const& lhs) noexcept + : lhs_(lhs) {} + comparer& operator=(const comparer&) = delete; + // visitor + template + bool operator()(T const& rhs_content) const + { + T const& lhs_content = lhs_.template get(); + return Comp()(lhs_content, rhs_content); + } +private: + Variant const& lhs_; +}; + +// operator<< helper +template +struct printer +{ +public: + explicit printer(Out & out) + : out_(out) {} + printer& operator=(const printer&) = delete; + +// visitor + template + void operator()(const T& operand) const + { + out_ << operand; + } +private: + Out & out_; +}; + +} // detail + +template +struct variant +{ +private: + + static const std::size_t data_size = static_max::value; + static const std::size_t data_align = static_max::value; + + using data_t = typename std::aligned_storage::type; + + using helper_t = variant_helper; + + std::size_t type_id; + data_t data; + +public: + + VARIANT_INLINE variant() + : type_id(detail::invalid_value) {} + + template + VARIANT_INLINE explicit variant(T const& val) noexcept + : type_id(detail::type_traits::id) + { + static_assert(detail::is_valid_type::value, "Not a valid type for this variant"); + new (&data) T(val); + } + + template + VARIANT_INLINE variant(T && val) noexcept + : type_id(detail::type_traits::id) + { + static_assert(detail::is_valid_type::value, "Not a valid type for this variant"); + new (&data) T(std::forward(val)); // nothrow + } + + VARIANT_INLINE variant(variant const& old) + : type_id(old.type_id) + { + helper_t::copy(old.type_id, &old.data, &data); + } + + VARIANT_INLINE variant(variant&& old) noexcept + : type_id(old.type_id) + { + helper_t::move(old.type_id, &old.data, &data); + } + + friend void swap(variant & first, variant & second) + { + using std::swap; //enable ADL + swap(first.type_id, second.type_id); + swap(first.data, second.data); + } + + VARIANT_INLINE variant& operator= (variant other) + { + swap(*this, other); + return *this; + } + + template + VARIANT_INLINE void is() const + { + return (type_id == detail::type_traits::id); + } + + VARIANT_INLINE void valid() const + { + return (type_id != detail::invalid_value); + } + + template + VARIANT_INLINE void set(Args&&... args) + { + helper_t::destroy(type_id, &data); + new (&data) T(std::forward(args)...); + type_id = detail::type_traits::id; + } + + template + VARIANT_INLINE T& get() + { + if (type_id == detail::type_traits::id) + { + return *reinterpret_cast(&data); + } + else + { + throw std::runtime_error("in get()"); + } + } + + template + VARIANT_INLINE T const& get() const + { + if (type_id == detail::type_traits::id) + { + return *reinterpret_cast(&data); + } + else + { + throw std::runtime_error("in get()"); + } + } + + VARIANT_INLINE std::size_t get_type_id() const + { + return type_id; + } + + // visitor + template + typename std::result_of::type + VARIANT_INLINE static visit(V const& v, F f) + { + return detail::dispatcher::apply(v, f); + } + + ~variant() noexcept + { + helper_t::destroy(type_id, &data); + } + + // comparison operators + // equality + VARIANT_INLINE bool operator==(variant const& rhs) const + { + if (this->get_type_id() != rhs.get_type_id()) + return false; + detail::comparer visitor(*this); + return visit(rhs, visitor); + } + // less than + VARIANT_INLINE bool operator<(variant const& rhs) const + { + if (this->get_type_id() != rhs.get_type_id()) + { + return this->get_type_id() < rhs.get_type_id(); + // ^^ borrowed from boost::variant + } + detail::comparer visitor(*this); + return visit(rhs, visitor); + } +}; + +// unary visitor interface +template +typename std::result_of::type +VARIANT_INLINE static apply_visitor(V const& v, F f) +{ + return V::visit(v,f); +} + +// operator<< +template +VARIANT_INLINE std::basic_ostream& +operator<< (std::basic_ostream& out, Variant const& rhs) +{ + detail::printer > visitor(out); + apply_visitor(rhs, visitor); + return out; +} + +} + +#endif // UTIL_VARIANT_HPP diff --git a/src/map/vector_tile.cpp b/src/map/vector_tile.cpp index e64d7114b..c6ded9302 100644 --- a/src/map/vector_tile.cpp +++ b/src/map/vector_tile.cpp @@ -12,7 +12,7 @@ VectorTile::VectorTile(pbf tile) { while (tile.next()) { if (tile.tag == 3) { // layer VectorTileLayer layer(tile.message()); - layers.emplace(layer.name, layer); + layers.emplace(layer.name, std::forward(layer)); } else { tile.skip(); } @@ -34,7 +34,7 @@ VectorTileLayer::VectorTileLayer(pbf layer) : data(layer) { } else if (layer.tag == 3) { // keys keys.emplace_back(layer.string()); } else if (layer.tag == 4) { // values - values.emplace_back(parseValue(layer.message())); + values.emplace_back(std::move(parseValue(layer.message()))); } else if (layer.tag == 5) { // extent extent = layer.varint(); } else { diff --git a/src/style/style.cpp b/src/style/style.cpp index 0e8e9f3b6..7ac5e9ef7 100644 --- a/src/style/style.cpp +++ b/src/style/style.cpp @@ -60,7 +60,7 @@ std::pair Style::parseBucket(pbf data) { } } - return { name, bucket }; + return { name, std::forward(bucket) }; } LayerDescription Style::parseLayer(pbf data) { diff --git a/src/style/value.cpp b/src/style/value.cpp index 99086f377..4ea1a6cd8 100644 --- a/src/style/value.cpp +++ b/src/style/value.cpp @@ -8,7 +8,7 @@ llmr::Value llmr::parseValue(pbf data) { case 1: // string_value return data.string(); case 2: // float_value - return data.float32(); + return static_cast(data.float32()); case 3: // double_value return data.float64(); case 4: // int_value