|
|
@ -0,0 +1,442 @@ |
|
|
|
From 8e9679af05f0132e2934f8d7e1bfbaf36dc57cd7 Mon Sep 17 00:00:00 2001 |
|
|
|
From: =?UTF-8?q?Torbj=C3=B6rn=20Klatt?= <t.klatt.oss@mailbox.org> |
|
|
|
Date: Tue, 27 Nov 2018 20:56:02 +0100 |
|
|
|
Subject: [PATCH] switch boost::variant to std::variant and to C++17 |
|
|
|
|
|
|
|
This is based on work by @mbits-libs as published on Github: |
|
|
|
mbits-libs/libmstch@747c5eed3ddbff94a0354f1b1d217f547867ef27 |
|
|
|
|
|
|
|
The `is_node_empty` type had to be extended to detect the std::variant's |
|
|
|
default type `std::monostate` as an empty value. This change made all |
|
|
|
the unit tests pass. |
|
|
|
---
|
|
|
|
CMakeLists.txt | 6 ++- |
|
|
|
include/mstch/mstch.hpp | 41 ++++++++++++++++---- |
|
|
|
src/CMakeLists.txt | 6 +-- |
|
|
|
src/utils.hpp | 13 ++++--- |
|
|
|
src/visitor/get_token.hpp | 4 +- |
|
|
|
src/visitor/has_token.hpp | 4 +- |
|
|
|
src/visitor/is_node_empty.hpp | 16 +++++--- |
|
|
|
src/visitor/render_node.hpp | 69 +++++++++++++++------------------- |
|
|
|
src/visitor/render_section.hpp | 48 ++++++++++++----------- |
|
|
|
test/CMakeLists.txt | 3 -- |
|
|
|
test/test_main.cpp | 2 +- |
|
|
|
11 files changed, 115 insertions(+), 97 deletions(-) |
|
|
|
|
|
|
|
diff --git a/CMakeLists.txt b/CMakeLists.txt
|
|
|
|
index 7ee9f01..9df666f 100644
|
|
|
|
--- a/CMakeLists.txt
|
|
|
|
+++ b/CMakeLists.txt
|
|
|
|
@@ -7,10 +7,12 @@ option(WITH_BENCHMARK "enable building benchmark executable" OFF)
|
|
|
|
set(CMAKE_INCLUDE_CURRENT_DIR ON) |
|
|
|
set(CMAKE_INCLUDE_CURRENT_DIR_IN_INTERFACE ON) |
|
|
|
|
|
|
|
-set(mstch_VERSION 1.0.1)
|
|
|
|
+set(mstch_VERSION 1.0.2)
|
|
|
|
|
|
|
|
if(NOT MSVC) |
|
|
|
- add_compile_options(-Wall -Wextra)
|
|
|
|
+ add_compile_options(-std=c++17 -Wall -Wextra -O3)
|
|
|
|
+else()
|
|
|
|
+ add_compile_options(/std:c++latest)
|
|
|
|
endif() |
|
|
|
|
|
|
|
add_subdirectory(src) |
|
|
|
diff --git a/include/mstch/mstch.hpp b/include/mstch/mstch.hpp
|
|
|
|
index 0af9e4d..2ea9855 100644
|
|
|
|
--- a/include/mstch/mstch.hpp
|
|
|
|
+++ b/include/mstch/mstch.hpp
|
|
|
|
@@ -5,8 +5,7 @@
|
|
|
|
#include <string> |
|
|
|
#include <memory> |
|
|
|
#include <functional> |
|
|
|
-
|
|
|
|
-#include <boost/variant.hpp>
|
|
|
|
+#include <variant>
|
|
|
|
|
|
|
|
namespace mstch { |
|
|
|
|
|
|
|
@@ -93,12 +92,38 @@ class lambda_t {
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
-using node = boost::make_recursive_variant<
|
|
|
|
- std::nullptr_t, std::string, int, double, bool,
|
|
|
|
- internal::lambda_t<boost::recursive_variant_>,
|
|
|
|
- std::shared_ptr<internal::object_t<boost::recursive_variant_>>,
|
|
|
|
- std::map<std::string, boost::recursive_variant_>,
|
|
|
|
- std::vector<boost::recursive_variant_>>::type;
|
|
|
|
+struct node : std::variant<
|
|
|
|
+ std::monostate, std::nullptr_t, std::string, int, unsigned int, double, bool,
|
|
|
|
+ internal::lambda_t<node>,
|
|
|
|
+ std::shared_ptr<internal::object_t<node>>,
|
|
|
|
+ std::map<const std::string, node>,
|
|
|
|
+ std::vector<node>
|
|
|
|
+> {
|
|
|
|
+ using empty_type = std::monostate;
|
|
|
|
+ using lambda_type = internal::lambda_t<node>;
|
|
|
|
+ using shared_ptr_type = std::shared_ptr<internal::object_t<node>>;
|
|
|
|
+ using map_type = std::map<const std::string, node>;
|
|
|
|
+ using vector_type = std::vector<node>;
|
|
|
|
+
|
|
|
|
+ using base_type = std::variant<
|
|
|
|
+ std::monostate, std::nullptr_t, std::string, int, unsigned int, double, bool,
|
|
|
|
+ internal::lambda_t<node>,
|
|
|
|
+ std::shared_ptr<internal::object_t<node>>,
|
|
|
|
+ std::map<const std::string, node>,
|
|
|
|
+ std::vector<node>
|
|
|
|
+ >;
|
|
|
|
+
|
|
|
|
+ using base_type::base_type;
|
|
|
|
+
|
|
|
|
+ node() : base_type(std::in_place_type<std::monostate>) {}
|
|
|
|
+
|
|
|
|
+ explicit node(const char* value) : base_type(std::in_place_type<std::string>, value) {}
|
|
|
|
+
|
|
|
|
+ base_type& base() { return *this; }
|
|
|
|
+ base_type const& base() const { return *this; }
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+using empty = std::monostate;
|
|
|
|
using object = internal::object_t<node>; |
|
|
|
using lambda = internal::lambda_t<node>; |
|
|
|
using map = std::map<std::string, node>; |
|
|
|
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
|
|
|
|
index 6517fc4..22d4f41 100644
|
|
|
|
--- a/src/CMakeLists.txt
|
|
|
|
+++ b/src/CMakeLists.txt
|
|
|
|
@@ -1,11 +1,8 @@
|
|
|
|
-find_package(Boost 1.54 REQUIRED)
|
|
|
|
-
|
|
|
|
set(mstch_INCLUDE_DIR |
|
|
|
${PROJECT_SOURCE_DIR}/include CACHE STRING "mstch include directory") |
|
|
|
|
|
|
|
include_directories( |
|
|
|
- ${mstch_INCLUDE_DIR}
|
|
|
|
- ${Boost_INCLUDE_DIR})
|
|
|
|
+ ${mstch_INCLUDE_DIR})
|
|
|
|
|
|
|
|
set(SRC |
|
|
|
state/in_section.cpp |
|
|
|
@@ -62,3 +59,4 @@ install(FILES
|
|
|
|
"${CMAKE_CURRENT_BINARY_DIR}/mstch/mstch-config-version.cmake" |
|
|
|
DESTINATION lib/cmake/mstch |
|
|
|
COMPONENT Devel) |
|
|
|
+
|
|
|
|
diff --git a/src/utils.hpp b/src/utils.hpp
|
|
|
|
index 9041a3e..ea19029 100644
|
|
|
|
--- a/src/utils.hpp
|
|
|
|
+++ b/src/utils.hpp
|
|
|
|
@@ -1,7 +1,8 @@
|
|
|
|
#pragma once |
|
|
|
|
|
|
|
#include <string> |
|
|
|
-#include <boost/variant/apply_visitor.hpp>
|
|
|
|
+#include <vector>
|
|
|
|
+#include <variant>
|
|
|
|
|
|
|
|
namespace mstch { |
|
|
|
|
|
|
|
@@ -13,11 +14,13 @@ citer first_not_ws(criter begin, criter end);
|
|
|
|
std::string html_escape(const std::string& str); |
|
|
|
criter reverse(citer it); |
|
|
|
|
|
|
|
-template<class... Args>
|
|
|
|
-auto visit(Args&&... args) -> decltype(boost::apply_visitor(
|
|
|
|
- std::forward<Args>(args)...))
|
|
|
|
+template<class Visitor, class... Visited>
|
|
|
|
+decltype(auto) visit(Visitor&& visitor, Visited&&... nodes)
|
|
|
|
{ |
|
|
|
- return boost::apply_visitor(std::forward<Args>(args)...);
|
|
|
|
+ return std::visit(std::forward<Visitor>(visitor), nodes.base()...);
|
|
|
|
} |
|
|
|
|
|
|
|
+template <typename T, typename U>
|
|
|
|
+constexpr bool is_v = std::is_same_v<T, U>;
|
|
|
|
+
|
|
|
|
} |
|
|
|
diff --git a/src/visitor/get_token.hpp b/src/visitor/get_token.hpp
|
|
|
|
index d41ab6e..f7b1714 100644
|
|
|
|
--- a/src/visitor/get_token.hpp
|
|
|
|
+++ b/src/visitor/get_token.hpp
|
|
|
|
@@ -1,13 +1,11 @@
|
|
|
|
#pragma once |
|
|
|
|
|
|
|
-#include <boost/variant/static_visitor.hpp>
|
|
|
|
-
|
|
|
|
#include "mstch/mstch.hpp" |
|
|
|
#include "has_token.hpp" |
|
|
|
|
|
|
|
namespace mstch { |
|
|
|
|
|
|
|
-class get_token: public boost::static_visitor<const mstch::node&> {
|
|
|
|
+class get_token {
|
|
|
|
public: |
|
|
|
get_token(const std::string& token, const mstch::node& node): |
|
|
|
m_token(token), m_node(node) |
|
|
|
diff --git a/src/visitor/has_token.hpp b/src/visitor/has_token.hpp
|
|
|
|
index 5ab30d4..0076f97 100644
|
|
|
|
--- a/src/visitor/has_token.hpp
|
|
|
|
+++ b/src/visitor/has_token.hpp
|
|
|
|
@@ -1,12 +1,10 @@
|
|
|
|
#pragma once |
|
|
|
|
|
|
|
-#include <boost/variant/static_visitor.hpp>
|
|
|
|
-
|
|
|
|
#include "mstch/mstch.hpp" |
|
|
|
|
|
|
|
namespace mstch { |
|
|
|
|
|
|
|
-class has_token: public boost::static_visitor<bool> {
|
|
|
|
+class has_token {
|
|
|
|
public: |
|
|
|
has_token(const std::string& token): m_token(token) { |
|
|
|
} |
|
|
|
diff --git a/src/visitor/is_node_empty.hpp b/src/visitor/is_node_empty.hpp
|
|
|
|
index a0ae432..d7434e7 100644
|
|
|
|
--- a/src/visitor/is_node_empty.hpp
|
|
|
|
+++ b/src/visitor/is_node_empty.hpp
|
|
|
|
@@ -1,18 +1,20 @@
|
|
|
|
#pragma once |
|
|
|
|
|
|
|
-#include <boost/variant/static_visitor.hpp>
|
|
|
|
-
|
|
|
|
#include "mstch/mstch.hpp" |
|
|
|
|
|
|
|
namespace mstch { |
|
|
|
|
|
|
|
-class is_node_empty: public boost::static_visitor<bool> {
|
|
|
|
+class is_node_empty {
|
|
|
|
public: |
|
|
|
template<class T> |
|
|
|
bool operator()(const T&) const { |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
+ bool operator()(const empty&) const {
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
bool operator()(const std::nullptr_t&) const { |
|
|
|
return true; |
|
|
|
} |
|
|
|
@@ -21,6 +23,10 @@ class is_node_empty: public boost::static_visitor<bool> {
|
|
|
|
return value == 0; |
|
|
|
} |
|
|
|
|
|
|
|
+ bool operator()(const unsigned int& value) const {
|
|
|
|
+ return value == 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
bool operator()(const double& value) const { |
|
|
|
return value == 0; |
|
|
|
} |
|
|
|
@@ -30,11 +36,11 @@ class is_node_empty: public boost::static_visitor<bool> {
|
|
|
|
} |
|
|
|
|
|
|
|
bool operator()(const std::string& value) const { |
|
|
|
- return value == "";
|
|
|
|
+ return value.empty();
|
|
|
|
} |
|
|
|
|
|
|
|
bool operator()(const array& array) const { |
|
|
|
- return array.size() == 0;
|
|
|
|
+ return array.empty();
|
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
diff --git a/src/visitor/render_node.hpp b/src/visitor/render_node.hpp
|
|
|
|
index d5d5b40..d8f5315 100644
|
|
|
|
--- a/src/visitor/render_node.hpp
|
|
|
|
+++ b/src/visitor/render_node.hpp
|
|
|
|
@@ -1,7 +1,6 @@
|
|
|
|
#pragma once |
|
|
|
|
|
|
|
#include <sstream> |
|
|
|
-#include <boost/variant/static_visitor.hpp>
|
|
|
|
|
|
|
|
#include "render_context.hpp" |
|
|
|
#include "mstch/mstch.hpp" |
|
|
|
@@ -9,47 +8,41 @@
|
|
|
|
|
|
|
|
namespace mstch { |
|
|
|
|
|
|
|
-class render_node: public boost::static_visitor<std::string> {
|
|
|
|
- public:
|
|
|
|
+class render_node {
|
|
|
|
+public:
|
|
|
|
enum class flag { none, escape_html }; |
|
|
|
- render_node(render_context& ctx, flag p_flag = flag::none):
|
|
|
|
- m_ctx(ctx), m_flag(p_flag)
|
|
|
|
+ render_node(render_context& ctx, flag p_flag = flag::none) :
|
|
|
|
+ m_ctx(ctx), m_flag(p_flag)
|
|
|
|
{ |
|
|
|
} |
|
|
|
|
|
|
|
- template<class T>
|
|
|
|
- std::string operator()(const T&) const {
|
|
|
|
- return "";
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- std::string operator()(const int& value) const {
|
|
|
|
- return std::to_string(value);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- std::string operator()(const double& value) const {
|
|
|
|
- std::stringstream ss;
|
|
|
|
- ss << value;
|
|
|
|
- return ss.str();
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- std::string operator()(const bool& value) const {
|
|
|
|
- return value ? "true" : "false";
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- std::string operator()(const lambda& value) const {
|
|
|
|
- template_type interpreted{value([this](const mstch::node& n) {
|
|
|
|
- return mstch::visit(render_node(m_ctx), n);
|
|
|
|
- })};
|
|
|
|
- auto rendered = render_context::push(m_ctx).render(interpreted);
|
|
|
|
- return (m_flag == flag::escape_html) ? html_escape(rendered) : rendered;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- std::string operator()(const std::string& value) const {
|
|
|
|
- return (m_flag == flag::escape_html) ? html_escape(value) : value;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- private:
|
|
|
|
- render_context& m_ctx;
|
|
|
|
+ template <typename T>
|
|
|
|
+ std::string operator()(const T& value) const {
|
|
|
|
+ if constexpr(is_v<T, int>) {
|
|
|
|
+ return std::to_string(value);
|
|
|
|
+ } else if constexpr(is_v<T, unsigned int>) {
|
|
|
|
+ return std::to_string(value);
|
|
|
|
+ } else if constexpr(is_v<T, double>) {
|
|
|
|
+ std::stringstream ss;
|
|
|
|
+ ss << value;
|
|
|
|
+ return ss.str();
|
|
|
|
+ } else if constexpr(is_v<T, bool>) {
|
|
|
|
+ return value ? "true" : "false";
|
|
|
|
+ } else if constexpr(is_v<T, lambda>) {
|
|
|
|
+ template_type interpreted{ value([this](const mstch::node& n) {
|
|
|
|
+ return mstch::visit(render_node(m_ctx), n);
|
|
|
|
+ }) };
|
|
|
|
+ auto rendered = render_context::push(m_ctx).render(interpreted);
|
|
|
|
+ return (m_flag == flag::escape_html) ? html_escape(rendered) : rendered;
|
|
|
|
+ } else if constexpr(is_v<T, std::string>) {
|
|
|
|
+ return (m_flag == flag::escape_html) ? html_escape(value) : value;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return {};
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+private:
|
|
|
|
+ render_context & m_ctx;
|
|
|
|
flag m_flag; |
|
|
|
}; |
|
|
|
|
|
|
|
diff --git a/src/visitor/render_section.hpp b/src/visitor/render_section.hpp
|
|
|
|
index ac753e3..0c08c17 100644
|
|
|
|
--- a/src/visitor/render_section.hpp
|
|
|
|
+++ b/src/visitor/render_section.hpp
|
|
|
|
@@ -1,7 +1,5 @@
|
|
|
|
#pragma once |
|
|
|
|
|
|
|
-#include <boost/variant/static_visitor.hpp>
|
|
|
|
-
|
|
|
|
#include "render_context.hpp" |
|
|
|
#include "mstch/mstch.hpp" |
|
|
|
#include "utils.hpp" |
|
|
|
@@ -9,7 +7,7 @@
|
|
|
|
|
|
|
|
namespace mstch { |
|
|
|
|
|
|
|
-class render_section: public boost::static_visitor<std::string> {
|
|
|
|
+class render_section {
|
|
|
|
public: |
|
|
|
enum class flag { none, keep_array }; |
|
|
|
render_section( |
|
|
|
@@ -22,29 +20,29 @@ class render_section: public boost::static_visitor<std::string> {
|
|
|
|
} |
|
|
|
|
|
|
|
template<class T> |
|
|
|
- std::string operator()(const T& t) const {
|
|
|
|
- return render_context::push(m_ctx, t).render(m_section);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- std::string operator()(const lambda& fun) const {
|
|
|
|
- std::string section_str;
|
|
|
|
- for (auto& token: m_section)
|
|
|
|
- section_str += token.raw();
|
|
|
|
- template_type interpreted{fun([this](const mstch::node& n) {
|
|
|
|
- return mstch::visit(render_node(m_ctx), n);
|
|
|
|
- }, section_str), m_delims};
|
|
|
|
- return render_context::push(m_ctx).render(interpreted);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- std::string operator()(const array& array) const {
|
|
|
|
- std::string out;
|
|
|
|
- if (m_flag == flag::keep_array)
|
|
|
|
- return render_context::push(m_ctx, array).render(m_section);
|
|
|
|
- else
|
|
|
|
- for (auto& item: array)
|
|
|
|
- out += mstch::visit(render_section(
|
|
|
|
+ std::string operator()(const T& value) const {
|
|
|
|
+ if constexpr(is_v<T, lambda>) {
|
|
|
|
+ std::string section_str;
|
|
|
|
+ for (auto& token : m_section) {
|
|
|
|
+ section_str += token.raw();
|
|
|
|
+ }
|
|
|
|
+ template_type interpreted{ value([this](const mstch::node& n) {
|
|
|
|
+ return mstch::visit(render_node(m_ctx), n);
|
|
|
|
+ }, section_str), m_delims };
|
|
|
|
+ return render_context::push(m_ctx).render(interpreted);
|
|
|
|
+ } else if constexpr(is_v<T, array>) {
|
|
|
|
+ std::string out;
|
|
|
|
+ if (m_flag == flag::keep_array) {
|
|
|
|
+ return render_context::push(m_ctx, value).render(m_section);
|
|
|
|
+ } else {
|
|
|
|
+ for (auto& item: value) {
|
|
|
|
+ out += mstch::visit(render_section(
|
|
|
|
m_ctx, m_section, m_delims, flag::keep_array), item); |
|
|
|
- return out;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return out;
|
|
|
|
+ }
|
|
|
|
+ return render_context::push(m_ctx, value).render(m_section);
|
|
|
|
} |
|
|
|
|
|
|
|
private: |
|
|
|
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
|
|
|
|
index 061bc2e..b290b65 100644
|
|
|
|
--- a/test/CMakeLists.txt
|
|
|
|
+++ b/test/CMakeLists.txt
|
|
|
|
@@ -1,10 +1,7 @@
|
|
|
|
-find_package(Boost 1.54 REQUIRED)
|
|
|
|
-
|
|
|
|
include_directories( |
|
|
|
${CMAKE_SOURCE_DIR}/include |
|
|
|
${CMAKE_SOURCE_DIR}/vendor/Catch/single_include |
|
|
|
${CMAKE_SOURCE_DIR}/vendor/rapidjson/include |
|
|
|
- ${Boost_INCLUDE_DIR})
|
|
|
|
|
|
|
|
file(GLOB data_files RELATIVE |
|
|
|
"${CMAKE_SOURCE_DIR}/test/data" |
|
|
|
diff --git a/test/test_main.cpp b/test/test_main.cpp
|
|
|
|
index a52fe3c..d823a24 100644
|
|
|
|
--- a/test/test_main.cpp
|
|
|
|
+++ b/test/test_main.cpp
|
|
|
|
@@ -65,7 +65,7 @@ mstch::node parse_with_rapidjson(const std::string& str) {
|
|
|
|
} |
|
|
|
|
|
|
|
#define SPECS_TEST(x) TEST_CASE("specs_" #x) { \ |
|
|
|
- using boost::get; \
|
|
|
|
+ using std::get; \
|
|
|
|
auto data = parse_with_rapidjson(x ## _json); \ |
|
|
|
for (auto& test_item: get<mstch::array>(get<mstch::map>(data)["tests"])) {\ |
|
|
|
auto test = get<mstch::map>(test_item); \ |
|
|
|
--
|
|
|
|
2.17.1 |
|
|
|
|