From 99ec1d9d745e773120f0ba67cfdd0b9f00c3cd40 Mon Sep 17 00:00:00 2001 From: Bart Louwers Date: Thu, 17 Oct 2024 18:02:22 +0200 Subject: [PATCH] Replace `mapbox/variant.hpp` with `std::variant` (#1) * replace mapbox/variant.hpp in feature.hpp * renmove mapbox variant from geometry.hpp * Update README.md * remove mapbox/variant.hpp from CMakeLists.txt * rename ci job * remove header comments * implement operator== for feature --- .github/workflows/ci.yml | 2 +- CMakeLists.txt | 11 +- README.md | 16 +- include/maplibre/feature.hpp | 174 +++++++++++------- include/maplibre/geometry/for_each_point.hpp | 14 +- include/maplibre/geometry/geometry.hpp | 19 +- include/maplibre/geometry/line_string.hpp | 3 +- .../maplibre/geometry/multi_line_string.hpp | 3 +- include/maplibre/geometry/multi_point.hpp | 3 +- include/maplibre/geometry_io.hpp | 10 +- test/feature.cpp | 52 ++++-- test/for_each_point.cpp | 4 +- test/geometry.cpp | 44 ++--- 13 files changed, 208 insertions(+), 147 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fca60ee..d0dfbfc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,7 +10,7 @@ on: - main jobs: - pre_job: + ci: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 diff --git a/CMakeLists.txt b/CMakeLists.txt index 2269102..93ef060 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.30) project(hpp_skel LANGUAGES CXX) -set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED on) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) @@ -49,15 +49,6 @@ FetchContent_Declare( ) FetchContent_MakeAvailable(Catch2) -FetchContent_Declare( - MapboxVariant - GIT_REPOSITORY https://github.com/mapbox/variant.git - GIT_TAG origin/master -) -FetchContent_MakeAvailable(MapboxVariant) - -include_directories(SYSTEM ${mapboxvariant_SOURCE_DIR}/include) - target_link_libraries(unit-tests PRIVATE Catch2WithMain) # libbenchmark.a supports threads and therefore needs pthread support diff --git a/README.md b/README.md index 6383b89..a5b21c3 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,9 @@ Provides header-only, generic C++ interfaces for geometry types, geometry collec - `maplibre::geometry::polygon` - `maplibre::geometry::multi_polygon` - `maplibre::geometry::geometry_collection` - - `maplibre::feature::feature` (experimental) + - `maplibre::feature::feature` + +Dependency of [MapLibre Native](https://github.com/maplibre/maplibre-native). ### Design @@ -24,15 +26,15 @@ They should also be a robust and high performance container for data processing - Header-only - Fast compile - - c++11/c++14 compatibility + - C++20 - No external dependencies for usage of core types (point, line_string, etc) - - Minimal dependencies for usage of enclosing `geometry` type (`maplibre::variant`) + - No dependencies for usage of enclosing `geometry` type - Easily [adaptable to `boost::geometry`](http://www.boost.org/doc/libs/1_56_0/libs/geometry/doc/html/geometry/examples/example__adapting_a_legacy_geometry_object_model.html) ### Usage -Using a single type directly (requires no external dependencies): +Using a single type directly: ```cpp #include @@ -46,11 +48,11 @@ int main() { } ``` -Creating a geometry collection (depends on https://github.com/mapbox/variant): +Creating a geometry collection: ```cpp #include -#include +#include #include using maplibre::geometry::geometry_collection; @@ -80,6 +82,6 @@ int main() { gc.emplace_back(point_type(1.0,0.0)); geometry const& geom = gc.at(0); printer visitor; - mapbox::util::apply_visitor(visitor,geom); + std::visit(visitor, geom); } ``` diff --git a/include/maplibre/feature.hpp b/include/maplibre/feature.hpp index f1ef89a..b6f9973 100644 --- a/include/maplibre/feature.hpp +++ b/include/maplibre/feature.hpp @@ -2,10 +2,9 @@ #include -#include - #include #include +#include #include #include #include @@ -13,30 +12,6 @@ namespace maplibre { namespace feature { -// comparator functors -struct equal_comp_shared_ptr -{ - -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wfloat-equal" - template - bool operator()(T const& lhs, T const& rhs) const - { - return lhs == rhs; - } -#pragma GCC diagnostic pop - - template - bool operator()(std::shared_ptr const& lhs, std::shared_ptr const& rhs) const - { - if (lhs == rhs) - { - return true; - } - return *lhs == *rhs; - } -}; - struct value; struct null_value_t @@ -49,25 +24,25 @@ constexpr bool operator<(const null_value_t&, const null_value_t&) { return fals constexpr null_value_t null_value = null_value_t(); -#define DECLARE_VALUE_TYPE_ACCESOR(NAME, TYPE) \ - TYPE* get##NAME() noexcept \ - { \ - return match( \ - [](TYPE& val) -> TYPE* { return &val; }, \ - [](auto&) -> TYPE* { return nullptr; }); \ - } \ - const TYPE* get##NAME() const noexcept \ - { \ - return const_cast(this)->get##NAME(); \ - } +// helper class for std::visit +namespace { +template +struct overloaded : Ts... +{ + using Ts::operator()...; +}; +}; // namespace + +struct value; + // Multiple numeric types (uint64_t, int64_t, double) are present in order to support // the widest possible range of JSON numbers, which do not have a maximum range. // Implementations that produce `value`s should use that order for type preference, // 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>, - std::shared_ptr>>; +using value_base = std::variant>, + std::shared_ptr>>; struct value : public value_base { @@ -105,31 +80,33 @@ struct value : public value_base value(object_type object) : value_base(std::make_shared(std::forward(object))) {} value(object_ptr_type object) : value_base(object) {} - bool operator==(value const& rhs) const + bool operator==(const value& rhs) const { - assert(valid() && rhs.valid()); - if (this->which() != rhs.which()) + if (index() != rhs.index()) return false; + + if (std::holds_alternative(*this)) { - return false; + return *std::get(*this) == *std::get(rhs); } - mapbox::util::detail::comparer visitor(*this); - return visit(rhs, visitor); - } - explicit operator bool() const { return !is(); } + if (std::holds_alternative(*this)) + { + return *std::get(*this) == *std::get(rhs); + } + + return *static_cast(this) == rhs; + } - 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(String, std::string) + explicit operator bool() const { return !std::holds_alternative(variant()); } array_ptr_type getArray() noexcept { - return match( - [](array_ptr_type& val) -> array_ptr_type { return val; }, - [](auto&) -> array_ptr_type { return nullptr; }); + return std::visit(overloaded{ + [](array_ptr_type& val) -> array_ptr_type { return val; }, + [](auto&) -> array_ptr_type { return nullptr; }}, + variant()); } + const_array_ptr_type getArray() const noexcept { return const_cast(this)->getArray(); @@ -137,22 +114,95 @@ struct value : public value_base object_ptr_type getObject() noexcept { - return match( - [](object_ptr_type& val) -> object_ptr_type { return val; }, - [](auto&) -> object_ptr_type { return nullptr; }); + return std::visit(overloaded{ + [](object_ptr_type& val) -> object_ptr_type { return val; }, + [](auto&) -> object_ptr_type { return nullptr; }}, + variant()); } + const_object_ptr_type getObject() const noexcept { return const_cast(this)->getObject(); } -}; -#undef DECLARE_VALUE_TYPE_ACCESOR + value_base& variant() + { + return *static_cast(this); + } + + value_base const& variant() const + { + return *static_cast(this); + } + + int64_t* getInt() noexcept + { + return std::visit(overloaded{ + [](int64_t& val) -> int64_t* { return &val; }, + [](auto&) -> int64_t* { return nullptr; }}, + variant()); + } + + const int64_t* getInt() const noexcept + { + return const_cast(this)->getInt(); + } + + uint64_t* getUint() noexcept + { + return std::visit(overloaded{ + [](uint64_t& val) -> uint64_t* { return &val; }, + [](auto&) -> uint64_t* { return nullptr; }}, + variant()); + } + + const uint64_t* getUint() const noexcept + { + return const_cast(this)->getUint(); + } + + bool* getBool() noexcept + { + return std::visit(overloaded{ + [](bool& val) -> bool* { return &val; }, + [](auto&) -> bool* { return nullptr; }}, + variant()); + } + + const bool* getBool() const noexcept + { + return const_cast(this)->getBool(); + } + double* getDouble() noexcept + { + return std::visit(overloaded{ + [](double& val) -> double* { return &val; }, + [](auto&) -> double* { return nullptr; }}, + variant()); + } + + const double* getDouble() const noexcept + { + return const_cast(this)->getDouble(); + } + std::string* getString() noexcept + { + return std::visit(overloaded{ + [](std::string& val) -> std::string* { return &val; }, + [](auto&) -> std::string* { return nullptr; }}, + variant()); + } + + const std::string* getString() const noexcept + { + return const_cast(this)->getString(); + } +}; using property_map = value::object_type; // The same considerations and requirement for numeric types apply as for `value_base`. -using identifier = mapbox::util::variant; +using identifier = std::variant; template struct feature diff --git a/include/maplibre/geometry/for_each_point.hpp b/include/maplibre/geometry/for_each_point.hpp index 215023a..fd5929d 100644 --- a/include/maplibre/geometry/for_each_point.hpp +++ b/include/maplibre/geometry/for_each_point.hpp @@ -22,19 +22,21 @@ auto for_each_point(Container&& container, F&& f) -> decltype(container.begin(), container.end(), void()); template -void for_each_point(mapbox::util::variant const& geom, F&& f) +void for_each_point(std::variant const& geom, F&& f) { - mapbox::util::variant::visit(geom, [&](auto const& g) { + std::visit([&](auto const& g) { for_each_point(g, f); - }); + }, + geom); } template -void for_each_point(mapbox::util::variant& geom, F&& f) +void for_each_point(std::variant& geom, F&& f) { - mapbox::util::variant::visit(geom, [&](auto& g) { + std::visit([&](auto& g) { for_each_point(g, f); - }); + }, + geom); } template diff --git a/include/maplibre/geometry/geometry.hpp b/include/maplibre/geometry/geometry.hpp index e1e9c26..b8b4427 100644 --- a/include/maplibre/geometry/geometry.hpp +++ b/include/maplibre/geometry/geometry.hpp @@ -8,9 +8,8 @@ #include #include -#include - // stl +#include #include namespace maplibre { @@ -20,14 +19,14 @@ template class Cont = std::vector> struct geometry_collection; template class Cont = std::vector> -using geometry_base = mapbox::util::variant, - line_string, - polygon, - multi_point, - multi_line_string, - multi_polygon, - geometry_collection>; +using geometry_base = std::variant, + line_string, + polygon, + multi_point, + multi_line_string, + multi_polygon, + geometry_collection>; template class Cont = std::vector> struct geometry : geometry_base diff --git a/include/maplibre/geometry/line_string.hpp b/include/maplibre/geometry/line_string.hpp index c5f5370..06c6773 100644 --- a/include/maplibre/geometry/line_string.hpp +++ b/include/maplibre/geometry/line_string.hpp @@ -1,8 +1,7 @@ #pragma once -// mapbox #include -// stl + #include namespace maplibre { diff --git a/include/maplibre/geometry/multi_line_string.hpp b/include/maplibre/geometry/multi_line_string.hpp index 57224e7..8828db2 100644 --- a/include/maplibre/geometry/multi_line_string.hpp +++ b/include/maplibre/geometry/multi_line_string.hpp @@ -1,8 +1,7 @@ #pragma once -// mapbox #include -// stl + #include namespace maplibre { diff --git a/include/maplibre/geometry/multi_point.hpp b/include/maplibre/geometry/multi_point.hpp index 64eeb5b..392834b 100644 --- a/include/maplibre/geometry/multi_point.hpp +++ b/include/maplibre/geometry/multi_point.hpp @@ -1,8 +1,7 @@ #pragma once -// mapbox #include -// stl + #include namespace maplibre { diff --git a/include/maplibre/geometry_io.hpp b/include/maplibre/geometry_io.hpp index aeb9ebe..583eb85 100644 --- a/include/maplibre/geometry_io.hpp +++ b/include/maplibre/geometry_io.hpp @@ -76,7 +76,7 @@ std::ostream& operator<<(std::ostream& os, const multi_polygon& geom) template std::ostream& operator<<(std::ostream& os, const geometry& geom) { - geometry::visit(geom, [&](const auto& g) { os << g; }); + std::visit([&](const auto& g) { os << g; }, geom); return os; } @@ -148,7 +148,7 @@ struct value_to_stream_visitor { out << ','; } - mapbox::util::apply_visitor(*this, item); + std::visit(*this, item); } out << ']'; } @@ -181,7 +181,7 @@ struct value_to_stream_visitor auto const val = map.find(k); quote_string(k, out); out << ':'; - mapbox::util::apply_visitor(*this, val->second); + std::visit(*this, val->second); } out << '}'; } @@ -208,7 +208,7 @@ inline std::ostream& operator<<(std::ostream& os, std::vector()); - CHECK(v.template get() == arg); + CHECK(std::holds_alternative(v)); + CHECK(std::get(v) == arg); } catch (...) { @@ -31,8 +31,8 @@ try { value v{arg}; CHECK(v); - CHECK(v.template is()); - CHECK(*(v.template get()) == *arg); + CHECK(std::holds_alternative(v)); + CHECK(*(std::get(v)) == *arg); } catch (...) { @@ -45,8 +45,8 @@ try { value v{arg}; CHECK(v); - CHECK(v.template is()); - CHECK(*(v.template get()) == arg); + CHECK(std::holds_alternative(v)); + CHECK(*(std::get(v)) == arg); } catch (...) { @@ -73,13 +73,13 @@ TEST_CASE("test value") checkPtrType2(map); value intV{32}; - CHECK_THROWS(intV.get()); + CHECK_THROWS(std::get(intV)); auto* result = intV.getInt(); CHECK(result); CHECK(*result == 32); *result = 100; - CHECK(intV.get() == 100); + CHECK(std::get(intV) == 100); CHECK_FALSE(intV.getUint()); CHECK_FALSE(intV.getBool()); @@ -89,10 +89,29 @@ TEST_CASE("test value") CHECK_FALSE(intV.getString()); } +TEST_CASE("test value operator==") +{ + value true1 = true; + value true2 = true; + value false1 = false; + CHECK(true1 == true2); + CHECK(true1 != false1); + + value::array_type vec = {1, 2, 3}; + value arr1 = std::make_shared(vec); + value arr2 = std::make_shared(vec); + CHECK(arr1 == arr2); + + value::object_type obj = {{"hello", 1}, {"world", true}}; + value obj1 = std::make_shared(obj); + value obj2 = std::make_shared(obj); + CHECK(obj1 == obj2); +} + TEST_CASE("test feature") { feature pf{point()}; - CHECK(pf.geometry.is>()); + CHECK(std::holds_alternative>(pf.geometry)); CHECK(pf.properties.empty()); auto& p = pf.properties; @@ -104,21 +123,22 @@ TEST_CASE("test feature") p["int"] = int64_t(-10); p["null"] = null_value; - REQUIRE(p["bool"].is()); + REQUIRE(std::holds_alternative(p["bool"])); CHECK(p["bool"] == true); - REQUIRE(p["string"].is()); + REQUIRE(std::holds_alternative(p["string"])); CHECK(p["string"] == std::string("foo")); - REQUIRE(p["double"].is()); + REQUIRE(std::holds_alternative(p["double"])); CHECK(p["double"] == 2.5); - REQUIRE(p["uint"].is()); + REQUIRE(std::holds_alternative(p["uint"])); CHECK(p["uint"] == uint64_t(10)); - REQUIRE(p["int"].is()); + REQUIRE(std::holds_alternative(p["int"])); CHECK(p["int"] == int64_t(-10)); - REQUIRE(p["null"].is()); + REQUIRE(std::holds_alternative(p["null"])); CHECK(p["null"] == null_value); p["null"] = null_value_t{}; - REQUIRE(p["null"].is()); + REQUIRE(std::holds_alternative(p["null"])); + CHECK(p["null"] == null_value); CHECK(p == p); diff --git a/test/for_each_point.cpp b/test/for_each_point.cpp index 1b5af7e..939568a 100644 --- a/test/for_each_point.cpp +++ b/test/for_each_point.cpp @@ -47,7 +47,7 @@ TEST_CASE("test for each point") CHECK(g == geometry(polygon({{{0, -1}, {-2, -3}}}))); // Custom geometry type - using my_geometry = mapbox::util::variant>; + using my_geometry = std::variant>; CHECK(count_points(my_geometry(point())) == 1); // Custom point type @@ -57,5 +57,5 @@ TEST_CASE("test for each point") int16_t y; }; CHECK(count_points(std::vector({my_point{0, 1}})) == 1); - CHECK(count_points(mapbox::util::variant(my_point{0, 1})) == 1); + CHECK(count_points(std::variant(my_point{0, 1})) == 1); } diff --git a/test/geometry.cpp b/test/geometry.cpp index d0ccb4f..67e5448 100644 --- a/test/geometry.cpp +++ b/test/geometry.cpp @@ -14,11 +14,11 @@ using maplibre::geometry::polygon; TEST_CASE("test empty in geometry") { geometry g0; - CHECK(g0.is()); + CHECK(std::holds_alternative(g0)); empty n1; geometry g1(n1); - CHECK(g1.is()); + CHECK(std::holds_alternative(g1)); CHECK(g0 == g1); } @@ -27,10 +27,10 @@ TEST_CASE("test point in geometry") { point p1(1, 2); geometry g1(p1); - CHECK(g1.is>()); + CHECK(std::holds_alternative>(g1)); geometry g2(p1); - CHECK(g2.is>()); + CHECK(std::holds_alternative>(g2)); CHECK(g1 == g2); } @@ -41,10 +41,10 @@ TEST_CASE("test multi point in geometry") mp1.emplace_back(1, 2); geometry g1(mp1); - CHECK(g1.is>()); + CHECK(std::holds_alternative>(g1)); geometry g2(mp1); - CHECK(g2.is>()); + CHECK(std::holds_alternative>(g2)); CHECK(g1 == g2); } @@ -56,10 +56,10 @@ TEST_CASE("test line string in geometry") ls1.emplace_back(3, 4); geometry g1(ls1); - CHECK(g1.is>()); + CHECK(std::holds_alternative>(g1)); geometry g2(ls1); - CHECK(g2.is>()); + CHECK(std::holds_alternative>(g2)); CHECK(g1 == g2); } @@ -69,10 +69,10 @@ TEST_CASE("test multi line string in geometry") multi_line_string mls1 = {{{1, 2}, {3, 4}}, {{5, 6}, {7, 8}}}; geometry g1(mls1); - CHECK(g1.is>()); + CHECK(std::holds_alternative>(g1)); geometry g2(mls1); - CHECK(g2.is>()); + CHECK(std::holds_alternative>(g2)); CHECK(g1 == g2); } @@ -82,10 +82,10 @@ TEST_CASE("test polygon in geometry") polygon poly1 = {{{1, 2}, {3, 4}}, {{5, 6}, {7, 8}}}; geometry g1(poly1); - CHECK(g1.is>()); + CHECK(std::holds_alternative>(g1)); geometry g2(poly1); - CHECK(g2.is>()); + CHECK(std::holds_alternative>(g2)); CHECK(g1 == g2); } @@ -95,10 +95,10 @@ TEST_CASE("test multi polygon in geometry") multi_polygon mp1 = {{{{1, 2}, {3, 4}}, {{5, 6}, {7, 8}}}}; geometry g1(mp1); - CHECK(g1.is>()); + CHECK(std::holds_alternative>(g1)); geometry g2(mp1); - CHECK(g2.is>()); + CHECK(std::holds_alternative>(g2)); CHECK(g1 == g2); } @@ -106,28 +106,28 @@ TEST_CASE("test multi polygon in geometry") TEST_CASE("test geometry") { geometry eg; // default constructed geometry is empty - CHECK(eg.is()); + CHECK(std::holds_alternative(eg)); geometry pg{point()}; - CHECK(pg.is>()); + CHECK(std::holds_alternative>(pg)); geometry lsg{line_string()}; - CHECK(lsg.is>()); + CHECK(std::holds_alternative>(lsg)); geometry pgg{polygon()}; - CHECK(pgg.is>()); + CHECK(std::holds_alternative>(pgg)); geometry mpg{multi_point()}; - CHECK(mpg.is>()); + CHECK(std::holds_alternative>(mpg)); geometry mlsg{multi_line_string()}; - CHECK(mlsg.is>()); + CHECK(std::holds_alternative>(mlsg)); geometry mpgg{multi_polygon()}; - CHECK(mpgg.is>()); + CHECK(std::holds_alternative>(mpgg)); geometry gcg{geometry_collection()}; - CHECK(gcg.is>()); + CHECK(std::holds_alternative>(gcg)); CHECK(pg == pg); CHECK(!(pg != pg));