Skip to content

Commit

Permalink
Replace mapbox/variant.hpp with std::variant (#1)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
louwers authored Oct 17, 2024
1 parent 1297004 commit 99ec1d9
Show file tree
Hide file tree
Showing 13 changed files with 208 additions and 147 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ on:
- main

jobs:
pre_job:
ci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
Expand Down
11 changes: 1 addition & 10 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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)

Expand Down Expand Up @@ -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
Expand Down
16 changes: 9 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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 <maplibre/geometry/point.hpp>
Expand All @@ -46,11 +48,11 @@ int main() {
}
```

Creating a geometry collection (depends on https://github.com/mapbox/variant):
Creating a geometry collection:

```cpp
#include <maplibre/geometry/geometry.hpp>
#include <maplibre/variant.hpp>
#include <variant>
#include <iostream>

using maplibre::geometry::geometry_collection;
Expand Down Expand Up @@ -80,6 +82,6 @@ int main() {
gc.emplace_back(point_type(1.0,0.0));
geometry<double> const& geom = gc.at(0);
printer visitor;
mapbox::util::apply_visitor(visitor,geom);
std::visit(visitor, geom);
}
```
174 changes: 112 additions & 62 deletions include/maplibre/feature.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,41 +2,16 @@

#include <maplibre/geometry.hpp>

#include <mapbox/variant.hpp>

#include <cstdint>
#include <string>
#include <variant>
#include <vector>
#include <memory>
#include <unordered_map>

namespace maplibre {
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 All @@ -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<value*>(this)->get##NAME(); \
}
// helper class for std::visit
namespace {
template <class... Ts>
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<null_value_t, bool, uint64_t, int64_t, double, std::string,
std::shared_ptr<std::vector<value>>,
std::shared_ptr<std::unordered_map<std::string, value>>>;
using value_base = std::variant<null_value_t, bool, uint64_t, int64_t, double, std::string,
std::shared_ptr<std::vector<value>>,
std::shared_ptr<std::unordered_map<std::string, value>>>;

struct value : public value_base
{
Expand Down Expand Up @@ -105,54 +80,129 @@ struct value : public value_base
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
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<value::array_ptr_type>(*this))
{
return false;
return *std::get<value::array_ptr_type>(*this) == *std::get<value::array_ptr_type>(rhs);
}
mapbox::util::detail::comparer<value, equal_comp_shared_ptr> visitor(*this);
return visit(rhs, visitor);
}

explicit operator bool() const { return !is<null_value_t>(); }
if (std::holds_alternative<value::object_ptr_type>(*this))
{
return *std::get<value::object_ptr_type>(*this) == *std::get<value::object_ptr_type>(rhs);
}

return *static_cast<const value_base*>(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<null_value_t>(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<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; });
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<value*>(this)->getObject();
}
};

#undef DECLARE_VALUE_TYPE_ACCESOR
value_base& variant()
{
return *static_cast<value_base*>(this);
}

value_base const& variant() const
{
return *static_cast<const value_base*>(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<value*>(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<value*>(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<value*>(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<value*>(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<value*>(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<null_value_t, uint64_t, int64_t, double, std::string>;
using identifier = std::variant<null_value_t, uint64_t, int64_t, double, std::string>;

template <class T>
struct feature
Expand Down
14 changes: 8 additions & 6 deletions include/maplibre/geometry/for_each_point.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,21 @@ auto for_each_point(Container&& container, F&& f)
-> decltype(container.begin(), container.end(), void());

template <typename... Types, typename F>
void for_each_point(mapbox::util::variant<Types...> const& geom, F&& f)
void for_each_point(std::variant<Types...> const& geom, F&& f)
{
mapbox::util::variant<Types...>::visit(geom, [&](auto const& g) {
std::visit([&](auto const& g) {
for_each_point(g, f);
});
},
geom);
}

template <typename... Types, typename F>
void for_each_point(mapbox::util::variant<Types...>& geom, F&& f)
void for_each_point(std::variant<Types...>& geom, F&& f)
{
mapbox::util::variant<Types...>::visit(geom, [&](auto& g) {
std::visit([&](auto& g) {
for_each_point(g, f);
});
},
geom);
}

template <typename Container, typename F>
Expand Down
19 changes: 9 additions & 10 deletions include/maplibre/geometry/geometry.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@
#include <maplibre/geometry/multi_line_string.hpp>
#include <maplibre/geometry/multi_polygon.hpp>

#include <mapbox/variant.hpp>

// stl
#include <variant>
#include <vector>

namespace maplibre {
Expand All @@ -20,14 +19,14 @@ template <typename T, template <typename...> class Cont = std::vector>
struct geometry_collection;

template <typename T, template <typename...> class Cont = std::vector>
using geometry_base = mapbox::util::variant<empty,
point<T>,
line_string<T, Cont>,
polygon<T, Cont>,
multi_point<T, Cont>,
multi_line_string<T, Cont>,
multi_polygon<T, Cont>,
geometry_collection<T, Cont>>;
using geometry_base = std::variant<empty,
point<T>,
line_string<T, Cont>,
polygon<T, Cont>,
multi_point<T, Cont>,
multi_line_string<T, Cont>,
multi_polygon<T, Cont>,
geometry_collection<T, Cont>>;

template <typename T, template <typename...> class Cont = std::vector>
struct geometry : geometry_base<T, Cont>
Expand Down
3 changes: 1 addition & 2 deletions include/maplibre/geometry/line_string.hpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
#pragma once

// mapbox
#include <maplibre/geometry/point.hpp>
// stl

#include <vector>

namespace maplibre {
Expand Down
Loading

0 comments on commit 99ec1d9

Please sign in to comment.