From 8e9679af05f0132e2934f8d7e1bfbaf36dc57cd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torbj=C3=B6rn=20Klatt?= 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 #include #include - -#include +#include 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, - std::shared_ptr>, - std::map, - std::vector>::type; +struct node : std::variant< + std::monostate, std::nullptr_t, std::string, int, unsigned int, double, bool, + internal::lambda_t, + std::shared_ptr>, + std::map, + std::vector +> { + using empty_type = std::monostate; + using lambda_type = internal::lambda_t; + using shared_ptr_type = std::shared_ptr>; + using map_type = std::map; + using vector_type = std::vector; + + using base_type = std::variant< + std::monostate, std::nullptr_t, std::string, int, unsigned int, double, bool, + internal::lambda_t, + std::shared_ptr>, + std::map, + std::vector + >; + + using base_type::base_type; + + node() : base_type(std::in_place_type) {} + + explicit node(const char* value) : base_type(std::in_place_type, value) {} + + base_type& base() { return *this; } + base_type const& base() const { return *this; } +}; + +using empty = std::monostate; using object = internal::object_t; using lambda = internal::lambda_t; using map = std::map; 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 -#include +#include +#include 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 -auto visit(Args&&... args) -> decltype(boost::apply_visitor( - std::forward(args)...)) +template +decltype(auto) visit(Visitor&& visitor, Visited&&... nodes) { - return boost::apply_visitor(std::forward(args)...); + return std::visit(std::forward(visitor), nodes.base()...); } +template +constexpr bool is_v = std::is_same_v; + } 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 - #include "mstch/mstch.hpp" #include "has_token.hpp" namespace mstch { -class get_token: public boost::static_visitor { +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 - #include "mstch/mstch.hpp" namespace mstch { -class has_token: public boost::static_visitor { +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 - #include "mstch/mstch.hpp" namespace mstch { -class is_node_empty: public boost::static_visitor { +class is_node_empty { public: template 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 { 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 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 -#include #include "render_context.hpp" #include "mstch/mstch.hpp" @@ -9,47 +8,41 @@ namespace mstch { -class render_node: public boost::static_visitor { - 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 - 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 + std::string operator()(const T& value) const { + if constexpr(is_v) { + return std::to_string(value); + } else if constexpr(is_v) { + return std::to_string(value); + } else if constexpr(is_v) { + std::stringstream ss; + ss << value; + return ss.str(); + } else if constexpr(is_v) { + return value ? "true" : "false"; + } else if constexpr(is_v) { + 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) { + 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 - #include "render_context.hpp" #include "mstch/mstch.hpp" #include "utils.hpp" @@ -9,7 +7,7 @@ namespace mstch { -class render_section: public boost::static_visitor { +class render_section { public: enum class flag { none, keep_array }; render_section( @@ -22,29 +20,29 @@ class render_section: public boost::static_visitor { } template - 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) { + 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) { + 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(get(data)["tests"])) {\ auto test = get(test_item); \ -- 2.17.1