From 0bffc32e8f6fdeec70583253ac79397052511bd1 Mon Sep 17 00:00:00 2001 From: Marius Bancila Date: Sun, 6 Sep 2020 16:05:54 +0300 Subject: [PATCH] support for C++11 --- CMakeLists.txt | 12 ++-- README.md | 2 +- benchmark/CMakeLists.txt | 4 ++ include/croncpp.h | 148 ++++++++++++++++++++++++++++----------- test/CMakeLists.txt | 4 ++ test/test_standard.cpp | 2 +- 6 files changed, 124 insertions(+), 48 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7dd965c..e655d19 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,14 +1,18 @@ -cmake_minimum_required(VERSION 3.8) +cmake_minimum_required(VERSION 3.10) project( croncpp VERSION 0.0.0 LANGUAGES CXX) + +if(NOT DEFINED CXX_STD_VER) + set(CXX_STD_VER cxx_std_17) +endif() add_library(croncpp INTERFACE) target_include_directories( croncpp INTERFACE $ $) -target_compile_features(croncpp INTERFACE cxx_std_17) +target_compile_features(croncpp INTERFACE ${CXX_STD_VER}) add_library(croncpp::croncpp ALIAS croncpp) install( @@ -30,7 +34,7 @@ install( DESTINATION lib/cmake/croncpp) add_library(croncpp_options INTERFACE) -target_compile_features(croncpp_options INTERFACE cxx_std_17) +target_compile_features(croncpp_options INTERFACE ${CXX_STD_VER}) if(WIN32) target_compile_options(croncpp_options INTERFACE "/EHc") @@ -50,7 +54,7 @@ else() endif() if(CRONCPP_BUILD_TESTS) - enable_testing() + enable_testing() add_subdirectory(test) endif() diff --git a/README.md b/README.md index fe44af3..df36a30 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # croncpp -croncpp is a C++17 header-only cross-platform library for handling CRON expressions. It implements two basic operations: parsing an expression and computing the next occurence of the scheduled time. +croncpp is a C++11/14/17 header-only cross-platform library for handling CRON expressions. It implements two basic operations: parsing an expression and computing the next occurence of the scheduled time. [![Build Status](https://travis-ci.org/mariusbancila/croncpp.svg?branch=master)](https://travis-ci.org/mariusbancila/croncpp) [![Tests status](https://ci.appveyor.com/api/projects/status/lewwk7b42xwfp4xy?svg=true&pendingText=tests%20-%20pending&failingText=tests%20-%20FAILED&passingText=tests%20-%20OK)](https://ci.appveyor.com/project/mariusbancila/croncpp) diff --git a/benchmark/CMakeLists.txt b/benchmark/CMakeLists.txt index dd27bd2..b2b6a56 100644 --- a/benchmark/CMakeLists.txt +++ b/benchmark/CMakeLists.txt @@ -1,5 +1,9 @@ set(SOURCES main.cpp) add_executable(bench_croncpp ${SOURCES}) + +target_compile_features(bench_croncpp PUBLIC ${CXX_STD_VER}) +set_target_properties(bench_croncpp PROPERTIES CXX_EXTENSIONS OFF) + target_link_libraries(bench_croncpp PRIVATE croncpp croncpp_options) target_include_directories(bench_croncpp PRIVATE ${CMAKE_SOURCE_DIR}/catch) diff --git a/include/croncpp.h b/include/croncpp.h index 503ec77..e3880ea 100644 --- a/include/croncpp.h +++ b/include/croncpp.h @@ -3,15 +3,30 @@ #include #include #include -#include #include #include #include #include #include +#if __cplusplus > 201402L +#include +#define IS_CPP17 +#endif + namespace cron { +#ifdef IS_CPP17 + #define HAS_STRING_VIEW + #define STRING_VIEW std::string_view + #define STRING_VIEW_NPOS std::string_view::npos + #define CONSTEXPTR constexpr +#else + #define STRING_VIEW std::string const & + #define STRING_VIEW_NPOS std::string::npos + #define CONSTEXPTR +#endif + using cron_int = uint8_t; constexpr std::time_t INVALID_TIME = static_cast(-1); @@ -37,15 +52,12 @@ namespace cron static bool find_next(cronexpr const & cex, std::tm& date, size_t const dot); - - template - constexpr auto& cron_field_ref(cronexpr& cex); } struct bad_cronexpr : public std::runtime_error { public: - explicit bad_cronexpr(std::string_view message) : + explicit bad_cronexpr(STRING_VIEW message) : std::runtime_error(message.data()) {} }; @@ -73,8 +85,22 @@ namespace cron static const cron_int CRON_MAX_YEARS_DIFF = 4; +#ifdef IS_CPP17 static const inline std::vector DAYS = { "SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT" }; static const inline std::vector MONTHS = { "NIL", "JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC" }; +#else + static std::vector& DAYS() + { + static std::vector days = { "SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT" }; + return days; + } + + static std::vector& MONTHS() + { + static std::vector months = { "NIL", "JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC" }; + return months; + } +#endif }; struct cron_oracle_traits @@ -99,8 +125,23 @@ namespace cron static const cron_int CRON_MAX_YEARS_DIFF = 4; +#ifdef IS_CPP17 static const inline std::vector DAYS = { "NIL", "SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT" }; static const inline std::vector MONTHS = { "JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC" }; +#else + + static std::vector& DAYS() + { + static std::vector days = { "NIL", "SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT" }; + return days; + } + + static std::vector& MONTHS() + { + static std::vector months = { "JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC" }; + return months; + } +#endif }; struct cron_quartz_traits @@ -125,8 +166,22 @@ namespace cron static const cron_int CRON_MAX_YEARS_DIFF = 4; +#ifdef IS_CPP17 static const inline std::vector DAYS = { "NIL", "SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT" }; static const inline std::vector MONTHS = { "NIL", "JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC" }; +#else + static std::vector& DAYS() + { + static std::vector days = { "NIL", "SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT" }; + return days; + } + + static std::vector& MONTHS() + { + static std::vector months = { "NIL", "JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC" }; + return months; + } +#endif }; class cronexpr @@ -141,15 +196,15 @@ namespace cron friend bool operator==(cronexpr const & e1, cronexpr const & e2); friend bool operator!=(cronexpr const & e1, cronexpr const & e2); - template - friend constexpr auto& detail::cron_field_ref(cronexpr& cex); - template friend bool detail::find_next(cronexpr const & cex, std::tm& date, size_t const dot); friend std::string to_string(cronexpr const & cex); + + template + friend static cronexpr make_cron(STRING_VIEW expr); }; inline bool operator==(cronexpr const & e1, cronexpr const & e2) @@ -196,7 +251,7 @@ namespace cron #endif } - inline std::tm to_tm(std::string_view time) + inline std::tm to_tm(STRING_VIEW time) { std::istringstream str(time.data()); str.imbue(std::locale(setlocale(LC_ALL, nullptr))); @@ -228,7 +283,7 @@ namespace cron return text; } - static std::vector split(std::string_view text, char const delimiter) + static std::vector split(STRING_VIEW text, char const delimiter) { std::vector tokens; std::string token; @@ -240,16 +295,16 @@ namespace cron return tokens; } - constexpr inline bool contains(std::string_view text, char const ch) noexcept + CONSTEXPTR inline bool contains(STRING_VIEW text, char const ch) noexcept { - return std::string_view::npos != text.find_first_of(ch); + return STRING_VIEW_NPOS != text.find_first_of(ch); } } namespace detail { - inline cron_int to_cron_int(std::string_view text) + inline cron_int to_cron_int(STRING_VIEW text) { try { @@ -276,7 +331,7 @@ namespace cron } static std::pair make_range( - std::string_view field, + STRING_VIEW field, cron_int const minval, cron_int const maxval) { @@ -320,7 +375,7 @@ namespace cron template static void set_cron_field( - std::string_view value, + STRING_VIEW value, std::bitset& target, cron_int const minval, cron_int const maxval) @@ -336,7 +391,13 @@ namespace cron { if (!utils::contains(field, '/')) { +#ifdef IS_CPP17 auto[first, last] = detail::make_range(field, minval, maxval); +#else + auto range = detail::make_range(field, minval, maxval); + auto first = range.first; + auto last = range.second; +#endif for (cron_int i = first - minval; i <= last - minval; ++i) { target.set(i); @@ -348,7 +409,13 @@ namespace cron if (parts.size() != 2) throw bad_cronexpr("Incrementer must have two fields"); +#ifdef IS_CPP17 auto[first, last] = detail::make_range(parts[0], minval, maxval); +#else + auto range = detail::make_range(parts[0], minval, maxval); + auto first = range.first; + auto last = range.second; +#endif if (!utils::contains(parts[0], '-')) { @@ -373,7 +440,14 @@ namespace cron std::bitset<7>& target) { auto days = utils::to_upper(value); - auto days_replaced = detail::replace_ordinals(days, Traits::DAYS); + auto days_replaced = detail::replace_ordinals( + days, +#ifdef IS_CPP17 + Traits::DAYS +#else + Traits::DAYS() +#endif + ); if (days_replaced.size() == 1 && days_replaced[0] == '?') days_replaced[0] = '*'; @@ -406,7 +480,14 @@ namespace cron std::bitset<12>& target) { auto month = utils::to_upper(value); - auto month_replaced = replace_ordinals(month, Traits::MONTHS); + auto month_replaced = replace_ordinals( + month, +#if IS_CPP17 + Traits::MONTHS +#else + Traits::MONTHS() +#endif + ); set_cron_field( month_replaced, @@ -709,27 +790,10 @@ namespace cron return res; } - - template - constexpr auto& cron_field_ref(cronexpr& cex) - { - if constexpr (field == cron_field::day_of_month) - return cex.days_of_month; - else if constexpr (field == cron_field::day_of_week) - return cex.days_of_week; - else if constexpr (field == cron_field::hour_of_day) - return cex.hours; - else if constexpr (field == cron_field::minute) - return cex.minutes; - else if constexpr (field == cron_field::second) - return cex.seconds; - else if constexpr (field == cron_field::month) - return cex.months; - } } template - static cronexpr make_cron(std::string_view expr) + static cronexpr make_cron(STRING_VIEW expr) { cronexpr cex; @@ -739,20 +803,20 @@ namespace cron auto fields = utils::split(expr, ' '); fields.erase( std::remove_if(std::begin(fields), std::end(fields), - [](std::string_view s) {return s.empty(); }), + [](STRING_VIEW s) {return s.empty(); }), std::end(fields)); if (fields.size() != 6) throw bad_cronexpr("cron expression must have six fields"); - detail::set_cron_field(fields[0], detail::cron_field_ref(cex), Traits::CRON_MIN_SECONDS, Traits::CRON_MAX_SECONDS); - detail::set_cron_field(fields[1], detail::cron_field_ref(cex), Traits::CRON_MIN_MINUTES, Traits::CRON_MAX_MINUTES); - detail::set_cron_field(fields[2], detail::cron_field_ref(cex), Traits::CRON_MIN_HOURS, Traits::CRON_MAX_HOURS); + detail::set_cron_field(fields[0], cex.seconds, Traits::CRON_MIN_SECONDS, Traits::CRON_MAX_SECONDS); + detail::set_cron_field(fields[1], cex.minutes, Traits::CRON_MIN_MINUTES, Traits::CRON_MAX_MINUTES); + detail::set_cron_field(fields[2], cex.hours, Traits::CRON_MIN_HOURS, Traits::CRON_MAX_HOURS); - detail::set_cron_days_of_week(fields[5], detail::cron_field_ref(cex)); + detail::set_cron_days_of_week(fields[5], cex.days_of_week); - detail::set_cron_days_of_month(fields[3], detail::cron_field_ref(cex)); + detail::set_cron_days_of_month(fields[3], cex.days_of_month); - detail::set_cron_month(fields[4], detail::cron_field_ref(cex)); + detail::set_cron_month(fields[4], cex.months); return cex; } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 7150485..51c7fc0 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,6 +1,10 @@ set(SOURCES main.cpp test_oracle.cpp test_standard.cpp) add_executable(test_croncpp ${SOURCES}) + +target_compile_features(test_croncpp PUBLIC ${CXX_STD_VER}) +set_target_properties(test_croncpp PROPERTIES CXX_EXTENSIONS OFF) + target_link_libraries(test_croncpp PRIVATE croncpp croncpp_options) target_include_directories(test_croncpp PRIVATE ${CMAKE_SOURCE_DIR}/catch) diff --git a/test/test_standard.cpp b/test/test_standard.cpp index 388db49..37fa3bc 100644 --- a/test/test_standard.cpp +++ b/test/test_standard.cpp @@ -39,7 +39,7 @@ void check_next(std::string_view expr, std::string_view time, std::string_view e REQUIRE(value == expected); } */ -void check_next(std::string_view expr, std::string_view time, std::string_view expected) +void check_next(STRING_VIEW expr, STRING_VIEW time, STRING_VIEW expected) { auto cex = make_cron(expr);