From e6e37bb138b08a4074085f8292c24f64183010de Mon Sep 17 00:00:00 2001 From: builder Date: Wed, 17 Jul 2024 17:57:05 +0000 Subject: [PATCH] Update to SDK 2.13.1 This commit adds the following changes: * Update to SDK 2.13.1 * Add new NetworkConfiguration sample * Update all ArUco samples to use Zivid API. This removes openCV contrib dependency. * Use `Calibration::detectCalibrationBoard(camera)` function instead of `Calibration::detectFeaturePoints(camera)` --- README.md | 9 +- .../platform-dependent/ubuntu-20.04/setup.sh | 2 +- source/3rd-party/clipp/include/clipp.h | 11067 ++++++++-------- .../HandEyeCalibration/HandEyeCalibration.cpp | 7 +- .../MultiCameraCalibration.cpp | 2 +- .../MultiCameraCalibrationFromZDF.cpp | 2 +- .../MultiCamera/MultiCameraTutorial.md | 8 +- .../ROIBoxViaArucoMarker.cpp | 199 +- .../ROIBoxViaCheckerboard.cpp | 6 +- .../ReprojectPoints/ReprojectPoints.cpp | 6 +- .../TransformPointCloudViaArucoMarker.cpp | 240 +- .../TransformPointCloudViaCheckerboard.cpp | 3 +- source/Applications/PointCloudTutorial.md | 69 +- source/CMakeLists.txt | 37 +- source/Camera/Basic/CaptureTutorial.md | 3 - .../NetworkConfiguration.cpp | 108 + source/Camera/InfoUtilOther/Warmup/Warmup.cpp | 12 +- .../ZividBenchmark/ZividBenchmark.cpp | 78 +- .../CorrectCameraInField.cpp | 3 +- .../VerifyCameraInField.cpp | 3 +- .../VerifyCameraInFieldFromZDF.cpp | 5 +- 21 files changed, 5640 insertions(+), 6229 deletions(-) create mode 100644 source/Camera/InfoUtilOther/NetworkConfiguration/NetworkConfiguration.cpp diff --git a/README.md b/README.md index f01d8927..a73b96a6 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # C++ samples -This repository contains cpp code samples for Zivid SDK v2.12.0. For +This repository contains cpp code samples for Zivid SDK v2.13.1. For tested compatibility with earlier SDK versions, please check out [accompanying releases](https://github.com/zivid/zivid-cpp-samples/tree/master/../../releases). @@ -80,6 +80,8 @@ from the camera can be used. - [FrameInfo](https://github.com/zivid/zivid-cpp-samples/tree/master/source/Camera/InfoUtilOther/FrameInfo/FrameInfo.cpp) - Read frame info from the Zivid camera. - [GetCameraIntrinsics](https://github.com/zivid/zivid-cpp-samples/tree/master/source/Camera/InfoUtilOther/GetCameraIntrinsics/GetCameraIntrinsics.cpp) - Read intrinsic parameters from the Zivid camera (OpenCV model) or estimate them from the point cloud. + - [NetworkConfiguration](https://github.com/zivid/zivid-cpp-samples/tree/master/source/Camera/InfoUtilOther/NetworkConfiguration/NetworkConfiguration.cpp) - Uses Zivid API to change the IP address of the Zivid + camera. - [SettingsInfo](https://github.com/zivid/zivid-cpp-samples/tree/master/source/Camera/InfoUtilOther/SettingsInfo/SettingsInfo.cpp) - Read settings info from the Zivid camera. - [Warmup](https://github.com/zivid/zivid-cpp-samples/tree/master/source/Camera/InfoUtilOther/Warmup/Warmup.cpp) - Short example of a basic way to warm up the camera with specified time and capture cycle. @@ -127,8 +129,9 @@ from the camera can be used. - [ROIBoxViaCheckerboard](https://github.com/zivid/zivid-cpp-samples/tree/master/source/Applications/Advanced/ROIBoxViaCheckerboard/ROIBoxViaCheckerboard.cpp) - Filter the point cloud based on a ROI box given relative to the Zivid Calibration Board. - [TransformPointCloudFromMillimetersToMeters](https://github.com/zivid/zivid-cpp-samples/tree/master/source/Applications/Advanced/TransformPointCloudFromMillimetersToMeters/TransformPointCloudFromMillimetersToMeters.cpp) - Transform point cloud data from millimeters to meters. - - [TransformPointCloudViaArucoMarker](https://github.com/zivid/zivid-cpp-samples/tree/master/source/Applications/Advanced/TransformPointCloudViaArucoMarker/TransformPointCloudViaArucoMarker.cpp) - Transform a point cloud from camera to ArUco Marker + - [TransformPointCloudViaArucoMarker](https://github.com/zivid/zivid-cpp-samples/tree/master/source/Applications/Advanced/TransformPointCloudViaArucoMarker/TransformPointCloudViaArucoMarker.cpp) - Transform a point cloud from camera to ArUco marker coordinate frame by estimating the marker's pose from the + point cloud. - [TransformPointCloudViaCheckerboard](https://github.com/zivid/zivid-cpp-samples/tree/master/source/Applications/Advanced/TransformPointCloudViaCheckerboard/TransformPointCloudViaCheckerboard.cpp) - Transform a point cloud from camera to checkerboard (Zivid Calibration Board) coordinate frame by getting checkerboard pose from the API. @@ -228,7 +231,7 @@ If you want to use Zivid in HALCON, we provide a GenICam GenTL producer that comes with the [Zivid Software](http://www.zivid.com/downloads). Zivid and HALCON are compatible with Windows 10 and 11, and Ubuntu -20.04, 22.04. +20.04, 22.04, 24.04. ----- diff --git a/continuous-integration/linux/platform-dependent/ubuntu-20.04/setup.sh b/continuous-integration/linux/platform-dependent/ubuntu-20.04/setup.sh index 4898fc69..e9396e8a 100755 --- a/continuous-integration/linux/platform-dependent/ubuntu-20.04/setup.sh +++ b/continuous-integration/linux/platform-dependent/ubuntu-20.04/setup.sh @@ -32,4 +32,4 @@ function install_www_deb { rm -r $TMP_DIR || exit } -install_www_deb "https://downloads.zivid.com/sdk/releases/2.12.0+6afd4961-1/u${VERSION_ID:0:2}/zivid_2.12.0+6afd4961-1_amd64.deb" || exit +install_www_deb "https://downloads.zivid.com/sdk/releases/2.13.1+18e79e79-1/u${VERSION_ID:0:2}/zivid_2.13.1+18e79e79-1_amd64.deb" || exit diff --git a/source/3rd-party/clipp/include/clipp.h b/source/3rd-party/clipp/include/clipp.h index f01b42e7..a1148f31 100644 --- a/source/3rd-party/clipp/include/clipp.h +++ b/source/3rd-party/clipp/include/clipp.h @@ -31,728 +31,788 @@ #ifndef AM_CLIPP_H__ #define AM_CLIPP_H__ -#include -#include +#include +#include #include #include -#include -#include -#include +#include #include -#include +#include +#include #include -#include +#include +#include #include -#include +#include +#include + -/*************************************************************************/ /** +/*************************************************************************//** * * @brief primary namespace * *****************************************************************************/ -namespace clipp -{ - /***************************************************************************** +namespace clipp { + + + +/***************************************************************************** * * basic constants and datatype definitions * *****************************************************************************/ - using arg_index = int; +using arg_index = int; + +using arg_string = std::string; +using doc_string = std::string; - using arg_string = std::string; - using doc_string = std::string; +using arg_list = std::vector; - using arg_list = std::vector; - /*************************************************************************/ /** + +/*************************************************************************//** * * @brief tristate * *****************************************************************************/ - enum class tri : char - { - no, - yes, - either - }; +enum class tri : char { no, yes, either }; + +inline constexpr bool operator == (tri t, bool b) noexcept { + return b ? t != tri::no : t != tri::yes; +} +inline constexpr bool operator == (bool b, tri t) noexcept { return (t == b); } +inline constexpr bool operator != (tri t, bool b) noexcept { return !(t == b); } +inline constexpr bool operator != (bool b, tri t) noexcept { return !(t == b); } - inline constexpr bool operator==(tri t, bool b) noexcept - { - return b ? t != tri::no : t != tri::yes; - } - inline constexpr bool operator==(bool b, tri t) noexcept - { - return (t == b); - } - inline constexpr bool operator!=(tri t, bool b) noexcept - { - return !(t == b); - } - inline constexpr bool operator!=(bool b, tri t) noexcept - { - return !(t == b); - } - /*************************************************************************/ /** + +/*************************************************************************//** * * @brief (start,size) index range * *****************************************************************************/ - class subrange - { - public: - using size_type = arg_string::size_type; +class subrange { +public: + using size_type = arg_string::size_type; - /** @brief default: no match */ - explicit constexpr subrange() noexcept - : at_{ arg_string::npos } - , length_{ 0 } - {} + /** @brief default: no match */ + explicit constexpr + subrange() noexcept : + at_{arg_string::npos}, length_{0} + {} - /** @brief match length & position within subject string */ - explicit constexpr subrange(size_type pos, size_type len) noexcept - : at_{ pos } - , length_{ len } - {} + /** @brief match length & position within subject string */ + explicit constexpr + subrange(size_type pos, size_type len) noexcept : + at_{pos}, length_{len} + {} - /** @brief position of the match within the subject string */ - constexpr size_type at() const noexcept - { - return at_; - } - /** @brief length of the matching subsequence */ - constexpr size_type length() const noexcept - { - return length_; - } + /** @brief position of the match within the subject string */ + constexpr size_type at() const noexcept { return at_; } + /** @brief length of the matching subsequence */ + constexpr size_type length() const noexcept { return length_; } - /** @brief returns true, if query string is a prefix of the subject string */ - constexpr bool prefix() const noexcept - { - return at_ == 0; - } + /** @brief returns true, if query string is a prefix of the subject string */ + constexpr bool prefix() const noexcept { + return at_ == 0; + } - /** @brief returns true, if query is a substring of the query string */ - constexpr explicit operator bool() const noexcept - { - return at_ != arg_string::npos; - } + /** @brief returns true, if query is a substring of the query string */ + constexpr explicit operator bool () const noexcept { + return at_ != arg_string::npos; + } + +private: + size_type at_; + size_type length_; +}; - private: - size_type at_; - size_type length_; - }; - /*************************************************************************/ /** + +/*************************************************************************//** * * @brief match predicates * *****************************************************************************/ - using match_predicate = std::function; - using match_function = std::function; +using match_predicate = std::function; +using match_function = std::function; + + + + + + +/*************************************************************************//** + * + * @brief type txt (NOT FOR DIRECT USE IN CLIENT CODE!) + * no interface guarantees; might be changed or removed in the future + * + *****************************************************************************/ +namespace txt { + inline bool isspace(char c) { + return (c >= 0) && std::isspace(c); + } + inline bool isdigit(char c) { + return (c >= 0) && std::isdigit(c); + } + inline bool isalnum(char c) { + return (c >= 0) && std::isalnum(c); + } + inline bool isalpha(char c) { + return (c >= 0) && std::isalpha(c); + } +} - /*************************************************************************/ /** +/*************************************************************************//** * * @brief type traits (NOT FOR DIRECT USE IN CLIENT CODE!) * no interface guarantees; might be changed or removed in the future * *****************************************************************************/ - namespace traits - { - /*************************************************************************/ /** +namespace traits { + +/*************************************************************************//** * * @brief function (class) signature type trait * *****************************************************************************/ - template - constexpr auto check_is_callable(int) -> decltype( - std::declval()(std::declval()...), - std::integral_constant::type>::value>{}); +template +constexpr auto +check_is_callable(int) -> decltype( + std::declval()(std::declval()...), +#if defined(__cpp_lib_is_invocable) + std::integral_constant::type>::value>{} ); +#else + std::integral_constant::type>::value>{} ); +#endif + +template +constexpr auto +check_is_callable(long) -> std::false_type; + +template +constexpr auto +check_is_callable_without_arg(int) -> decltype( + std::declval()(), +#if defined(__cpp_lib_is_invocable) + std::integral_constant::type>::value>{} ); +#else + std::integral_constant::type>::value>{} ); +#endif + +template +constexpr auto +check_is_callable_without_arg(long) -> std::false_type; + - template - constexpr auto check_is_callable(long) -> std::false_type; - template - constexpr auto check_is_callable_without_arg(int) - -> decltype(std::declval()(), - std::integral_constant::type>::value>{}); +template +constexpr auto +check_is_void_callable(int) -> decltype( + std::declval()(std::declval()...), std::true_type{}); - template - constexpr auto check_is_callable_without_arg(long) -> std::false_type; +template +constexpr auto +check_is_void_callable(long) -> std::false_type; - template - constexpr auto check_is_void_callable(int) - -> decltype(std::declval()(std::declval()...), std::true_type{}); +template +constexpr auto +check_is_void_callable_without_arg(int) -> decltype( + std::declval()(), std::true_type{}); - template - constexpr auto check_is_void_callable(long) -> std::false_type; +template +constexpr auto +check_is_void_callable_without_arg(long) -> std::false_type; - template - constexpr auto check_is_void_callable_without_arg(int) -> decltype(std::declval()(), std::true_type{}); - template - constexpr auto check_is_void_callable_without_arg(long) -> std::false_type; - template - struct is_callable; +template +struct is_callable; - template - struct is_callable : decltype(check_is_callable(0)) - {}; - template - struct is_callable : decltype(check_is_callable_without_arg(0)) - {}; +template +struct is_callable : + decltype(check_is_callable(0)) +{}; - template - struct is_callable : decltype(check_is_void_callable(0)) - {}; +template +struct is_callable : + decltype(check_is_callable_without_arg(0)) +{}; - template - struct is_callable : decltype(check_is_void_callable_without_arg(0)) - {}; - /*************************************************************************/ /** +template +struct is_callable : + decltype(check_is_void_callable(0)) +{}; + +template +struct is_callable : + decltype(check_is_void_callable_without_arg(0)) +{}; + + + +/*************************************************************************//** * * @brief input range type trait * *****************************************************************************/ - template - constexpr auto check_is_input_range(int) - -> decltype(begin(std::declval()), end(std::declval()), std::true_type{}); +template +constexpr auto +check_is_input_range(int) -> decltype( + begin(std::declval()), end(std::declval()), + std::true_type{}); - template - constexpr auto check_is_input_range(char) - -> decltype(std::begin(std::declval()), std::end(std::declval()), std::true_type{}); +template +constexpr auto +check_is_input_range(char) -> decltype( + std::begin(std::declval()), std::end(std::declval()), + std::true_type{}); - template - constexpr auto check_is_input_range(long) -> std::false_type; +template +constexpr auto +check_is_input_range(long) -> std::false_type; - template - struct is_input_range : decltype(check_is_input_range(0)) - {}; +template +struct is_input_range : + decltype(check_is_input_range(0)) +{}; - /*************************************************************************/ /** + + +/*************************************************************************//** * * @brief size() member type trait * *****************************************************************************/ - template - constexpr auto check_has_size_getter(int) -> decltype(std::declval().size(), std::true_type{}); +template +constexpr auto +check_has_size_getter(int) -> + decltype(std::declval().size(), std::true_type{}); + +template +constexpr auto +check_has_size_getter(long) -> std::false_type; + +template +struct has_size_getter : + decltype(check_has_size_getter(0)) +{}; - template - constexpr auto check_has_size_getter(long) -> std::false_type; +} // namespace traits - template - struct has_size_getter : decltype(check_has_size_getter(0)) - {}; - } // namespace traits - /*************************************************************************/ /** + + + +/*************************************************************************//** * * @brief helpers (NOT FOR DIRECT USE IN CLIENT CODE!) * no interface guarantees; might be changed or removed in the future * *****************************************************************************/ - namespace detail - { - /*************************************************************************/ /** +namespace detail { + + +/*************************************************************************//** * @brief forwards string to first non-whitespace char; * std string -> unsigned conv yields max value, but we want 0; * also checks for nullptr *****************************************************************************/ - inline bool fwd_to_unsigned_int(const char *&s) - { - if(!s) return false; - for(; std::isspace(*s); ++s) - ; - if(!s[0] || s[0] == '-') return false; - if(s[0] == '-') return false; - return true; - } +inline bool +fwd_to_unsigned_int(const char*& s) +{ + if(!s) return false; + for(; txt::isspace(*s); ++s); + if(!s[0] || s[0] == '-') return false; + return true; +} - /*************************************************************************/ /** + +/*************************************************************************//** * * @brief value limits clamping * *****************************************************************************/ - template sizeof(T))> - struct limits_clamped - { - static T from(const V &v) - { - if(v >= V(std::numeric_limits::max())) - { - return std::numeric_limits::max(); - } - if(v <= V(std::numeric_limits::lowest())) - { - return std::numeric_limits::lowest(); - } - return T(v); - } - }; +template sizeof(T))> +struct limits_clamped { + static T from(const V& v) { + if(v >= V(std::numeric_limits::max())) { + return std::numeric_limits::max(); + } + if(v <= V(std::numeric_limits::lowest())) { + return std::numeric_limits::lowest(); + } + return T(v); + } +}; - template - struct limits_clamped - { - static T from(const V &v) - { - return T(v); - } - }; +template +struct limits_clamped { + static T from(const V& v) { return T(v); } +}; - /*************************************************************************/ /** + +/*************************************************************************//** * * @brief returns value of v as a T, clamped at T's maximum * *****************************************************************************/ - template - inline T clamped_on_limits(const V &v) - { - return limits_clamped::from(v); - } +template +inline T clamped_on_limits(const V& v) { + return limits_clamped::from(v); +} + - /*************************************************************************/ /** + + +/*************************************************************************//** * * @brief type conversion helpers * *****************************************************************************/ - template - struct make - { - static inline T from(const char *s) - { - if(!s) return false; - //a conversion from const char* to / must exist - return static_cast(s); - } - }; +template +struct make { + static inline T from(const char* s) { + if(!s) return false; + //a conversion from const char* to / must exist + return static_cast(s); + } +}; - template<> - struct make - { - static inline bool from(const char *s) - { - if(!s) return false; - return static_cast(s); - } - }; +template<> +struct make { + static inline bool from(const char* s) { + if(!s) return false; + return static_cast(s); + } +}; - template<> - struct make - { - static inline unsigned char from(const char *s) - { - if(!fwd_to_unsigned_int(s)) return (0); - return clamped_on_limits(std::strtoull(s, nullptr, 10)); - } - }; +template<> +struct make { + static inline unsigned char from(const char* s) { + if(!fwd_to_unsigned_int(s)) return (0); + return clamped_on_limits(std::strtoull(s,nullptr,10)); + } +}; - template<> - struct make - { - static inline unsigned short int from(const char *s) - { - if(!fwd_to_unsigned_int(s)) return (0); - return clamped_on_limits(std::strtoull(s, nullptr, 10)); - } - }; +template<> +struct make { + static inline unsigned short int from(const char* s) { + if(!fwd_to_unsigned_int(s)) return (0); + return clamped_on_limits(std::strtoull(s,nullptr,10)); + } +}; - template<> - struct make - { - static inline unsigned int from(const char *s) - { - if(!fwd_to_unsigned_int(s)) return (0); - return clamped_on_limits(std::strtoull(s, nullptr, 10)); - } - }; +template<> +struct make { + static inline unsigned int from(const char* s) { + if(!fwd_to_unsigned_int(s)) return (0); + return clamped_on_limits(std::strtoull(s,nullptr,10)); + } +}; - template<> - struct make - { - static inline unsigned long int from(const char *s) - { - if(!fwd_to_unsigned_int(s)) return (0); - return clamped_on_limits(std::strtoull(s, nullptr, 10)); - } - }; +template<> +struct make { + static inline unsigned long int from(const char* s) { + if(!fwd_to_unsigned_int(s)) return (0); + return clamped_on_limits(std::strtoull(s,nullptr,10)); + } +}; - template<> - struct make - { - static inline unsigned long long int from(const char *s) - { - if(!fwd_to_unsigned_int(s)) return (0); - return clamped_on_limits(std::strtoull(s, nullptr, 10)); - } - }; +template<> +struct make { + static inline unsigned long long int from(const char* s) { + if(!fwd_to_unsigned_int(s)) return (0); + return clamped_on_limits(std::strtoull(s,nullptr,10)); + } +}; + +template<> +struct make { + static inline char from(const char* s) { + //parse as single character? + const auto n = std::strlen(s); + if(n == 1) return s[0]; + //parse as integer + return clamped_on_limits(std::strtoll(s,nullptr,10)); + } +}; - template<> - struct make - { - static inline char from(const char *s) - { - //parse as single character? - const auto n = std::strlen(s); - if(n == 1) return s[0]; - //parse as integer - return clamped_on_limits(std::strtoll(s, nullptr, 10)); - } - }; +template<> +struct make { + static inline short int from(const char* s) { + return clamped_on_limits(std::strtoll(s,nullptr,10)); + } +}; - template<> - struct make - { - static inline short int from(const char *s) - { - return clamped_on_limits(std::strtoll(s, nullptr, 10)); - } - }; +template<> +struct make { + static inline int from(const char* s) { + return clamped_on_limits(std::strtoll(s,nullptr,10)); + } +}; - template<> - struct make - { - static inline int from(const char *s) - { - return clamped_on_limits(std::strtoll(s, nullptr, 10)); - } - }; +template<> +struct make { + static inline long int from(const char* s) { + return clamped_on_limits(std::strtoll(s,nullptr,10)); + } +}; - template<> - struct make - { - static inline long int from(const char *s) - { - return clamped_on_limits(std::strtoll(s, nullptr, 10)); - } - }; +template<> +struct make { + static inline long long int from(const char* s) { + return (std::strtoll(s,nullptr,10)); + } +}; - template<> - struct make - { - static inline long long int from(const char *s) - { - return (std::strtoll(s, nullptr, 10)); - } - }; +template<> +struct make { + static inline float from(const char* s) { + return (std::strtof(s,nullptr)); + } +}; - template<> - struct make - { - static inline float from(const char *s) - { - return (std::strtof(s, nullptr)); - } - }; +template<> +struct make { + static inline double from(const char* s) { + return (std::strtod(s,nullptr)); + } +}; - template<> - struct make - { - static inline double from(const char *s) - { - return (std::strtod(s, nullptr)); - } - }; +template<> +struct make { + static inline long double from(const char* s) { + return (std::strtold(s,nullptr)); + } +}; - template<> - struct make - { - static inline long double from(const char *s) - { - return (std::strtold(s, nullptr)); - } - }; +template<> +struct make { + static inline std::string from(const char* s) { + return std::string(s); + } +}; - template<> - struct make - { - static inline std::string from(const char *s) - { - return std::string(s); - } - }; - /*************************************************************************/ /** + +/*************************************************************************//** * * @brief assigns boolean constant to one or multiple target objects * *****************************************************************************/ - template - class assign_value - { - public: - template - explicit constexpr assign_value(T &target, X &&value) noexcept - : t_{ std::addressof(target) } - , v_{ std::forward(value) } - {} +template +class assign_value +{ +public: + template + explicit constexpr + assign_value(T& target, X&& value) noexcept : + t_{std::addressof(target)}, v_{std::forward(value)} + {} + + void operator () () const { + if(t_) *t_ = v_; + } + +private: + T* t_; + V v_; +}; - void operator()() const - { - if(t_) *t_ = v_; - } - private: - T *t_; - V v_; - }; - /*************************************************************************/ /** +/*************************************************************************//** * * @brief flips bools * *****************************************************************************/ - class flip_bool - { - public: - explicit constexpr flip_bool(bool &target) noexcept - : b_{ &target } - {} +class flip_bool +{ +public: + explicit constexpr + flip_bool(bool& target) noexcept : + b_{&target} + {} + + void operator () () const { + if(b_) *b_ = !*b_; + } - void operator()() const - { - if(b_) *b_ = !*b_; - } +private: + bool* b_; +}; - private: - bool *b_; - }; - /*************************************************************************/ /** + +/*************************************************************************//** * * @brief increments using operator ++ * *****************************************************************************/ - template - class increment - { - public: - explicit constexpr increment(T &target) noexcept - : t_{ std::addressof(target) } - {} +template +class increment +{ +public: + explicit constexpr + increment(T& target) noexcept : t_{std::addressof(target)} {} - void operator()() const - { - if(t_) ++(*t_); - } + void operator () () const { + if(t_) ++(*t_); + } + +private: + T* t_; +}; - private: - T *t_; - }; - /*************************************************************************/ /** + +/*************************************************************************//** * * @brief decrements using operator -- * *****************************************************************************/ - template - class decrement - { - public: - explicit constexpr decrement(T &target) noexcept - : t_{ std::addressof(target) } - {} +template +class decrement +{ +public: + explicit constexpr + decrement(T& target) noexcept : t_{std::addressof(target)} {} - void operator()() const - { - if(t_) --(*t_); - } + void operator () () const { + if(t_) --(*t_); + } + +private: + T* t_; +}; - private: - T *t_; - }; - /*************************************************************************/ /** + +/*************************************************************************//** * * @brief increments by a fixed amount using operator += * *****************************************************************************/ - template - class increment_by - { - public: - explicit constexpr increment_by(T &target, T by) noexcept - : t_{ std::addressof(target) } - , by_{ std::move(by) } - {} +template +class increment_by +{ +public: + explicit constexpr + increment_by(T& target, T by) noexcept : + t_{std::addressof(target)}, by_{std::move(by)} + {} + + void operator () () const { + if(t_) (*t_) += by_; + } + +private: + T* t_; + T by_; +}; - void operator()() const - { - if(t_) (*t_) += by_; - } - private: - T *t_; - T by_; - }; - /*************************************************************************/ /** + +/*************************************************************************//** * * @brief makes a value from a string and assigns it to an object * *****************************************************************************/ - template - class map_arg_to - { - public: - explicit constexpr map_arg_to(T &target) noexcept - : t_{ std::addressof(target) } - {} +template +class map_arg_to +{ +public: + explicit constexpr + map_arg_to(T& target) noexcept : t_{std::addressof(target)} {} - void operator()(const char *s) const - { - if(t_ && s) *t_ = detail::make::from(s); - } + void operator () (const char* s) const { + if(t_ && s) *t_ = detail::make::from(s); + } - private: - T *t_; - }; +private: + T* t_; +}; - //------------------------------------------------------------------- - /** + +//------------------------------------------------------------------- +/** * @brief specialization for vectors: append element */ - template - class map_arg_to> - { - public: - map_arg_to(std::vector &target) - : t_{ std::addressof(target) } - {} +template +class map_arg_to> +{ +public: + map_arg_to(std::vector& target): t_{std::addressof(target)} {} - void operator()(const char *s) const - { - if(t_ && s) t_->push_back(detail::make::from(s)); - } + void operator () (const char* s) const { + if(t_ && s) t_->push_back(detail::make::from(s)); + } - private: - std::vector *t_; - }; +private: + std::vector* t_; +}; - //------------------------------------------------------------------- - /** + +//------------------------------------------------------------------- +/** * @brief specialization for bools: * set to true regardless of string content */ - template<> - class map_arg_to - { - public: - map_arg_to(bool &target) - : t_{ &target } - {} +template<> +class map_arg_to +{ +public: + map_arg_to(bool& target): t_{&target} {} + + void operator () (const char* s) const { + if(t_ && s) *t_ = true; + } + +private: + bool* t_; +}; + + +} // namespace detail + - void operator()(const char *s) const - { - if(t_ && s) *t_ = true; - } - private: - bool *t_; - }; - } // namespace detail - /*************************************************************************/ /** + +/*************************************************************************//** * * @brief string matching and processing tools * *****************************************************************************/ - namespace str - { - /*************************************************************************/ /** +namespace str { + + +/*************************************************************************//** * * @brief converts string to value of target type 'T' * *****************************************************************************/ - template - T make(const arg_string &s) - { - return detail::make::from(s); - } +template +T make(const arg_string& s) +{ + return detail::make::from(s); +} + - /*************************************************************************/ /** + +/*************************************************************************//** * * @brief removes trailing whitespace from string * *****************************************************************************/ - template - inline void trimr(std::basic_string &s) - { - if(s.empty()) return; +template +inline void +trimr(std::basic_string& s) +{ + if(s.empty()) return; - s.erase(std::find_if_not(s.rbegin(), s.rend(), [](char c) { return std::isspace(c); }).base(), s.end()); - } + s.erase( + std::find_if_not(s.rbegin(), s.rend(), + [](char c) { return txt::isspace(c);} ).base(), + s.end() ); +} - /*************************************************************************/ /** + +/*************************************************************************//** * * @brief removes leading whitespace from string * *****************************************************************************/ - template - inline void triml(std::basic_string &s) - { - if(s.empty()) return; +template +inline void +triml(std::basic_string& s) +{ + if(s.empty()) return; - s.erase(s.begin(), std::find_if_not(s.begin(), s.end(), [](char c) { return std::isspace(c); })); - } + s.erase( + s.begin(), + std::find_if_not(s.begin(), s.end(), + [](char c) { return txt::isspace(c);}) + ); +} - /*************************************************************************/ /** + +/*************************************************************************//** * * @brief removes leading and trailing whitespace from string * *****************************************************************************/ - template - inline void trim(std::basic_string &s) - { - triml(s); - trimr(s); - } +template +inline void +trim(std::basic_string& s) +{ + triml(s); + trimr(s); +} - /*************************************************************************/ /** + +/*************************************************************************//** * * @brief removes all whitespaces from string * *****************************************************************************/ - template - inline void remove_ws(std::basic_string &s) - { - if(s.empty()) return; +template +inline void +remove_ws(std::basic_string& s) +{ + if(s.empty()) return; - s.erase(std::remove_if(s.begin(), s.end(), [](char c) { return std::isspace(c); }), s.end()); - } + s.erase(std::remove_if(s.begin(), s.end(), + [](char c) { return txt::isspace(c); }), + s.end() ); +} - /*************************************************************************/ /** + +/*************************************************************************//** * * @brief returns true, if the 'prefix' argument * is a prefix of the 'subject' argument * *****************************************************************************/ - template - inline bool has_prefix(const std::basic_string &subject, const std::basic_string &prefix) - { - if(prefix.size() > subject.size()) return false; - return subject.find(prefix) == 0; - } +template +inline bool +has_prefix(const std::basic_string& subject, + const std::basic_string& prefix) +{ + if(prefix.size() > subject.size()) return false; + return subject.find(prefix) == 0; +} - /*************************************************************************/ /** + +/*************************************************************************//** * * @brief returns true, if the 'postfix' argument * is a postfix of the 'subject' argument * *****************************************************************************/ - template - inline bool has_postfix(const std::basic_string &subject, const std::basic_string &postfix) - { - if(postfix.size() > subject.size()) return false; - return (subject.size() - postfix.size()) == subject.find(postfix); - } +template +inline bool +has_postfix(const std::basic_string& subject, + const std::basic_string& postfix) +{ + if(postfix.size() > subject.size()) return false; + return (subject.size() - postfix.size()) == subject.find(postfix); +} - /*************************************************************************/ /** + + +/*************************************************************************//** * * @brief returns longest common prefix of several * sequential random access containers @@ -761,41 +821,46 @@ namespace clipp * the elements of InputRange require a size() member * *****************************************************************************/ - template - auto longest_common_prefix(const InputRange &strs) -> typename std::decay::type - { - static_assert(traits::is_input_range(), "parameter must satisfy the InputRange concept"); +template +auto +longest_common_prefix(const InputRange& strs) + -> typename std::decay::type +{ + static_assert(traits::is_input_range(), + "parameter must satisfy the InputRange concept"); - static_assert(traits::has_size_getter::type>(), - "elements of input range must have a ::size() member function"); + static_assert(traits::has_size_getter< + typename std::decay::type>(), + "elements of input range must have a ::size() member function"); - using std::begin; - using std::end; + using std::begin; + using std::end; - using item_t = typename std::decay::type; - using str_size_t = typename std::decaysize())>::type; + using item_t = typename std::decay::type; + using str_size_t = typename std::decaysize())>::type; - const auto n = size_t(distance(begin(strs), end(strs))); - if(n < 1) return item_t(""); - if(n == 1) return *begin(strs); + const auto n = size_t(distance(begin(strs), end(strs))); + if(n < 1) return item_t(""); + if(n == 1) return *begin(strs); - //length of shortest string - auto m = std::min_element(begin(strs), end(strs), [](const item_t &a, const item_t &b) { - return a.size() < b.size(); - })->size(); + //length of shortest string + auto m = std::min_element(begin(strs), end(strs), + [](const item_t& a, const item_t& b) { + return a.size() < b.size(); })->size(); - //check each character until we find a mismatch - for(str_size_t i = 0; i < m; ++i) - { - for(str_size_t j = 1; j < n; ++j) - { - if(strs[j][i] != strs[j - 1][i]) return strs[0].substr(0, i); - } - } - return strs[0].substr(0, m); + //check each character until we find a mismatch + for(str_size_t i = 0; i < m; ++i) { + for(str_size_t j = 1; j < n; ++j) { + if(strs[j][i] != strs[j-1][i]) + return strs[0].substr(0, i); } + } + return strs[0].substr(0, m); +} - /*************************************************************************/ /** + + +/*************************************************************************//** * * @brief returns longest substring range that could be found in 'arg' * @@ -803,31 +868,35 @@ namespace clipp * @param substrings range of candidate substrings * *****************************************************************************/ - template - subrange longest_substring_match(const std::basic_string &arg, const InputRange &substrings) - { - using string_t = std::basic_string; +template +subrange +longest_substring_match(const std::basic_string& arg, + const InputRange& substrings) +{ + using string_t = std::basic_string; - static_assert(traits::is_input_range(), "parameter must satisfy the InputRange concept"); + static_assert(traits::is_input_range(), + "parameter must satisfy the InputRange concept"); - static_assert(std::is_same::type>(), - "substrings must have same type as 'arg'"); + static_assert(std::is_same::type>(), + "substrings must have same type as 'arg'"); - auto i = string_t::npos; - auto n = string_t::size_type(0); - for(const auto &s : substrings) - { - auto j = arg.find(s); - if(j != string_t::npos && s.size() > n) - { - i = j; - n = s.size(); - } - } - return subrange{ i, n }; + auto i = string_t::npos; + auto n = string_t::size_type(0); + for(const auto& s : substrings) { + auto j = arg.find(s); + if(j != string_t::npos && s.size() > n) { + i = j; + n = s.size(); } + } + return subrange{i,n}; +} - /*************************************************************************/ /** + + +/*************************************************************************//** * * @brief returns longest prefix range that could be found in 'arg' * @@ -835,206 +904,208 @@ namespace clipp * @param prefixes range of candidate prefix strings * *****************************************************************************/ - template - subrange longest_prefix_match(const std::basic_string &arg, const InputRange &prefixes) - { - using string_t = std::basic_string; - using s_size_t = typename string_t::size_type; +template +subrange +longest_prefix_match(const std::basic_string& arg, + const InputRange& prefixes) +{ + using string_t = std::basic_string; + using s_size_t = typename string_t::size_type; - static_assert(traits::is_input_range(), "parameter must satisfy the InputRange concept"); + static_assert(traits::is_input_range(), + "parameter must satisfy the InputRange concept"); - static_assert(std::is_same::type>(), - "prefixes must have same type as 'arg'"); + static_assert(std::is_same::type>(), + "prefixes must have same type as 'arg'"); - auto i = string_t::npos; - auto n = s_size_t(0); - for(const auto &s : prefixes) - { - auto j = arg.find(s); - if(j == 0 && s.size() > n) - { - i = 0; - n = s.size(); - } - } - return subrange{ i, n }; + auto i = string_t::npos; + auto n = s_size_t(0); + for(const auto& s : prefixes) { + auto j = arg.find(s); + if(j == 0 && s.size() > n) { + i = 0; + n = s.size(); } + } + return subrange{i,n}; +} - /*************************************************************************/ /** + + +/*************************************************************************//** * * @brief returns the first occurrence of 'query' within 'subject' * *****************************************************************************/ - template - inline subrange substring_match(const std::basic_string &subject, - const std::basic_string &query) - { - if(subject.empty() && query.empty()) return subrange(0, 0); - if(subject.empty() || query.empty()) return subrange{}; - auto i = subject.find(query); - if(i == std::basic_string::npos) return subrange{}; - return subrange{ i, query.size() }; - } +template +inline subrange +substring_match(const std::basic_string& subject, + const std::basic_string& query) +{ + if(subject.empty() && query.empty()) return subrange(0,0); + if(subject.empty() || query.empty()) return subrange{}; + auto i = subject.find(query); + if(i == std::basic_string::npos) return subrange{}; + return subrange{i,query.size()}; +} + - /*************************************************************************/ /** + +/*************************************************************************//** * * @brief returns first substring match (pos,len) within the input string * that represents a number * (with at maximum one decimal point and digit separators) * *****************************************************************************/ - template - subrange first_number_match(std::basic_string s, - C digitSeparator = C(','), - C decimalPoint = C('.'), - C exponential = C('e')) - { - using string_t = std::basic_string; +template +subrange +first_number_match(std::basic_string s, + C digitSeparator = C(','), + C decimalPoint = C('.'), + C exponential = C('e')) +{ + using string_t = std::basic_string; - str::trim(s); - if(s.empty()) return subrange{}; + str::trim(s); + if(s.empty()) return subrange{}; - auto i = s.find_first_of("0123456789+-"); - if(i == string_t::npos) - { - i = s.find(decimalPoint); - if(i == string_t::npos) return subrange{}; - } + auto i = s.find_first_of("0123456789+-"); + if(i == string_t::npos) { + i = s.find(decimalPoint); + if(i == string_t::npos) return subrange{}; + } - bool point = false; - bool sep = false; - auto exp = string_t::npos; - auto j = i + 1; - for(; j < s.size(); ++j) - { - if(s[j] == digitSeparator) - { - if(!sep) - sep = true; - else - break; - } - else - { - sep = false; - if(s[j] == decimalPoint) - { - //only one decimal point before exponent allowed - if(!point && exp == string_t::npos) - point = true; - else - break; - } - else if(std::tolower(s[j]) == std::tolower(exponential)) - { - //only one exponent separator allowed - if(exp == string_t::npos) - exp = j; - else - break; - } - else if(exp != string_t::npos && (exp + 1) == j) - { - //only sign or digit after exponent separator - if(s[j] != '+' && s[j] != '-' && !std::isdigit(s[j])) break; - } - else if(!std::isdigit(s[j])) - { - break; - } - } + bool point = false; + bool sep = false; + auto exp = string_t::npos; + auto j = i + 1; + for(; j < s.size(); ++j) { + if(s[j] == digitSeparator) { + if(!sep) sep = true; else break; + } + else { + sep = false; + if(s[j] == decimalPoint) { + //only one decimal point before exponent allowed + if(!point && exp == string_t::npos) point = true; else break; } + else if(std::tolower(s[j]) == std::tolower(exponential)) { + //only one exponent separator allowed + if(exp == string_t::npos) exp = j; else break; + } + else if(exp != string_t::npos && (exp+1) == j) { + //only sign or digit after exponent separator + if(s[j] != '+' && s[j] != '-' && !txt::isdigit(s[j])) break; + } + else if(!txt::isdigit(s[j])) { + break; + } + } + } + + //if length == 1 then must be a digit + if(j-i == 1 && !txt::isdigit(s[i])) return subrange{}; + + return subrange{i,j-i}; +} - //if length == 1 then must be a digit - if(j - i == 1 && !std::isdigit(s[i])) return subrange{}; - return subrange{ i, j - i }; - } - /*************************************************************************/ /** +/*************************************************************************//** * * @brief returns first substring match (pos,len) * that represents an integer (with optional digit separators) * *****************************************************************************/ - template - subrange first_integer_match(std::basic_string s, C digitSeparator = C(',')) - { - using string_t = std::basic_string; +template +subrange +first_integer_match(std::basic_string s, + C digitSeparator = C(',')) +{ + using string_t = std::basic_string; - str::trim(s); - if(s.empty()) return subrange{}; + str::trim(s); + if(s.empty()) return subrange{}; - auto i = s.find_first_of("0123456789+-"); - if(i == string_t::npos) return subrange{}; + auto i = s.find_first_of("0123456789+-"); + if(i == string_t::npos) return subrange{}; - bool sep = false; - auto j = i + 1; - for(; j < s.size(); ++j) - { - if(s[j] == digitSeparator) - { - if(!sep) - sep = true; - else - break; - } - else - { - sep = false; - if(!std::isdigit(s[j])) break; - } - } + bool sep = false; + auto j = i + 1; + for(; j < s.size(); ++j) { + if(s[j] == digitSeparator) { + if(!sep) sep = true; else break; + } + else { + sep = false; + if(!txt::isdigit(s[j])) break; + } + } - //if length == 1 then must be a digit - if(j - i == 1 && !std::isdigit(s[i])) return subrange{}; + //if length == 1 then must be a digit + if(j-i == 1 && !txt::isdigit(s[i])) return subrange{}; - return subrange{ i, j - i }; - } + return subrange{i,j-i}; +} - /*************************************************************************/ /** + + +/*************************************************************************//** * * @brief returns true if candidate string represents a number * *****************************************************************************/ - template - bool represents_number(const std::basic_string &candidate, - C digitSeparator = C(','), - C decimalPoint = C('.'), - C exponential = C('e')) - { - const auto match = str::first_number_match(candidate, digitSeparator, decimalPoint, exponential); +template +bool represents_number(const std::basic_string& candidate, + C digitSeparator = C(','), + C decimalPoint = C('.'), + C exponential = C('e')) +{ + const auto match = str::first_number_match(candidate, digitSeparator, + decimalPoint, exponential); + + return (match && match.length() == candidate.size()); +} - return (match && match.length() == candidate.size()); - } - /*************************************************************************/ /** + +/*************************************************************************//** * * @brief returns true if candidate string represents an integer * *****************************************************************************/ - template - bool represents_integer(const std::basic_string &candidate, C digitSeparator = C(',')) - { - const auto match = str::first_integer_match(candidate, digitSeparator); - return (match && match.length() == candidate.size()); - } +template +bool represents_integer(const std::basic_string& candidate, + C digitSeparator = C(',')) +{ + const auto match = str::first_integer_match(candidate, digitSeparator); + return (match && match.length() == candidate.size()); +} + +} // namespace str + - } // namespace str - /*************************************************************************/ /** + + + +/*************************************************************************//** * * @brief makes function object with a const char* parameter * that assigns a value to a ref-captured object * *****************************************************************************/ - template - inline detail::assign_value set(T &target, V value) - { - return detail::assign_value{ target, std::move(value) }; - } +template +inline detail::assign_value +set(T& target, V value) { + return detail::assign_value{target, std::move(value)}; +} + - /*************************************************************************/ /** + +/*************************************************************************//** * * @brief makes parameter-less function object * that assigns value(s) to a ref-captured object; @@ -1043,1469 +1114,1498 @@ namespace clipp * bools are always set to true if the argument is not nullptr * *****************************************************************************/ - template - inline detail::map_arg_to set(T &target) - { - return detail::map_arg_to{ target }; - } +template +inline detail::map_arg_to +set(T& target) { + return detail::map_arg_to{target}; +} + + - /*************************************************************************/ /** +/*************************************************************************//** * * @brief makes function object that sets a bool to true * *****************************************************************************/ - inline detail::assign_value set(bool &target) - { - return detail::assign_value{ target, true }; - } +inline detail::assign_value +set(bool& target) { + return detail::assign_value{target,true}; +} - /*************************************************************************/ /** +/*************************************************************************//** * * @brief makes function object that sets a bool to false * *****************************************************************************/ - inline detail::assign_value unset(bool &target) - { - return detail::assign_value{ target, false }; - } +inline detail::assign_value +unset(bool& target) { + return detail::assign_value{target,false}; +} - /*************************************************************************/ /** +/*************************************************************************//** * * @brief makes function object that flips the value of a ref-captured bool * *****************************************************************************/ - inline detail::flip_bool flip(bool &b) - { - return detail::flip_bool(b); - } +inline detail::flip_bool +flip(bool& b) { + return detail::flip_bool(b); +} + + - /*************************************************************************/ /** + + +/*************************************************************************//** * * @brief makes function object that increments using operator ++ * *****************************************************************************/ - template - inline detail::increment increment(T &target) - { - return detail::increment{ target }; - } +template +inline detail::increment +increment(T& target) { + return detail::increment{target}; +} - /*************************************************************************/ /** +/*************************************************************************//** * * @brief makes function object that decrements using operator -- * *****************************************************************************/ - template - inline detail::increment_by increment(T &target, T by) - { - return detail::increment_by{ target, std::move(by) }; - } +template +inline detail::increment_by +increment(T& target, T by) { + return detail::increment_by{target, std::move(by)}; +} - /*************************************************************************/ /** +/*************************************************************************//** * * @brief makes function object that increments by a fixed amount using operator += * *****************************************************************************/ - template - inline detail::decrement decrement(T &target) - { - return detail::decrement{ target }; - } +template +inline detail::decrement +decrement(T& target) { + return detail::decrement{target}; +} + + + + + - /*************************************************************************/ /** +/*************************************************************************//** * * @brief helpers (NOT FOR DIRECT USE IN CLIENT CODE!) * *****************************************************************************/ - namespace detail - { - /*************************************************************************/ /** +namespace detail { + + +/*************************************************************************//** * * @brief mixin that provides action definition and execution * *****************************************************************************/ - template - class action_provider - { - private: - //--------------------------------------------------------------- - using simple_action = std::function; - using arg_action = std::function; - using index_action = std::function; - - //----------------------------------------------------- - class simple_action_adapter - { - public: - simple_action_adapter() = default; - simple_action_adapter(const simple_action &a) - : action_(a) - {} - simple_action_adapter(simple_action &&a) - : action_(std::move(a)) - {} - void operator()(const char *) const - { - action_(); - } - void operator()(int) const - { - action_(); - } +template +class action_provider +{ +private: + //--------------------------------------------------------------- + using simple_action = std::function; + using arg_action = std::function; + using index_action = std::function; + + //----------------------------------------------------- + class simple_action_adapter { + public: + simple_action_adapter() = default; + simple_action_adapter(const simple_action& a): action_(a) {} + simple_action_adapter(simple_action&& a): action_(std::move(a)) {} + void operator() (const char*) const { action_(); } + void operator() (int) const { action_(); } + private: + simple_action action_; + }; - private: - simple_action action_; - }; - public: - //--------------------------------------------------------------- - /** @brief adds an action that has an operator() that is callable +public: + //--------------------------------------------------------------- + /** @brief adds an action that has an operator() that is callable * with a 'const char*' argument */ - Derived &call(arg_action a) - { - argActions_.push_back(std::move(a)); - return *static_cast(this); - } + Derived& + call(arg_action a) { + argActions_.push_back(std::move(a)); + return *static_cast(this); + } - /** @brief adds an action that has an operator()() */ - Derived &call(simple_action a) - { - argActions_.push_back(simple_action_adapter(std::move(a))); - return *static_cast(this); - } + /** @brief adds an action that has an operator()() */ + Derived& + call(simple_action a) { + argActions_.push_back(simple_action_adapter(std::move(a))); + return *static_cast(this); + } - /** @brief adds an action that has an operator() that is callable + /** @brief adds an action that has an operator() that is callable * with a 'const char*' argument */ - Derived &operator()(arg_action a) - { - return call(std::move(a)); - } + Derived& operator () (arg_action a) { return call(std::move(a)); } - /** @brief adds an action that has an operator()() */ - Derived &operator()(simple_action a) - { - return call(std::move(a)); - } + /** @brief adds an action that has an operator()() */ + Derived& operator () (simple_action a) { return call(std::move(a)); } - //--------------------------------------------------------------- - /** @brief adds an action that will set the value of 't' from + + //--------------------------------------------------------------- + /** @brief adds an action that will set the value of 't' from * a 'const char*' arg */ - template - Derived &set(Target &t) - { - static_assert(!std::is_pointer::value, "parameter target type must not be a pointer"); + template + Derived& + set(Target& t) { + static_assert(!std::is_pointer::value, + "parameter target type must not be a pointer"); - return call(clipp::set(t)); - } + return call(clipp::set(t)); + } + + /** @brief adds an action that will set the value of 't' to 'v' */ + template + Derived& + set(Target& t, Value&& v) { + return call(clipp::set(t, std::forward(v))); + } - /** @brief adds an action that will set the value of 't' to 'v' */ - template - Derived &set(Target &t, Value &&v) - { - return call(clipp::set(t, std::forward(v))); - } - //--------------------------------------------------------------- - /** @brief adds an action that will be called if a parameter + //--------------------------------------------------------------- + /** @brief adds an action that will be called if a parameter * matches an argument for the 2nd, 3rd, 4th, ... time */ - Derived &if_repeated(simple_action a) - { - repeatActions_.push_back(simple_action_adapter{ std::move(a) }); - return *static_cast(this); - } - /** @brief adds an action that will be called with the argument's + Derived& + if_repeated(simple_action a) { + repeatActions_.push_back(simple_action_adapter{std::move(a)}); + return *static_cast(this); + } + /** @brief adds an action that will be called with the argument's * index if a parameter matches an argument for * the 2nd, 3rd, 4th, ... time */ - Derived &if_repeated(index_action a) - { - repeatActions_.push_back(std::move(a)); - return *static_cast(this); - } + Derived& + if_repeated(index_action a) { + repeatActions_.push_back(std::move(a)); + return *static_cast(this); + } - //--------------------------------------------------------------- - /** @brief adds an action that will be called if a required parameter + + //--------------------------------------------------------------- + /** @brief adds an action that will be called if a required parameter * is missing */ - Derived &if_missing(simple_action a) - { - missingActions_.push_back(simple_action_adapter{ std::move(a) }); - return *static_cast(this); - } - /** @brief adds an action that will be called if a required parameter + Derived& + if_missing(simple_action a) { + missingActions_.push_back(simple_action_adapter{std::move(a)}); + return *static_cast(this); + } + /** @brief adds an action that will be called if a required parameter * is missing; the action will get called with the index of * the command line argument where the missing event occurred first */ - Derived &if_missing(index_action a) - { - missingActions_.push_back(std::move(a)); - return *static_cast(this); - } + Derived& + if_missing(index_action a) { + missingActions_.push_back(std::move(a)); + return *static_cast(this); + } + - //--------------------------------------------------------------- - /** @brief adds an action that will be called if a parameter + //--------------------------------------------------------------- + /** @brief adds an action that will be called if a parameter * was matched, but was unreachable in the current scope */ - Derived &if_blocked(simple_action a) - { - blockedActions_.push_back(simple_action_adapter{ std::move(a) }); - return *static_cast(this); - } - /** @brief adds an action that will be called if a parameter + Derived& + if_blocked(simple_action a) { + blockedActions_.push_back(simple_action_adapter{std::move(a)}); + return *static_cast(this); + } + /** @brief adds an action that will be called if a parameter * was matched, but was unreachable in the current scope; * the action will be called with the index of * the command line argument where the problem occurred */ - Derived &if_blocked(index_action a) - { - blockedActions_.push_back(std::move(a)); - return *static_cast(this); - } + Derived& + if_blocked(index_action a) { + blockedActions_.push_back(std::move(a)); + return *static_cast(this); + } - //--------------------------------------------------------------- - /** @brief adds an action that will be called if a parameter match + + //--------------------------------------------------------------- + /** @brief adds an action that will be called if a parameter match * was in conflict with a different alternative parameter */ - Derived &if_conflicted(simple_action a) - { - conflictActions_.push_back(simple_action_adapter{ std::move(a) }); - return *static_cast(this); - } - /** @brief adds an action that will be called if a parameter match + Derived& + if_conflicted(simple_action a) { + conflictActions_.push_back(simple_action_adapter{std::move(a)}); + return *static_cast(this); + } + /** @brief adds an action that will be called if a parameter match * was in conflict with a different alternative parameter; * the action will be called with the index of * the command line argument where the problem occurred */ - Derived &if_conflicted(index_action a) - { - conflictActions_.push_back(std::move(a)); - return *static_cast(this); - } + Derived& + if_conflicted(index_action a) { + conflictActions_.push_back(std::move(a)); + return *static_cast(this); + } + - //--------------------------------------------------------------- - /** @brief adds targets = either objects whose values should be + //--------------------------------------------------------------- + /** @brief adds targets = either objects whose values should be * set by command line arguments or actions that should * be called in case of a match */ - template - Derived &target(T &&t, Ts &&...ts) - { - target(std::forward(t)); - target(std::forward(ts)...); - return *static_cast(this); - } + template + Derived& + target(T&& t, Ts&&... ts) { + target(std::forward(t)); + target(std::forward(ts)...); + return *static_cast(this); + } - /** @brief adds action that should be called in case of a match */ - template::type>() - && (traits::is_callable() - || traits::is_callable())>::type> - Derived &target(T &&t) - { - call(std::forward(t)); - return *static_cast(this); - } + /** @brief adds action that should be called in case of a match */ + template::type>() && + (traits::is_callable() || + traits::is_callable() ) + >::type> + Derived& + target(T&& t) { + call(std::forward(t)); + return *static_cast(this); + } - /** @brief adds object whose value should be set by command line arguments + /** @brief adds object whose value should be set by command line arguments */ - template::type>() - || (!traits::is_callable() - && !traits::is_callable())>::type> - Derived &target(T &t) - { - set(t); - return *static_cast(this); - } + template::type>() || + (!traits::is_callable() && + !traits::is_callable() ) + >::type> + Derived& + target(T& t) { + set(t); + return *static_cast(this); + } - //TODO remove ugly empty param list overload - Derived &target() - { - return *static_cast(this); - } + //TODO remove ugly empty param list overload + Derived& + target() { + return *static_cast(this); + } - //--------------------------------------------------------------- - /** @brief adds target, see member function 'target' */ - template - inline friend Derived &operator<<(Target &&t, Derived &p) - { - p.target(std::forward(t)); - return p; - } - /** @brief adds target, see member function 'target' */ - template - inline friend Derived &&operator<<(Target &&t, Derived &&p) - { - p.target(std::forward(t)); - return std::move(p); - } - //----------------------------------------------------- - /** @brief adds target, see member function 'target' */ - template - inline friend Derived &operator>>(Derived &p, Target &&t) - { - p.target(std::forward(t)); - return p; - } - /** @brief adds target, see member function 'target' */ - template - inline friend Derived &&operator>>(Derived &&p, Target &&t) - { - p.target(std::forward(t)); - return std::move(p); - } + //--------------------------------------------------------------- + /** @brief adds target, see member function 'target' */ + template + inline friend Derived& + operator << (Target&& t, Derived& p) { + p.target(std::forward(t)); + return p; + } + /** @brief adds target, see member function 'target' */ + template + inline friend Derived&& + operator << (Target&& t, Derived&& p) { + p.target(std::forward(t)); + return std::move(p); + } - //--------------------------------------------------------------- - /** @brief executes all argument actions */ - void execute_actions(const arg_string &arg) const - { - int i = 0; - for(const auto &a : argActions_) - { - ++i; - a(arg.c_str()); - } - } + //----------------------------------------------------- + /** @brief adds target, see member function 'target' */ + template + inline friend Derived& + operator >> (Derived& p, Target&& t) { + p.target(std::forward(t)); + return p; + } + /** @brief adds target, see member function 'target' */ + template + inline friend Derived&& + operator >> (Derived&& p, Target&& t) { + p.target(std::forward(t)); + return std::move(p); + } + + + //--------------------------------------------------------------- + /** @brief executes all argument actions */ + void execute_actions(const arg_string& arg) const { + for(const auto& a : argActions_) { + a(arg.c_str()); + } + } + + /** @brief executes repeat actions */ + void notify_repeated(arg_index idx) const { + for(const auto& a : repeatActions_) a(idx); + } + /** @brief executes missing error actions */ + void notify_missing(arg_index idx) const { + for(const auto& a : missingActions_) a(idx); + } + /** @brief executes blocked error actions */ + void notify_blocked(arg_index idx) const { + for(const auto& a : blockedActions_) a(idx); + } + /** @brief executes conflict error actions */ + void notify_conflict(arg_index idx) const { + for(const auto& a : conflictActions_) a(idx); + } + +private: + //--------------------------------------------------------------- + std::vector argActions_; + std::vector repeatActions_; + std::vector missingActions_; + std::vector blockedActions_; + std::vector conflictActions_; +}; - /** @brief executes repeat actions */ - void notify_repeated(arg_index idx) const - { - for(const auto &a : repeatActions_) - a(idx); - } - /** @brief executes missing error actions */ - void notify_missing(arg_index idx) const - { - for(const auto &a : missingActions_) - a(idx); - } - /** @brief executes blocked error actions */ - void notify_blocked(arg_index idx) const - { - for(const auto &a : blockedActions_) - a(idx); - } - /** @brief executes conflict error actions */ - void notify_conflict(arg_index idx) const - { - for(const auto &a : conflictActions_) - a(idx); - } - private: - //--------------------------------------------------------------- - std::vector argActions_; - std::vector repeatActions_; - std::vector missingActions_; - std::vector blockedActions_; - std::vector conflictActions_; - }; - /*************************************************************************/ /** + + + +/*************************************************************************//** * * @brief mixin that provides basic common settings of parameters and groups * *****************************************************************************/ - template - class token - { - public: - //--------------------------------------------------------------- - using doc_string = clipp::doc_string; +template +class token +{ +public: + //--------------------------------------------------------------- + using doc_string = clipp::doc_string; - //--------------------------------------------------------------- - /** @brief returns documentation string */ - const doc_string &doc() const noexcept - { - return doc_; - } - /** @brief sets documentations string */ - Derived &doc(const doc_string &txt) - { - doc_ = txt; - return *static_cast(this); - } + //--------------------------------------------------------------- + /** @brief returns documentation string */ + const doc_string& doc() const noexcept { + return doc_; + } - /** @brief sets documentations string */ - Derived &doc(doc_string &&txt) - { - doc_ = std::move(txt); - return *static_cast(this); - } + /** @brief sets documentations string */ + Derived& doc(const doc_string& txt) { + doc_ = txt; + return *static_cast(this); + } - //--------------------------------------------------------------- - /** @brief returns if a group/parameter is repeatable */ - bool repeatable() const noexcept - { - return repeatable_; - } + /** @brief sets documentations string */ + Derived& doc(doc_string&& txt) { + doc_ = std::move(txt); + return *static_cast(this); + } - /** @brief sets repeatability of group/parameter */ - Derived &repeatable(bool yes) noexcept - { - repeatable_ = yes; - return *static_cast(this); - } - //--------------------------------------------------------------- - /** @brief returns if a group/parameter is blocking/positional */ - bool blocking() const noexcept - { - return blocking_; - } + //--------------------------------------------------------------- + /** @brief returns if a group/parameter is repeatable */ + bool repeatable() const noexcept { + return repeatable_; + } - /** @brief determines, if a group/parameter is blocking/positional */ - Derived &blocking(bool yes) noexcept - { - blocking_ = yes; - return *static_cast(this); - } + /** @brief sets repeatability of group/parameter */ + Derived& repeatable(bool yes) noexcept { + repeatable_ = yes; + return *static_cast(this); + } - private: - //--------------------------------------------------------------- - doc_string doc_; - bool repeatable_ = false; - bool blocking_ = false; - }; - /*************************************************************************/ /** + //--------------------------------------------------------------- + /** @brief returns if a group/parameter is blocking/positional */ + bool blocking() const noexcept { + return blocking_; + } + + /** @brief determines, if a group/parameter is blocking/positional */ + Derived& blocking(bool yes) noexcept { + blocking_ = yes; + return *static_cast(this); + } + + +private: + //--------------------------------------------------------------- + doc_string doc_; + bool repeatable_ = false; + bool blocking_ = false; +}; + + + + +/*************************************************************************//** * * @brief sets documentation strings on a token * *****************************************************************************/ - template - inline T &operator%(doc_string docstr, token &p) - { - return p.doc(std::move(docstr)); - } - //--------------------------------------------------------- - template - inline T &&operator%(doc_string docstr, token &&p) - { - return std::move(p.doc(std::move(docstr))); - } +template +inline T& +operator % (doc_string docstr, token& p) +{ + return p.doc(std::move(docstr)); +} +//--------------------------------------------------------- +template +inline T&& +operator % (doc_string docstr, token&& p) +{ + return std::move(p.doc(std::move(docstr))); +} + +//--------------------------------------------------------- +template +inline T& +operator % (token& p, doc_string docstr) +{ + return p.doc(std::move(docstr)); +} +//--------------------------------------------------------- +template +inline T&& +operator % (token&& p, doc_string docstr) +{ + return std::move(p.doc(std::move(docstr))); +} - //--------------------------------------------------------- - template - inline T &operator%(token &p, doc_string docstr) - { - return p.doc(std::move(docstr)); - } - //--------------------------------------------------------- - template - inline T &&operator%(token &&p, doc_string docstr) - { - return std::move(p.doc(std::move(docstr))); - } - /*************************************************************************/ /** + + +/*************************************************************************//** * * @brief sets documentation strings on a token * *****************************************************************************/ - template - inline T &doc(doc_string docstr, token &p) - { - return p.doc(std::move(docstr)); - } - //--------------------------------------------------------- - template - inline T &&doc(doc_string docstr, token &&p) - { - return std::move(p.doc(std::move(docstr))); - } +template +inline T& +doc(doc_string docstr, token& p) +{ + return p.doc(std::move(docstr)); +} +//--------------------------------------------------------- +template +inline T&& +doc(doc_string docstr, token&& p) +{ + return std::move(p.doc(std::move(docstr))); +} - } // namespace detail - /*************************************************************************/ /** + +} // namespace detail + + + +/*************************************************************************//** * * @brief contains parameter matching functions and function classes * *****************************************************************************/ - namespace match - { - /*************************************************************************/ /** +namespace match { + + +/*************************************************************************//** * * @brief predicate that is always true * *****************************************************************************/ - inline bool any(const arg_string &) - { - return true; - } +inline bool +any(const arg_string&) { return true; } - /*************************************************************************/ /** +/*************************************************************************//** * * @brief predicate that is always false * *****************************************************************************/ - inline bool none(const arg_string &) - { - return false; - } +inline bool +none(const arg_string&) { return false; } + - /*************************************************************************/ /** + +/*************************************************************************//** * * @brief predicate that returns true if the argument string is non-empty string * *****************************************************************************/ - inline bool nonempty(const arg_string &s) - { - return !s.empty(); - } +inline bool +nonempty(const arg_string& s) { + return !s.empty(); +} + - /*************************************************************************/ /** + +/*************************************************************************//** * * @brief predicate that returns true if the argument is a non-empty * string that consists only of alphanumeric characters * *****************************************************************************/ - inline bool alphanumeric(const arg_string &s) - { - if(s.empty()) return false; - return std::all_of(s.begin(), s.end(), [](char c) { return std::isalnum(c); }); - } +inline bool +alphanumeric(const arg_string& s) { + if(s.empty()) return false; + return std::all_of(s.begin(), s.end(), [](char c) {return txt::isalnum(c); }); +} + - /*************************************************************************/ /** + +/*************************************************************************//** * * @brief predicate that returns true if the argument is a non-empty * string that consists only of alphabetic characters * *****************************************************************************/ - inline bool alphabetic(const arg_string &s) - { - return std::all_of(s.begin(), s.end(), [](char c) { return std::isalpha(c); }); - } +inline bool +alphabetic(const arg_string& s) { + return std::all_of(s.begin(), s.end(), [](char c) {return txt::isalpha(c); }); +} + - /*************************************************************************/ /** + +/*************************************************************************//** * * @brief predicate that returns false if the argument string is * equal to any string from the exclusion list * *****************************************************************************/ - class none_of - { - public: - none_of(arg_list strs) - : excluded_{ std::move(strs) } - {} - - template - none_of(arg_string str, Strings &&...strs) - : excluded_{ std::move(str), std::forward(strs)... } - {} +class none_of +{ +public: + none_of(arg_list strs): + excluded_{std::move(strs)} + {} + + template + none_of(arg_string str, Strings&&... strs): + excluded_{std::move(str), std::forward(strs)...} + {} + + template + none_of(const char* str, Strings&&... strs): + excluded_{arg_string(str), std::forward(strs)...} + {} + + bool operator () (const arg_string& arg) const { + return (std::find(begin(excluded_), end(excluded_), arg) + == end(excluded_)); + } - template - none_of(const char *str, Strings &&...strs) - : excluded_{ arg_string(str), std::forward(strs)... } - {} +private: + arg_list excluded_; +}; - bool operator()(const arg_string &arg) const - { - return (std::find(begin(excluded_), end(excluded_), arg) == end(excluded_)); - } - private: - arg_list excluded_; - }; - /*************************************************************************/ /** +/*************************************************************************//** * * @brief predicate that returns the first substring match within the input * string that rmeepresents a number * (with at maximum one decimal point and digit separators) * *****************************************************************************/ - class numbers - { - public: - explicit numbers(char decimalPoint = '.', char digitSeparator = ' ', char exponentSeparator = 'e') - : decpoint_{ decimalPoint } - , separator_{ digitSeparator } - , exp_{ exponentSeparator } - {} +class numbers +{ +public: + explicit + numbers(char decimalPoint = '.', + char digitSeparator = ' ', + char exponentSeparator = 'e') + : + decpoint_{decimalPoint}, separator_{digitSeparator}, + exp_{exponentSeparator} + {} + + subrange operator () (const arg_string& s) const { + return str::first_number_match(s, separator_, decpoint_, exp_); + } - subrange operator()(const arg_string &s) const - { - return str::first_number_match(s, separator_, decpoint_, exp_); - } +private: + char decpoint_; + char separator_; + char exp_; +}; - private: - char decpoint_; - char separator_; - char exp_; - }; - /*************************************************************************/ /** + +/*************************************************************************//** * * @brief predicate that returns true if the input string represents an integer * (with optional digit separators) * *****************************************************************************/ - class integers - { - public: - explicit integers(char digitSeparator = ' ') - : separator_{ digitSeparator } - {} +class integers { +public: + explicit + integers(char digitSeparator = ' '): separator_{digitSeparator} {} + + subrange operator () (const arg_string& s) const { + return str::first_integer_match(s, separator_); + } + +private: + char separator_; +}; - subrange operator()(const arg_string &s) const - { - return str::first_integer_match(s, separator_); - } - private: - char separator_; - }; - /*************************************************************************/ /** +/*************************************************************************//** * * @brief predicate that returns true if the input string represents * a non-negative integer (with optional digit separators) * *****************************************************************************/ - class positive_integers - { - public: - explicit positive_integers(char digitSeparator = ' ') - : separator_{ digitSeparator } - {} +class positive_integers { +public: + explicit + positive_integers(char digitSeparator = ' '): separator_{digitSeparator} {} - subrange operator()(const arg_string &s) const - { - auto match = str::first_integer_match(s, separator_); - if(!match) return subrange{}; - if(s[match.at()] == '-') return subrange{}; - return match; - } + subrange operator () (const arg_string& s) const { + auto match = str::first_integer_match(s, separator_); + if(!match) return subrange{}; + if(s[match.at()] == '-') return subrange{}; + return match; + } + +private: + char separator_; +}; - private: - char separator_; - }; - /*************************************************************************/ /** + +/*************************************************************************//** * * @brief predicate that returns true if the input string * contains a given substring * *****************************************************************************/ - class substring - { - public: - explicit substring(arg_string str) - : str_{ std::move(str) } - {} +class substring +{ +public: + explicit + substring(arg_string str): str_{std::move(str)} {} - subrange operator()(const arg_string &s) const - { - return str::substring_match(s, str_); - } + subrange operator () (const arg_string& s) const { + return str::substring_match(s, str_); + } + +private: + arg_string str_; +}; - private: - arg_string str_; - }; - /*************************************************************************/ /** + +/*************************************************************************//** * * @brief predicate that returns true if the input string starts * with a given prefix * *****************************************************************************/ - class prefix - { - public: - explicit prefix(arg_string p) - : prefix_{ std::move(p) } - {} +class prefix { +public: + explicit + prefix(arg_string p): prefix_{std::move(p)} {} + + bool operator () (const arg_string& s) const { + return s.find(prefix_) == 0; + } + +private: + arg_string prefix_; +}; - bool operator()(const arg_string &s) const - { - return s.find(prefix_) == 0; - } - private: - arg_string prefix_; - }; - /*************************************************************************/ /** +/*************************************************************************//** * * @brief predicate that returns true if the input string does not start * with a given prefix * *****************************************************************************/ - class prefix_not - { - public: - explicit prefix_not(arg_string p) - : prefix_{ std::move(p) } - {} +class prefix_not { +public: + explicit + prefix_not(arg_string p): prefix_{std::move(p)} {} - bool operator()(const arg_string &s) const - { - return s.find(prefix_) != 0; - } + bool operator () (const arg_string& s) const { + return s.find(prefix_) != 0; + } + +private: + arg_string prefix_; +}; - private: - arg_string prefix_; - }; - /** @brief alias for prefix_not */ - using noprefix = prefix_not; +/** @brief alias for prefix_not */ +using noprefix = prefix_not; - /*************************************************************************/ /** + + +/*************************************************************************//** * * @brief predicate that returns true if the length of the input string * is wihtin a given interval * *****************************************************************************/ - class length - { - public: - explicit length(std::size_t exact) - : min_{ exact } - , max_{ exact } - {} +class length { +public: + explicit + length(std::size_t exact): + min_{exact}, max_{exact} + {} - explicit length(std::size_t min, std::size_t max) - : min_{ min } - , max_{ max } - {} + explicit + length(std::size_t min, std::size_t max): + min_{min}, max_{max} + {} - bool operator()(const arg_string &s) const - { - return s.size() >= min_ && s.size() <= max_; - } + bool operator () (const arg_string& s) const { + return s.size() >= min_ && s.size() <= max_; + } - private: - std::size_t min_; - std::size_t max_; - }; +private: + std::size_t min_; + std::size_t max_; +}; - /*************************************************************************/ /** + +/*************************************************************************//** * * @brief makes function object that returns true if the input string has a * given minimum length * *****************************************************************************/ - inline length min_length(std::size_t min) - { - return length{ min, arg_string::npos - 1 }; - } +inline length min_length(std::size_t min) +{ + return length{min, arg_string::npos-1}; +} - /*************************************************************************/ /** +/*************************************************************************//** * * @brief makes function object that returns true if the input string is * not longer than a given maximum length * *****************************************************************************/ - inline length max_length(std::size_t max) - { - return length{ 0, max }; - } +inline length max_length(std::size_t max) +{ + return length{0, max}; +} + + +} // namespace match + - } // namespace match - /*************************************************************************/ /** + + +/*************************************************************************//** * * @brief command line parameter that can match one or many arguments. * *****************************************************************************/ - class parameter : public detail::token, public detail::action_provider - { - /** @brief adapts a 'match_predicate' to the 'match_function' interface */ - class predicate_adapter - { - public: - explicit predicate_adapter(match_predicate pred) - : match_{ std::move(pred) } - {} - - subrange operator()(const arg_string &arg) const - { - return match_(arg) ? subrange{ 0, arg.size() } : subrange{}; - } - - private: - match_predicate match_; - }; - +class parameter : + public detail::token, + public detail::action_provider +{ + /** @brief adapts a 'match_predicate' to the 'match_function' interface */ + class predicate_adapter { public: - //--------------------------------------------------------------- - /** @brief makes default parameter, that will match nothing */ - parameter() - : flags_{} - , matcher_{ predicate_adapter{ match::none } } - , label_{} - , required_{ false } - , greedy_{ false } - {} + explicit + predicate_adapter(match_predicate pred): match_{std::move(pred)} {} - /** @brief makes "flag" parameter */ - template - explicit parameter(arg_string str, Strings &&...strs) - : flags_{} - , matcher_{ predicate_adapter{ match::none } } - , label_{} - , required_{ false } - , greedy_{ false } - { - add_flags(std::move(str), std::forward(strs)...); + subrange operator () (const arg_string& arg) const { + return match_(arg) ? subrange{0,arg.size()} : subrange{}; } - /** @brief makes "flag" parameter from range of strings */ - explicit parameter(const arg_list &flaglist) - : flags_{} - , matcher_{ predicate_adapter{ match::none } } - , label_{} - , required_{ false } - , greedy_{ false } - { - add_flags(flaglist); - } + private: + match_predicate match_; + }; - //----------------------------------------------------- - /** @brief makes "value" parameter with custom match predicate +public: + //--------------------------------------------------------------- + /** @brief makes default parameter, that will match nothing */ + parameter(): + flags_{}, + matcher_{predicate_adapter{match::none}}, + label_{}, required_{false}, greedy_{false} + {} + + /** @brief makes "flag" parameter */ + template + explicit + parameter(arg_string str, Strings&&... strs): + flags_{}, + matcher_{predicate_adapter{match::none}}, + label_{}, required_{false}, greedy_{false} + { + add_flags(std::move(str), std::forward(strs)...); + } + + /** @brief makes "flag" parameter from range of strings */ + explicit + parameter(const arg_list& flaglist): + flags_{}, + matcher_{predicate_adapter{match::none}}, + label_{}, required_{false}, greedy_{false} + { + add_flags(flaglist); + } + + //----------------------------------------------------- + /** @brief makes "value" parameter with custom match predicate * (= yes/no matcher) */ - explicit parameter(match_predicate filter) - : flags_{} - , matcher_{ predicate_adapter{ std::move(filter) } } - , label_{} - , required_{ false } - , greedy_{ false } - {} - - /** @brief makes "value" parameter with custom match function + explicit + parameter(match_predicate filter): + flags_{}, + matcher_{predicate_adapter{std::move(filter)}}, + label_{}, required_{false}, greedy_{false} + {} + + /** @brief makes "value" parameter with custom match function * (= partial matcher) */ - explicit parameter(match_function filter) - : flags_{} - , matcher_{ std::move(filter) } - , label_{} - , required_{ false } - , greedy_{ false } - {} + explicit + parameter(match_function filter): + flags_{}, + matcher_{std::move(filter)}, + label_{}, required_{false}, greedy_{false} + {} + + + //--------------------------------------------------------------- + /** @brief returns if a parameter is required */ + bool + required() const noexcept { + return required_; + } - //--------------------------------------------------------------- - /** @brief returns if a parameter is required */ - bool required() const noexcept - { - return required_; - } + /** @brief determines if a parameter is required */ + parameter& + required(bool yes) noexcept { + required_ = yes; + return *this; + } - /** @brief determines if a parameter is required */ - parameter &required(bool yes) noexcept - { - required_ = yes; - return *this; - } - //--------------------------------------------------------------- - /** @brief returns if a parameter should match greedily */ - bool greedy() const noexcept - { - return greedy_; - } + //--------------------------------------------------------------- + /** @brief returns if a parameter should match greedily */ + bool + greedy() const noexcept { + return greedy_; + } + + /** @brief determines if a parameter should match greedily */ + parameter& + greedy(bool yes) noexcept { + greedy_ = yes; + return *this; + } - /** @brief determines if a parameter should match greedily */ - parameter &greedy(bool yes) noexcept - { - greedy_ = yes; - return *this; - } - //--------------------------------------------------------------- - /** @brief returns parameter label; + //--------------------------------------------------------------- + /** @brief returns parameter label; * will be used for documentation, if flags are empty */ - const doc_string &label() const - { - return label_; - } + const doc_string& + label() const { + return label_; + } - /** @brief sets parameter label; + /** @brief sets parameter label; * will be used for documentation, if flags are empty */ - parameter &label(const doc_string &lbl) - { - label_ = lbl; - return *this; - } + parameter& + label(const doc_string& lbl) { + label_ = lbl; + return *this; + } - /** @brief sets parameter label; + /** @brief sets parameter label; * will be used for documentation, if flags are empty */ - parameter &label(doc_string &&lbl) - { - label_ = lbl; - return *this; - } + parameter& + label(doc_string&& lbl) { + label_ = lbl; + return *this; + } + - //--------------------------------------------------------------- - /** @brief returns either longest matching prefix of 'arg' in any + //--------------------------------------------------------------- + /** @brief returns either longest matching prefix of 'arg' in any * of the flags or the result of the custom match operation */ - subrange match(const arg_string &arg) const - { - if(flags_.empty()) - { - return matcher_(arg); - } - else - { - //empty flags are not allowed - if(arg.empty()) return subrange{}; + subrange + match(const arg_string& arg) const + { + if(flags_.empty()) { + return matcher_(arg); + } + else { + //empty flags are not allowed + if(arg.empty()) return subrange{}; - if(std::find(flags_.begin(), flags_.end(), arg) != flags_.end()) - { - return subrange{ 0, arg.size() }; - } - return str::longest_prefix_match(arg, flags_); + if(std::find(flags_.begin(), flags_.end(), arg) != flags_.end()) { + return subrange{0,arg.size()}; } + return str::longest_prefix_match(arg, flags_); } + } - //--------------------------------------------------------------- - /** @brief access range of flag strings */ - const arg_list &flags() const noexcept - { - return flags_; - } - /** @brief access custom match operation */ - const match_function &matcher() const noexcept - { - return matcher_; - } + //--------------------------------------------------------------- + /** @brief access range of flag strings */ + const arg_list& + flags() const noexcept { + return flags_; + } - //--------------------------------------------------------------- - /** @brief prepend prefix to each flag */ - inline friend parameter &with_prefix(const arg_string &prefix, parameter &p) - { - if(prefix.empty() || p.flags().empty()) return p; + /** @brief access custom match operation */ + const match_function& + matcher() const noexcept { + return matcher_; + } - for(auto &f : p.flags_) - { - if(f.find(prefix) != 0) f.insert(0, prefix); - } - return p; + + //--------------------------------------------------------------- + /** @brief prepend prefix to each flag */ + inline friend parameter& + with_prefix(const arg_string& prefix, parameter& p) + { + if(prefix.empty() || p.flags().empty()) return p; + + for(auto& f : p.flags_) { + if(f.find(prefix) != 0) f.insert(0, prefix); } + return p; + } - /** @brief prepend prefix to each flag + + /** @brief prepend prefix to each flag */ - inline friend parameter &with_prefixes_short_long(const arg_string &shortpfx, - const arg_string &longpfx, - parameter &p) - { - if(shortpfx.empty() && longpfx.empty()) return p; - if(p.flags().empty()) return p; + inline friend parameter& + with_prefixes_short_long( + const arg_string& shortpfx, const arg_string& longpfx, + parameter& p) + { + if(shortpfx.empty() && longpfx.empty()) return p; + if(p.flags().empty()) return p; - for(auto &f : p.flags_) - { - if(f.size() == 1) - { - if(f.find(shortpfx) != 0) f.insert(0, shortpfx); - } - else - { - if(f.find(longpfx) != 0) f.insert(0, longpfx); - } + for(auto& f : p.flags_) { + if(f.size() == 1) { + if(f.find(shortpfx) != 0) f.insert(0, shortpfx); + } else { + if(f.find(longpfx) != 0) f.insert(0, longpfx); } - return p; } + return p; + } - //--------------------------------------------------------------- - /** @brief prepend suffix to each flag */ - inline friend parameter &with_suffix(const arg_string &suffix, parameter &p) - { - if(suffix.empty() || p.flags().empty()) return p; - for(auto &f : p.flags_) - { - if(f.find(suffix) + suffix.size() != f.size()) - { - f.insert(f.end(), suffix.begin(), suffix.end()); - } + //--------------------------------------------------------------- + /** @brief prepend suffix to each flag */ + inline friend parameter& + with_suffix(const arg_string& suffix, parameter& p) + { + if(suffix.empty() || p.flags().empty()) return p; + + for(auto& f : p.flags_) { + if(f.find(suffix) + suffix.size() != f.size()) { + f.insert(f.end(), suffix.begin(), suffix.end()); } - return p; } + return p; + } + - /** @brief prepend suffix to each flag + /** @brief prepend suffix to each flag */ - inline friend parameter &with_suffixes_short_long(const arg_string &shortsfx, - const arg_string &longsfx, - parameter &p) - { - if(shortsfx.empty() && longsfx.empty()) return p; - if(p.flags().empty()) return p; + inline friend parameter& + with_suffixes_short_long( + const arg_string& shortsfx, const arg_string& longsfx, + parameter& p) + { + if(shortsfx.empty() && longsfx.empty()) return p; + if(p.flags().empty()) return p; - for(auto &f : p.flags_) - { - if(f.size() == 1) - { - if(f.find(shortsfx) + shortsfx.size() != f.size()) - { - f.insert(f.end(), shortsfx.begin(), shortsfx.end()); - } + for(auto& f : p.flags_) { + if(f.size() == 1) { + if(f.find(shortsfx) + shortsfx.size() != f.size()) { + f.insert(f.end(), shortsfx.begin(), shortsfx.end()); } - else - { - if(f.find(longsfx) + longsfx.size() != f.size()) - { - f.insert(f.end(), longsfx.begin(), longsfx.end()); - } + } else { + if(f.find(longsfx) + longsfx.size() != f.size()) { + f.insert(f.end(), longsfx.begin(), longsfx.end()); } } - return p; } + return p; + } - private: - //--------------------------------------------------------------- - void add_flags(arg_string str) - { - //empty flags are not allowed - str::remove_ws(str); - if(!str.empty()) flags_.push_back(std::move(str)); - } +private: + //--------------------------------------------------------------- + void add_flags(arg_string str) { + //empty flags are not allowed + str::remove_ws(str); + if(!str.empty()) flags_.push_back(std::move(str)); + } - //--------------------------------------------------------------- - void add_flags(const arg_list &strs) - { - if(strs.empty()) return; - flags_.reserve(flags_.size() + strs.size()); - for(const auto &s : strs) - add_flags(s); - } + //--------------------------------------------------------------- + void add_flags(const arg_list& strs) { + if(strs.empty()) return; + flags_.reserve(flags_.size() + strs.size()); + for(const auto& s : strs) add_flags(s); + } - template - void add_flags(String1 &&s1, String2 &&s2, Strings &&...ss) - { - flags_.reserve(2 + sizeof...(ss)); - add_flags(std::forward(s1)); - add_flags(std::forward(s2), std::forward(ss)...); - } + template + void + add_flags(String1&& s1, String2&& s2, Strings&&... ss) { + flags_.reserve(2 + sizeof...(ss)); + add_flags(std::forward(s1)); + add_flags(std::forward(s2), std::forward(ss)...); + } - arg_list flags_; - match_function matcher_; - doc_string label_; - bool required_ = false; - bool greedy_ = false; - }; + arg_list flags_; + match_function matcher_; + doc_string label_; + bool required_ = false; + bool greedy_ = false; +}; - /*************************************************************************/ /** + + + +/*************************************************************************//** * * @brief makes required non-blocking exact match parameter * *****************************************************************************/ - template - inline parameter command(String &&flag, Strings &&...flags) - { - return parameter{ std::forward(flag), std::forward(flags)... } - .required(true) - .blocking(true) - .repeatable(false); - } +template +inline parameter +command(String&& flag, Strings&&... flags) +{ + return parameter{std::forward(flag), std::forward(flags)...} + .required(true).blocking(true).repeatable(false); +} + + - /*************************************************************************/ /** +/*************************************************************************//** * * @brief makes required non-blocking exact match parameter * *****************************************************************************/ - template - inline parameter required(String &&flag, Strings &&...flags) - { - return parameter{ std::forward(flag), std::forward(flags)... } - .required(true) - .blocking(false) - .repeatable(false); - } +template +inline parameter +required(String&& flag, Strings&&... flags) +{ + return parameter{std::forward(flag), std::forward(flags)...} + .required(true).blocking(false).repeatable(false); +} + - /*************************************************************************/ /** + +/*************************************************************************//** * * @brief makes optional, non-blocking exact match parameter * *****************************************************************************/ - template - inline parameter option(String &&flag, Strings &&...flags) - { - return parameter{ std::forward(flag), std::forward(flags)... } - .required(false) - .blocking(false) - .repeatable(false); - } +template +inline parameter +option(String&& flag, Strings&&... flags) +{ + return parameter{std::forward(flag), std::forward(flags)...} + .required(false).blocking(false).repeatable(false); +} - /*************************************************************************/ /** + + +/*************************************************************************//** * * @brief makes required, blocking, repeatable value parameter; * matches any non-empty string * *****************************************************************************/ - template - inline parameter value(const doc_string &label, Targets &&...tgts) - { - return parameter{ match::nonempty } - .label(label) - .target(std::forward(tgts)...) - .required(true) - .blocking(true) - .repeatable(false); - } - - template::value - || traits::is_callable::value>::type> - inline parameter value(Filter &&filter, doc_string label, Targets &&...tgts) - { - return parameter{ std::forward(filter) } - .label(label) - .target(std::forward(tgts)...) - .required(true) - .blocking(true) - .repeatable(false); - } +template +inline parameter +value(const doc_string& label, Targets&&... tgts) +{ + return parameter{match::nonempty} + .label(label) + .target(std::forward(tgts)...) + .required(true).blocking(true).repeatable(false); +} + +template::value || + traits::is_callable::value>::type> +inline parameter +value(Filter&& filter, doc_string label, Targets&&... tgts) +{ + return parameter{std::forward(filter)} + .label(label) + .target(std::forward(tgts)...) + .required(true).blocking(true).repeatable(false); +} + - /*************************************************************************/ /** + +/*************************************************************************//** * * @brief makes required, blocking, repeatable value parameter; * matches any non-empty string * *****************************************************************************/ - template - inline parameter values(const doc_string &label, Targets &&...tgts) - { - return parameter{ match::nonempty } - .label(label) - .target(std::forward(tgts)...) - .required(true) - .blocking(true) - .repeatable(true); - } - - template::value - || traits::is_callable::value>::type> - inline parameter values(Filter &&filter, doc_string label, Targets &&...tgts) - { - return parameter{ std::forward(filter) } - .label(label) - .target(std::forward(tgts)...) - .required(true) - .blocking(true) - .repeatable(true); - } +template +inline parameter +values(const doc_string& label, Targets&&... tgts) +{ + return parameter{match::nonempty} + .label(label) + .target(std::forward(tgts)...) + .required(true).blocking(true).repeatable(true); +} + +template::value || + traits::is_callable::value>::type> +inline parameter +values(Filter&& filter, doc_string label, Targets&&... tgts) +{ + return parameter{std::forward(filter)} + .label(label) + .target(std::forward(tgts)...) + .required(true).blocking(true).repeatable(true); +} + + - /*************************************************************************/ /** +/*************************************************************************//** * * @brief makes optional, blocking value parameter; * matches any non-empty string * *****************************************************************************/ - template - inline parameter opt_value(const doc_string &label, Targets &&...tgts) - { - return parameter{ match::nonempty } - .label(label) - .target(std::forward(tgts)...) - .required(false) - .blocking(false) - .repeatable(false); - } - - template::value - || traits::is_callable::value>::type> - inline parameter opt_value(Filter &&filter, doc_string label, Targets &&...tgts) - { - return parameter{ std::forward(filter) } - .label(label) - .target(std::forward(tgts)...) - .required(false) - .blocking(false) - .repeatable(false); - } +template +inline parameter +opt_value(const doc_string& label, Targets&&... tgts) +{ + return parameter{match::nonempty} + .label(label) + .target(std::forward(tgts)...) + .required(false).blocking(false).repeatable(false); +} + +template::value || + traits::is_callable::value>::type> +inline parameter +opt_value(Filter&& filter, doc_string label, Targets&&... tgts) +{ + return parameter{std::forward(filter)} + .label(label) + .target(std::forward(tgts)...) + .required(false).blocking(false).repeatable(false); +} + - /*************************************************************************/ /** + +/*************************************************************************//** * * @brief makes optional, blocking, repeatable value parameter; * matches any non-empty string * *****************************************************************************/ - template - inline parameter opt_values(const doc_string &label, Targets &&...tgts) - { - return parameter{ match::nonempty } - .label(label) - .target(std::forward(tgts)...) - .required(false) - .blocking(false) - .repeatable(true); - } - - template::value - || traits::is_callable::value>::type> - inline parameter opt_values(Filter &&filter, doc_string label, Targets &&...tgts) - { - return parameter{ std::forward(filter) } - .label(label) - .target(std::forward(tgts)...) - .required(false) - .blocking(false) - .repeatable(true); - } +template +inline parameter +opt_values(const doc_string& label, Targets&&... tgts) +{ + return parameter{match::nonempty} + .label(label) + .target(std::forward(tgts)...) + .required(false).blocking(false).repeatable(true); +} + +template::value || + traits::is_callable::value>::type> +inline parameter +opt_values(Filter&& filter, doc_string label, Targets&&... tgts) +{ + return parameter{std::forward(filter)} + .label(label) + .target(std::forward(tgts)...) + .required(false).blocking(false).repeatable(true); +} - /*************************************************************************/ /** + + +/*************************************************************************//** * * @brief makes required, blocking value parameter; * matches any string consisting of alphanumeric characters * *****************************************************************************/ - template - inline parameter word(const doc_string &label, Targets &&...tgts) - { - return parameter{ match::alphanumeric } - .label(label) - .target(std::forward(tgts)...) - .required(true) - .blocking(true) - .repeatable(false); - } +template +inline parameter +word(const doc_string& label, Targets&&... tgts) +{ + return parameter{match::alphanumeric} + .label(label) + .target(std::forward(tgts)...) + .required(true).blocking(true).repeatable(false); +} + - /*************************************************************************/ /** + +/*************************************************************************//** * * @brief makes required, blocking, repeatable value parameter; * matches any string consisting of alphanumeric characters * *****************************************************************************/ - template - inline parameter words(const doc_string &label, Targets &&...tgts) - { - return parameter{ match::alphanumeric } - .label(label) - .target(std::forward(tgts)...) - .required(true) - .blocking(true) - .repeatable(true); - } +template +inline parameter +words(const doc_string& label, Targets&&... tgts) +{ + return parameter{match::alphanumeric} + .label(label) + .target(std::forward(tgts)...) + .required(true).blocking(true).repeatable(true); +} + + - /*************************************************************************/ /** +/*************************************************************************//** * * @brief makes optional, blocking value parameter; * matches any string consisting of alphanumeric characters * *****************************************************************************/ - template - inline parameter opt_word(const doc_string &label, Targets &&...tgts) - { - return parameter{ match::alphanumeric } - .label(label) - .target(std::forward(tgts)...) - .required(false) - .blocking(false) - .repeatable(false); - } +template +inline parameter +opt_word(const doc_string& label, Targets&&... tgts) +{ + return parameter{match::alphanumeric} + .label(label) + .target(std::forward(tgts)...) + .required(false).blocking(false).repeatable(false); +} + - /*************************************************************************/ /** + +/*************************************************************************//** * * @brief makes optional, blocking, repeatable value parameter; * matches any string consisting of alphanumeric characters * *****************************************************************************/ - template - inline parameter opt_words(const doc_string &label, Targets &&...tgts) - { - return parameter{ match::alphanumeric } - .label(label) - .target(std::forward(tgts)...) - .required(false) - .blocking(false) - .repeatable(true); - } +template +inline parameter +opt_words(const doc_string& label, Targets&&... tgts) +{ + return parameter{match::alphanumeric} + .label(label) + .target(std::forward(tgts)...) + .required(false).blocking(false).repeatable(true); +} - /*************************************************************************/ /** + + +/*************************************************************************//** * * @brief makes required, blocking value parameter; * matches any string that represents a number * *****************************************************************************/ - template - inline parameter number(const doc_string &label, Targets &&...tgts) - { - return parameter{ match::numbers{} } - .label(label) - .target(std::forward(tgts)...) - .required(true) - .blocking(true) - .repeatable(false); - } +template +inline parameter +number(const doc_string& label, Targets&&... tgts) +{ + return parameter{match::numbers{}} + .label(label) + .target(std::forward(tgts)...) + .required(true).blocking(true).repeatable(false); +} + - /*************************************************************************/ /** + +/*************************************************************************//** * * @brief makes required, blocking, repeatable value parameter; * matches any string that represents a number * *****************************************************************************/ - template - inline parameter numbers(const doc_string &label, Targets &&...tgts) - { - return parameter{ match::numbers{} } - .label(label) - .target(std::forward(tgts)...) - .required(true) - .blocking(true) - .repeatable(true); - } +template +inline parameter +numbers(const doc_string& label, Targets&&... tgts) +{ + return parameter{match::numbers{}} + .label(label) + .target(std::forward(tgts)...) + .required(true).blocking(true).repeatable(true); +} + + - /*************************************************************************/ /** +/*************************************************************************//** * * @brief makes optional, blocking value parameter; * matches any string that represents a number * *****************************************************************************/ - template - inline parameter opt_number(const doc_string &label, Targets &&...tgts) - { - return parameter{ match::numbers{} } - .label(label) - .target(std::forward(tgts)...) - .required(false) - .blocking(false) - .repeatable(false); - } +template +inline parameter +opt_number(const doc_string& label, Targets&&... tgts) +{ + return parameter{match::numbers{}} + .label(label) + .target(std::forward(tgts)...) + .required(false).blocking(false).repeatable(false); +} + - /*************************************************************************/ /** + +/*************************************************************************//** * * @brief makes optional, blocking, repeatable value parameter; * matches any string that represents a number * *****************************************************************************/ - template - inline parameter opt_numbers(const doc_string &label, Targets &&...tgts) - { - return parameter{ match::numbers{} } - .label(label) - .target(std::forward(tgts)...) - .required(false) - .blocking(false) - .repeatable(true); - } +template +inline parameter +opt_numbers(const doc_string& label, Targets&&... tgts) +{ + return parameter{match::numbers{}} + .label(label) + .target(std::forward(tgts)...) + .required(false).blocking(false).repeatable(true); +} - /*************************************************************************/ /** + + +/*************************************************************************//** * * @brief makes required, blocking value parameter; * matches any string that represents an integer * *****************************************************************************/ - template - inline parameter integer(const doc_string &label, Targets &&...tgts) - { - return parameter{ match::integers{} } - .label(label) - .target(std::forward(tgts)...) - .required(true) - .blocking(true) - .repeatable(false); - } +template +inline parameter +integer(const doc_string& label, Targets&&... tgts) +{ + return parameter{match::integers{}} + .label(label) + .target(std::forward(tgts)...) + .required(true).blocking(true).repeatable(false); +} + - /*************************************************************************/ /** + +/*************************************************************************//** * * @brief makes required, blocking, repeatable value parameter; * matches any string that represents an integer * *****************************************************************************/ - template - inline parameter integers(const doc_string &label, Targets &&...tgts) - { - return parameter{ match::integers{} } - .label(label) - .target(std::forward(tgts)...) - .required(true) - .blocking(true) - .repeatable(true); - } +template +inline parameter +integers(const doc_string& label, Targets&&... tgts) +{ + return parameter{match::integers{}} + .label(label) + .target(std::forward(tgts)...) + .required(true).blocking(true).repeatable(true); +} + + - /*************************************************************************/ /** +/*************************************************************************//** * * @brief makes optional, blocking value parameter; * matches any string that represents an integer * *****************************************************************************/ - template - inline parameter opt_integer(const doc_string &label, Targets &&...tgts) - { - return parameter{ match::integers{} } - .label(label) - .target(std::forward(tgts)...) - .required(false) - .blocking(false) - .repeatable(false); - } +template +inline parameter +opt_integer(const doc_string& label, Targets&&... tgts) +{ + return parameter{match::integers{}} + .label(label) + .target(std::forward(tgts)...) + .required(false).blocking(false).repeatable(false); +} + - /*************************************************************************/ /** + +/*************************************************************************//** * * @brief makes optional, blocking, repeatable value parameter; * matches any string that represents an integer * *****************************************************************************/ - template - inline parameter opt_integers(const doc_string &label, Targets &&...tgts) - { - return parameter{ match::integers{} } - .label(label) - .target(std::forward(tgts)...) - .required(false) - .blocking(false) - .repeatable(true); - } +template +inline parameter +opt_integers(const doc_string& label, Targets&&... tgts) +{ + return parameter{match::integers{}} + .label(label) + .target(std::forward(tgts)...) + .required(false).blocking(false).repeatable(true); +} - /*************************************************************************/ /** + + +/*************************************************************************//** * * @brief makes catch-all value parameter * *****************************************************************************/ - template - inline parameter any_other(Targets &&...tgts) - { - return parameter{ match::any } - .target(std::forward(tgts)...) - .required(false) - .blocking(false) - .repeatable(true); - } +template +inline parameter +any_other(Targets&&... tgts) +{ + return parameter{match::any} + .target(std::forward(tgts)...) + .required(false).blocking(false).repeatable(true); +} + - /*************************************************************************/ /** + +/*************************************************************************//** * * @brief makes catch-all value parameter with custom filter * *****************************************************************************/ - template::value - || traits::is_callable::value>::type> - inline parameter any(Filter &&filter, Targets &&...tgts) - { - return parameter{ std::forward(filter) } - .target(std::forward(tgts)...) - .required(false) - .blocking(false) - .repeatable(true); - } +template::value || + traits::is_callable::value>::type> +inline parameter +any(Filter&& filter, Targets&&... tgts) +{ + return parameter{std::forward(filter)} + .target(std::forward(tgts)...) + .required(false).blocking(false).repeatable(true); +} + + - /*************************************************************************/ /** + +/*************************************************************************//** * * @brief group of parameters and/or other groups; * can be configured to act as a group of alternatives (exclusive match) * *****************************************************************************/ - class group : public detail::token - { - //--------------------------------------------------------------- - /** +class group : + public detail::token +{ + //--------------------------------------------------------------- + /** * @brief tagged union type that either stores a parameter or a group * and provides a common interface to them * could be replaced by std::variant in the future @@ -2515,1331 +2615,1217 @@ namespace clipp * nasty problems associated with it and the implementation * becomes bloated and needlessly complicated. */ - template - struct child_t - { - enum class type : char - { - param, - group - }; + template + struct child_t { + enum class type : char {param, group}; + public: - public: - explicit child_t(const Param &v) - : m_{ v } - , type_{ type::param } - {} - child_t(Param &&v) noexcept - : m_{ std::move(v) } - , type_{ type::param } - {} + explicit + child_t(const Param& v) : m_{v}, type_{type::param} {} + child_t( Param&& v) noexcept : m_{std::move(v)}, type_{type::param} {} - explicit child_t(const Group &g) - : m_{ g } - , type_{ type::group } - {} - child_t(Group &&g) noexcept - : m_{ std::move(g) } - , type_{ type::group } - {} + explicit + child_t(const Group& g) : m_{g}, type_{type::group} {} + child_t( Group&& g) noexcept : m_{std::move(g)}, type_{type::group} {} - child_t(const child_t &src) - : type_{ src.type_ } - { - switch(type_) - { - default: - case type::param: new(&m_) data{ src.m_.param }; break; - case type::group: new(&m_) data{ src.m_.group }; break; - } + child_t(const child_t& src): type_{src.type_} { + switch(type_) { + default: + case type::param: new(&m_)data{src.m_.param}; break; + case type::group: new(&m_)data{src.m_.group}; break; } + } - child_t(child_t &&src) noexcept - : type_{ src.type_ } - { - switch(type_) - { - default: - case type::param: new(&m_) data{ std::move(src.m_.param) }; break; - case type::group: new(&m_) data{ std::move(src.m_.group) }; break; - } + child_t(child_t&& src) noexcept : type_{src.type_} { + switch(type_) { + default: + case type::param: new(&m_)data{std::move(src.m_.param)}; break; + case type::group: new(&m_)data{std::move(src.m_.group)}; break; } + } - child_t &operator=(const child_t &src) - { - destroy_content(); - type_ = src.type_; - switch(type_) - { - default: - case type::param: new(&m_) data{ src.m_.param }; break; - case type::group: new(&m_) data{ src.m_.group }; break; - } - return *this; + child_t& operator = (const child_t& src) { + destroy_content(); + type_ = src.type_; + switch(type_) { + default: + case type::param: new(&m_)data{src.m_.param}; break; + case type::group: new(&m_)data{src.m_.group}; break; } + return *this; + } - child_t &operator=(child_t &&src) noexcept - { - destroy_content(); - type_ = src.type_; - switch(type_) - { - default: - case type::param: new(&m_) data{ std::move(src.m_.param) }; break; - case type::group: new(&m_) data{ std::move(src.m_.group) }; break; - } - return *this; + child_t& operator = (child_t&& src) noexcept { + destroy_content(); + type_ = src.type_; + switch(type_) { + default: + case type::param: new(&m_)data{std::move(src.m_.param)}; break; + case type::group: new(&m_)data{std::move(src.m_.group)}; break; } + return *this; + } - ~child_t() - { - destroy_content(); - } + ~child_t() { + destroy_content(); + } - const doc_string &doc() const noexcept - { - switch(type_) - { - default: - case type::param: return m_.param.doc(); - case type::group: return m_.group.doc(); - } + const doc_string& + doc() const noexcept { + switch(type_) { + default: + case type::param: return m_.param.doc(); + case type::group: return m_.group.doc(); } + } - bool blocking() const noexcept - { - switch(type_) - { - case type::param: return m_.param.blocking(); - case type::group: return m_.group.blocking(); - default: return false; - } + bool blocking() const noexcept { + switch(type_) { + case type::param: return m_.param.blocking(); + case type::group: return m_.group.blocking(); + default: return false; } - bool repeatable() const noexcept - { - switch(type_) - { - case type::param: return m_.param.repeatable(); - case type::group: return m_.group.repeatable(); - default: return false; - } + } + bool repeatable() const noexcept { + switch(type_) { + case type::param: return m_.param.repeatable(); + case type::group: return m_.group.repeatable(); + default: return false; } - bool required() const noexcept - { - switch(type_) - { - case type::param: return m_.param.required(); - case type::group: - return (m_.group.exclusive() && m_.group.all_required()) - || (!m_.group.exclusive() && m_.group.any_required()); - default: return false; - } + } + bool required() const noexcept { + switch(type_) { + case type::param: return m_.param.required(); + case type::group: + return (m_.group.exclusive() && m_.group.all_required() ) || + (!m_.group.exclusive() && m_.group.any_required() ); + default: return false; } - bool exclusive() const noexcept - { - switch(type_) - { - case type::group: return m_.group.exclusive(); - case type::param: - default: return false; - } + } + bool exclusive() const noexcept { + switch(type_) { + case type::group: return m_.group.exclusive(); + case type::param: + default: return false; } - std::size_t param_count() const noexcept - { - switch(type_) - { - case type::group: return m_.group.param_count(); - case type::param: - default: return std::size_t(1); - } + } + std::size_t param_count() const noexcept { + switch(type_) { + case type::group: return m_.group.param_count(); + case type::param: + default: return std::size_t(1); } - std::size_t depth() const noexcept - { - switch(type_) - { - case type::group: return m_.group.depth(); - case type::param: - default: return std::size_t(0); - } + } + std::size_t depth() const noexcept { + switch(type_) { + case type::group: return m_.group.depth(); + case type::param: + default: return std::size_t(0); } + } - void execute_actions(const arg_string &arg) const - { - switch(type_) - { - default: - case type::group: return; - case type::param: m_.param.execute_actions(arg); break; - } + void execute_actions(const arg_string& arg) const { + switch(type_) { + default: + case type::group: return; + case type::param: m_.param.execute_actions(arg); break; } - void notify_repeated(arg_index idx) const - { - switch(type_) - { - default: - case type::group: return; - case type::param: m_.param.notify_repeated(idx); break; - } + } + + void notify_repeated(arg_index idx) const { + switch(type_) { + default: + case type::group: return; + case type::param: m_.param.notify_repeated(idx); break; } - void notify_missing(arg_index idx) const - { - switch(type_) - { - default: - case type::group: return; - case type::param: m_.param.notify_missing(idx); break; - } + } + void notify_missing(arg_index idx) const { + switch(type_) { + default: + case type::group: return; + case type::param: m_.param.notify_missing(idx); break; } - void notify_blocked(arg_index idx) const - { - switch(type_) - { - default: - case type::group: return; - case type::param: m_.param.notify_blocked(idx); break; - } + } + void notify_blocked(arg_index idx) const { + switch(type_) { + default: + case type::group: return; + case type::param: m_.param.notify_blocked(idx); break; } - void notify_conflict(arg_index idx) const - { - switch(type_) - { - default: - case type::group: return; - case type::param: m_.param.notify_conflict(idx); break; - } + } + void notify_conflict(arg_index idx) const { + switch(type_) { + default: + case type::group: return; + case type::param: m_.param.notify_conflict(idx); break; } + } - bool is_param() const noexcept - { - return type_ == type::param; - } - bool is_group() const noexcept - { - return type_ == type::group; - } + bool is_param() const noexcept { return type_ == type::param; } + bool is_group() const noexcept { return type_ == type::group; } - Param &as_param() noexcept - { - return m_.param; - } - Group &as_group() noexcept - { - return m_.group; - } + Param& as_param() noexcept { return m_.param; } + Group& as_group() noexcept { return m_.group; } - const Param &as_param() const noexcept - { - return m_.param; - } - const Group &as_group() const noexcept - { - return m_.group; - } + const Param& as_param() const noexcept { return m_.param; } + const Group& as_group() const noexcept { return m_.group; } - private: - void destroy_content() - { - switch(type_) - { - default: - case type::param: m_.param.~Param(); break; - case type::group: m_.group.~Group(); break; - } + private: + void destroy_content() { + switch(type_) { + default: + case type::param: m_.param.~Param(); break; + case type::group: m_.group.~Group(); break; } + } - union data - { - data() - {} - - data(const Param &v) - : param{ v } - {} - data(Param &&v) noexcept - : param{ std::move(v) } - {} - - data(const Group &g) - : group{ g } - {} - data(Group &&g) noexcept - : group{ std::move(g) } - {} - ~data() - {} - - Param param; - Group group; - }; - - data m_; - type type_; - }; + union data { + data() {} - public: - //--------------------------------------------------------------- - using child = child_t; - using value_type = child; + data(const Param& v) : param{v} {} + data( Param&& v) noexcept : param{std::move(v)} {} - private: - using children_store = std::vector; + data(const Group& g) : group{g} {} + data( Group&& g) noexcept : group{std::move(g)} {} + ~data() {} - public: - using const_iterator = children_store::const_iterator; - using iterator = children_store::iterator; - using size_type = children_store::size_type; + Param param; + Group group; + }; - //--------------------------------------------------------------- - /** - * @brief recursively iterates over all nodes - */ - class depth_first_traverser - { - public: - //----------------------------------------------------- - struct context - { - context() = default; - context(const group &p) - : parent{ &p } - , cur{ p.begin() } - , end{ p.end() } - {} - const group *parent = nullptr; - const_iterator cur; - const_iterator end; - }; - using context_list = std::vector; - - //----------------------------------------------------- - class memento - { - friend class depth_first_traverser; - int level_; - context context_; - - public: - int level() const noexcept - { - return level_; - } - const child *param() const noexcept - { - return &(*context_.cur); - } - }; + data m_; + type type_; + }; - depth_first_traverser() = default; - explicit depth_first_traverser(const group &cur) - : stack_{} - { - if(!cur.empty()) stack_.emplace_back(cur); - } +public: + //--------------------------------------------------------------- + using child = child_t; + using value_type = child; - explicit operator bool() const noexcept - { - return !stack_.empty(); - } +private: + using children_store = std::vector; - int level() const noexcept - { - return int(stack_.size()); - } +public: + using const_iterator = children_store::const_iterator; + using iterator = children_store::iterator; + using size_type = children_store::size_type; - bool is_first_in_parent() const noexcept - { - if(stack_.empty()) return false; - return (stack_.back().cur == stack_.back().parent->begin()); - } - bool is_last_in_parent() const noexcept - { - if(stack_.empty()) return false; - return (stack_.back().cur + 1 == stack_.back().end); - } + //--------------------------------------------------------------- + /** + * @brief recursively iterates over all nodes + */ + class depth_first_traverser + { + public: + //----------------------------------------------------- + struct context { + context() = default; + context(const group& p): + parent{&p}, cur{p.begin()}, end{p.end()} + {} + const group* parent = nullptr; + const_iterator cur; + const_iterator end; + }; + using context_list = std::vector; - bool is_last_in_path() const noexcept - { - if(stack_.empty()) return false; - for(const auto &t : stack_) - { - if(t.cur + 1 != t.end) return false; - } - const auto &top = stack_.back(); - //if we have to descend into group on next ++ => not last in path - if(top.cur->is_group()) return false; - return true; - } + //----------------------------------------------------- + class memento { + friend class depth_first_traverser; + int level_; + context context_; + public: + int level() const noexcept { return level_; } + const child* param() const noexcept { return context_.parent ? &(*context_.cur) : nullptr; } + }; - /** @brief inside a group of alternatives >= minlevel */ - bool is_alternative(int minlevel = 0) const noexcept - { - if(stack_.empty()) return false; - if(minlevel > 0) minlevel -= 1; - if(minlevel >= int(stack_.size())) return false; - return std::any_of(stack_.begin() + minlevel, stack_.end(), [](const context &c) { - return c.parent->exclusive(); - }); - } + depth_first_traverser() = default; - /** @brief repeatable or inside a repeatable group >= minlevel */ - bool is_repeatable(int minlevel = 0) const noexcept - { - if(stack_.empty()) return false; - if(stack_.back().cur->repeatable()) return true; - if(minlevel > 0) minlevel -= 1; - if(minlevel >= int(stack_.size())) return false; - return std::any_of(stack_.begin() + minlevel, stack_.end(), [](const context &c) { - return c.parent->repeatable(); - }); - } + explicit + depth_first_traverser(const group& cur): stack_{} { + if(!cur.empty()) stack_.emplace_back(cur); + } - /** @brief inside a particular group */ - bool is_inside(const group *g) const noexcept - { - if(!g) return false; - return std::any_of(stack_.begin(), stack_.end(), [g](const context &c) { return c.parent == g; }); - } + explicit operator bool() const noexcept { + return !stack_.empty(); + } - /** @brief inside group with joinable flags */ - bool joinable() const noexcept - { - if(stack_.empty()) return false; - return std::any_of(stack_.begin(), stack_.end(), [](const context &c) { return c.parent->joinable(); }); - } + int level() const noexcept { + return int(stack_.size()); + } - const context_list &stack() const - { - return stack_; - } + bool is_first_in_parent() const noexcept { + if(stack_.empty()) return false; + return (stack_.back().cur == stack_.back().parent->begin()); + } - /** @brief innermost repeat group */ - const group *innermost_repeat_group() const noexcept - { - auto i = std::find_if(stack_.rbegin(), stack_.rend(), [](const context &c) { - return c.parent->repeatable(); - }); - return i != stack_.rend() ? i->parent : nullptr; - } + bool is_last_in_parent() const noexcept { + if(stack_.empty()) return false; + return (stack_.back().cur+1 == stack_.back().end); + } - /** @brief innermost exclusive (alternatives) group */ - const group *innermost_exclusive_group() const noexcept - { - auto i = std::find_if(stack_.rbegin(), stack_.rend(), [](const context &c) { - return c.parent->exclusive(); - }); - return i != stack_.rend() ? i->parent : nullptr; + bool is_last_in_path() const noexcept { + if(stack_.empty()) return false; + for(const auto& t : stack_) { + if(t.cur+1 != t.end) return false; } + const auto& top = stack_.back(); + //if we have to descend into group on next ++ => not last in path + if(top.cur->is_group()) return false; + return true; + } - /** @brief innermost blocking group */ - const group *innermost_blocking_group() const noexcept - { - auto i = - std::find_if(stack_.rbegin(), stack_.rend(), [](const context &c) { return c.parent->blocking(); }); - return i != stack_.rend() ? i->parent : nullptr; - } + /** @brief inside a group of alternatives >= minlevel */ + bool is_alternative(int minlevel = 0) const noexcept { + if(stack_.empty()) return false; + if(minlevel > 0) minlevel -= 1; + if(minlevel >= int(stack_.size())) return false; + return std::any_of(stack_.begin() + minlevel, stack_.end(), + [](const context& c) { return c.parent->exclusive(); }); + } - /** @brief returns the outermost group that will be left on next ++*/ - const group *outermost_blocking_group_fully_explored() const noexcept - { - if(stack_.empty()) return nullptr; + /** @brief repeatable or inside a repeatable group >= minlevel */ + bool is_repeatable(int minlevel = 0) const noexcept { + if(stack_.empty()) return false; + if(stack_.back().cur->repeatable()) return true; + if(minlevel > 0) minlevel -= 1; + if(minlevel >= int(stack_.size())) return false; + return std::any_of(stack_.begin() + minlevel, stack_.end(), + [](const context& c) { return c.parent->repeatable(); }); + } - const group *g = nullptr; - for(auto i = stack_.rbegin(); i != stack_.rend(); ++i) - { - if(i->cur + 1 == i->end) - { - if(i->parent->blocking()) g = i->parent; - } - else - { - return g; - } + /** @brief inside a particular group */ + bool is_inside(const group* g) const noexcept { + if(!g) return false; + return std::any_of(stack_.begin(), stack_.end(), + [g](const context& c) { return c.parent == g; }); + } + + /** @brief inside group with joinable flags */ + bool joinable() const noexcept { + if(stack_.empty()) return false; + return std::any_of(stack_.begin(), stack_.end(), + [](const context& c) { return c.parent->joinable(); }); + } + + const context_list& + stack() const { + return stack_; + } + + /** @brief innermost repeat group */ + const group* + innermost_repeat_group() const noexcept { + auto i = std::find_if(stack_.rbegin(), stack_.rend(), + [](const context& c) { return c.parent->repeatable(); }); + return i != stack_.rend() ? i->parent : nullptr; + } + + /** @brief innermost exclusive (alternatives) group */ + const group* + innermost_exclusive_group() const noexcept { + auto i = std::find_if(stack_.rbegin(), stack_.rend(), + [](const context& c) { return c.parent->exclusive(); }); + return i != stack_.rend() ? i->parent : nullptr; + } + + /** @brief innermost blocking group */ + const group* + innermost_blocking_group() const noexcept { + auto i = std::find_if(stack_.rbegin(), stack_.rend(), + [](const context& c) { return c.parent->blocking(); }); + return i != stack_.rend() ? i->parent : nullptr; + } + + /** @brief returns the outermost group that will be left on next ++*/ + const group* + outermost_blocking_group_fully_explored() const noexcept { + if(stack_.empty()) return nullptr; + + const group* g = nullptr; + for(auto i = stack_.rbegin(); i != stack_.rend(); ++i) { + if(i->cur+1 == i->end) { + if(i->parent->blocking()) g = i->parent; + } else { + return g; } - return g; } + return g; + } - /** @brief outermost join group */ - const group *outermost_join_group() const noexcept - { - auto i = - std::find_if(stack_.begin(), stack_.end(), [](const context &c) { return c.parent->joinable(); }); - return i != stack_.end() ? i->parent : nullptr; - } + /** @brief outermost join group */ + const group* + outermost_join_group() const noexcept { + auto i = std::find_if(stack_.begin(), stack_.end(), + [](const context& c) { return c.parent->joinable(); }); + return i != stack_.end() ? i->parent : nullptr; + } - const group *root() const noexcept - { - return stack_.empty() ? nullptr : stack_.front().parent; - } + const group* root() const noexcept { + return stack_.empty() ? nullptr : stack_.front().parent; + } - /** @brief common flag prefix of all flags in current group */ - arg_string common_flag_prefix() const noexcept - { - if(stack_.empty()) return ""; - auto g = outermost_join_group(); - return g ? g->common_flag_prefix() : arg_string(""); - } + /** @brief common flag prefix of all flags in current group */ + arg_string common_flag_prefix() const noexcept { + if(stack_.empty()) return ""; + auto g = outermost_join_group(); + return g ? g->common_flag_prefix() : arg_string(""); + } - const child &operator*() const noexcept - { - return *stack_.back().cur; - } + const child& + operator * () const noexcept { + return *stack_.back().cur; + } - const child *operator->() const noexcept - { - return &(*stack_.back().cur); - } + const child* + operator -> () const noexcept { + return &(*stack_.back().cur); + } - const group &parent() const noexcept - { - return *(stack_.back().parent); - } + const group& + parent() const noexcept { + return *(stack_.back().parent); + } - /** @brief go to next element of depth first search */ - depth_first_traverser &operator++() - { - if(stack_.empty()) return *this; - //at group -> decend into group - if(stack_.back().cur->is_group()) - { - stack_.emplace_back(stack_.back().cur->as_group()); - } - else - { - next_sibling(); - } - return *this; + + /** @brief go to next element of depth first search */ + depth_first_traverser& + operator ++ () { + if(stack_.empty()) return *this; + //at group -> descend into group + if(stack_.back().cur->is_group()) { + stack_.emplace_back(stack_.back().cur->as_group()); + } + else { + next_sibling(); } + return *this; + } - /** @brief go to next sibling of current */ - depth_first_traverser &next_sibling() - { + /** @brief go to next sibling of current */ + depth_first_traverser& + next_sibling() { + if(stack_.empty()) return *this; + ++stack_.back().cur; + //at the end of current group? + while(stack_.back().cur == stack_.back().end) { + //go to parent + stack_.pop_back(); if(stack_.empty()) return *this; + //go to next sibling in parent ++stack_.back().cur; - //at the end of current group? - while(stack_.back().cur == stack_.back().end) - { - //go to parent - stack_.pop_back(); - if(stack_.empty()) return *this; - //go to next sibling in parent - ++stack_.back().cur; - } - return *this; } + return *this; + } - /** @brief go to next position after siblings of current */ - depth_first_traverser &next_after_siblings() - { - if(stack_.empty()) return *this; - stack_.back().cur = stack_.back().end - 1; - next_sibling(); - return *this; - } + /** @brief go to next position after siblings of current */ + depth_first_traverser& + next_after_siblings() { + if(stack_.empty()) return *this; + stack_.back().cur = stack_.back().end-1; + next_sibling(); + return *this; + } - /** + /** * @brief */ - depth_first_traverser &back_to_ancestor(const group *g) - { - if(!g) return *this; - while(!stack_.empty()) - { - const auto &top = stack_.back().cur; - if(top->is_group() && &(top->as_group()) == g) return *this; - stack_.pop_back(); - } - return *this; + depth_first_traverser& + back_to_ancestor(const group* g) { + if(!g) return *this; + while(!stack_.empty()) { + const auto& top = stack_.back().cur; + if(top->is_group() && &(top->as_group()) == g) return *this; + stack_.pop_back(); } + return *this; + } - /** @brief don't visit next siblings, go back to parent on next ++ + /** @brief don't visit next siblings, go back to parent on next ++ * note: renders siblings unreachable for *this **/ - depth_first_traverser &skip_siblings() - { - if(stack_.empty()) return *this; - //future increments won't visit subsequent siblings: - stack_.back().end = stack_.back().cur + 1; - return *this; - } + depth_first_traverser& + skip_siblings() { + if(stack_.empty()) return *this; + //future increments won't visit subsequent siblings: + stack_.back().end = stack_.back().cur+1; + return *this; + } - /** @brief skips all other alternatives in surrounding exclusive groups + /** @brief skips all other alternatives in surrounding exclusive groups * on next ++ * note: renders alternatives unreachable for *this */ - depth_first_traverser &skip_alternatives() - { - if(stack_.empty()) return *this; - - //exclude all other alternatives in surrounding groups - //by making their current position the last one - for(auto &c : stack_) - { - if(c.parent && c.parent->exclusive() && c.cur < c.end) c.end = c.cur + 1; - } - - return *this; - } - - void invalidate() - { - stack_.clear(); - } + depth_first_traverser& + skip_alternatives() { + if(stack_.empty()) return *this; - inline friend bool operator==(const depth_first_traverser &a, const depth_first_traverser &b) - { - if(a.stack_.empty() || b.stack_.empty()) return false; - - //parents not the same -> different position - if(a.stack_.back().parent != b.stack_.back().parent) return false; - - bool aEnd = a.stack_.back().cur == a.stack_.back().end; - bool bEnd = b.stack_.back().cur == b.stack_.back().end; - //either both at the end of the same parent => same position - if(aEnd && bEnd) return true; - //or only one at the end => not at the same position - if(aEnd || bEnd) return false; - return std::addressof(*a.stack_.back().cur) == std::addressof(*b.stack_.back().cur); - } - inline friend bool operator!=(const depth_first_traverser &a, const depth_first_traverser &b) - { - return !(a == b); + //exclude all other alternatives in surrounding groups + //by making their current position the last one + for(auto& c : stack_) { + if(c.parent && c.parent->exclusive() && c.cur < c.end) + c.end = c.cur+1; } - memento undo_point() const - { - memento m; - m.level_ = int(stack_.size()); - if(!stack_.empty()) m.context_ = stack_.back(); - return m; - } + return *this; + } - void undo(const memento &m) - { - if(m.level_ < 1) return; - if(m.level_ <= int(stack_.size())) - { - stack_.erase(stack_.begin() + m.level_, stack_.end()); - stack_.back() = m.context_; - } - else if(stack_.empty() && m.level_ == 1) - { - stack_.push_back(m.context_); - } - } + void invalidate() { + stack_.clear(); + } - private: - context_list stack_; - }; + inline friend bool operator == (const depth_first_traverser& a, + const depth_first_traverser& b) + { + if(a.stack_.empty() || b.stack_.empty()) return false; - //--------------------------------------------------------------- - group() = default; + //parents not the same -> different position + if(a.stack_.back().parent != b.stack_.back().parent) return false; - template - explicit group(doc_string docstr, Param param, Params... params) - : children_{} - , exclusive_{ false } - , joinable_{ false } - , scoped_{ true } + bool aEnd = a.stack_.back().cur == a.stack_.back().end; + bool bEnd = b.stack_.back().cur == b.stack_.back().end; + //either both at the end of the same parent => same position + if(aEnd && bEnd) return true; + //or only one at the end => not at the same position + if(aEnd || bEnd) return false; + return std::addressof(*a.stack_.back().cur) == + std::addressof(*b.stack_.back().cur); + } + inline friend bool operator != (const depth_first_traverser& a, + const depth_first_traverser& b) { - doc(std::move(docstr)); - push_back(std::move(param), std::move(params)...); + return !(a == b); } - template - explicit group(parameter param, Params... params) - : children_{} - , exclusive_{ false } - , joinable_{ false } - , scoped_{ true } - { - push_back(std::move(param), std::move(params)...); + memento + undo_point() const { + memento m; + m.level_ = int(stack_.size()); + if(!stack_.empty()) m.context_ = stack_.back(); + return m; } - template - explicit group(group p1, P2 p2, Ps... ps) - : children_{} - , exclusive_{ false } - , joinable_{ false } - , scoped_{ true } - { - push_back(std::move(p1), std::move(p2), std::move(ps)...); + void undo(const memento& m) { + if(m.level_ < 1) return; + if(m.level_ <= int(stack_.size())) { + stack_.erase(stack_.begin() + m.level_, stack_.end()); + stack_.back() = m.context_; + } + else if(stack_.empty() && m.level_ == 1) { + stack_.push_back(m.context_); + } } - //----------------------------------------------------- - group(const group &) = default; - group(group &&) = default; + private: + context_list stack_; + }; + + + //--------------------------------------------------------------- + group() = default; + + template + explicit + group(doc_string docstr, Param param, Params... params): + children_{}, exclusive_{false}, joinable_{false}, scoped_{true} + { + doc(std::move(docstr)); + push_back(std::move(param), std::move(params)...); + } + + template + explicit + group(parameter param, Params... params): + children_{}, exclusive_{false}, joinable_{false}, scoped_{true} + { + push_back(std::move(param), std::move(params)...); + } + + template + explicit + group(group p1, P2 p2, Ps... ps): + children_{}, exclusive_{false}, joinable_{false}, scoped_{true} + { + push_back(std::move(p1), std::move(p2), std::move(ps)...); + } + + + //----------------------------------------------------- + group(const group&) = default; + group(group&&) = default; - //--------------------------------------------------------------- - group &operator=(const group &) = default; - group &operator=(group &&) = default; - //--------------------------------------------------------------- - /** @brief determines if a command line argument can be matched by a + //--------------------------------------------------------------- + group& operator = (const group&) = default; + group& operator = (group&&) = default; + + + //--------------------------------------------------------------- + /** @brief determines if a command line argument can be matched by a * combination of (partial) matches through any number of children */ - group &joinable(bool yes) - { - joinable_ = yes; - return *this; - } + group& joinable(bool yes) { + joinable_ = yes; + return *this; + } - /** @brief returns if a command line argument can be matched by a + /** @brief returns if a command line argument can be matched by a * combination of (partial) matches through any number of children */ - bool joinable() const noexcept - { - return joinable_; - } + bool joinable() const noexcept { + return joinable_; + } + - //--------------------------------------------------------------- - /** @brief turns explicit scoping on or off + //--------------------------------------------------------------- + /** @brief turns explicit scoping on or off * operators , & | and other combinating functions will * not merge groups that are marked as scoped */ - group &scoped(bool yes) - { - scoped_ = yes; - return *this; - } + group& scoped(bool yes) { + scoped_ = yes; + return *this; + } - /** @brief returns true if operators , & | and other combinating functions + /** @brief returns true if operators , & | and other combinating functions * will merge groups and false otherwise */ - bool scoped() const noexcept - { - return scoped_; - } + bool scoped() const noexcept + { + return scoped_; + } - //--------------------------------------------------------------- - /** @brief determines if children are mutually exclusive alternatives */ - group &exclusive(bool yes) - { - exclusive_ = yes; - return *this; - } - /** @brief returns if children are mutually exclusive alternatives */ - bool exclusive() const noexcept - { - return exclusive_; - } - //--------------------------------------------------------------- - /** @brief returns true, if any child is required to match */ - bool any_required() const - { - return std::any_of(children_.begin(), children_.end(), [](const child &n) { return n.required(); }); - } - /** @brief returns true, if all children are required to match */ - bool all_required() const - { - return std::all_of(children_.begin(), children_.end(), [](const child &n) { return n.required(); }); - } + //--------------------------------------------------------------- + /** @brief determines if children are mutually exclusive alternatives */ + group& exclusive(bool yes) { + exclusive_ = yes; + return *this; + } + /** @brief returns if children are mutually exclusive alternatives */ + bool exclusive() const noexcept { + return exclusive_; + } - //--------------------------------------------------------------- - /** @brief returns true if any child is optional (=non-required) */ - bool any_optional() const - { - return !all_required(); - } - /** @brief returns true if all children are optional (=non-required) */ - bool all_optional() const - { - return !any_required(); - } - //--------------------------------------------------------------- - /** @brief returns if the entire group is blocking / positional */ - bool blocking() const noexcept - { - return token::blocking() || (exclusive() && all_blocking()); - } - //----------------------------------------------------- - /** @brief determines if the entire group is blocking / positional */ - group &blocking(bool yes) - { - return token::blocking(yes); - } + //--------------------------------------------------------------- + /** @brief returns true, if any child is required to match */ + bool any_required() const + { + return std::any_of(children_.begin(), children_.end(), + [](const child& n){ return n.required(); }); + } + /** @brief returns true, if all children are required to match */ + bool all_required() const + { + return std::all_of(children_.begin(), children_.end(), + [](const child& n){ return n.required(); }); + } - //--------------------------------------------------------------- - /** @brief returns true if any child is blocking */ - bool any_blocking() const - { - return std::any_of(children_.begin(), children_.end(), [](const child &n) { return n.blocking(); }); - } - //--------------------------------------------------------------- - /** @brief returns true if all children is blocking */ - bool all_blocking() const - { - return std::all_of(children_.begin(), children_.end(), [](const child &n) { return n.blocking(); }); - } - //--------------------------------------------------------------- - /** @brief returns if any child is a value parameter (recursive) */ - bool any_flagless() const - { - return std::any_of(children_.begin(), children_.end(), [](const child &p) { + //--------------------------------------------------------------- + /** @brief returns true if any child is optional (=non-required) */ + bool any_optional() const { + return !all_required(); + } + /** @brief returns true if all children are optional (=non-required) */ + bool all_optional() const { + return !any_required(); + } + + + //--------------------------------------------------------------- + /** @brief returns if the entire group is blocking / positional */ + bool blocking() const noexcept { + return token::blocking() || (exclusive() && all_blocking()); + } + //----------------------------------------------------- + /** @brief determines if the entire group is blocking / positional */ + group& blocking(bool yes) { + return token::blocking(yes); + } + + //--------------------------------------------------------------- + /** @brief returns true if any child is blocking */ + bool any_blocking() const + { + return std::any_of(children_.begin(), children_.end(), + [](const child& n){ return n.blocking(); }); + } + //--------------------------------------------------------------- + /** @brief returns true if all children is blocking */ + bool all_blocking() const + { + return std::all_of(children_.begin(), children_.end(), + [](const child& n){ return n.blocking(); }); + } + + + //--------------------------------------------------------------- + /** @brief returns if any child is a value parameter (recursive) */ + bool any_flagless() const + { + return std::any_of(children_.begin(), children_.end(), + [](const child& p){ return p.is_param() && p.as_param().flags().empty(); }); - } - /** @brief returns if all children are value parameters (recursive) */ - bool all_flagless() const - { - return std::all_of(children_.begin(), children_.end(), [](const child &p) { + } + /** @brief returns if all children are value parameters (recursive) */ + bool all_flagless() const + { + return std::all_of(children_.begin(), children_.end(), + [](const child& p){ return p.is_param() && p.as_param().flags().empty(); }); - } + } - //--------------------------------------------------------------- - /** @brief adds child parameter at the end */ - group &push_back(const parameter &v) - { - children_.emplace_back(v); - return *this; - } - //----------------------------------------------------- - /** @brief adds child parameter at the end */ - group &push_back(parameter &&v) - { - children_.emplace_back(std::move(v)); - return *this; - } - //----------------------------------------------------- - /** @brief adds child group at the end */ - group &push_back(const group &g) - { - children_.emplace_back(g); - return *this; - } - //----------------------------------------------------- - /** @brief adds child group at the end */ - group &push_back(group &&g) - { - children_.emplace_back(std::move(g)); - return *this; - } - //----------------------------------------------------- - /** @brief adds children (groups and/or parameters) */ - template - group &push_back(Param1 &¶m1, Param2 &¶m2, Params &&...params) - { - children_.reserve(children_.size() + 2 + sizeof...(params)); - push_back(std::forward(param1)); - push_back(std::forward(param2), std::forward(params)...); - return *this; - } + //--------------------------------------------------------------- + /** @brief adds child parameter at the end */ + group& + push_back(const parameter& v) { + children_.emplace_back(v); + return *this; + } + //----------------------------------------------------- + /** @brief adds child parameter at the end */ + group& + push_back(parameter&& v) { + children_.emplace_back(std::move(v)); + return *this; + } + //----------------------------------------------------- + /** @brief adds child group at the end */ + group& + push_back(const group& g) { + children_.emplace_back(g); + return *this; + } + //----------------------------------------------------- + /** @brief adds child group at the end */ + group& + push_back(group&& g) { + children_.emplace_back(std::move(g)); + return *this; + } - //--------------------------------------------------------------- - /** @brief adds child parameter at the beginning */ - group &push_front(const parameter &v) - { - children_.emplace(children_.begin(), v); - return *this; - } - //----------------------------------------------------- - /** @brief adds child parameter at the beginning */ - group &push_front(parameter &&v) - { - children_.emplace(children_.begin(), std::move(v)); - return *this; - } - //----------------------------------------------------- - /** @brief adds child group at the beginning */ - group &push_front(const group &g) - { - children_.emplace(children_.begin(), g); - return *this; - } - //----------------------------------------------------- - /** @brief adds child group at the beginning */ - group &push_front(group &&g) - { - children_.emplace(children_.begin(), std::move(g)); - return *this; - } - //--------------------------------------------------------------- - /** @brief adds all children of other group at the end */ - group &merge(group &&g) - { - children_.insert(children_.end(), std::make_move_iterator(g.begin()), std::make_move_iterator(g.end())); - return *this; - } - //----------------------------------------------------- - /** @brief adds all children of several other groups at the end */ - template - group &merge(group &&g1, group &&g2, Groups &&...gs) - { - merge(std::move(g1)); - merge(std::move(g2), std::forward(gs)...); - return *this; - } + //----------------------------------------------------- + /** @brief adds children (groups and/or parameters) */ + template + group& + push_back(Param1&& param1, Param2&& param2, Params&&... params) + { + children_.reserve(children_.size() + 2 + sizeof...(params)); + push_back(std::forward(param1)); + push_back(std::forward(param2), std::forward(params)...); + return *this; + } - //--------------------------------------------------------------- - /** @brief indexed, nutable access to child */ - child &operator[](size_type index) noexcept - { - return children_[index]; - } - /** @brief indexed, non-nutable access to child */ - const child &operator[](size_type index) const noexcept - { - return children_[index]; - } - //--------------------------------------------------------------- - /** @brief mutable access to first child */ - child &front() noexcept - { - return children_.front(); - } - /** @brief non-mutable access to first child */ - const child &front() const noexcept - { - return children_.front(); - } - //----------------------------------------------------- - /** @brief mutable access to last child */ - child &back() noexcept - { - return children_.back(); - } - /** @brief non-mutable access to last child */ - const child &back() const noexcept - { - return children_.back(); - } + //--------------------------------------------------------------- + /** @brief adds child parameter at the beginning */ + group& + push_front(const parameter& v) { + children_.emplace(children_.begin(), v); + return *this; + } + //----------------------------------------------------- + /** @brief adds child parameter at the beginning */ + group& + push_front(parameter&& v) { + children_.emplace(children_.begin(), std::move(v)); + return *this; + } + //----------------------------------------------------- + /** @brief adds child group at the beginning */ + group& + push_front(const group& g) { + children_.emplace(children_.begin(), g); + return *this; + } + //----------------------------------------------------- + /** @brief adds child group at the beginning */ + group& + push_front(group&& g) { + children_.emplace(children_.begin(), std::move(g)); + return *this; + } - //--------------------------------------------------------------- - /** @brief returns true, if group has no children, false otherwise */ - bool empty() const noexcept - { - return children_.empty(); - } - /** @brief returns number of children */ - size_type size() const noexcept - { - return children_.size(); - } + //--------------------------------------------------------------- + /** @brief adds all children of other group at the end */ + group& + merge(group&& g) + { + children_.insert(children_.end(), + std::make_move_iterator(g.begin()), + std::make_move_iterator(g.end())); + return *this; + } + //----------------------------------------------------- + /** @brief adds all children of several other groups at the end */ + template + group& + merge(group&& g1, group&& g2, Groups&&... gs) + { + merge(std::move(g1)); + merge(std::move(g2), std::forward(gs)...); + return *this; + } - /** @brief returns number of nested levels; 1 for a flat group */ - size_type depth() const - { - size_type n = 0; - for(const auto &c : children_) - { - auto l = 1 + c.depth(); - if(l > n) n = l; - } - return n; - } - //--------------------------------------------------------------- - /** @brief returns mutating iterator to position of first element */ - iterator begin() noexcept - { - return children_.begin(); - } - /** @brief returns non-mutating iterator to position of first element */ - const_iterator begin() const noexcept - { - return children_.begin(); - } - /** @brief returns non-mutating iterator to position of first element */ - const_iterator cbegin() const noexcept - { - return children_.begin(); - } + //--------------------------------------------------------------- + /** @brief indexed, nutable access to child */ + child& operator [] (size_type index) noexcept { + return children_[index]; + } + /** @brief indexed, non-nutable access to child */ + const child& operator [] (size_type index) const noexcept { + return children_[index]; + } - /** @brief returns mutating iterator to position one past the last element */ - iterator end() noexcept - { - return children_.end(); - } - /** @brief returns non-mutating iterator to position one past the last element */ - const_iterator end() const noexcept - { - return children_.end(); - } - /** @brief returns non-mutating iterator to position one past the last element */ - const_iterator cend() const noexcept - { - return children_.end(); - } + //--------------------------------------------------------------- + /** @brief mutable access to first child */ + child& front() noexcept { return children_.front(); } + /** @brief non-mutable access to first child */ + const child& front() const noexcept { return children_.front(); } + //----------------------------------------------------- + /** @brief mutable access to last child */ + child& back() noexcept { return children_.back(); } + /** @brief non-mutable access to last child */ + const child& back() const noexcept { return children_.back(); } + + + //--------------------------------------------------------------- + /** @brief returns true, if group has no children, false otherwise */ + bool empty() const noexcept { return children_.empty(); } + + /** @brief returns number of children */ + size_type size() const noexcept { return children_.size(); } + + /** @brief returns number of nested levels; 1 for a flat group */ + size_type depth() const { + size_type n = 0; + for(const auto& c : children_) { + auto l = 1 + c.depth(); + if(l > n) n = l; + } + return n; + } + + + //--------------------------------------------------------------- + /** @brief returns mutating iterator to position of first element */ + iterator begin() noexcept { return children_.begin(); } + /** @brief returns non-mutating iterator to position of first element */ + const_iterator begin() const noexcept { return children_.begin(); } + /** @brief returns non-mutating iterator to position of first element */ + const_iterator cbegin() const noexcept { return children_.begin(); } - //--------------------------------------------------------------- - /** @brief returns augmented iterator for depth first searches + /** @brief returns mutating iterator to position one past the last element */ + iterator end() noexcept { return children_.end(); } + /** @brief returns non-mutating iterator to position one past the last element */ + const_iterator end() const noexcept { return children_.end(); } + /** @brief returns non-mutating iterator to position one past the last element */ + const_iterator cend() const noexcept { return children_.end(); } + + + //--------------------------------------------------------------- + /** @brief returns augmented iterator for depth first searches * @details traverser knows end of iteration and can skip over children */ - depth_first_traverser begin_dfs() const noexcept - { - return depth_first_traverser{ *this }; - } + depth_first_traverser + begin_dfs() const noexcept { + return depth_first_traverser{*this}; + } - //--------------------------------------------------------------- - /** @brief returns recursive parameter count */ - size_type param_count() const - { - size_type c = 0; - for(const auto &n : children_) - { - c += n.param_count(); - } - return c; - } - //--------------------------------------------------------------- - /** @brief returns range of all flags (recursive) */ - arg_list all_flags() const - { - std::vector all; - gather_flags(children_, all); - return all; + //--------------------------------------------------------------- + /** @brief returns recursive parameter count */ + size_type param_count() const { + size_type c = 0; + for(const auto& n : children_) { + c += n.param_count(); } + return c; + } - /** @brief returns true, if no flag occurs as true - * prefix of any other flag (identical flags will be ignored) */ - bool flags_are_prefix_free() const - { - const auto fs = all_flags(); - using std::begin; - using std::end; - for(auto i = begin(fs), e = end(fs); i != e; ++i) - { - if(!i->empty()) - { - for(auto j = i + 1; j != e; ++j) - { - if(!j->empty() && *i != *j) - { - if(i->find(*j) == 0) return false; - if(j->find(*i) == 0) return false; - } + //--------------------------------------------------------------- + /** @brief returns range of all flags (recursive) */ + arg_list all_flags() const + { + std::vector all; + gather_flags(children_, all); + return all; + } + + /** @brief returns true, if no flag occurs as true + * prefix of any other flag (identical flags will be ignored) */ + bool flags_are_prefix_free() const + { + const auto fs = all_flags(); + + using std::begin; using std::end; + for(auto i = begin(fs), e = end(fs); i != e; ++i) { + if(!i->empty()) { + for(auto j = i+1; j != e; ++j) { + if(!j->empty() && *i != *j) { + if(i->find(*j) == 0) return false; + if(j->find(*i) == 0) return false; } } } - - return true; } - //--------------------------------------------------------------- - /** @brief returns longest common prefix of all flags */ - arg_string common_flag_prefix() const - { - arg_list prefixes; - gather_prefixes(children_, prefixes); - return str::longest_common_prefix(prefixes); - } + return true; + } - private: - //--------------------------------------------------------------- - static void gather_flags(const children_store &nodes, arg_list &all) - { - for(const auto &p : nodes) - { - if(p.is_group()) - { - gather_flags(p.as_group().children_, all); - } - else - { - const auto &pf = p.as_param().flags(); - using std::begin; - using std::end; - if(!pf.empty()) all.insert(end(all), begin(pf), end(pf)); - } + + //--------------------------------------------------------------- + /** @brief returns longest common prefix of all flags */ + arg_string common_flag_prefix() const + { + arg_list prefixes; + gather_prefixes(children_, prefixes); + return str::longest_common_prefix(prefixes); + } + + +private: + //--------------------------------------------------------------- + static void + gather_flags(const children_store& nodes, arg_list& all) + { + for(const auto& p : nodes) { + if(p.is_group()) { + gather_flags(p.as_group().children_, all); + } + else { + const auto& pf = p.as_param().flags(); + using std::begin; + using std::end; + if(!pf.empty()) all.insert(end(all), begin(pf), end(pf)); } } - //--------------------------------------------------------------- - static void gather_prefixes(const children_store &nodes, arg_list &all) - { - for(const auto &p : nodes) - { - if(p.is_group()) - { - gather_prefixes(p.as_group().children_, all); - } - else if(!p.as_param().flags().empty()) - { - auto pfx = str::longest_common_prefix(p.as_param().flags()); - if(!pfx.empty()) all.push_back(std::move(pfx)); - } + } + //--------------------------------------------------------------- + static void + gather_prefixes(const children_store& nodes, arg_list& all) + { + for(const auto& p : nodes) { + if(p.is_group()) { + gather_prefixes(p.as_group().children_, all); + } + else if(!p.as_param().flags().empty()) { + auto pfx = str::longest_common_prefix(p.as_param().flags()); + if(!pfx.empty()) all.push_back(std::move(pfx)); } } + } + + //--------------------------------------------------------------- + children_store children_; + bool exclusive_ = false; + bool joinable_ = false; + bool scoped_ = false; +}; - //--------------------------------------------------------------- - children_store children_; - bool exclusive_ = false; - bool joinable_ = false; - bool scoped_ = false; - }; - /*************************************************************************/ /** + +/*************************************************************************//** * * @brief group or parameter * *****************************************************************************/ - using pattern = group::child; +using pattern = group::child; + - /*************************************************************************/ /** + +/*************************************************************************//** * * @brief apply an action to all parameters in a group * *****************************************************************************/ - template - void for_all_params(group &g, Action &&action) - { - for(auto &p : g) - { - if(p.is_group()) - { - for_all_params(p.as_group(), action); - } - else - { - action(p.as_param()); - } +template +void for_all_params(group& g, Action&& action) +{ + for(auto& p : g) { + if(p.is_group()) { + for_all_params(p.as_group(), action); + } + else { + action(p.as_param()); } } +} - template - void for_all_params(const group &g, Action &&action) - { - for(auto &p : g) - { - if(p.is_group()) - { - for_all_params(p.as_group(), action); - } - else - { - action(p.as_param()); - } +template +void for_all_params(const group& g, Action&& action) +{ + for(auto& p : g) { + if(p.is_group()) { + for_all_params(p.as_group(), action); + } + else { + action(p.as_param()); } } +} + - /*************************************************************************/ /** + +/*************************************************************************//** * * @brief makes a group of parameters and/or groups * *****************************************************************************/ - inline group operator,(parameter a, parameter b) - { - return group{ std::move(a), std::move(b) }.scoped(false); - } +inline group +operator , (parameter a, parameter b) +{ + return group{std::move(a), std::move(b)}.scoped(false); +} - //--------------------------------------------------------- - inline group operator,(parameter a, group b) - { - return !b.scoped() && !b.blocking() && !b.exclusive() && !b.repeatable() && !b.joinable() - && (b.doc().empty() || b.doc() == a.doc()) - ? b.push_front(std::move(a)) - : group{ std::move(a), std::move(b) }.scoped(false); - } +//--------------------------------------------------------- +inline group +operator , (parameter a, group b) +{ + return !b.scoped() && !b.blocking() && !b.exclusive() && !b.repeatable() + && !b.joinable() && (b.doc().empty() || b.doc() == a.doc()) + ? b.push_front(std::move(a)) + : group{std::move(a), std::move(b)}.scoped(false); +} + +//--------------------------------------------------------- +inline group +operator , (group a, parameter b) +{ + return !a.scoped() && !a.blocking() && !a.exclusive() && !a.repeatable() + && !a.joinable() && (a.doc().empty() || a.doc() == b.doc()) + ? a.push_back(std::move(b)) + : group{std::move(a), std::move(b)}.scoped(false); +} + +//--------------------------------------------------------- +inline group +operator , (group a, group b) +{ + return !a.scoped() && !a.blocking() && !a.exclusive() && !a.repeatable() + && !a.joinable() && (a.doc().empty() || a.doc() == b.doc()) + ? a.push_back(std::move(b)) + : group{std::move(a), std::move(b)}.scoped(false); +} - //--------------------------------------------------------- - inline group operator,(group a, parameter b) - { - return !a.scoped() && !a.blocking() && !a.exclusive() && !a.repeatable() && !a.joinable() - && (a.doc().empty() || a.doc() == b.doc()) - ? a.push_back(std::move(b)) - : group{ std::move(a), std::move(b) }.scoped(false); - } - //--------------------------------------------------------- - inline group operator,(group a, group b) - { - return !a.scoped() && !a.blocking() && !a.exclusive() && !a.repeatable() && !a.joinable() - && (a.doc().empty() || a.doc() == b.doc()) - ? a.push_back(std::move(b)) - : group{ std::move(a), std::move(b) }.scoped(false); - } - /*************************************************************************/ /** +/*************************************************************************//** * * @brief makes a group of alternative parameters or groups * *****************************************************************************/ - template - inline group one_of(Param param, Params... params) - { - return group{ std::move(param), std::move(params)... }.exclusive(true); - } +template +inline group +one_of(Param param, Params... params) +{ + return group{std::move(param), std::move(params)...}.exclusive(true); +} + - /*************************************************************************/ /** +/*************************************************************************//** * * @brief makes a group of alternative parameters or groups * *****************************************************************************/ - inline group operator|(parameter a, parameter b) - { - return group{ std::move(a), std::move(b) }.scoped(false).exclusive(true); - } +inline group +operator | (parameter a, parameter b) +{ + return group{std::move(a), std::move(b)}.scoped(false).exclusive(true); +} - //------------------------------------------------------------------- - inline group operator|(parameter a, group b) - { - return !b.scoped() && !b.blocking() && b.exclusive() && !b.repeatable() && !b.joinable() - && (b.doc().empty() || b.doc() == a.doc()) - ? b.push_front(std::move(a)) - : group{ std::move(a), std::move(b) }.scoped(false).exclusive(true); - } +//------------------------------------------------------------------- +inline group +operator | (parameter a, group b) +{ + return !b.scoped() && !b.blocking() && b.exclusive() && !b.repeatable() + && !b.joinable() + && (b.doc().empty() || b.doc() == a.doc()) + ? b.push_front(std::move(a)) + : group{std::move(a), std::move(b)}.scoped(false).exclusive(true); +} + +//------------------------------------------------------------------- +inline group +operator | (group a, parameter b) +{ + return !a.scoped() && a.exclusive() && !a.repeatable() && !a.joinable() + && a.blocking() == b.blocking() + && (a.doc().empty() || a.doc() == b.doc()) + ? a.push_back(std::move(b)) + : group{std::move(a), std::move(b)}.scoped(false).exclusive(true); +} + +inline group +operator | (group a, group b) +{ + return !a.scoped() && a.exclusive() &&!a.repeatable() && !a.joinable() + && a.blocking() == b.blocking() + && (a.doc().empty() || a.doc() == b.doc()) + ? a.push_back(std::move(b)) + : group{std::move(a), std::move(b)}.scoped(false).exclusive(true); +} - //------------------------------------------------------------------- - inline group operator|(group a, parameter b) - { - return !a.scoped() && a.exclusive() && !a.repeatable() && !a.joinable() && a.blocking() == b.blocking() - && (a.doc().empty() || a.doc() == b.doc()) - ? a.push_back(std::move(b)) - : group{ std::move(a), std::move(b) }.scoped(false).exclusive(true); - } - inline group operator|(group a, group b) - { - return !a.scoped() && a.exclusive() && !a.repeatable() && !a.joinable() && a.blocking() == b.blocking() - && (a.doc().empty() || a.doc() == b.doc()) - ? a.push_back(std::move(b)) - : group{ std::move(a), std::move(b) }.scoped(false).exclusive(true); - } - /*************************************************************************/ /** +/*************************************************************************//** * * @brief helpers (NOT FOR DIRECT USE IN CLIENT CODE!) * no interface guarantees; might be changed or removed in the future * *****************************************************************************/ - namespace detail - { - inline void set_blocking(bool) - {} +namespace detail { - template - void set_blocking(bool yes, P &p, Ps &...ps) - { - p.blocking(yes); - set_blocking(yes, ps...); - } +inline void set_blocking(bool) {} - } // namespace detail +template +void set_blocking(bool yes, P& p, Ps&... ps) { + p.blocking(yes); + set_blocking(yes, ps...); +} - /*************************************************************************/ /** +} // namespace detail + + +/*************************************************************************//** * * @brief makes a parameter/group sequence by making all input objects blocking * *****************************************************************************/ - template - inline group in_sequence(Param param, Params... params) - { - detail::set_blocking(true, param, params...); - return group{ std::move(param), std::move(params)... }.scoped(true); - } +template +inline group +in_sequence(Param param, Params... params) +{ + detail::set_blocking(true, param, params...); + return group{std::move(param), std::move(params)...}.scoped(true); +} - /*************************************************************************/ /** + +/*************************************************************************//** * * @brief makes a parameter/group sequence by making all input objects blocking * *****************************************************************************/ - inline group operator&(parameter a, parameter b) - { - a.blocking(true); - b.blocking(true); - return group{ std::move(a), std::move(b) }.scoped(true); - } +inline group +operator & (parameter a, parameter b) +{ + a.blocking(true); + b.blocking(true); + return group{std::move(a), std::move(b)}.scoped(true); +} + +//--------------------------------------------------------- +inline group +operator & (parameter a, group b) +{ + a.blocking(true); + return group{std::move(a), std::move(b)}.scoped(true); +} - //--------------------------------------------------------- - inline group operator&(parameter a, group b) +//--------------------------------------------------------- +inline group +operator & (group a, parameter b) +{ + b.blocking(true); + if(a.all_blocking() && !a.exclusive() && !a.repeatable() && !a.joinable() + && (a.doc().empty() || a.doc() == b.doc())) { - a.blocking(true); - return group{ std::move(a), std::move(b) }.scoped(true); + return a.push_back(std::move(b)); } - - //--------------------------------------------------------- - inline group operator&(group a, parameter b) - { - b.blocking(true); - if(a.all_blocking() && !a.exclusive() && !a.repeatable() && !a.joinable() - && (a.doc().empty() || a.doc() == b.doc())) - { - return a.push_back(std::move(b)); - } - else - { - if(!a.all_blocking()) a.blocking(true); - return group{ std::move(a), std::move(b) }.scoped(true); - } + else { + if(!a.all_blocking()) a.blocking(true); + return group{std::move(a), std::move(b)}.scoped(true); } +} - inline group operator&(group a, group b) +inline group +operator & (group a, group b) +{ + if(!b.all_blocking()) b.blocking(true); + if(a.all_blocking() && !a.exclusive() && !a.repeatable() + && !a.joinable() && (a.doc().empty() || a.doc() == b.doc())) { - if(!b.all_blocking()) b.blocking(true); - if(a.all_blocking() && !a.exclusive() && !a.repeatable() && !a.joinable() - && (a.doc().empty() || a.doc() == b.doc())) - { - return a.push_back(std::move(b)); - } - else - { - if(!a.all_blocking()) a.blocking(true); - return group{ std::move(a), std::move(b) }.scoped(true); - } + return a.push_back(std::move(b)); + } + else { + if(!a.all_blocking()) a.blocking(true); + return group{std::move(a), std::move(b)}.scoped(true); } +} - /*************************************************************************/ /** + + +/*************************************************************************//** * * @brief makes a group of parameters and/or groups * where all single char flag params ("-a", "b", ...) are joinable * *****************************************************************************/ - inline group joinable(group g) - { - return g.joinable(true); - } +inline group +joinable(group g) { + return g.joinable(true); +} - //------------------------------------------------------------------- - template - inline group joinable(parameter param, Params... params) - { - return group{ std::move(param), std::move(params)... }.joinable(true); - } +//------------------------------------------------------------------- +template +inline group +joinable(parameter param, Params... params) +{ + return group{std::move(param), std::move(params)...}.joinable(true); +} - template - inline group joinable(group p1, P2 p2, Ps... ps) - { - return group{ std::move(p1), std::move(p2), std::move(ps)... }.joinable(true); - } +template +inline group +joinable(group p1, P2 p2, Ps... ps) +{ + return group{std::move(p1), std::move(p2), std::move(ps)...}.joinable(true); +} - template - inline group joinable(doc_string docstr, Param param, Params... params) - { - return group{ std::move(param), std::move(params)... }.joinable(true).doc(std::move(docstr)); - } +template +inline group +joinable(doc_string docstr, Param param, Params... params) +{ + return group{std::move(param), std::move(params)...} + .joinable(true).doc(std::move(docstr)); +} - /*************************************************************************/ /** + + +/*************************************************************************//** * * @brief makes a repeatable copy of a parameter * *****************************************************************************/ - inline parameter repeatable(parameter p) - { - return p.repeatable(true); - } +inline parameter +repeatable(parameter p) { + return p.repeatable(true); +} - /*************************************************************************/ /** +/*************************************************************************//** * * @brief makes a repeatable copy of a group * *****************************************************************************/ - inline group repeatable(group g) - { - return g.repeatable(true); - } +inline group +repeatable(group g) { + return g.repeatable(true); +} + + - /*************************************************************************/ /** +/*************************************************************************//** * * @brief makes a group of parameters and/or groups * that is repeatable as a whole @@ -3848,160 +3834,184 @@ namespace clipp * repeatable children. * *****************************************************************************/ - template - inline group repeatable(parameter p1, P2 p2, Ps... ps) - { - return group{ std::move(p1), std::move(p2), std::move(ps)... }.repeatable(true); - } +template +inline group +repeatable(parameter p1, P2 p2, Ps... ps) +{ + return group{std::move(p1), std::move(p2), + std::move(ps)...}.repeatable(true); +} - template - inline group repeatable(group p1, P2 p2, Ps... ps) - { - return group{ std::move(p1), std::move(p2), std::move(ps)... }.repeatable(true); - } +template +inline group +repeatable(group p1, P2 p2, Ps... ps) +{ + return group{std::move(p1), std::move(p2), + std::move(ps)...}.repeatable(true); +} - /*************************************************************************/ /** + + +/*************************************************************************//** * * @brief makes a parameter greedy (match with top priority) * *****************************************************************************/ - inline parameter greedy(parameter p) - { - return p.greedy(true); - } +inline parameter +greedy(parameter p) { + return p.greedy(true); +} + +inline parameter +operator ! (parameter p) { + return greedy(p); +} + - inline parameter operator!(parameter p) - { - return greedy(p); - } - /*************************************************************************/ /** +/*************************************************************************//** * * @brief recursively prepends a prefix to all flags * *****************************************************************************/ - inline parameter &&with_prefix(const arg_string &prefix, parameter &&p) - { - return std::move(with_prefix(prefix, p)); - } +inline parameter&& +with_prefix(const arg_string& prefix, parameter&& p) { + return std::move(with_prefix(prefix, p)); +} - //------------------------------------------------------------------- - inline group &with_prefix(const arg_string &prefix, group &g) - { - for(auto &p : g) - { - if(p.is_group()) - { - with_prefix(prefix, p.as_group()); - } - else - { - with_prefix(prefix, p.as_param()); - } + +//------------------------------------------------------------------- +inline group& +with_prefix(const arg_string& prefix, group& g) +{ + for(auto& p : g) { + if(p.is_group()) { + with_prefix(prefix, p.as_group()); + } else { + with_prefix(prefix, p.as_param()); } - return g; } + return g; +} - inline group &&with_prefix(const arg_string &prefix, group &¶ms) - { - return std::move(with_prefix(prefix, params)); - } - template - inline group with_prefix(arg_string prefix, Param &¶m, Params &&...params) - { - return with_prefix(prefix, group{ std::forward(param), std::forward(params)... }); - } +inline group&& +with_prefix(const arg_string& prefix, group&& params) +{ + return std::move(with_prefix(prefix, params)); +} - /*************************************************************************/ /** - * - * @brief recursively prepends a prefix to all flags - * - * @param shortpfx : used for single-letter flags + +template +inline group +with_prefix(arg_string prefix, Param&& param, Params&&... params) +{ + return with_prefix(prefix, group{std::forward(param), + std::forward(params)...}); +} + + + +/*************************************************************************//** + * + * @brief recursively prepends a prefix to all flags + * + * @param shortpfx : used for single-letter flags * @param longpfx : used for flags with length > 1 * *****************************************************************************/ - inline parameter &&with_prefixes_short_long(const arg_string &shortpfx, const arg_string &longpfx, parameter &&p) - { - return std::move(with_prefixes_short_long(shortpfx, longpfx, p)); - } +inline parameter&& +with_prefixes_short_long(const arg_string& shortpfx, const arg_string& longpfx, + parameter&& p) +{ + return std::move(with_prefixes_short_long(shortpfx, longpfx, p)); +} - //------------------------------------------------------------------- - inline group &with_prefixes_short_long(const arg_string &shortFlagPrefix, - const arg_string &longFlagPrefix, - group &g) - { - for(auto &p : g) - { - if(p.is_group()) - { - with_prefixes_short_long(shortFlagPrefix, longFlagPrefix, p.as_group()); - } - else - { - with_prefixes_short_long(shortFlagPrefix, longFlagPrefix, p.as_param()); - } + +//------------------------------------------------------------------- +inline group& +with_prefixes_short_long(const arg_string& shortFlagPrefix, + const arg_string& longFlagPrefix, + group& g) +{ + for(auto& p : g) { + if(p.is_group()) { + with_prefixes_short_long(shortFlagPrefix, longFlagPrefix, p.as_group()); + } else { + with_prefixes_short_long(shortFlagPrefix, longFlagPrefix, p.as_param()); } - return g; } + return g; +} - inline group &&with_prefixes_short_long(const arg_string &shortFlagPrefix, - const arg_string &longFlagPrefix, - group &¶ms) - { - return std::move(with_prefixes_short_long(shortFlagPrefix, longFlagPrefix, params)); - } - template - inline group with_prefixes_short_long(const arg_string &shortFlagPrefix, - const arg_string &longFlagPrefix, - Param &¶m, - Params &&...params) - { - return with_prefixes_short_long(shortFlagPrefix, - longFlagPrefix, - group{ std::forward(param), std::forward(params)... }); - } +inline group&& +with_prefixes_short_long(const arg_string& shortFlagPrefix, + const arg_string& longFlagPrefix, + group&& params) +{ + return std::move(with_prefixes_short_long(shortFlagPrefix, longFlagPrefix, + params)); +} + + +template +inline group +with_prefixes_short_long(const arg_string& shortFlagPrefix, + const arg_string& longFlagPrefix, + Param&& param, Params&&... params) +{ + return with_prefixes_short_long(shortFlagPrefix, longFlagPrefix, + group{std::forward(param), + std::forward(params)...}); +} + - /*************************************************************************/ /** + +/*************************************************************************//** * * @brief recursively prepends a suffix to all flags * *****************************************************************************/ - inline parameter &&with_suffix(const arg_string &suffix, parameter &&p) - { - return std::move(with_suffix(suffix, p)); - } +inline parameter&& +with_suffix(const arg_string& suffix, parameter&& p) { + return std::move(with_suffix(suffix, p)); +} - //------------------------------------------------------------------- - inline group &with_suffix(const arg_string &suffix, group &g) - { - for(auto &p : g) - { - if(p.is_group()) - { - with_suffix(suffix, p.as_group()); - } - else - { - with_suffix(suffix, p.as_param()); - } + +//------------------------------------------------------------------- +inline group& +with_suffix(const arg_string& suffix, group& g) +{ + for(auto& p : g) { + if(p.is_group()) { + with_suffix(suffix, p.as_group()); + } else { + with_suffix(suffix, p.as_param()); } - return g; } + return g; +} - inline group &&with_suffix(const arg_string &suffix, group &¶ms) - { - return std::move(with_suffix(suffix, params)); - } - template - inline group with_suffix(arg_string suffix, Param &¶m, Params &&...params) - { - return with_suffix(suffix, group{ std::forward(param), std::forward(params)... }); - } +inline group&& +with_suffix(const arg_string& suffix, group&& params) +{ + return std::move(with_suffix(suffix, params)); +} + - /*************************************************************************/ /** +template +inline group +with_suffix(arg_string suffix, Param&& param, Params&&... params) +{ + return with_suffix(suffix, group{std::forward(param), + std::forward(params)...}); +} + + + +/*************************************************************************//** * * @brief recursively prepends a suffix to all flags * @@ -4009,57 +4019,69 @@ namespace clipp * @param longsfx : used for flags with length > 1 * *****************************************************************************/ - inline parameter &&with_suffixes_short_long(const arg_string &shortsfx, const arg_string &longsfx, parameter &&p) - { - return std::move(with_suffixes_short_long(shortsfx, longsfx, p)); - } +inline parameter&& +with_suffixes_short_long(const arg_string& shortsfx, const arg_string& longsfx, + parameter&& p) +{ + return std::move(with_suffixes_short_long(shortsfx, longsfx, p)); +} - //------------------------------------------------------------------- - inline group &with_suffixes_short_long(const arg_string &shortFlagSuffix, - const arg_string &longFlagSuffix, - group &g) - { - for(auto &p : g) - { - if(p.is_group()) - { - with_suffixes_short_long(shortFlagSuffix, longFlagSuffix, p.as_group()); - } - else - { - with_suffixes_short_long(shortFlagSuffix, longFlagSuffix, p.as_param()); - } + +//------------------------------------------------------------------- +inline group& +with_suffixes_short_long(const arg_string& shortFlagSuffix, + const arg_string& longFlagSuffix, + group& g) +{ + for(auto& p : g) { + if(p.is_group()) { + with_suffixes_short_long(shortFlagSuffix, longFlagSuffix, p.as_group()); + } else { + with_suffixes_short_long(shortFlagSuffix, longFlagSuffix, p.as_param()); } - return g; } + return g; +} - inline group &&with_suffixes_short_long(const arg_string &shortFlagSuffix, - const arg_string &longFlagSuffix, - group &¶ms) - { - return std::move(with_suffixes_short_long(shortFlagSuffix, longFlagSuffix, params)); - } - template - inline group with_suffixes_short_long(const arg_string &shortFlagSuffix, - const arg_string &longFlagSuffix, - Param &¶m, - Params &&...params) - { - return with_suffixes_short_long(shortFlagSuffix, - longFlagSuffix, - group{ std::forward(param), std::forward(params)... }); - } +inline group&& +with_suffixes_short_long(const arg_string& shortFlagSuffix, + const arg_string& longFlagSuffix, + group&& params) +{ + return std::move(with_suffixes_short_long(shortFlagSuffix, longFlagSuffix, + params)); +} + + +template +inline group +with_suffixes_short_long(const arg_string& shortFlagSuffix, + const arg_string& longFlagSuffix, + Param&& param, Params&&... params) +{ + return with_suffixes_short_long(shortFlagSuffix, longFlagSuffix, + group{std::forward(param), + std::forward(params)...}); +} - /*************************************************************************/ /** + + + + + + + +/*************************************************************************//** * * @brief parsing implementation details * *****************************************************************************/ - namespace detail - { - /*************************************************************************/ /** +namespace detail { + + +/*************************************************************************//** * * @brief DFS traverser that keeps track of 'scopes' * scope = all parameters that are either bounded by @@ -4067,3363 +4089,2998 @@ namespace clipp * or the beginning/end of the outermost group * *****************************************************************************/ - class scoped_dfs_traverser - { - public: - using dfs_traverser = group::depth_first_traverser; - - scoped_dfs_traverser() = default; - - explicit scoped_dfs_traverser(const group &g) - : pos_{ g } - , lastMatch_{} - , posAfterLastMatch_{} - , scopes_{} - , ignoreBlocks_{ false } - , repeatGroupStarted_{ false } - , repeatGroupContinues_{ false } - {} +class scoped_dfs_traverser +{ +public: + using dfs_traverser = group::depth_first_traverser; - const dfs_traverser &base() const noexcept - { - return pos_; - } - const dfs_traverser &last_match() const noexcept - { - return lastMatch_; - } + scoped_dfs_traverser() = default; - const group &parent() const noexcept - { - return pos_.parent(); - } + explicit + scoped_dfs_traverser(const group& g): + pos_{g}, lastMatch_{}, posAfterLastMatch_{}, scopes_{}, + ignoreBlocks_{false}, + repeatGroupStarted_{false}, repeatGroupContinues_{false} + {} - const group *innermost_repeat_group() const noexcept - { - return pos_.innermost_repeat_group(); - } - const group *outermost_join_group() const noexcept - { - return pos_.outermost_join_group(); - } - const group *innermost_blocking_group() const noexcept - { - return pos_.innermost_blocking_group(); - } - const group *innermost_exclusive_group() const noexcept - { - return pos_.innermost_exclusive_group(); - } + const dfs_traverser& base() const noexcept { return pos_; } + const dfs_traverser& last_match() const noexcept { return lastMatch_; } - const pattern *operator->() const noexcept - { - return pos_.operator->(); - } - const pattern &operator*() const noexcept - { - return *pos_; - } + const group& parent() const noexcept { return pos_.parent(); } - const pattern *ptr() const noexcept - { - return pos_.operator->(); - } + const group* innermost_repeat_group() const noexcept { + return pos_.innermost_repeat_group(); + } + const group* outermost_join_group() const noexcept { + return pos_.outermost_join_group(); + } + const group* innermost_blocking_group() const noexcept { + return pos_.innermost_blocking_group(); + } + const group* innermost_exclusive_group() const noexcept { + return pos_.innermost_exclusive_group(); + } - explicit operator bool() const noexcept - { - return bool(pos_); - } + const pattern* operator ->() const noexcept { return pos_.operator->(); } + const pattern& operator *() const noexcept { return *pos_; } - bool joinable() const noexcept - { - return pos_.joinable(); - } - arg_string common_flag_prefix() const - { - return pos_.common_flag_prefix(); - } + const pattern* ptr() const noexcept { return pos_.operator->(); } - void ignore_blocking(bool yes) - { - ignoreBlocks_ = yes; - } + explicit operator bool() const noexcept { return bool(pos_); } - void invalidate() - { - pos_.invalidate(); - } + bool joinable() const noexcept { return pos_.joinable(); } + arg_string common_flag_prefix() const { return pos_.common_flag_prefix(); } - bool matched() const noexcept - { - return (pos_ == lastMatch_); - } + void ignore_blocking(bool yes) { ignoreBlocks_ = yes; } - bool start_of_repeat_group() const noexcept - { - return repeatGroupStarted_; - } + void invalidate() { + pos_.invalidate(); + } - //----------------------------------------------------- - scoped_dfs_traverser &next_sibling() - { - pos_.next_sibling(); - return *this; - } + bool matched() const noexcept { + return (pos_ == lastMatch_); + } - scoped_dfs_traverser &next_after_siblings() - { - pos_.next_after_siblings(); - return *this; - } + bool start_of_repeat_group() const noexcept { return repeatGroupStarted_; } - //----------------------------------------------------- - scoped_dfs_traverser &operator++() - { - if(!pos_) return *this; + //----------------------------------------------------- + scoped_dfs_traverser& + next_sibling() { pos_.next_sibling(); return *this; } - if(pos_.is_last_in_path()) - { - return_to_outermost_scope(); - return *this; - } + scoped_dfs_traverser& + next_after_siblings() { pos_.next_after_siblings(); return *this; } - //current pattern can block if it didn't match already - if(ignoreBlocks_ || matched()) - { - ++pos_; - } - else if(!pos_->is_group()) - { - //current group can block if we didn't have any match in it - const group *g = pos_.outermost_blocking_group_fully_explored(); - //no match in 'g' before -> skip to after its siblings - if(g && !lastMatch_.is_inside(g)) - { - pos_.back_to_ancestor(g).next_after_siblings(); - if(!pos_) return_to_outermost_scope(); - } - else if(pos_->blocking()) - { - if(pos_.parent().exclusive()) - { - pos_.next_sibling(); - } - else - { - //no match => skip siblings of blocking param - pos_.next_after_siblings(); - } - if(!pos_) return_to_outermost_scope(); - } - else - { - ++pos_; - } - } - else - { - ++pos_; + + //----------------------------------------------------- + scoped_dfs_traverser& + operator ++ () + { + if(!pos_) return *this; + + if(pos_.is_last_in_path()) { + return_to_outermost_scope(); + return *this; + } + + //current pattern can block if it didn't match already + if(ignoreBlocks_ || matched()) { + ++pos_; + } + else if(!pos_->is_group()) { + //current group can block if we didn't have any match in it + const group* g = pos_.outermost_blocking_group_fully_explored(); + //no match in 'g' before -> skip to after its siblings + if(g && !lastMatch_.is_inside(g)) { + pos_.back_to_ancestor(g).next_after_siblings(); + if(!pos_) return_to_outermost_scope(); + } + else if(pos_->blocking()) { + if(pos_.parent().exclusive()) { + pos_.next_sibling(); + } else { + //no match => skip siblings of blocking param + pos_.next_after_siblings(); } - check_if_left_scope(); - return *this; + if(!pos_) return_to_outermost_scope(); + } else { + ++pos_; } + } else { + ++pos_; + } + check_if_left_scope(); + return *this; + } - //----------------------------------------------------- - void next_after_match(scoped_dfs_traverser match) - { - if(!match || ignoreBlocks_) return; - - check_repeat_group_start(match); + //----------------------------------------------------- + void next_after_match(scoped_dfs_traverser match) + { + if(!match || ignoreBlocks_) return; - lastMatch_ = match.base(); + check_repeat_group_start(match); - // if there is a blocking ancestor -> go back to it - if(!match->blocking()) - { - match.pos_.back_to_ancestor(match.innermost_blocking_group()); - } + lastMatch_ = match.base(); - //if match is not in current position & current position is blocking - //=> current position has to be advanced by one so that it is - //no longer reachable within current scope - //(can happen for repeatable, blocking parameters) - if(match.base() != pos_ && pos_->blocking()) pos_.next_sibling(); + // if there is a blocking ancestor -> go back to it + if(!match->blocking()) { + match.pos_.back_to_ancestor(match.innermost_blocking_group()); + } - if(match->blocking()) - { - if(match.pos_.is_alternative()) - { - //discard other alternatives - match.pos_.skip_alternatives(); - } + //if match is not in current position & current position is blocking + //=> current position has to be advanced by one so that it is + //no longer reachable within current scope + //(can happen for repeatable, blocking parameters) + if(match.base() != pos_ && pos_->blocking()) pos_.next_sibling(); - if(is_last_in_current_scope(match.pos_)) - { - //if current param is not repeatable -> back to previous scope - if(!match->repeatable() && !match->is_group()) - { - pos_ = std::move(match.pos_); - if(!scopes_.empty()) pos_.undo(scopes_.top()); - } - else - { //stay at match position - pos_ = std::move(match.pos_); - } - } - else - { //not last in current group - //if current param is not repeatable, go directly to next - if(!match->repeatable() && !match->is_group()) - { - ++match.pos_; - } + if(match->blocking()) { + if(match.pos_.is_alternative()) { + //discard other alternatives + match.pos_.skip_alternatives(); + } - if(match.pos_.level() > pos_.level()) - { - scopes_.push(pos_.undo_point()); - pos_ = std::move(match.pos_); - } - else if(match.pos_.level() < pos_.level()) - { - return_to_level(match.pos_.level()); - } - else - { - pos_ = std::move(match.pos_); - } - } - posAfterLastMatch_ = pos_; + if(is_last_in_current_scope(match.pos_)) { + //if current param is not repeatable -> back to previous scope + if(!match->repeatable() && !match->is_group()) { + pos_ = std::move(match.pos_); + if(!scopes_.empty()) pos_.undo(scopes_.top()); } - else - { - if(match.pos_.level() < pos_.level()) - { - return_to_level(match.pos_.level()); - } - posAfterLastMatch_ = pos_; + else { //stay at match position + pos_ = std::move(match.pos_); } - repeatGroupContinues_ = repeat_group_continues(); - } - - private: - //----------------------------------------------------- - bool is_last_in_current_scope(const dfs_traverser &pos) const - { - if(scopes_.empty()) return pos.is_last_in_path(); - //check if we would leave the current scope on ++ - auto p = pos; - ++p; - return p.level() < scopes_.top().level(); } - - //----------------------------------------------------- - void check_repeat_group_start(const scoped_dfs_traverser &newMatch) - { - const auto newrg = newMatch.innermost_repeat_group(); - if(!newrg) - { - repeatGroupStarted_ = false; + else { //not last in current group + //if current param is not repeatable, go directly to next + if(!match->repeatable() && !match->is_group()) { + ++match.pos_; } - else if(lastMatch_.innermost_repeat_group() != newrg) - { - repeatGroupStarted_ = true; + + if(match.pos_.level() > pos_.level()) { + scopes_.push(pos_.undo_point()); + pos_ = std::move(match.pos_); } - else if(!repeatGroupContinues_ || !newMatch.repeatGroupContinues_) - { - repeatGroupStarted_ = true; + else if(match.pos_.level() < pos_.level()) { + return_to_level(match.pos_.level()); } - else - { - //special case: repeat group is outermost group - //=> we can never really 'leave' and 'reenter' it - //but if the current scope is the first element, then we are - //conceptually at a position 'before' the group - repeatGroupStarted_ = - scopes_.empty() || (newrg == pos_.root() && scopes_.top().param() == &(*pos_.root()->begin())); + else { + pos_ = std::move(match.pos_); } - repeatGroupContinues_ = repeatGroupStarted_; } - - //----------------------------------------------------- - bool repeat_group_continues() const - { - if(!repeatGroupContinues_) return false; - const auto curRepGroup = pos_.innermost_repeat_group(); - if(!curRepGroup) return false; - if(curRepGroup != lastMatch_.innermost_repeat_group()) return false; - if(!posAfterLastMatch_) return false; - return true; + posAfterLastMatch_ = pos_; + } + else { + if(match.pos_.level() < pos_.level()) { + return_to_level(match.pos_.level()); } + posAfterLastMatch_ = pos_; + } + repeatGroupContinues_ = repeat_group_continues(); + } - //----------------------------------------------------- - void check_if_left_scope() - { - if(posAfterLastMatch_) - { - if(pos_.level() < posAfterLastMatch_.level()) - { - while(!scopes_.empty() && scopes_.top().level() >= pos_.level()) - { - pos_.undo(scopes_.top()); - scopes_.pop(); - } - posAfterLastMatch_.invalidate(); - } - } - while(!scopes_.empty() && scopes_.top().level() > pos_.level()) - { +private: + //----------------------------------------------------- + bool is_last_in_current_scope(const dfs_traverser& pos) const + { + if(scopes_.empty()) return pos.is_last_in_path(); + //check if we would leave the current scope on ++ + auto p = pos; + ++p; + return p.level() < scopes_.top().level(); + } + + //----------------------------------------------------- + void check_repeat_group_start(const scoped_dfs_traverser& newMatch) + { + const auto newrg = newMatch.innermost_repeat_group(); + if(!newrg) { + repeatGroupStarted_ = false; + } + else if(lastMatch_.innermost_repeat_group() != newrg) { + repeatGroupStarted_ = true; + } + else if(!repeatGroupContinues_ || !newMatch.repeatGroupContinues_) { + repeatGroupStarted_ = true; + } + else { + //special case: repeat group is outermost group + //=> we can never really 'leave' and 'reenter' it + //but if the current scope is the first element, then we are + //conceptually at a position 'before' the group + repeatGroupStarted_ = scopes_.empty() || ( + newrg == pos_.root() && !pos_.root()->empty() && + scopes_.top().param() == &(*pos_.root()->begin()) ); + } + repeatGroupContinues_ = repeatGroupStarted_; + } + + //----------------------------------------------------- + bool repeat_group_continues() const + { + if(!repeatGroupContinues_) return false; + const auto curRepGroup = pos_.innermost_repeat_group(); + if(!curRepGroup) return false; + if(curRepGroup != lastMatch_.innermost_repeat_group()) return false; + if(!posAfterLastMatch_) return false; + return true; + } + + //----------------------------------------------------- + void check_if_left_scope() + { + if(posAfterLastMatch_) { + if(pos_.level() < posAfterLastMatch_.level()) { + while(!scopes_.empty() && scopes_.top().level() >= pos_.level()) { pos_.undo(scopes_.top()); scopes_.pop(); } - repeatGroupContinues_ = repeat_group_continues(); + posAfterLastMatch_.invalidate(); } + } + while(!scopes_.empty() && scopes_.top().level() > pos_.level()) { + pos_.undo(scopes_.top()); + scopes_.pop(); + } + repeatGroupContinues_ = repeat_group_continues(); + } - //----------------------------------------------------- - void return_to_outermost_scope() - { - posAfterLastMatch_.invalidate(); + //----------------------------------------------------- + void return_to_outermost_scope() + { + posAfterLastMatch_.invalidate(); - if(scopes_.empty()) - { - pos_.invalidate(); - repeatGroupContinues_ = false; - return; - } + if(scopes_.empty()) { + pos_.invalidate(); + repeatGroupContinues_ = false; + return; + } - while(!scopes_.empty() && (!pos_ || pos_.level() >= 1)) - { - pos_.undo(scopes_.top()); - scopes_.pop(); - } - while(!scopes_.empty()) - scopes_.pop(); + while(!scopes_.empty() && (!pos_ || pos_.level() >= 1)) { + pos_.undo(scopes_.top()); + scopes_.pop(); + } + while(!scopes_.empty()) scopes_.pop(); + + repeatGroupContinues_ = repeat_group_continues(); + } + + //----------------------------------------------------- + void return_to_level(int level) + { + if(pos_.level() <= level) return; + while(!scopes_.empty() && pos_.level() > level) { + pos_.undo(scopes_.top()); + scopes_.pop(); + } + }; + + dfs_traverser pos_; + dfs_traverser lastMatch_; + dfs_traverser posAfterLastMatch_; + std::stack scopes_; + bool ignoreBlocks_ = false; + bool repeatGroupStarted_ = false; + bool repeatGroupContinues_ = false; +}; - repeatGroupContinues_ = repeat_group_continues(); - } - //----------------------------------------------------- - void return_to_level(int level) - { - if(pos_.level() <= level) return; - while(!scopes_.empty() && pos_.level() > level) - { - pos_.undo(scopes_.top()); - scopes_.pop(); - } - }; - - dfs_traverser pos_; - dfs_traverser lastMatch_; - dfs_traverser posAfterLastMatch_; - std::stack scopes_; - bool ignoreBlocks_ = false; - bool repeatGroupStarted_ = false; - bool repeatGroupContinues_ = false; - }; - /***************************************************************************** + +/***************************************************************************** * * some parameter property predicates * *****************************************************************************/ - struct select_all - { - bool operator()(const parameter &) const noexcept - { - return true; - } - }; +struct select_all { + bool operator () (const parameter&) const noexcept { return true; } +}; - struct select_flags - { - bool operator()(const parameter &p) const noexcept - { - return !p.flags().empty(); - } - }; +struct select_flags { + bool operator () (const parameter& p) const noexcept { + return !p.flags().empty(); + } +}; + +struct select_values { + bool operator () (const parameter& p) const noexcept { + return p.flags().empty(); + } +}; - struct select_values - { - bool operator()(const parameter &p) const noexcept - { - return p.flags().empty(); - } - }; - /*************************************************************************/ /** + +/*************************************************************************//** * * @brief result of a matching operation * *****************************************************************************/ - class match_t - { - public: - using size_type = arg_string::size_type; +class match_t { +public: + using size_type = arg_string::size_type; - match_t() = default; + match_t() = default; - match_t(arg_string s, scoped_dfs_traverser p) - : str_{ std::move(s) } - , pos_{ std::move(p) } - {} + match_t(arg_string s, scoped_dfs_traverser p): + str_{std::move(s)}, pos_{std::move(p)} + {} - size_type length() const noexcept - { - return str_.size(); - } + size_type length() const noexcept { return str_.size(); } - const arg_string &str() const noexcept - { - return str_; - } - const scoped_dfs_traverser &pos() const noexcept - { - return pos_; - } + const arg_string& str() const noexcept { return str_; } + const scoped_dfs_traverser& pos() const noexcept { return pos_; } - explicit operator bool() const noexcept - { - return bool(pos_); - } + explicit operator bool() const noexcept { return bool(pos_); } + +private: + arg_string str_; + scoped_dfs_traverser pos_; +}; - private: - arg_string str_; - scoped_dfs_traverser pos_; - }; - /*************************************************************************/ /** + +/*************************************************************************//** * * @brief finds the first parameter that matches a given string; * candidate parameters are traversed using a scoped DFS traverser * *****************************************************************************/ - template - match_t full_match(scoped_dfs_traverser pos, const arg_string &arg, const ParamSelector &select) - { - while(pos) - { - if(pos->is_param()) - { - const auto ¶m = pos->as_param(); - if(select(param)) - { - const auto match = param.match(arg); - if(match && match.length() == arg.size()) - { - return match_t{ arg, std::move(pos) }; - } - } +template +match_t +full_match(scoped_dfs_traverser pos, const arg_string& arg, + const ParamSelector& select) +{ + while(pos) { + if(pos->is_param()) { + const auto& param = pos->as_param(); + if(select(param)) { + const auto match = param.match(arg); + if(match && match.length() == arg.size()) { + return match_t{arg, std::move(pos)}; } - ++pos; } - return match_t{}; } + ++pos; + } + return match_t{}; +} + - /*************************************************************************/ /** + +/*************************************************************************//** * * @brief finds the first parameter that matches any (non-empty) prefix * of a given string; * candidate parameters are traversed using a scoped DFS traverser * *****************************************************************************/ - template - match_t longest_prefix_match(scoped_dfs_traverser pos, const arg_string &arg, const ParamSelector &select) - { - match_t longest; - - while(pos) - { - if(pos->is_param()) - { - const auto ¶m = pos->as_param(); - if(select(param)) - { - auto match = param.match(arg); - if(match.prefix()) - { - if(match.length() == arg.size()) - { - return match_t{ arg, std::move(pos) }; - } - else if(match.length() > longest.length()) - { - longest = match_t{ arg.substr(match.at(), match.length()), pos }; - } - } +template +match_t +longest_prefix_match(scoped_dfs_traverser pos, const arg_string& arg, + const ParamSelector& select) +{ + match_t longest; + + while(pos) { + if(pos->is_param()) { + const auto& param = pos->as_param(); + if(select(param)) { + auto match = param.match(arg); + if(match.prefix()) { + if(match.length() == arg.size()) { + return match_t{arg, std::move(pos)}; + } + else if(match.length() > longest.length()) { + longest = match_t{arg.substr(match.at(), match.length()), + std::move(pos)}; } } - ++pos; } - return longest; } + ++pos; + } + return longest; +} + + - /*************************************************************************/ /** +/*************************************************************************//** * * @brief finds the first parameter that partially matches a given string; * candidate parameters are traversed using a scoped DFS traverser * *****************************************************************************/ - template - match_t partial_match(scoped_dfs_traverser pos, const arg_string &arg, const ParamSelector &select) - { - while(pos) - { - if(pos->is_param()) - { - const auto ¶m = pos->as_param(); - if(select(param)) - { - const auto match = param.match(arg); - if(match) - { - return match_t{ arg.substr(match.at(), match.length()), std::move(pos) }; - } - } +template +match_t +partial_match(scoped_dfs_traverser pos, const arg_string& arg, + const ParamSelector& select) +{ + while(pos) { + if(pos->is_param()) { + const auto& param = pos->as_param(); + if(select(param)) { + const auto match = param.match(arg); + if(match) { + return match_t{arg.substr(match.at(), match.length()), + std::move(pos)}; } - ++pos; } - return match_t{}; } + ++pos; + } + return match_t{}; +} + +} //namespace detail + + + - } //namespace detail - /***************************************************************/ /** + +/***************************************************************//** * * @brief default command line arguments parser * *******************************************************************/ - class parser - { - public: - using dfs_traverser = group::depth_first_traverser; - using scoped_dfs_traverser = detail::scoped_dfs_traverser; +class parser +{ +public: + using dfs_traverser = group::depth_first_traverser; + using scoped_dfs_traverser = detail::scoped_dfs_traverser; - /*****************************************************/ /** + + /*****************************************************//** * @brief arg -> parameter mapping *********************************************************/ - class arg_mapping - { - public: - friend class parser; - - explicit arg_mapping(arg_index idx, arg_string s, const dfs_traverser &match) - : index_{ idx } - , arg_{ std::move(s) } - , match_{ match } - , repeat_{ 0 } - , startsRepeatGroup_{ false } - , blocked_{ false } - , conflict_{ false } - {} + class arg_mapping { + public: + friend class parser; + + explicit + arg_mapping(arg_index idx, arg_string s, + const dfs_traverser& match) + : + index_{idx}, arg_{std::move(s)}, match_{match}, + repeat_{0}, startsRepeatGroup_{false}, + blocked_{false}, conflict_{false} + {} - explicit arg_mapping(arg_index idx, arg_string s) - : index_{ idx } - , arg_{ std::move(s) } - , match_{} - , repeat_{ 0 } - , startsRepeatGroup_{ false } - , blocked_{ false } - , conflict_{ false } - {} + explicit + arg_mapping(arg_index idx, arg_string s) : + index_{idx}, arg_{std::move(s)}, match_{}, + repeat_{0}, startsRepeatGroup_{false}, + blocked_{false}, conflict_{false} + {} - arg_index index() const noexcept - { - return index_; - } - const arg_string &arg() const noexcept - { - return arg_; - } + arg_index index() const noexcept { return index_; } + const arg_string& arg() const noexcept { return arg_; } - const parameter *param() const noexcept - { - return match_ && match_->is_param() ? &(match_->as_param()) : nullptr; - } + const parameter* param() const noexcept { + return match_ && match_->is_param() + ? &(match_->as_param()) : nullptr; + } - std::size_t repeat() const noexcept - { - return repeat_; - } + std::size_t repeat() const noexcept { return repeat_; } - bool blocked() const noexcept - { - return blocked_; - } - bool conflict() const noexcept - { - return conflict_; - } + bool blocked() const noexcept { return blocked_; } + bool conflict() const noexcept { return conflict_; } - bool bad_repeat() const noexcept - { - if(!param()) return false; - return repeat_ > 0 && !param()->repeatable() && !match_.innermost_repeat_group(); - } + bool bad_repeat() const noexcept { + if(!param()) return false; + return repeat_ > 0 && !param()->repeatable() + && !match_.innermost_repeat_group(); + } - bool any_error() const noexcept - { - return !match_ || blocked() || conflict() || bad_repeat(); - } + bool any_error() const noexcept { + return !match_ || blocked() || conflict() || bad_repeat(); + } - private: - arg_index index_; - arg_string arg_; - dfs_traverser match_; - std::size_t repeat_; - bool startsRepeatGroup_; - bool blocked_; - bool conflict_; - }; + private: + arg_index index_; + arg_string arg_; + dfs_traverser match_; + std::size_t repeat_; + bool startsRepeatGroup_; + bool blocked_; + bool conflict_; + }; - /*****************************************************/ /** + /*****************************************************//** * @brief references a non-matched, required parameter *********************************************************/ - class missing_event - { - public: - explicit missing_event(const parameter *p, arg_index after) - : param_{ p } - , aftIndex_{ after } - {} + class missing_event { + public: + explicit + missing_event(const parameter* p, arg_index after): + param_{p}, aftIndex_{after} + {} - const parameter *param() const noexcept - { - return param_; - } + const parameter* param() const noexcept { return param_; } - arg_index after_index() const noexcept - { - return aftIndex_; - } + arg_index after_index() const noexcept { return aftIndex_; } - private: - const parameter *param_; - arg_index aftIndex_; - }; + private: + const parameter* param_; + arg_index aftIndex_; + }; - //----------------------------------------------------- - using missing_events = std::vector; - using arg_mappings = std::vector; + //----------------------------------------------------- + using missing_events = std::vector; + using arg_mappings = std::vector; - private: - struct miss_candidate - { - miss_candidate(dfs_traverser p, arg_index idx, bool firstInRepeatGroup = false) - : pos{ std::move(p) } - , index{ idx } - , startsRepeatGroup{ firstInRepeatGroup } - {} - dfs_traverser pos; - arg_index index; - bool startsRepeatGroup; - }; - using miss_candidates = std::vector; +private: + struct miss_candidate { + miss_candidate(dfs_traverser p, arg_index idx, + bool firstInRepeatGroup = false): + pos{std::move(p)}, index{idx}, + startsRepeatGroup{firstInRepeatGroup} + {} - public: - //--------------------------------------------------------------- - /** @brief initializes parser with a command line interface + dfs_traverser pos; + arg_index index; + bool startsRepeatGroup; + }; + using miss_candidates = std::vector; + + +public: + //--------------------------------------------------------------- + /** @brief initializes parser with a command line interface * @param offset = argument index offset used for reports * */ - explicit parser(const group &root, arg_index offset = 0) - : root_{ &root } - , pos_{ root } - , index_{ offset - 1 } - , eaten_{ 0 } - , args_{} - , missCand_{} - , blocked_{ false } - { - for_each_potential_miss(dfs_traverser{ root }, - [this](const dfs_traverser &p) { missCand_.emplace_back(p, index_); }); - } + explicit + parser(const group& root, arg_index offset = 0): + root_{&root}, pos_{root}, + index_{offset-1}, eaten_{0}, + args_{}, missCand_{}, blocked_{false} + { + for_each_potential_miss(dfs_traverser{root}, + [this](const dfs_traverser& p){ + missCand_.emplace_back(p, index_); + }); + } - //--------------------------------------------------------------- - /** @brief processes one command line argument */ - bool operator()(const arg_string &arg) - { - ++eaten_; - ++index_; - if(!valid()) return false; + //--------------------------------------------------------------- + /** @brief processes one command line argument */ + bool operator() (const arg_string& arg) + { + ++eaten_; + ++index_; - if(!blocked_ && try_match(arg)) return true; + if(!valid()) return false; - if(try_match_blocked(arg)) return false; + if(!blocked_ && try_match(arg)) return true; - //skipping of blocking & required patterns is not allowed - if(!blocked_ && !pos_.matched() && pos_->required() && pos_->blocking()) - { - blocked_ = true; - } + if(try_match_blocked(arg)) return false; - add_nomatch(arg); - return false; + //skipping of blocking & required patterns is not allowed + if(!blocked_ && !pos_.matched() && pos_->required() && pos_->blocking()) { + blocked_ = true; } - //--------------------------------------------------------------- - /** @brief returns range of argument -> parameter mappings */ - const arg_mappings &args() const - { - return args_; - } + add_nomatch(arg); + return false; + } - /** @brief returns list of missing events */ - missing_events missed() const - { - missing_events misses; - misses.reserve(missCand_.size()); - for(auto i = missCand_.begin(); i != missCand_.end(); ++i) - { - misses.emplace_back(&(i->pos->as_param()), i->index); - } - return misses; - } - /** @brief returns number of processed command line arguments */ - arg_index parse_count() const noexcept - { - return eaten_; + //--------------------------------------------------------------- + /** @brief returns range of argument -> parameter mappings */ + const arg_mappings& args() const { + return args_; + } + + /** @brief returns list of missing events */ + missing_events missed() const { + missing_events misses; + misses.reserve(missCand_.size()); + for(auto i = missCand_.begin(); i != missCand_.end(); ++i) { + misses.emplace_back(&(i->pos->as_param()), i->index); } + return misses; + } + + /** @brief returns number of processed command line arguments */ + arg_index parse_count() const noexcept { return eaten_; } - /** @brief returns false if previously processed command line arguments + /** @brief returns false if previously processed command line arguments * lead to an invalid / inconsistent parsing result */ - bool valid() const noexcept - { - return bool(pos_); - } + bool valid() const noexcept { return bool(pos_); } - /** @brief returns false if previously processed command line arguments + /** @brief returns false if previously processed command line arguments * lead to an invalid / inconsistent parsing result */ - explicit operator bool() const noexcept - { - return valid(); - } + explicit operator bool() const noexcept { return valid(); } - private: - //--------------------------------------------------------------- - using match_t = detail::match_t; - //--------------------------------------------------------------- - /** @brief try to match argument with unreachable parameter */ - bool try_match_blocked(const arg_string &arg) - { - //try to match ahead (using temporary parser) - if(pos_) - { - auto ahead = *this; - if(try_match_blocked(std::move(ahead), arg)) return true; - } +private: + //--------------------------------------------------------------- + using match_t = detail::match_t; - //try to match from the beginning (using temporary parser) - if(root_) - { - parser all{ *root_, index_ + 1 }; - if(try_match_blocked(std::move(all), arg)) return true; - } - return false; + //--------------------------------------------------------------- + /** @brief try to match argument with unreachable parameter */ + bool try_match_blocked(const arg_string& arg) + { + //try to match ahead (using temporary parser) + if(pos_) { + auto ahead = *this; + if(try_match_blocked(std::move(ahead), arg)) return true; } - //--------------------------------------------------------------- - bool try_match_blocked(parser &&parse, const arg_string &arg) - { - const auto nold = int(parse.args_.size()); + //try to match from the beginning (using temporary parser) + if(root_) { + parser all{*root_, index_+1}; + if(try_match_blocked(std::move(all), arg)) return true; + } + + return false; + } - parse.pos_.ignore_blocking(true); + //--------------------------------------------------------------- + bool try_match_blocked(parser&& parse, const arg_string& arg) + { + const auto nold = int(parse.args_.size()); - if(!parse.try_match(arg)) return false; + parse.pos_.ignore_blocking(true); - for(auto i = parse.args_.begin() + nold; i != parse.args_.end(); ++i) - { - args_.push_back(*i); - args_.back().blocked_ = true; - } - return true; + if(!parse.try_match(arg)) return false; + + for(auto i = parse.args_.begin() + nold; i != parse.args_.end(); ++i) { + args_.push_back(*i); + args_.back().blocked_ = true; } + return true; + } - //--------------------------------------------------------------- - /** @brief try to find a parameter/pattern that matches 'arg' */ - bool try_match(const arg_string &arg) - { - //match greedy parameters before everything else - if(pos_->is_param() && pos_->blocking() && pos_->as_param().greedy()) - { - const auto match = pos_->as_param().match(arg); - if(match && match.length() == arg.size()) - { - add_match(detail::match_t{ arg, pos_ }); - return true; - } + //--------------------------------------------------------------- + /** @brief try to find a parameter/pattern that matches 'arg' */ + bool try_match(const arg_string& arg) + { + //match greedy parameters before everything else + if(pos_->is_param() && pos_->blocking() && pos_->as_param().greedy()) { + const auto match = pos_->as_param().match(arg); + if(match && match.length() == arg.size()) { + add_match(detail::match_t{arg,pos_}); + return true; } - - //try flags first (alone, joinable or strict sequence) - if(try_match_full(arg, detail::select_flags{})) return true; - if(try_match_joined_flags(arg)) return true; - if(try_match_joined_sequence(arg, detail::select_flags{})) return true; - //try value params (alone or strict sequence) - if(try_match_full(arg, detail::select_values{})) return true; - if(try_match_joined_sequence(arg, detail::select_all{})) return true; - //try joinable params + values in any order - if(try_match_joined_params(arg)) return true; - return false; } - //--------------------------------------------------------------- - /** + //try flags first (alone, joinable or strict sequence) + if(try_match_full(arg, detail::select_flags{})) return true; + if(try_match_joined_flags(arg)) return true; + if(try_match_joined_sequence(arg, detail::select_flags{})) return true; + //try value params (alone or strict sequence) + if(try_match_full(arg, detail::select_values{})) return true; + if(try_match_joined_sequence(arg, detail::select_all{})) return true; + //try joinable params + values in any order + if(try_match_joined_params(arg)) return true; + return false; + } + + //--------------------------------------------------------------- + /** * @brief try to match full argument * @param select : predicate that candidate parameters must satisfy */ - template - bool try_match_full(const arg_string &arg, const ParamSelector &select) - { - auto match = detail::full_match(pos_, arg, select); - if(!match) return false; - add_match(match); - return true; - } + template + bool try_match_full(const arg_string& arg, const ParamSelector& select) + { + auto match = detail::full_match(pos_, arg, select); + if(!match) return false; + add_match(match); + return true; + } - //--------------------------------------------------------------- - /** + //--------------------------------------------------------------- + /** * @brief try to match argument as blocking sequence of parameters * @param select : predicate that a parameter matching the prefix of * 'arg' must satisfy */ - template - bool try_match_joined_sequence(arg_string arg, const ParamSelector &acceptFirst) - { - auto fstMatch = detail::longest_prefix_match(pos_, arg, acceptFirst); + template + bool try_match_joined_sequence(arg_string arg, + const ParamSelector& acceptFirst) + { + auto fstMatch = detail::longest_prefix_match(pos_, arg, acceptFirst); - if(!fstMatch) return false; + if(!fstMatch) return false; - if(fstMatch.str().size() == arg.size()) - { - add_match(fstMatch); - return true; - } + if(fstMatch.str().size() == arg.size()) { + add_match(fstMatch); + return true; + } - if(!fstMatch.pos()->blocking()) return false; + if(!fstMatch.pos()->blocking()) return false; - auto pos = fstMatch.pos(); - pos.ignore_blocking(true); - const auto parent = &pos.parent(); - if(!pos->repeatable()) ++pos; + auto pos = fstMatch.pos(); + pos.ignore_blocking(true); + const auto parent = &pos.parent(); + if(!pos->repeatable()) ++pos; - arg.erase(0, fstMatch.str().size()); - std::vector matches{ std::move(fstMatch) }; + arg.erase(0, fstMatch.str().size()); + std::vector matches { std::move(fstMatch) }; - while(!arg.empty() && pos && pos->blocking() && pos->is_param() && (&pos.parent() == parent)) - { - auto match = pos->as_param().match(arg); + while(!arg.empty() && pos && + pos->blocking() && pos->is_param() && + (&pos.parent() == parent)) + { + auto match = pos->as_param().match(arg); - if(match.prefix()) - { - matches.emplace_back(arg.substr(0, match.length()), pos); - arg.erase(0, match.length()); - if(!pos->repeatable()) ++pos; - } - else - { - if(!pos->repeatable()) return false; - ++pos; - } + if(match.prefix()) { + matches.emplace_back(arg.substr(0,match.length()), pos); + arg.erase(0, match.length()); + if(!pos->repeatable()) ++pos; + } + else { + if(!pos->repeatable()) return false; + ++pos; } - //if arg not fully covered => discard temporary matches - if(!arg.empty() || matches.empty()) return false; - for(const auto &m : matches) - add_match(m); - return true; } + //if arg not fully covered => discard temporary matches + if(!arg.empty() || matches.empty()) return false; - //----------------------------------------------------- - /** @brief try to match 'arg' as a concatenation of joinable flags */ - bool try_match_joined_flags(const arg_string &arg) - { - return find_join_group(pos_, [&](const group &g) { - return try_match_joined(g, arg, detail::select_flags{}, g.common_flag_prefix()); - }); - } + for(const auto& m : matches) add_match(m); + return true; + } - //--------------------------------------------------------------- - /** @brief try to match 'arg' as a concatenation of joinable parameters */ - bool try_match_joined_params(const arg_string &arg) - { - return find_join_group(pos_, - [&](const group &g) { return try_match_joined(g, arg, detail::select_all{}); }); - } + //----------------------------------------------------- + /** @brief try to match 'arg' as a concatenation of joinable flags */ + bool try_match_joined_flags(const arg_string& arg) + { + return find_join_group(pos_, [&](const group& g) { + return try_match_joined(g, arg, detail::select_flags{}, + g.common_flag_prefix()); + }); + } - //----------------------------------------------------- - /** @brief try to match 'arg' as concatenation of joinable parameters + //--------------------------------------------------------------- + /** @brief try to match 'arg' as a concatenation of joinable parameters */ + bool try_match_joined_params(const arg_string& arg) + { + return find_join_group(pos_, [&](const group& g) { + return try_match_joined(g, arg, detail::select_all{}); + }); + } + + //----------------------------------------------------- + /** @brief try to match 'arg' as concatenation of joinable parameters * that are all contained within one group */ - template - bool try_match_joined(const group &joinGroup, - arg_string arg, - const ParamSelector &select, - const arg_string &prefix = "") - { - //temporary parser with 'joinGroup' as top-level group - parser parse{ joinGroup }; - //records temporary matches - std::vector matches; - - while(!arg.empty()) - { - auto match = detail::longest_prefix_match(parse.pos_, arg, select); + template + bool try_match_joined(const group& joinGroup, arg_string arg, + const ParamSelector& select, + const arg_string& prefix = "") + { + //temporary parser with 'joinGroup' as top-level group + parser parse {joinGroup}; + //records temporary matches + std::vector matches; - if(!match) return false; + while(!arg.empty()) { + auto match = detail::longest_prefix_match(parse.pos_, arg, select); - arg.erase(0, match.str().size()); - //make sure prefix is always present after the first match - //so that, e.g., flags "-a" and "-b" will be found in "-ab" - if(!arg.empty() && !prefix.empty() && arg.find(prefix) != 0 && prefix != match.str()) - { - arg.insert(0, prefix); - } + if(!match) return false; - parse.add_match(match); - matches.push_back(std::move(match)); + arg.erase(0, match.str().size()); + //make sure prefix is always present after the first match + //so that, e.g., flags "-a" and "-b" will be found in "-ab" + if(!arg.empty() && !prefix.empty() && arg.find(prefix) != 0 && + prefix != match.str()) + { + arg.insert(0,prefix); } - if(!arg.empty() || matches.empty()) return false; + parse.add_match(match); + matches.push_back(std::move(match)); + } - if(!parse.missCand_.empty()) return false; - for(const auto &a : parse.args_) - if(a.any_error()) return false; + if(matches.empty()) return false; - //replay matches onto *this - for(const auto &m : matches) - add_match(m); - return true; + if(!parse.missCand_.empty()) return false; + for(const auto& a : parse.args_) if(a.any_error()) return false; + + //replay matches onto *this + for(const auto& m : matches) add_match(m); + return true; + } + + //----------------------------------------------------- + template + bool find_join_group(const scoped_dfs_traverser& start, + const GroupSelector& accept) const + { + if(start && start.parent().joinable()) { + const auto& g = start.parent(); + if(accept(g)) return true; + return false; } - //----------------------------------------------------- - template - bool find_join_group(const scoped_dfs_traverser &start, const GroupSelector &accept) const - { - if(start && start.parent().joinable()) - { - const auto &g = start.parent(); + auto pos = start; + while(pos) { + if(pos->is_group() && pos->as_group().joinable()) { + const auto& g = pos->as_group(); if(accept(g)) return true; - return false; + pos.next_sibling(); } - - auto pos = start; - while(pos) - { - if(pos->is_group() && pos->as_group().joinable()) - { - const auto &g = pos->as_group(); - if(accept(g)) return true; - pos.next_sibling(); - } - else - { - ++pos; - } + else { + ++pos; } - return false; } + return false; + } - //--------------------------------------------------------------- - void add_nomatch(const arg_string &arg) - { - args_.emplace_back(index_, arg); - } - //--------------------------------------------------------------- - void add_match(const match_t &match) - { - const auto &pos = match.pos(); - if(!pos || !pos->is_param()) return; + //--------------------------------------------------------------- + void add_nomatch(const arg_string& arg) { + args_.emplace_back(index_, arg); + } - pos_.next_after_match(pos); - arg_mapping newArg{ index_, match.str(), pos.base() }; - newArg.repeat_ = occurrences_of(&pos->as_param()); - newArg.conflict_ = check_conflicts(pos.base()); - newArg.startsRepeatGroup_ = pos_.start_of_repeat_group(); - args_.push_back(std::move(newArg)); + //--------------------------------------------------------------- + void add_match(const match_t& match) + { + const auto& pos = match.pos(); + if(!pos || !pos->is_param()) return; - add_miss_candidates_after(pos); - clean_miss_candidates_for(pos.base()); - discard_alternative_miss_candidates(pos.base()); - } + pos_.next_after_match(pos); - //----------------------------------------------------- - bool check_conflicts(const dfs_traverser &match) - { - if(pos_.start_of_repeat_group()) return false; - bool conflict = false; - for(const auto &m : match.stack()) - { - if(m.parent->exclusive()) - { - for(auto i = args_.rbegin(); i != args_.rend(); ++i) - { - if(!i->blocked()) - { - for(const auto &c : i->match_.stack()) - { - //sibling within same exclusive group => conflict - if(c.parent == m.parent && c.cur != m.cur) - { - conflict = true; - i->conflict_ = true; - } + arg_mapping newArg{index_, match.str(), pos.base()}; + newArg.repeat_ = occurrences_of(&pos->as_param()); + newArg.conflict_ = check_conflicts(pos.base()); + newArg.startsRepeatGroup_ = pos_.start_of_repeat_group(); + args_.push_back(std::move(newArg)); + + add_miss_candidates_after(pos); + clean_miss_candidates_for(pos.base()); + discard_alternative_miss_candidates(pos.base()); + + } + + //----------------------------------------------------- + bool check_conflicts(const dfs_traverser& match) + { + if(pos_.start_of_repeat_group()) return false; + bool conflict = false; + for(const auto& m : match.stack()) { + if(m.parent->exclusive()) { + for(auto i = args_.rbegin(); i != args_.rend(); ++i) { + if(!i->blocked()) { + for(const auto& c : i->match_.stack()) { + //sibling within same exclusive group => conflict + if(c.parent == m.parent && c.cur != m.cur) { + conflict = true; + i->conflict_ = true; } } - //check for conflicts only within current repeat cycle - if(i->startsRepeatGroup_) break; } + //check for conflicts only within current repeat cycle + if(i->startsRepeatGroup_) break; } } - return conflict; } + return conflict; + } - //----------------------------------------------------- - void clean_miss_candidates_for(const dfs_traverser &match) - { - auto i = std::find_if(missCand_.rbegin(), missCand_.rend(), [&](const miss_candidate &m) { + //----------------------------------------------------- + void clean_miss_candidates_for(const dfs_traverser& match) + { + auto i = std::find_if(missCand_.rbegin(), missCand_.rend(), + [&](const miss_candidate& m) { return &(*m.pos) == &(*match); }); - if(i != missCand_.rend()) - { - missCand_.erase(prev(i.base())); - } + if(i != missCand_.rend()) { + missCand_.erase(prev(i.base())); } + } - //----------------------------------------------------- - void discard_alternative_miss_candidates(const dfs_traverser &match) - { - if(missCand_.empty()) return; - //find out, if miss candidate is sibling of one of the same - //alternative groups that the current match is a member of - //if so, we can discard the miss - - //go through all exclusive groups of matching pattern - for(const auto &m : match.stack()) - { - if(m.parent->exclusive()) - { - for(auto i = int(missCand_.size()) - 1; i >= 0; --i) - { - bool removed = false; - for(const auto &c : missCand_[i].pos.stack()) - { - //sibling within same exclusive group => discard - if(c.parent == m.parent && c.cur != m.cur) - { - missCand_.erase(missCand_.begin() + i); - if(missCand_.empty()) return; - removed = true; - break; - } - } - //remove miss candidates only within current repeat cycle - if(i > 0 && removed) - { - if(missCand_[i - 1].startsRepeatGroup) break; - } - else - { - if(missCand_[i].startsRepeatGroup) break; + //----------------------------------------------------- + void discard_alternative_miss_candidates(const dfs_traverser& match) + { + if(missCand_.empty()) return; + //find out, if miss candidate is sibling of one of the same + //alternative groups that the current match is a member of + //if so, we can discard the miss + + //go through all exclusive groups of matching pattern + for(const auto& m : match.stack()) { + if(m.parent->exclusive()) { + for(auto i = int(missCand_.size())-1; i >= 0; --i) { + bool removed = false; + for(const auto& c : missCand_[i].pos.stack()) { + //sibling within same exclusive group => discard + if(c.parent == m.parent && c.cur != m.cur) { + missCand_.erase(missCand_.begin() + i); + if(missCand_.empty()) return; + removed = true; + break; } } + //remove miss candidates only within current repeat cycle + if(i > 0 && removed) { + if(missCand_[i-1].startsRepeatGroup) break; + } else { + if(missCand_[i].startsRepeatGroup) break; + } } } } + } - //----------------------------------------------------- - void add_miss_candidates_after(const scoped_dfs_traverser &match) - { - auto npos = match.base(); - if(npos.is_alternative()) npos.skip_alternatives(); - ++npos; - //need to add potential misses if: - //either new repeat group was started - const auto newRepGroup = match.innermost_repeat_group(); - if(newRepGroup) - { - if(pos_.start_of_repeat_group()) - { - for_each_potential_miss(std::move(npos), [&, this](const dfs_traverser &pos) { + //----------------------------------------------------- + void add_miss_candidates_after(const scoped_dfs_traverser& match) + { + auto npos = match.base(); + if(npos.is_alternative()) npos.skip_alternatives(); + ++npos; + //need to add potential misses if: + //either new repeat group was started + const auto newRepGroup = match.innermost_repeat_group(); + if(newRepGroup) { + if(pos_.start_of_repeat_group()) { + for_each_potential_miss(std::move(npos), + [&,this](const dfs_traverser& pos) { //only add candidates within repeat group - if(newRepGroup == pos.innermost_repeat_group()) - { + if(newRepGroup == pos.innermost_repeat_group()) { missCand_.emplace_back(pos, index_, true); } }); - } } - //... or an optional blocking param was hit - else if(match->blocking() && !match->required() && npos.level() >= match.base().level()) - { - for_each_potential_miss(std::move(npos), [&, this](const dfs_traverser &pos) { + } + //... or an optional blocking param was hit + else if(match->blocking() && !match->required() && + npos.level() >= match.base().level()) + { + for_each_potential_miss(std::move(npos), + [&,this](const dfs_traverser& pos) { //only add new candidates - if(std::find_if(missCand_.begin(), - missCand_.end(), - [&](const miss_candidate &c) { return &(*c.pos) == &(*pos); }) - == missCand_.end()) + if(std::find_if(missCand_.begin(), missCand_.end(), + [&](const miss_candidate& c){ + return &(*c.pos) == &(*pos); + }) == missCand_.end()) { missCand_.emplace_back(pos, index_); } }); - } } - //----------------------------------------------------- - template - static void for_each_potential_miss(dfs_traverser pos, Action &&action) - { - const auto level = pos.level(); - while(pos && pos.level() >= level) - { - if(pos->is_group()) - { - const auto &g = pos->as_group(); - if(g.all_optional() || (g.exclusive() && g.any_optional())) - { - pos.next_sibling(); - } - else - { - ++pos; - } + } + + //----------------------------------------------------- + template + static void + for_each_potential_miss(dfs_traverser pos, Action&& action) + { + const auto level = pos.level(); + while(pos && pos.level() >= level) { + if(pos->is_group() ) { + const auto& g = pos->as_group(); + if(g.all_optional() || (g.exclusive() && g.any_optional())) { + pos.next_sibling(); + } else { + ++pos; } - else - { //param - if(pos->required()) - { - action(pos); - ++pos; - } - else if(pos->blocking()) - { //optional + blocking - pos.next_after_siblings(); - } - else - { - ++pos; - } + } else { //param + if(pos->required()) { + action(pos); + ++pos; + } else if(pos->blocking()) { //optional + blocking + pos.next_after_siblings(); + } else { + ++pos; } } } + } - //--------------------------------------------------------------- - std::size_t occurrences_of(const parameter *p) const - { - if(!p) return 0; - auto i = std::find_if(args_.rbegin(), args_.rend(), [p](const arg_mapping &a) { return a.param() == p; }); + //--------------------------------------------------------------- + std::size_t occurrences_of(const parameter* p) const + { + if(!p) return 0; + + auto i = std::find_if(args_.rbegin(), args_.rend(), + [p](const arg_mapping& a){ return a.param() == p; }); + + if(i != args_.rend()) return i->repeat() + 1; + return 0; + } + + + //--------------------------------------------------------------- + const group* root_; + scoped_dfs_traverser pos_; + arg_index index_; + arg_index eaten_; + arg_mappings args_; + miss_candidates missCand_; + bool blocked_; +}; + - if(i != args_.rend()) return i->repeat() + 1; - return 0; - } - //--------------------------------------------------------------- - const group *root_; - scoped_dfs_traverser pos_; - arg_index index_; - arg_index eaten_; - arg_mappings args_; - miss_candidates missCand_; - bool blocked_; - }; - /*************************************************************************/ /** +/*************************************************************************//** * * @brief contains argument -> parameter mappings * and missing parameters * *****************************************************************************/ - class parsing_result - { - public: - using arg_mapping = parser::arg_mapping; - using arg_mappings = parser::arg_mappings; - using missing_event = parser::missing_event; - using missing_events = parser::missing_events; - using iterator = arg_mappings::const_iterator; - - //----------------------------------------------------- - /** @brief default: empty result */ - parsing_result() = default; - - parsing_result(arg_mappings arg2param, missing_events misses) - : arg2param_{ std::move(arg2param) } - , missing_{ std::move(misses) } - {} - - //----------------------------------------------------- - /** @brief returns number of arguments that could not be mapped to +class parsing_result +{ +public: + using arg_mapping = parser::arg_mapping; + using arg_mappings = parser::arg_mappings; + using missing_event = parser::missing_event; + using missing_events = parser::missing_events; + using iterator = arg_mappings::const_iterator; + + //----------------------------------------------------- + /** @brief default: empty result */ + parsing_result() = default; + + parsing_result(arg_mappings arg2param, missing_events misses): + arg2param_{std::move(arg2param)}, missing_{std::move(misses)} + {} + + //----------------------------------------------------- + /** @brief returns number of arguments that could not be mapped to * a parameter */ - arg_mappings::size_type unmapped_args_count() const noexcept - { - return std::count_if(arg2param_.begin(), arg2param_.end(), [](const arg_mapping &a) { return !a.param(); }); - } + arg_mappings::size_type + unmapped_args_count() const noexcept { + return std::count_if(arg2param_.begin(), arg2param_.end(), + [](const arg_mapping& a){ return !a.param(); }); + } - /** @brief returns if any argument could only be matched by an + /** @brief returns if any argument could only be matched by an * unreachable parameter */ - bool any_blocked() const noexcept - { - return std::any_of(arg2param_.begin(), arg2param_.end(), [](const arg_mapping &a) { return a.blocked(); }); - } + bool any_blocked() const noexcept { + return std::any_of(arg2param_.begin(), arg2param_.end(), + [](const arg_mapping& a){ return a.blocked(); }); + } - /** @brief returns if any argument matched more than one parameter + /** @brief returns if any argument matched more than one parameter * that were mutually exclusive */ - bool any_conflict() const noexcept - { - return std::any_of(arg2param_.begin(), arg2param_.end(), [](const arg_mapping &a) { return a.conflict(); }); - } + bool any_conflict() const noexcept { + return std::any_of(arg2param_.begin(), arg2param_.end(), + [](const arg_mapping& a){ return a.conflict(); }); + } - /** @brief returns if any parameter matched repeatedly although + /** @brief returns if any parameter matched repeatedly although * it was not allowed to */ - bool any_bad_repeat() const noexcept - { - return std::any_of(arg2param_.begin(), arg2param_.end(), [](const arg_mapping &a) { - return a.bad_repeat(); - }); - } + bool any_bad_repeat() const noexcept { + return std::any_of(arg2param_.begin(), arg2param_.end(), + [](const arg_mapping& a){ return a.bad_repeat(); }); + } - /** @brief returns true if any parsing error / violation of the + /** @brief returns true if any parsing error / violation of the * command line interface definition occurred */ - bool any_error() const noexcept - { - return unmapped_args_count() > 0 || !missing().empty() || any_blocked() || any_conflict() - || any_bad_repeat(); - } + bool any_error() const noexcept { + return unmapped_args_count() > 0 || !missing().empty() || + any_blocked() || any_conflict() || any_bad_repeat(); + } - /** @brief returns true if no parsing error / violation of the + /** @brief returns true if no parsing error / violation of the * command line interface definition occurred */ - explicit operator bool() const noexcept - { - return !any_error(); - } + explicit operator bool() const noexcept { return !any_error(); } - /** @brief access to range of missing parameter match events */ - const missing_events &missing() const noexcept - { - return missing_; - } + /** @brief access to range of missing parameter match events */ + const missing_events& missing() const noexcept { return missing_; } - /** @brief returns non-mutating iterator to position of + /** @brief returns non-mutating iterator to position of * first argument -> parameter mapping */ - iterator begin() const noexcept - { - return arg2param_.begin(); - } - /** @brief returns non-mutating iterator to position one past the + iterator begin() const noexcept { return arg2param_.begin(); } + /** @brief returns non-mutating iterator to position one past the * last argument -> parameter mapping */ - iterator end() const noexcept - { - return arg2param_.end(); - } + iterator end() const noexcept { return arg2param_.end(); } - private: - //----------------------------------------------------- - arg_mappings arg2param_; - missing_events missing_; - }; +private: + //----------------------------------------------------- + arg_mappings arg2param_; + missing_events missing_; +}; - namespace detail - { - namespace - { - /*************************************************************************/ /** + + + +namespace detail { +namespace { + +/*************************************************************************//** * * @brief correct some common problems * does not - and MUST NOT - change the number of arguments * (no insertions or deletions allowed) * *****************************************************************************/ - void sanitize_args(arg_list &args) +void sanitize_args(arg_list& args) +{ + //e.g. {"-o12", ".34"} -> {"-o", "12.34"} + + if(args.empty()) return; + + for(auto i = begin(args)+1; i != end(args); ++i) { + if(i != begin(args) && i->size() > 1 && + i->find('.') == 0 && txt::isdigit((*i)[1]) ) + { + //find trailing digits in previous arg + using std::prev; + auto& prv = *prev(i); + auto fstDigit = std::find_if_not(prv.rbegin(), prv.rend(), + [](arg_string::value_type c){ + return txt::isdigit(c); + }).base(); + + //handle leading sign + if(fstDigit > prv.begin() && + (*prev(fstDigit) == '+' || *prev(fstDigit) == '-')) { - //e.g. {"-o12", ".34"} -> {"-o", "12.34"} + --fstDigit; + } - if(args.empty()) return; + //prepend digits from previous arg + i->insert(begin(*i), fstDigit, end(prv)); - for(auto i = begin(args) + 1; i != end(args); ++i) - { - if(i != begin(args) && i->size() > 1 && i->find('.') == 0 && std::isdigit((*i)[1])) - { - //find trailing digits in previous arg - using std::prev; - auto &prv = *prev(i); - auto fstDigit = std::find_if_not(prv.rbegin(), prv.rend(), [](arg_string::value_type c) { - return std::isdigit(c); - }).base(); - - //handle leading sign - if(fstDigit > prv.begin() && (*prev(fstDigit) == '+' || *prev(fstDigit) == '-')) - { - --fstDigit; - } + //erase digits in previous arg + prv.erase(fstDigit, end(prv)); + } + } +} - //prepend digits from previous arg - i->insert(begin(*i), fstDigit, end(prv)); - //erase digits in previous arg - prv.erase(fstDigit, end(prv)); - } - } - } - /*************************************************************************/ /** +/*************************************************************************//** * * @brief executes actions based on a parsing result * *****************************************************************************/ - void execute_actions(const parsing_result &res) - { - for(const auto &m : res) - { - if(m.param()) - { - const auto ¶m = *(m.param()); +void execute_actions(const parsing_result& res) +{ + for(const auto& m : res) { + if(m.param()) { + const auto& param = *(m.param()); - if(m.repeat() > 0) param.notify_repeated(m.index()); - if(m.blocked()) param.notify_blocked(m.index()); - if(m.conflict()) param.notify_conflict(m.index()); - //main action - if(!m.any_error()) param.execute_actions(m.arg()); - } - } + if(m.repeat() > 0) param.notify_repeated(m.index()); + if(m.blocked()) param.notify_blocked(m.index()); + if(m.conflict()) param.notify_conflict(m.index()); + //main action + if(!m.any_error()) param.execute_actions(m.arg()); + } + } + + for(auto m : res.missing()) { + if(m.param()) m.param()->notify_missing(m.after_index()); + } +} - for(auto m : res.missing()) - { - if(m.param()) m.param()->notify_missing(m.after_index()); - } - } - /*************************************************************************/ /** + +/*************************************************************************//** * * @brief parses input args * *****************************************************************************/ - static parsing_result parse_args(const arg_list &args, const group &cli, arg_index offset = 0) - { - //parse args and store unrecognized arg indices - parser parse{ cli, offset }; - for(const auto &arg : args) - { - parse(arg); - if(!parse.valid()) break; - } +static parsing_result +parse_args(const arg_list& args, const group& cli, + arg_index offset = 0) +{ + //parse args and store unrecognized arg indices + parser parse{cli, offset}; + for(const auto& arg : args) { + parse(arg); + if(!parse.valid()) break; + } - return parsing_result{ parse.args(), parse.missed() }; - } + return parsing_result{parse.args(), parse.missed()}; +} - /*************************************************************************/ /** +/*************************************************************************//** * * @brief parses input args & executes actions * *****************************************************************************/ - static parsing_result parse_and_execute(const arg_list &args, const group &cli, arg_index offset = 0) - { - auto result = parse_args(args, cli, offset); +static parsing_result +parse_and_execute(const arg_list& args, const group& cli, + arg_index offset = 0) +{ + auto result = parse_args(args, cli, offset); - execute_actions(result); + execute_actions(result); - return result; - } + return result; +} - } //anonymous namespace - } // namespace detail +} //anonymous namespace +} // namespace detail - /*************************************************************************/ /** + + + +/*************************************************************************//** * * @brief parses vector of arg strings and executes actions * *****************************************************************************/ - inline parsing_result parse(arg_list args, const group &cli) - { - detail::sanitize_args(args); - return detail::parse_and_execute(args, cli); - } +inline parsing_result +parse(arg_list args, const group& cli) +{ + detail::sanitize_args(args); + return detail::parse_and_execute(args, cli); +} + - /*************************************************************************/ /** +/*************************************************************************//** * * @brief parses initializer_list of C-style arg strings and executes actions * *****************************************************************************/ - inline parsing_result parse(std::initializer_list arglist, const group &cli) - { - arg_list args; - args.reserve(arglist.size()); - for(auto a : arglist) - { - args.push_back(a); - } - - return parse(std::move(args), cli); +inline parsing_result +parse(std::initializer_list arglist, const group& cli) +{ + arg_list args; + args.reserve(arglist.size()); + for(auto a : arglist) { + args.push_back(a); } - /*************************************************************************/ /** + return parse(std::move(args), cli); +} + + +/*************************************************************************//** * * @brief parses range of arg strings and executes actions * *****************************************************************************/ - template - inline parsing_result parse(InputIterator first, InputIterator last, const group &cli) - { - return parse(arg_list(first, last), cli); - } +template +inline parsing_result +parse(InputIterator first, InputIterator last, const group& cli) +{ + return parse(arg_list(first,last), cli); +} + - /*************************************************************************/ /** +/*************************************************************************//** * * @brief parses the standard array of command line arguments; omits argv[0] * *****************************************************************************/ - inline parsing_result parse(const int argc, char *argv[], const group &cli, arg_index offset = 1) - { - arg_list args; - if(offset < argc) args.assign(argv + offset, argv + argc); - detail::sanitize_args(args); - return detail::parse_and_execute(args, cli, offset); - } +inline parsing_result +parse(const int argc, const char** argv, const group& cli, arg_index offset = 1) +{ + arg_list args; + if(offset < argc) args.assign(argv+offset, argv+argc); + detail::sanitize_args(args); + return detail::parse_and_execute(args, cli, offset); +} - /*************************************************************************/ /** +/*************************************************************************//** + * + * @brief parses the standard array of command line arguments; omits argv[0] + * + *****************************************************************************/ +inline parsing_result +parse(const int argc, char** argv, const group& cli, arg_index offset = 1) +{ + return parse(argc, (const char **)argv, cli, offset); +} + + + + + +/*************************************************************************//** * * @brief filter predicate for parameters and groups; * Can be used to limit documentation generation to parameter subsets. * *****************************************************************************/ - class param_filter - { - public: - /** @brief only allow parameters with given prefix */ - param_filter &prefix(const arg_string &p) noexcept - { - prefix_ = p; - return *this; - } - /** @brief only allow parameters with given prefix */ - param_filter &prefix(arg_string &&p) noexcept - { - prefix_ = std::move(p); - return *this; - } - const arg_string &prefix() const noexcept - { - return prefix_; - } +class param_filter +{ +public: + /** @brief only allow parameters with given prefix */ + param_filter& prefix(const arg_string& p) noexcept { + prefix_ = p; return *this; + } + /** @brief only allow parameters with given prefix */ + param_filter& prefix(arg_string&& p) noexcept { + prefix_ = std::move(p); return *this; + } + const arg_string& prefix() const noexcept { return prefix_; } + + /** @brief only allow parameters with given requirement status */ + param_filter& required(tri t) noexcept { required_ = t; return *this; } + tri required() const noexcept { return required_; } + + /** @brief only allow parameters with given blocking status */ + param_filter& blocking(tri t) noexcept { blocking_ = t; return *this; } + tri blocking() const noexcept { return blocking_; } + + /** @brief only allow parameters with given repeatable status */ + param_filter& repeatable(tri t) noexcept { repeatable_ = t; return *this; } + tri repeatable() const noexcept { return repeatable_; } + + /** @brief only allow parameters with given docstring status */ + param_filter& has_doc(tri t) noexcept { hasDoc_ = t; return *this; } + tri has_doc() const noexcept { return hasDoc_; } + + + /** @brief returns true, if parameter satisfies all filters */ + bool operator() (const parameter& p) const noexcept { + if(!prefix_.empty()) { + if(!std::any_of(p.flags().begin(), p.flags().end(), + [&](const arg_string& flag){ + return str::has_prefix(flag, prefix_); + })) return false; + } + if(required() != p.required()) return false; + if(blocking() != p.blocking()) return false; + if(repeatable() != p.repeatable()) return false; + if(has_doc() != !p.doc().empty()) return false; + return true; + } - /** @brief only allow parameters with given requirement status */ - param_filter &required(tri t) noexcept - { - required_ = t; - return *this; - } - tri required() const noexcept - { - return required_; - } +private: + arg_string prefix_; + tri required_ = tri::either; + tri blocking_ = tri::either; + tri repeatable_ = tri::either; + tri hasDoc_ = tri::yes; +}; - /** @brief only allow parameters with given blocking status */ - param_filter &blocking(tri t) noexcept - { - blocking_ = t; - return *this; - } - tri blocking() const noexcept - { - return blocking_; - } - /** @brief only allow parameters with given repeatable status */ - param_filter &repeatable(tri t) noexcept - { - repeatable_ = t; - return *this; - } - tri repeatable() const noexcept - { - return repeatable_; - } - /** @brief only allow parameters with given docstring status */ - param_filter &has_doc(tri t) noexcept - { - hasDoc_ = t; - return *this; - } - tri has_doc() const noexcept - { - return hasDoc_; - } - /** @brief returns true, if parameter satisfies all filters */ - bool operator()(const parameter &p) const noexcept - { - if(!prefix_.empty()) - { - if(!std::any_of(p.flags().begin(), p.flags().end(), [&](const arg_string &flag) { - return str::has_prefix(flag, prefix_); - })) - return false; - } - if(required() != p.required()) return false; - if(blocking() != p.blocking()) return false; - if(repeatable() != p.repeatable()) return false; - if(has_doc() != !p.doc().empty()) return false; - return true; - } - private: - arg_string prefix_; - tri required_ = tri::either; - tri blocking_ = tri::either; - tri repeatable_ = tri::either; - tri hasDoc_ = tri::yes; - }; - /*************************************************************************/ /** +/*************************************************************************//** * * @brief documentation formatting options * *****************************************************************************/ - class doc_formatting - { - public: - using string = doc_string; +class doc_formatting +{ +public: + using string = doc_string; - /** @brief same as 'first_column' */ + /** @brief same as 'first_column' */ #if __cplusplus >= 201402L - [[deprecated]] + [[deprecated]] #endif - doc_formatting & - start_column(int col) - { - return first_column(col); - } + doc_formatting& start_column(int col) { return first_column(col); } #if __cplusplus >= 201402L - [[deprecated]] + [[deprecated]] #endif - int - start_column() const noexcept - { - return first_column(); - } - - /** @brief determines column where documentation printing starts */ - doc_formatting &first_column(int col) - { - //limit to [0,last_column] but push doc_column to the right if necessary - if(col < 0) - col = 0; - else if(col > last_column()) - col = last_column(); - if(col > doc_column()) doc_column(first_column()); - firstCol_ = col; - return *this; - } - int first_column() const noexcept - { - return firstCol_; - } + int start_column() const noexcept { return first_column(); } + + /** @brief determines column where documentation printing starts */ + doc_formatting& + first_column(int col) { + //limit to [0,last_column] but push doc_column to the right if necessary + if(col < 0) col = 0; + else if(col > last_column()) col = last_column(); + if(col > doc_column()) doc_column(first_column()); + firstCol_ = col; + return *this; + } + int first_column() const noexcept { + return firstCol_; + } - /** @brief determines column where docstrings start */ - doc_formatting &doc_column(int col) - { - //limit to [first_column,last_column] - if(col < 0) - col = 0; - else if(col < first_column()) - col = first_column(); - else if(col > last_column()) - col = last_column(); - docCol_ = col; - return *this; - } - int doc_column() const noexcept - { - return docCol_; - } + /** @brief determines column where docstrings start */ + doc_formatting& + doc_column(int col) { + //limit to [first_column,last_column] + if(col < 0) col = 0; + else if(col < first_column()) col = first_column(); + else if(col > last_column()) col = last_column(); + docCol_ = col; + return *this; + } + int doc_column() const noexcept { + return docCol_; + } - /** @brief determines column that no documentation text must exceed; + /** @brief determines column that no documentation text must exceed; * (text should be wrapped appropriately after this column) */ - doc_formatting &last_column(int col) - { - //limit to [first_column,oo] but push doc_column to the left if necessary - if(col < first_column()) col = first_column(); - if(col < doc_column()) doc_column(col); - lastCol_ = col; - return *this; - } + doc_formatting& + last_column(int col) { + //limit to [first_column,oo] but push doc_column to the left if necessary + if(col < first_column()) col = first_column(); + if(col < doc_column()) doc_column(col); + lastCol_ = col; + return *this; + } - int last_column() const noexcept - { - return lastCol_; - } + int last_column() const noexcept { + return lastCol_; + } - /** @brief determines indent of documentation lines + /** @brief determines indent of documentation lines * for children of a documented group */ - doc_formatting &indent_size(int indent) - { - indentSize_ = indent; - return *this; - } - int indent_size() const noexcept - { - return indentSize_; - } + doc_formatting& indent_size(int indent) { indentSize_ = indent; return *this; } + int indent_size() const noexcept { return indentSize_; } - /** @brief determines string to be used + /** @brief determines string to be used * if a parameter has no flags and no label */ - doc_formatting &empty_label(const string &label) - { - emptyLabel_ = label; - return *this; - } - const string &empty_label() const noexcept - { - return emptyLabel_; - } - - /** @brief determines string for separating parameters */ - doc_formatting ¶m_separator(const string &sep) - { - paramSep_ = sep; - return *this; - } - const string ¶m_separator() const noexcept - { - return paramSep_; - } - - /** @brief determines string for separating groups (in usage lines) */ - doc_formatting &group_separator(const string &sep) - { - groupSep_ = sep; - return *this; - } - const string &group_separator() const noexcept - { - return groupSep_; - } - - /** @brief determines string for separating alternative parameters */ - doc_formatting &alternative_param_separator(const string &sep) - { - altParamSep_ = sep; - return *this; - } - const string &alternative_param_separator() const noexcept - { - return altParamSep_; - } - - /** @brief determines string for separating alternative groups */ - doc_formatting &alternative_group_separator(const string &sep) - { - altGroupSep_ = sep; - return *this; - } - const string &alternative_group_separator() const noexcept - { - return altGroupSep_; - } - - /** @brief determines string for separating flags of the same parameter */ - doc_formatting &flag_separator(const string &sep) - { - flagSep_ = sep; - return *this; - } - const string &flag_separator() const noexcept - { - return flagSep_; - } - - /** @brief determines strings surrounding parameter labels */ - doc_formatting &surround_labels(const string &prefix, const string &postfix) - { - labelPre_ = prefix; - labelPst_ = postfix; - return *this; - } - const string &label_prefix() const noexcept - { - return labelPre_; - } - const string &label_postfix() const noexcept - { - return labelPst_; - } - - /** @brief determines strings surrounding optional parameters/groups */ - doc_formatting &surround_optional(const string &prefix, const string &postfix) - { - optionPre_ = prefix; - optionPst_ = postfix; - return *this; - } - const string &optional_prefix() const noexcept - { - return optionPre_; - } - const string &optional_postfix() const noexcept - { - return optionPst_; - } + doc_formatting& empty_label(const string& label) { + emptyLabel_ = label; + return *this; + } + const string& empty_label() const noexcept { return emptyLabel_; } - /** @brief determines strings surrounding repeatable parameters/groups */ - doc_formatting &surround_repeat(const string &prefix, const string &postfix) - { - repeatPre_ = prefix; - repeatPst_ = postfix; - return *this; - } - const string &repeat_prefix() const noexcept - { - return repeatPre_; - } - const string &repeat_postfix() const noexcept - { - return repeatPst_; - } + /** @brief determines string for separating parameters */ + doc_formatting& param_separator(const string& sep) { + paramSep_ = sep; + return *this; + } + const string& param_separator() const noexcept { return paramSep_; } - /** @brief determines strings surrounding exclusive groups */ - doc_formatting &surround_alternatives(const string &prefix, const string &postfix) - { - alternPre_ = prefix; - alternPst_ = postfix; - return *this; - } - const string &alternatives_prefix() const noexcept - { - return alternPre_; - } - const string &alternatives_postfix() const noexcept - { - return alternPst_; - } + /** @brief determines string for separating groups (in usage lines) */ + doc_formatting& group_separator(const string& sep) { + groupSep_ = sep; + return *this; + } + const string& group_separator() const noexcept { return groupSep_; } - /** @brief determines strings surrounding alternative flags */ - doc_formatting &surround_alternative_flags(const string &prefix, const string &postfix) - { - alternFlagPre_ = prefix; - alternFlagPst_ = postfix; - return *this; - } - const string &alternative_flags_prefix() const noexcept - { - return alternFlagPre_; - } - const string &alternative_flags_postfix() const noexcept - { - return alternFlagPst_; - } + /** @brief determines string for separating alternative parameters */ + doc_formatting& alternative_param_separator(const string& sep) { + altParamSep_ = sep; + return *this; + } + const string& alternative_param_separator() const noexcept { return altParamSep_; } - /** @brief determines strings surrounding non-exclusive groups */ - doc_formatting &surround_group(const string &prefix, const string &postfix) - { - groupPre_ = prefix; - groupPst_ = postfix; - return *this; - } - const string &group_prefix() const noexcept - { - return groupPre_; - } - const string &group_postfix() const noexcept - { - return groupPst_; - } + /** @brief determines string for separating alternative groups */ + doc_formatting& alternative_group_separator(const string& sep) { + altGroupSep_ = sep; + return *this; + } + const string& alternative_group_separator() const noexcept { return altGroupSep_; } - /** @brief determines strings surrounding joinable groups */ - doc_formatting &surround_joinable(const string &prefix, const string &postfix) - { - joinablePre_ = prefix; - joinablePst_ = postfix; - return *this; - } - const string &joinable_prefix() const noexcept - { - return joinablePre_; - } - const string &joinable_postfix() const noexcept - { - return joinablePst_; - } + /** @brief determines string for separating flags of the same parameter */ + doc_formatting& flag_separator(const string& sep) { + flagSep_ = sep; + return *this; + } + const string& flag_separator() const noexcept { return flagSep_; } + + /** @brief determines strings surrounding parameter labels */ + doc_formatting& + surround_labels(const string& prefix, const string& postfix) { + labelPre_ = prefix; + labelPst_ = postfix; + return *this; + } + const string& label_prefix() const noexcept { return labelPre_; } + const string& label_postfix() const noexcept { return labelPst_; } + + /** @brief determines strings surrounding optional parameters/groups */ + doc_formatting& + surround_optional(const string& prefix, const string& postfix) { + optionPre_ = prefix; + optionPst_ = postfix; + return *this; + } + const string& optional_prefix() const noexcept { return optionPre_; } + const string& optional_postfix() const noexcept { return optionPst_; } + + /** @brief determines strings surrounding repeatable parameters/groups */ + doc_formatting& + surround_repeat(const string& prefix, const string& postfix) { + repeatPre_ = prefix; + repeatPst_ = postfix; + return *this; + } + const string& repeat_prefix() const noexcept { return repeatPre_; } + const string& repeat_postfix() const noexcept { return repeatPst_; } + + /** @brief determines strings surrounding exclusive groups */ + doc_formatting& + surround_alternatives(const string& prefix, const string& postfix) { + alternPre_ = prefix; + alternPst_ = postfix; + return *this; + } + const string& alternatives_prefix() const noexcept { return alternPre_; } + const string& alternatives_postfix() const noexcept { return alternPst_; } + + /** @brief determines strings surrounding alternative flags */ + doc_formatting& + surround_alternative_flags(const string& prefix, const string& postfix) { + alternFlagPre_ = prefix; + alternFlagPst_ = postfix; + return *this; + } + const string& alternative_flags_prefix() const noexcept { return alternFlagPre_; } + const string& alternative_flags_postfix() const noexcept { return alternFlagPst_; } + + /** @brief determines strings surrounding non-exclusive groups */ + doc_formatting& + surround_group(const string& prefix, const string& postfix) { + groupPre_ = prefix; + groupPst_ = postfix; + return *this; + } + const string& group_prefix() const noexcept { return groupPre_; } + const string& group_postfix() const noexcept { return groupPst_; } + + /** @brief determines strings surrounding joinable groups */ + doc_formatting& + surround_joinable(const string& prefix, const string& postfix) { + joinablePre_ = prefix; + joinablePst_ = postfix; + return *this; + } + const string& joinable_prefix() const noexcept { return joinablePre_; } + const string& joinable_postfix() const noexcept { return joinablePst_; } - /** @brief determines maximum number of flags per parameter to be printed + /** @brief determines maximum number of flags per parameter to be printed * in detailed parameter documentation lines */ - doc_formatting &max_flags_per_param_in_doc(int max) - { - maxAltInDocs_ = max > 0 ? max : 0; - return *this; - } - int max_flags_per_param_in_doc() const noexcept - { - return maxAltInDocs_; - } + doc_formatting& max_flags_per_param_in_doc(int max) { + maxAltInDocs_ = max > 0 ? max : 0; + return *this; + } + int max_flags_per_param_in_doc() const noexcept { return maxAltInDocs_; } - /** @brief determines maximum number of flags per parameter to be printed + /** @brief determines maximum number of flags per parameter to be printed * in usage lines */ - doc_formatting &max_flags_per_param_in_usage(int max) - { - maxAltInUsage_ = max > 0 ? max : 0; - return *this; - } - int max_flags_per_param_in_usage() const noexcept - { - return maxAltInUsage_; - } + doc_formatting& max_flags_per_param_in_usage(int max) { + maxAltInUsage_ = max > 0 ? max : 0; + return *this; + } + int max_flags_per_param_in_usage() const noexcept { return maxAltInUsage_; } - /** @brief determines number of empty rows after one single-line + /** @brief determines number of empty rows after one single-line * documentation entry */ - doc_formatting &line_spacing(int lines) - { - lineSpc_ = lines > 0 ? lines : 0; - return *this; - } - int line_spacing() const noexcept - { - return lineSpc_; - } + doc_formatting& line_spacing(int lines) { + lineSpc_ = lines > 0 ? lines : 0; + return *this; + } + int line_spacing() const noexcept { return lineSpc_; } - /** @brief determines number of empty rows before and after a paragraph; + /** @brief determines number of empty rows before and after a paragraph; * a paragraph is defined by a documented group or if * a parameter documentation entry used more than one line */ - doc_formatting ¶graph_spacing(int lines) - { - paragraphSpc_ = lines > 0 ? lines : 0; - return *this; - } - int paragraph_spacing() const noexcept - { - return paragraphSpc_; - } + doc_formatting& paragraph_spacing(int lines) { + paragraphSpc_ = lines > 0 ? lines : 0; + return *this; + } + int paragraph_spacing() const noexcept { return paragraphSpc_; } - /** @brief determines if alternative flags with a common prefix should + /** @brief determines if alternative flags with a common prefix should * be printed in a merged fashion */ - doc_formatting &merge_alternative_flags_with_common_prefix(bool yes = true) - { - mergeAltCommonPfx_ = yes; - return *this; - } - bool merge_alternative_flags_with_common_prefix() const noexcept - { - return mergeAltCommonPfx_; - } + doc_formatting& merge_alternative_flags_with_common_prefix(bool yes = true) { + mergeAltCommonPfx_ = yes; + return *this; + } + bool merge_alternative_flags_with_common_prefix() const noexcept { + return mergeAltCommonPfx_; + } - /** @brief determines if joinable flags with a common prefix should + /** @brief determines if joinable flags with a common prefix should * be printed in a merged fashion */ - doc_formatting &merge_joinable_with_common_prefix(bool yes = true) - { - mergeJoinableCommonPfx_ = yes; - return *this; - } - bool merge_joinable_with_common_prefix() const noexcept - { - return mergeJoinableCommonPfx_; - } + doc_formatting& merge_joinable_with_common_prefix(bool yes = true) { + mergeJoinableCommonPfx_ = yes; + return *this; + } + bool merge_joinable_with_common_prefix() const noexcept { + return mergeJoinableCommonPfx_; + } - /** @brief determines if children of exclusive groups should be printed + /** @brief determines if children of exclusive groups should be printed * on individual lines if the exceed 'alternatives_min_split_size' */ - doc_formatting &split_alternatives(bool yes = true) - { - splitTopAlt_ = yes; - return *this; - } - bool split_alternatives() const noexcept - { - return splitTopAlt_; - } + doc_formatting& split_alternatives(bool yes = true) { + splitTopAlt_ = yes; + return *this; + } + bool split_alternatives() const noexcept { + return splitTopAlt_; + } - /** @brief determines how many children exclusive groups can have before + /** @brief determines how many children exclusive groups can have before * their children are printed on individual usage lines */ - doc_formatting &alternatives_min_split_size(int size) - { - groupSplitSize_ = size > 0 ? size : 0; - return *this; - } - int alternatives_min_split_size() const noexcept - { - return groupSplitSize_; - } + doc_formatting& alternatives_min_split_size(int size) { + groupSplitSize_ = size > 0 ? size : 0; + return *this; + } + int alternatives_min_split_size() const noexcept { return groupSplitSize_; } - /** @brief determines whether to ignore new line characters in docstrings + /** @brief determines whether to ignore new line characters in docstrings */ - doc_formatting &ignore_newline_chars(bool yes = true) - { - ignoreNewlines_ = yes; - return *this; - } - bool ignore_newline_chars() const noexcept - { - return ignoreNewlines_; - } - - private: - string paramSep_ = string(" "); - string groupSep_ = string(" "); - string altParamSep_ = string("|"); - string altGroupSep_ = string(" | "); - string flagSep_ = string(", "); - string labelPre_ = string("<"); - string labelPst_ = string(">"); - string optionPre_ = string("["); - string optionPst_ = string("]"); - string repeatPre_ = string(""); - string repeatPst_ = string("..."); - string groupPre_ = string("("); - string groupPst_ = string(")"); - string alternPre_ = string("("); - string alternPst_ = string(")"); - string alternFlagPre_ = string(""); - string alternFlagPst_ = string(""); - string joinablePre_ = string("("); - string joinablePst_ = string(")"); - string emptyLabel_ = string(""); - int firstCol_ = 8; - int docCol_ = 20; - int lastCol_ = 100; - int indentSize_ = 4; - int maxAltInUsage_ = 1; - int maxAltInDocs_ = 32; - int lineSpc_ = 0; - int paragraphSpc_ = 1; - int groupSplitSize_ = 3; - bool splitTopAlt_ = true; - bool mergeAltCommonPfx_ = false; - bool mergeJoinableCommonPfx_ = true; - bool ignoreNewlines_ = false; - }; + doc_formatting& ignore_newline_chars(bool yes = true) { + ignoreNewlines_ = yes; + return *this; + } + bool ignore_newline_chars() const noexcept { + return ignoreNewlines_; + } - namespace detail - { - /*************************************************************************/ /** +private: + string paramSep_ = string(" "); + string groupSep_ = string(" "); + string altParamSep_ = string("|"); + string altGroupSep_ = string(" | "); + string flagSep_ = string(", "); + string labelPre_ = string("<"); + string labelPst_ = string(">"); + string optionPre_ = string("["); + string optionPst_ = string("]"); + string repeatPre_ = string(""); + string repeatPst_ = string("..."); + string groupPre_ = string("("); + string groupPst_ = string(")"); + string alternPre_ = string("("); + string alternPst_ = string(")"); + string alternFlagPre_ = string(""); + string alternFlagPst_ = string(""); + string joinablePre_ = string("("); + string joinablePst_ = string(")"); + string emptyLabel_ = string(""); + int firstCol_ = 8; + int docCol_ = 20; + int lastCol_ = 100; + int indentSize_ = 4; + int maxAltInUsage_ = 1; + int maxAltInDocs_ = 32; + int lineSpc_ = 0; + int paragraphSpc_ = 1; + int groupSplitSize_ = 3; + bool splitTopAlt_ = true; + bool mergeAltCommonPfx_ = false; + bool mergeJoinableCommonPfx_ = true; + bool ignoreNewlines_ = false; +}; + + + +namespace detail { + +/*************************************************************************//** * * @brief stream decorator * that applies formatting like line wrapping * *****************************************************************************/ - template - class formatting_ostream - { - public: - using string_type = StringT; - using size_type = typename string_type::size_type; - using char_type = typename string_type::value_type; - - formatting_ostream(OStream &os) - : os_(os) - , curCol_{ 0 } - , firstCol_{ 0 } - , lastCol_{ 100 } - , hangingIndent_{ 0 } - , paragraphSpacing_{ 0 } - , paragraphSpacingThreshold_{ 2 } - , curBlankLines_{ 0 } - , curParagraphLines_{ 1 } - , totalNonBlankLines_{ 0 } - , ignoreInputNls_{ false } - {} +template +class formatting_ostream +{ +public: + using string_type = StringT; + using size_type = typename string_type::size_type; + using char_type = typename string_type::value_type; - //--------------------------------------------------------------- - const OStream &base() const noexcept - { - return os_; - } - OStream &base() noexcept - { - return os_; - } + formatting_ostream(OStream& os): + os_(os), + curCol_{0}, firstCol_{0}, lastCol_{100}, + hangingIndent_{0}, paragraphSpacing_{0}, paragraphSpacingThreshold_{2}, + curBlankLines_{0}, curParagraphLines_{1}, + totalNonBlankLines_{0}, + ignoreInputNls_{false} + {} - bool good() const - { - return os_.good(); - } - //--------------------------------------------------------------- - /** @brief determines the leftmost border of the text body */ - formatting_ostream &first_column(int c) - { - firstCol_ = c < 0 ? 0 : c; - return *this; - } - int first_column() const noexcept - { - return firstCol_; - } + //--------------------------------------------------------------- + const OStream& base() const noexcept { return os_; } + OStream& base() noexcept { return os_; } - /** @brief determines the rightmost border of the text body */ - formatting_ostream &last_column(int c) - { - lastCol_ = c < 0 ? 0 : c; - return *this; - } + bool good() const { return os_.good(); } - int last_column() const noexcept - { - return lastCol_; - } - int text_width() const noexcept - { - return lastCol_ - firstCol_; - } + //--------------------------------------------------------------- + /** @brief determines the leftmost border of the text body */ + formatting_ostream& first_column(int c) { + firstCol_ = c < 0 ? 0 : c; + return *this; + } + int first_column() const noexcept { return firstCol_; } - /** @brief additional indentation for the 2nd, 3rd, ... line of + /** @brief determines the rightmost border of the text body */ + formatting_ostream& last_column(int c) { + lastCol_ = c < 0 ? 0 : c; + return *this; + } + + int last_column() const noexcept { return lastCol_; } + + int text_width() const noexcept { + return lastCol_ - firstCol_; + } + + /** @brief additional indentation for the 2nd, 3rd, ... line of a paragraph (sequence of soft-wrapped lines) */ - formatting_ostream &hanging_indent(int amount) - { - hangingIndent_ = amount; - return *this; - } - int hanging_indent() const noexcept - { - return hangingIndent_; - } + formatting_ostream& hanging_indent(int amount) { + hangingIndent_ = amount; + return *this; + } + int hanging_indent() const noexcept { + return hangingIndent_; + } - /** @brief amount of blank lines between paragraphs */ - formatting_ostream ¶graph_spacing(int lines) - { - paragraphSpacing_ = lines; - return *this; - } - int paragraph_spacing() const noexcept - { - return paragraphSpacing_; - } + /** @brief amount of blank lines between paragraphs */ + formatting_ostream& paragraph_spacing(int lines) { + paragraphSpacing_ = lines; + return *this; + } + int paragraph_spacing() const noexcept { + return paragraphSpacing_; + } - /** @brief insert paragraph spacing + /** @brief insert paragraph spacing if paragraph is at least 'lines' lines long */ - formatting_ostream &min_paragraph_lines_for_spacing(int lines) - { - paragraphSpacingThreshold_ = lines; - return *this; - } - int min_paragraph_lines_for_spacing() const noexcept - { - return paragraphSpacingThreshold_; - } + formatting_ostream& min_paragraph_lines_for_spacing(int lines) { + paragraphSpacingThreshold_ = lines; + return *this; + } + int min_paragraph_lines_for_spacing() const noexcept { + return paragraphSpacingThreshold_; + } - /** @brief if set to true, newline characters will be ignored */ - formatting_ostream &ignore_newline_chars(bool yes) - { - ignoreInputNls_ = yes; - return *this; - } + /** @brief if set to true, newline characters will be ignored */ + formatting_ostream& ignore_newline_chars(bool yes) { + ignoreInputNls_ = yes; + return *this; + } - bool ignore_newline_chars() const noexcept - { - return ignoreInputNls_; - } + bool ignore_newline_chars() const noexcept { + return ignoreInputNls_; + } - //--------------------------------------------------------------- - /* @brief insert 'n' spaces */ - void write_spaces(int n) - { - if(n < 1) return; - os_ << string_type(size_type(n), ' '); - curCol_ += n; - } - /* @brief go to new line, but continue current paragraph */ - void wrap_soft(int times = 1) - { - if(times < 1) return; - if(times > 1) - { - os_ << string_type(size_type(times), '\n'); - } - else - { - os_ << '\n'; - } - curCol_ = 0; - ++curParagraphLines_; - } + //--------------------------------------------------------------- + /* @brief insert 'n' spaces */ + void write_spaces(int n) { + if(n < 1) return; + os_ << string_type(size_type(n), ' '); + curCol_ += n; + } + + /* @brief go to new line, but continue current paragraph */ + void wrap_soft(int times = 1) { + if(times < 1) return; + if(times > 1) { + os_ << string_type(size_type(times), '\n'); + } else { + os_ << '\n'; + } + curCol_ = 0; + ++curParagraphLines_; + } + + /* @brief go to new line, and start a new paragraph */ + void wrap_hard(int times = 1) { + if(times < 1) return; + + if(paragraph_spacing() > 0 && + paragraph_lines() >= min_paragraph_lines_for_spacing()) + { + times = paragraph_spacing() + 1; + } + + if(times > 1) { + os_ << string_type(size_type(times), '\n'); + curBlankLines_ += times - 1; + } else { + os_ << '\n'; + } + if(at_begin_of_line()) { + ++curBlankLines_; + } + curCol_ = 0; + curParagraphLines_ = 1; + } + - /* @brief go to new line, and start a new paragraph */ - void wrap_hard(int times = 1) - { - if(times < 1) return; + //--------------------------------------------------------------- + bool at_begin_of_line() const noexcept { + return curCol_ <= current_line_begin(); + } + int current_line_begin() const noexcept { + return in_hanging_part_of_paragraph() + ? firstCol_ + hangingIndent_ + : firstCol_; + } - if(paragraph_spacing() > 0 && paragraph_lines() >= min_paragraph_lines_for_spacing()) - { - times = paragraph_spacing() + 1; - } + int current_column() const noexcept { + return curCol_; + } - if(times > 1) - { - os_ << string_type(size_type(times), '\n'); - curBlankLines_ += times - 1; - } - else - { - os_ << '\n'; - } - if(at_begin_of_line()) - { - ++curBlankLines_; - } - curCol_ = 0; - curParagraphLines_ = 1; - } + int total_non_blank_lines() const noexcept { + return totalNonBlankLines_; + } + int paragraph_lines() const noexcept { + return curParagraphLines_; + } + int blank_lines_before_paragraph() const noexcept { + return curBlankLines_; + } - //--------------------------------------------------------------- - bool at_begin_of_line() const noexcept - { - return curCol_ <= current_line_begin(); - } - int current_line_begin() const noexcept - { - return in_hanging_part_of_paragraph() ? firstCol_ + hangingIndent_ : firstCol_; - } - int current_column() const noexcept - { - return curCol_; - } + //--------------------------------------------------------------- + template + friend formatting_ostream& + operator << (formatting_ostream& os, const T& x) { + os.write(x); + return os; + } - int total_non_blank_lines() const noexcept - { - return totalNonBlankLines_; - } - int paragraph_lines() const noexcept - { - return curParagraphLines_; - } - int blank_lines_before_paragraph() const noexcept - { - return curBlankLines_; - } + void flush() { + os_.flush(); + } - //--------------------------------------------------------------- - template - friend formatting_ostream &operator<<(formatting_ostream &os, const T &x) - { - os.write(x); - return os; - } - void flush() - { - os_.flush(); - } +private: + bool in_hanging_part_of_paragraph() const noexcept { + return hanging_indent() > 0 && paragraph_lines() > 1; + } + bool current_line_empty() const noexcept { + return curCol_ < 1; + } + bool left_of_text_area() const noexcept { + return curCol_ < current_line_begin(); + } + bool right_of_text_area() const noexcept { + return curCol_ > lastCol_; + } + int columns_left_in_line() const noexcept { + return lastCol_ - std::max(current_line_begin(), curCol_); + } - private: - bool in_hanging_part_of_paragraph() const noexcept - { - return hanging_indent() > 0 && paragraph_lines() > 1; - } - bool current_line_empty() const noexcept - { - return curCol_ < 1; - } - bool left_of_text_area() const noexcept - { - return curCol_ < current_line_begin(); - } - bool right_of_text_area() const noexcept - { - return curCol_ > lastCol_; - } - int columns_left_in_line() const noexcept - { - return lastCol_ - std::max(current_line_begin(), curCol_); - } + void fix_indent() { + if(left_of_text_area()) { + const auto fst = current_line_begin(); + write_spaces(fst - curCol_); + curCol_ = fst; + } + } - void fix_indent() - { - if(left_of_text_area()) - { - const auto fst = current_line_begin(); - write_spaces(fst - curCol_); - curCol_ = fst; - } - } + template + bool only_whitespace(Iter first, Iter last) const { + return last == std::find_if_not(first, last, + [](char_type c) { return txt::isspace(c); }); + } - template - bool only_whitespace(Iter first, Iter last) const - { - return last == std::find_if_not(first, last, [](char_type c) { return std::isspace(c); }); - } + /** @brief write any object */ + template + void write(const T& x) { + std::ostringstream ss; + ss << x; + write(std::move(ss).str()); + } - /** @brief write any object */ - template - void write(const T &x) - { - std::ostringstream ss; - ss << x; - write(std::move(ss).str()); - } + /** @brief write a stringstream */ + void write(const std::ostringstream& s) { + write(s.str()); + } - /** @brief write a stringstream */ - void write(const std::ostringstream &s) - { - write(s.str()); - } + /** @brief write a string */ + void write(const string_type& s) { + write(s.begin(), s.end()); + } - /** @brief write a string */ - void write(const string_type &s) - { - write(s.begin(), s.end()); + /** @brief partition output into lines */ + template + void write(Iter first, Iter last) + { + if(first == last) return; + if(*first == '\n') { + if(!ignore_newline_chars()) wrap_hard(); + ++first; + if(first == last) return; + } + auto i = std::find(first, last, '\n'); + if(i != last) { + if(ignore_newline_chars()) ++i; + if(i != last) { + write_line(first, i); + write(i, last); } + } + else { + write_line(first, last); + } + } - /** @brief partition output into lines */ - template - void write(Iter first, Iter last) - { - if(first == last) return; - if(*first == '\n') - { - if(!ignore_newline_chars()) wrap_hard(); - ++first; - if(first == last) return; - } - auto i = std::find(first, last, '\n'); - if(i != last) - { - if(ignore_newline_chars()) ++i; - if(i != last) - { - write_line(first, i); - write(i, last); - } - } - else - { - write_line(first, last); - } - } + /** @brief handle line wrapping due to column constraints */ + template + void write_line(Iter first, Iter last) + { + if(first == last) return; + if(only_whitespace(first, last)) return; + + if(right_of_text_area()) wrap_soft(); + + if(at_begin_of_line()) { + //discard whitespace, it we start a new line + first = std::find_if(first, last, + [](char_type c) { return !txt::isspace(c); }); + if(first == last) return; + } + + const auto n = int(std::distance(first,last)); + const auto m = columns_left_in_line(); + //if text to be printed is too long for one line -> wrap + if(n > m) { + //break before word, if break is mid-word + auto breakat = first + m; + while(breakat > first && !txt::isspace(*breakat)) --breakat; + //could not find whitespace before word -> try after the word + if(!txt::isspace(*breakat) && breakat == first) { + breakat = std::find_if(first+m, last, + [](char_type c) { return txt::isspace(c); }); + } + if(breakat > first) { + if(curCol_ < 1) ++totalNonBlankLines_; + fix_indent(); + std::copy(first, breakat, std::ostream_iterator(os_)); + curBlankLines_ = 0; + } + if(breakat < last) { + wrap_soft(); + write_line(breakat, last); + } + } + else { + if(curCol_ < 1) ++totalNonBlankLines_; + fix_indent(); + std::copy(first, last, std::ostream_iterator(os_)); + curCol_ += n; + curBlankLines_ = 0; + } + } - /** @brief handle line wrapping due to column constraints */ - template - void write_line(Iter first, Iter last) - { - if(first == last) return; - if(only_whitespace(first, last)) return; + /** @brief write a single character */ + void write(char_type c) + { + if(c == '\n') { + if(!ignore_newline_chars()) wrap_hard(); + } + else { + if(at_begin_of_line()) ++totalNonBlankLines_; + fix_indent(); + os_ << c; + ++curCol_; + } + } - if(right_of_text_area()) wrap_soft(); + OStream& os_; + int curCol_; + int firstCol_; + int lastCol_; + int hangingIndent_; + int paragraphSpacing_; + int paragraphSpacingThreshold_; + int curBlankLines_; + int curParagraphLines_; + int totalNonBlankLines_; + bool ignoreInputNls_; +}; - if(at_begin_of_line()) - { - //discard whitespace, it we start a new line - first = std::find_if(first, last, [](char_type c) { return !std::isspace(c); }); - if(first == last) return; - } - const auto n = int(std::distance(first, last)); - const auto m = columns_left_in_line(); - //if text to be printed is too long for one line -> wrap - if(n > m) - { - //break before word, if break is mid-word - auto breakat = first + m; - while(breakat > first && !std::isspace(*breakat)) - --breakat; - //could not find whitespace before word -> try after the word - if(!std::isspace(*breakat) && breakat == first) - { - breakat = std::find_if(first + m, last, [](char_type c) { return std::isspace(c); }); - } - if(breakat > first) - { - if(curCol_ < 1) ++totalNonBlankLines_; - fix_indent(); - std::copy(first, breakat, std::ostream_iterator(os_)); - curBlankLines_ = 0; - } - if(breakat < last) - { - wrap_soft(); - write_line(breakat, last); - } - } - else - { - if(curCol_ < 1) ++totalNonBlankLines_; - fix_indent(); - std::copy(first, last, std::ostream_iterator(os_)); - curCol_ += n; - curBlankLines_ = 0; - } - } +} - /** @brief write a single character */ - void write(char_type c) - { - if(c == '\n') - { - if(!ignore_newline_chars()) wrap_hard(); - } - else - { - if(at_begin_of_line()) ++totalNonBlankLines_; - fix_indent(); - os_ << c; - ++curCol_; - } - } - OStream &os_; - int curCol_; - int firstCol_; - int lastCol_; - int hangingIndent_; - int paragraphSpacing_; - int paragraphSpacingThreshold_; - int curBlankLines_; - int curParagraphLines_; - int totalNonBlankLines_; - bool ignoreInputNls_; - }; - } // namespace detail - /*************************************************************************/ /** +/*************************************************************************//** * * @brief generates usage lines * * @details lazily evaluated * *****************************************************************************/ - class usage_lines +class usage_lines +{ +public: + using string = doc_string; + + usage_lines(const group& cli, string prefix = "", + const doc_formatting& fmt = doc_formatting{}) + : + cli_(cli), fmt_(fmt), prefix_(std::move(prefix)) { - public: - using string = doc_string; + if(!prefix_.empty()) prefix_ += ' '; + } - usage_lines(const group &cli, string prefix = "", const doc_formatting &fmt = doc_formatting{}) - : cli_(cli) - , fmt_(fmt) - , prefix_(std::move(prefix)) - { - if(!prefix_.empty()) prefix_ += ' '; - } + usage_lines(const group& cli, const doc_formatting& fmt): + usage_lines(cli, "", fmt) + {} - usage_lines(const group &cli, const doc_formatting &fmt) - : usage_lines(cli, "", fmt) - {} + usage_lines& ommit_outermost_group_surrounders(bool yes) { + ommitOutermostSurrounders_ = yes; + return *this; + } + bool ommit_outermost_group_surrounders() const { + return ommitOutermostSurrounders_; + } - usage_lines &ommit_outermost_group_surrounders(bool yes) - { - ommitOutermostSurrounders_ = yes; - return *this; - } - bool ommit_outermost_group_surrounders() const - { - return ommitOutermostSurrounders_; - } + template + inline friend OStream& operator << (OStream& os, const usage_lines& p) { + p.write(os); + return os; + } - template - inline friend OStream &operator<<(OStream &os, const usage_lines &p) - { - p.write(os); - return os; - } + string str() const { + std::ostringstream os; os << *this; return os.str(); + } - string str() const - { - std::ostringstream os; - os << *this; - return os.str(); - } - private: - using stream_t = detail::formatting_ostream<>; - const group &cli_; - doc_formatting fmt_; - string prefix_; - bool ommitOutermostSurrounders_ = false; +private: + using stream_t = detail::formatting_ostream<>; + const group& cli_; + doc_formatting fmt_; + string prefix_; + bool ommitOutermostSurrounders_ = false; - //----------------------------------------------------- - struct context - { - group::depth_first_traverser pos; - std::stack separators; - std::stack postfixes; - int level = 0; - const group *outermost = nullptr; - bool linestart = false; - bool useOutermost = true; - int line = 0; - - bool is_singleton() const noexcept - { - return linestart && pos.is_last_in_path(); - } - bool is_alternative() const noexcept - { - return pos.parent().exclusive(); - } - }; - /***************************************************************/ /** + //----------------------------------------------------- + struct context { + group::depth_first_traverser pos; + std::stack separators; + std::stack postfixes; + int level = 0; + const group* outermost = nullptr; + bool linestart = false; + bool useOutermost = true; + int line = 0; + + bool is_singleton() const noexcept { + return linestart && pos.is_last_in_path(); + } + bool is_alternative() const noexcept { + return pos.parent().exclusive(); + } + }; + + + /***************************************************************//** * * @brief writes usage text for command line parameters * *******************************************************************/ - template - void write(OStream &os) const - { - detail::formatting_ostream fos(os); - fos.first_column(fmt_.first_column()); - fos.last_column(fmt_.last_column()); + template + void write(OStream& os) const + { + detail::formatting_ostream fos(os); + fos.first_column(fmt_.first_column()); + fos.last_column(fmt_.last_column()); - auto hindent = int(prefix_.size()); - if(fos.first_column() + hindent >= int(0.4 * fos.text_width())) - { - hindent = fmt_.indent_size(); - } - fos.hanging_indent(hindent); + auto hindent = int(prefix_.size()); + if(fos.first_column() + hindent >= int(0.4 * fos.text_width())) { + hindent = fmt_.indent_size(); + } + fos.hanging_indent(hindent); - fos.paragraph_spacing(fmt_.paragraph_spacing()); - fos.min_paragraph_lines_for_spacing(2); - fos.ignore_newline_chars(fmt_.ignore_newline_chars()); + fos.paragraph_spacing(fmt_.paragraph_spacing()); + fos.min_paragraph_lines_for_spacing(2); + fos.ignore_newline_chars(fmt_.ignore_newline_chars()); - context cur; - cur.pos = cli_.begin_dfs(); - cur.linestart = true; - cur.level = cur.pos.level(); - cur.outermost = &cli_; + context cur; + cur.pos = cli_.begin_dfs(); + cur.linestart = true; + cur.level = cur.pos.level(); + cur.outermost = &cli_; + + write(fos, cur, prefix_); + } - write(fos, cur, prefix_); - } - /***************************************************************/ /** + /***************************************************************//** * * @brief writes usage text for command line parameters * * @param prefix all that goes in front of current things to print * *******************************************************************/ - template - void write(OStream &os, context cur, string prefix) const - { - if(!cur.pos) return; + template + void write(OStream& os, context cur, string prefix) const + { + if(!cur.pos) return; - std::ostringstream buf; - if(cur.linestart) buf << prefix; - const auto initPos = buf.tellp(); + std::ostringstream buf; + if(cur.linestart) buf << prefix; + const auto initPos = buf.tellp(); - cur.level = cur.pos.level(); + cur.level = cur.pos.level(); - if(cur.useOutermost) - { - //we cannot start outside of the outermost group - //so we have to treat it separately - start_group(buf, cur.pos.parent(), cur); - if(!cur.pos) - { + if(cur.useOutermost) { + //we cannot start outside of the outermost group + //so we have to treat it separately + start_group(buf, cur.pos.parent(), cur); + if(!cur.pos) { + os << buf.str(); + return; + } + } + else { + //don't visit siblings of starter node + cur.pos.skip_siblings(); + } + check_end_group(buf, cur); + + do { + if(buf.tellp() > initPos) cur.linestart = false; + if(!cur.linestart && !cur.pos.is_first_in_parent()) { + buf << cur.separators.top(); + } + if(cur.pos->is_group()) { + start_group(buf, cur.pos->as_group(), cur); + if(!cur.pos) { os << buf.str(); return; } } - else - { - //don't visit siblings of starter node - cur.pos.skip_siblings(); + else { + buf << param_label(cur.pos->as_param(), cur); + ++cur.pos; } check_end_group(buf, cur); + } while(cur.pos); - do - { - if(buf.tellp() > initPos) cur.linestart = false; - if(!cur.linestart && !cur.pos.is_first_in_parent()) - { - buf << cur.separators.top(); - } - if(cur.pos->is_group()) - { - start_group(buf, cur.pos->as_group(), cur); - if(!cur.pos) - { - os << buf.str(); - return; - } - } - else - { - buf << param_label(cur.pos->as_param(), cur); - ++cur.pos; - } - check_end_group(buf, cur); - } while(cur.pos); + os << buf.str(); + } - os << buf.str(); - } - /***************************************************************/ /** + /***************************************************************//** * * @brief handles pattern group surrounders and separators * and alternative splitting * *******************************************************************/ - void start_group(std::ostringstream &os, const group &group, context &cur) const - { - //does cur.pos already point to a member or to group itself? - //needed for special treatment of outermost group - const bool alreadyInside = &(cur.pos.parent()) == &group; + void start_group(std::ostringstream& os, + const group& group, context& cur) const + { + //does cur.pos already point to a member or to group itself? + //needed for special treatment of outermost group + const bool alreadyInside = &(cur.pos.parent()) == &group; + + auto lbl = joined_label(group, cur); + if(!lbl.empty()) { + os << lbl; + cur.linestart = false; + //skip over entire group as its label has already been created + if(alreadyInside) { + cur.pos.next_after_siblings(); + } else { + cur.pos.next_sibling(); + } + } + else { + const bool splitAlternatives = group.exclusive() && + fmt_.split_alternatives() && + std::any_of(group.begin(), group.end(), + [this](const pattern& p) { + return int(p.param_count()) >= fmt_.alternatives_min_split_size(); + }); - auto lbl = joined_label(group, cur); - if(!lbl.empty()) - { - os << lbl; - cur.linestart = false; - //skip over entire group as its label has already been created - if(alreadyInside) - { - cur.pos.next_after_siblings(); - } - else - { - cur.pos.next_sibling(); - } - } - else - { - const bool splitAlternatives = group.exclusive() && fmt_.split_alternatives() - && std::any_of(group.begin(), group.end(), [this](const pattern &p) { - return int(p.param_count()) >= fmt_.alternatives_min_split_size(); - }); - - if(splitAlternatives) - { - cur.postfixes.push(""); - cur.separators.push(""); - //recursively print alternative paths in decision-DAG - //enter group? - if(!alreadyInside) ++cur.pos; - cur.linestart = true; - cur.useOutermost = false; - auto pfx = os.str(); - os.str(""); - //print paths in DAG starting at each group member - for(std::size_t i = 0; i < group.size(); ++i) - { - std::stringstream buf; - cur.outermost = cur.pos->is_group() ? &(cur.pos->as_group()) : nullptr; - write(buf, cur, pfx); - if(buf.tellp() > int(pfx.size())) - { - os << buf.str(); - if(i < group.size() - 1) - { - if(cur.line > 0) - { - os << string(fmt_.line_spacing(), '\n'); - } - ++cur.line; - os << '\n'; + if(splitAlternatives) { + cur.postfixes.push(""); + cur.separators.push(""); + //recursively print alternative paths in decision-DAG + //enter group? + if(!alreadyInside) ++cur.pos; + cur.linestart = true; + cur.useOutermost = false; + auto pfx = os.str(); + os.str(""); + //print paths in DAG starting at each group member + for(std::size_t i = 0; i < group.size(); ++i) { + std::stringstream buf; + cur.outermost = cur.pos->is_group() ? &(cur.pos->as_group()) : nullptr; + write(buf, cur, pfx); + if(buf.tellp() > int(pfx.size())) { + os << buf.str(); + if(i < group.size()-1) { + if(cur.line > 0) { + os << string(fmt_.line_spacing(), '\n'); } + ++cur.line; + os << '\n'; } - cur.pos.next_sibling(); //do not descend into members } - cur.pos.invalidate(); //signal end-of-path - return; - } - else - { - //pre & postfixes, separators - auto surround = group_surrounders(group, cur); - os << surround.first; - cur.postfixes.push(std::move(surround.second)); - cur.separators.push(group_separator(group, fmt_)); - //descend into group? - if(!alreadyInside) ++cur.pos; + cur.pos.next_sibling(); //do not descend into members } + cur.pos.invalidate(); //signal end-of-path + return; + } + else { + //pre & postfixes, separators + auto surround = group_surrounders(group, cur); + os << surround.first; + cur.postfixes.push(std::move(surround.second)); + cur.separators.push(group_separator(group, fmt_)); + //descend into group? + if(!alreadyInside) ++cur.pos; } - cur.level = cur.pos.level(); } + cur.level = cur.pos.level(); + } + - /***************************************************************/ /** + /***************************************************************//** * *******************************************************************/ - void check_end_group(std::ostringstream &os, context &cur) const - { - for(; cur.level > cur.pos.level(); --cur.level) - { - os << cur.postfixes.top(); - cur.postfixes.pop(); - cur.separators.pop(); - } - cur.level = cur.pos.level(); + void check_end_group(std::ostringstream& os, context& cur) const + { + for(; cur.level > cur.pos.level(); --cur.level) { + os << cur.postfixes.top(); + cur.postfixes.pop(); + cur.separators.pop(); } + cur.level = cur.pos.level(); + } + - /***************************************************************/ /** + /***************************************************************//** * * @brief makes usage label for one command line parameter * *******************************************************************/ - string param_label(const parameter &p, const context &cur) const - { - const auto &parent = cur.pos.parent(); + string param_label(const parameter& p, const context& cur) const + { + const auto& parent = cur.pos.parent(); - const bool startsOptionalSequence = parent.size() > 1 && p.blocking() && cur.pos.is_first_in_parent(); + const bool startsOptionalSequence = + parent.size() > 1 && p.blocking() && cur.pos.is_first_in_parent(); - const bool outermost = ommitOutermostSurrounders_ && cur.outermost == &parent; + const bool outermost = + ommitOutermostSurrounders_ && cur.outermost == &parent; - const bool showopt = !cur.is_alternative() && !p.required() && !startsOptionalSequence && !outermost; + const bool showopt = !cur.is_alternative() && !p.required() + && !startsOptionalSequence && !outermost; - const bool showrep = p.repeatable() && !outermost; + const bool showrep = p.repeatable() && !outermost; - string lbl; + string lbl; - if(showrep) lbl += fmt_.repeat_prefix(); - if(showopt) lbl += fmt_.optional_prefix(); + if(showrep) lbl += fmt_.repeat_prefix(); + if(showopt) lbl += fmt_.optional_prefix(); - const auto &flags = p.flags(); - if(!flags.empty()) - { - const int n = std::min(fmt_.max_flags_per_param_in_usage(), int(flags.size())); + const auto& flags = p.flags(); + if(!flags.empty()) { + const int n = std::min(fmt_.max_flags_per_param_in_usage(), + int(flags.size())); - const bool surrAlt = n > 1 && !showopt && !cur.is_singleton(); + const bool surrAlt = n > 1 && !showopt && !cur.is_singleton(); - if(surrAlt) lbl += fmt_.alternative_flags_prefix(); - bool sep = false; - for(int i = 0; i < n; ++i) - { - if(sep) - { - if(cur.is_singleton()) - lbl += fmt_.alternative_group_separator(); - else - lbl += fmt_.flag_separator(); - } - lbl += flags[i]; - sep = true; - } - if(surrAlt) lbl += fmt_.alternative_flags_postfix(); - } - else - { - if(!p.label().empty()) - { - lbl += fmt_.label_prefix() + p.label() + fmt_.label_postfix(); - } - else if(!fmt_.empty_label().empty()) - { - lbl += fmt_.label_prefix() + fmt_.empty_label() + fmt_.label_postfix(); - } - else - { - return ""; + if(surrAlt) lbl += fmt_.alternative_flags_prefix(); + bool sep = false; + for(int i = 0; i < n; ++i) { + if(sep) { + if(cur.is_singleton()) + lbl += fmt_.alternative_group_separator(); + else + lbl += fmt_.flag_separator(); } + lbl += flags[i]; + sep = true; } + if(surrAlt) lbl += fmt_.alternative_flags_postfix(); + } + else { + if(!p.label().empty()) { + lbl += fmt_.label_prefix() + + p.label() + + fmt_.label_postfix(); + } else if(!fmt_.empty_label().empty()) { + lbl += fmt_.label_prefix() + + fmt_.empty_label() + + fmt_.label_postfix(); + } else { + return ""; + } + } - if(showopt) lbl += fmt_.optional_postfix(); - if(showrep) lbl += fmt_.repeat_postfix(); + if(showopt) lbl += fmt_.optional_postfix(); + if(showrep) lbl += fmt_.repeat_postfix(); - return lbl; - } + return lbl; + } - /***************************************************************/ /** + + /***************************************************************//** * * @brief prints flags in one group in a merged fashion * *******************************************************************/ - string joined_label(const group &g, const context &cur) const - { - if(!fmt_.merge_alternative_flags_with_common_prefix() && !fmt_.merge_joinable_with_common_prefix()) - return ""; + string joined_label(const group& g, const context& cur) const + { + if(!fmt_.merge_alternative_flags_with_common_prefix() && + !fmt_.merge_joinable_with_common_prefix()) return ""; - const bool flagsonly = std::all_of(g.begin(), g.end(), [](const pattern &p) { + const bool flagsonly = std::all_of(g.begin(), g.end(), + [](const pattern& p){ return p.is_param() && !p.as_param().flags().empty(); }); - if(!flagsonly) return ""; + if(!flagsonly) return ""; - const bool showOpt = g.all_optional() && !(ommitOutermostSurrounders_ && cur.outermost == &g); + const bool showOpt = g.all_optional() && + !(ommitOutermostSurrounders_ && cur.outermost == &g); - auto pfx = g.common_flag_prefix(); - if(pfx.empty()) return ""; + auto pfx = g.common_flag_prefix(); + if(pfx.empty()) return ""; - const auto n = pfx.size(); - if(g.exclusive() && fmt_.merge_alternative_flags_with_common_prefix()) - { + const auto n = pfx.size(); + if(g.exclusive() && + fmt_.merge_alternative_flags_with_common_prefix()) + { + string lbl; + if(showOpt) lbl += fmt_.optional_prefix(); + lbl += pfx + fmt_.alternatives_prefix(); + bool first = true; + for(const auto& p : g) { + if(p.is_param()) { + if(first) + first = false; + else + lbl += fmt_.alternative_param_separator(); + lbl += p.as_param().flags().front().substr(n); + } + } + lbl += fmt_.alternatives_postfix(); + if(showOpt) lbl += fmt_.optional_postfix(); + return lbl; + } + //no alternatives, but joinable flags + else if(g.joinable() && + fmt_.merge_joinable_with_common_prefix()) + { + const bool allSingleChar = std::all_of(g.begin(), g.end(), + [&](const pattern& p){ + return p.is_param() && + p.as_param().flags().front().substr(n).size() == 1; + }); + + if(allSingleChar) { string lbl; if(showOpt) lbl += fmt_.optional_prefix(); - lbl += pfx + fmt_.alternatives_prefix(); - bool first = true; - for(const auto &p : g) - { + lbl += pfx; + for(const auto& p : g) { if(p.is_param()) - { - if(first) - first = false; - else - lbl += fmt_.alternative_param_separator(); lbl += p.as_param().flags().front().substr(n); - } } - lbl += fmt_.alternatives_postfix(); if(showOpt) lbl += fmt_.optional_postfix(); return lbl; } - //no alternatives, but joinable flags - else if(g.joinable() && fmt_.merge_joinable_with_common_prefix()) - { - const bool allSingleChar = std::all_of(g.begin(), g.end(), [&](const pattern &p) { - return p.is_param() && p.as_param().flags().front().substr(n).size() == 1; - }); - - if(allSingleChar) - { - string lbl; - if(showOpt) lbl += fmt_.optional_prefix(); - lbl += pfx; - for(const auto &p : g) - { - if(p.is_param()) lbl += p.as_param().flags().front().substr(n); - } - if(showOpt) lbl += fmt_.optional_postfix(); - return lbl; - } - } - - return ""; } - /***************************************************************/ /** - * - * @return symbols with which to surround a group - * - *******************************************************************/ - std::pair group_surrounders(const group &group, const context &cur) const - { - string prefix; - string postfix; + return ""; + } - const bool isOutermost = &group == cur.outermost; - if(isOutermost && ommitOutermostSurrounders_) return { string{}, string{} }; - if(group.exclusive()) - { - if(group.all_optional()) - { - prefix = fmt_.optional_prefix(); - postfix = fmt_.optional_postfix(); - if(group.all_flagless()) - { - prefix += fmt_.label_prefix(); - postfix = fmt_.label_prefix() + postfix; - } - } - else if(group.all_flagless()) - { - prefix = fmt_.label_prefix(); - postfix = fmt_.label_postfix(); - } - else if(!cur.is_singleton() || !isOutermost) - { - prefix = fmt_.alternatives_prefix(); - postfix = fmt_.alternatives_postfix(); - } - } - else if(group.size() > 1 && group.front().blocking() && !group.front().required()) - { - prefix = fmt_.optional_prefix(); - postfix = fmt_.optional_postfix(); - } - else if(group.size() > 1 && cur.is_alternative() && &group != cur.outermost) - { - prefix = fmt_.group_prefix(); - postfix = fmt_.group_postfix(); - } - else if(!group.exclusive() && group.joinable() && !cur.linestart) - { - prefix = fmt_.joinable_prefix(); - postfix = fmt_.joinable_postfix(); - } + /***************************************************************//** + * + * @return symbols with which to surround a group + * + *******************************************************************/ + std::pair + group_surrounders(const group& group, const context& cur) const + { + string prefix; + string postfix; - if(group.repeatable()) - { - if(prefix.empty()) prefix = fmt_.group_prefix(); - prefix = fmt_.repeat_prefix() + prefix; - if(postfix.empty()) postfix = fmt_.group_postfix(); - postfix += fmt_.repeat_postfix(); + const bool isOutermost = &group == cur.outermost; + if(isOutermost && ommitOutermostSurrounders_) + return {string{}, string{}}; + + if(group.exclusive()) { + if(group.all_optional()) { + prefix = fmt_.optional_prefix(); + postfix = fmt_.optional_postfix(); + if(group.all_flagless()) { + prefix += fmt_.label_prefix(); + postfix = fmt_.label_prefix() + postfix; + } + } else if(group.all_flagless()) { + prefix = fmt_.label_prefix(); + postfix = fmt_.label_postfix(); + } else if(!cur.is_singleton() || !isOutermost) { + prefix = fmt_.alternatives_prefix(); + postfix = fmt_.alternatives_postfix(); } + } + else if(group.size() > 1 && + group.front().blocking() && !group.front().required()) + { + prefix = fmt_.optional_prefix(); + postfix = fmt_.optional_postfix(); + } + else if(group.size() > 1 && cur.is_alternative() && + &group != cur.outermost) + { + prefix = fmt_.group_prefix(); + postfix = fmt_.group_postfix(); + } + else if(!group.exclusive() && + group.joinable() && !cur.linestart) + { + prefix = fmt_.joinable_prefix(); + postfix = fmt_.joinable_postfix(); + } - return { std::move(prefix), std::move(postfix) }; + if(group.repeatable()) { + if(prefix.empty()) prefix = fmt_.group_prefix(); + prefix = fmt_.repeat_prefix() + prefix; + if(postfix.empty()) postfix = fmt_.group_postfix(); + postfix += fmt_.repeat_postfix(); } - /***************************************************************/ /** + return {std::move(prefix), std::move(postfix)}; + } + + + /***************************************************************//** * * @return symbol that separates members of a group * *******************************************************************/ - static string group_separator(const group &group, const doc_formatting &fmt) - { - const bool only1ParamPerMember = - std::all_of(group.begin(), group.end(), [](const pattern &p) { return p.param_count() < 2; }); + static string + group_separator(const group& group, const doc_formatting& fmt) + { + const bool only1ParamPerMember = std::all_of(group.begin(), group.end(), + [](const pattern& p) { return p.param_count() < 2; }); - if(only1ParamPerMember) - { - if(group.exclusive()) - { - return fmt.alternative_param_separator(); - } - else - { - return fmt.param_separator(); - } + if(only1ParamPerMember) { + if(group.exclusive()) { + return fmt.alternative_param_separator(); + } else { + return fmt.param_separator(); } - else - { //there is at least one large group inside - if(group.exclusive()) - { - return fmt.alternative_group_separator(); - } - else - { - return fmt.group_separator(); - } + } + else { //there is at least one large group inside + if(group.exclusive()) { + return fmt.alternative_group_separator(); + } else { + return fmt.group_separator(); } } - }; + } +}; + + + - /*************************************************************************/ /** +/*************************************************************************//** * * @brief generates parameter and group documentation from docstrings * * @details lazily evaluated * *****************************************************************************/ - class documentation +class documentation +{ +public: + using string = doc_string; + using filter_function = std::function; + + documentation(const group& cli, + const doc_formatting& fmt = doc_formatting{}, + filter_function filter = param_filter{}) + : + cli_(cli), fmt_{fmt}, usgFmt_{fmt}, filter_{std::move(filter)} { - public: - using string = doc_string; - using filter_function = std::function; - - documentation(const group &cli, - const doc_formatting &fmt = doc_formatting{}, - filter_function filter = param_filter{}) - : cli_(cli) - , fmt_{ fmt } - , usgFmt_{ fmt } - , filter_{ std::move(filter) } - { - //necessary, because we re-use "usage_lines" to generate - //labels for documented groups - usgFmt_.max_flags_per_param_in_usage(usgFmt_.max_flags_per_param_in_doc()); - } + //necessary, because we re-use "usage_lines" to generate + //labels for documented groups + usgFmt_.max_flags_per_param_in_usage( + usgFmt_.max_flags_per_param_in_doc()); + } - documentation(const group &cli, filter_function filter) - : documentation{ cli, doc_formatting{}, std::move(filter) } - {} + documentation(const group& cli, filter_function filter) : + documentation{cli, doc_formatting{}, std::move(filter)} + {} - documentation(const group &cli, const param_filter &filter) - : documentation{ cli, doc_formatting{}, [filter](const parameter &p) { return filter(p); } } - {} + documentation(const group& cli, const param_filter& filter) : + documentation{cli, doc_formatting{}, + [filter](const parameter& p) { return filter(p); }} + {} - template - inline friend OStream &operator<<(OStream &os, const documentation &p) - { - p.write(os); - return os; - } + template + inline friend OStream& operator << (OStream& os, const documentation& p) { + p.write(os); + return os; + } - string str() const - { - std::ostringstream os; - write(os); - return os.str(); - } + string str() const { + std::ostringstream os; + write(os); + return os.str(); + } - private: - using dfs_traverser = group::depth_first_traverser; - const group &cli_; - doc_formatting fmt_; - doc_formatting usgFmt_; - filter_function filter_; - enum class paragraph - { - param, - group - }; +private: + using dfs_traverser = group::depth_first_traverser; + + const group& cli_; + doc_formatting fmt_; + doc_formatting usgFmt_; + filter_function filter_; + enum class paragraph { param, group }; - /***************************************************************/ /** + + /***************************************************************//** * * @brief writes documentation to output stream * *******************************************************************/ - template - void write(OStream &os) const - { - detail::formatting_ostream fos(os); - fos.first_column(fmt_.first_column()); - fos.last_column(fmt_.last_column()); - fos.hanging_indent(0); - fos.paragraph_spacing(0); - fos.ignore_newline_chars(fmt_.ignore_newline_chars()); - print_doc(fos, cli_); - } - - /***************************************************************/ /** + template + void write(OStream& os) const { + detail::formatting_ostream fos(os); + fos.first_column(fmt_.first_column()); + fos.last_column(fmt_.last_column()); + fos.hanging_indent(0); + fos.paragraph_spacing(0); + fos.ignore_newline_chars(fmt_.ignore_newline_chars()); + print_doc(fos, cli_); + } + + + /***************************************************************//** * * @brief writes full documentation text for command line parameters * *******************************************************************/ - template - void print_doc(detail::formatting_ostream &os, const group &cli, int indentLvl = 0) const - { - if(cli.empty()) return; + template + void print_doc(detail::formatting_ostream& os, + const group& cli, int indentLvl = 0) const + { + if(cli.empty()) return; - //if group itself doesn't have docstring - if(cli.doc().empty()) - { - for(const auto &p : cli) - { - print_doc(os, p, indentLvl); - } + //if group itself doesn't have docstring + if(cli.doc().empty()) { + for(const auto& p : cli) { + print_doc(os, p, indentLvl); } - else - { //group itself does have docstring - bool anyDocInside = - std::any_of(cli.begin(), cli.end(), [](const pattern &p) { return !p.doc().empty(); }); - - if(anyDocInside) - { //group docstring as title, then child entries - handle_spacing(os, paragraph::group, indentLvl); - os << cli.doc(); - for(const auto &p : cli) - { - print_doc(os, p, indentLvl + 1); - } - } - else - { //group label first then group docstring - auto lbl = usage_lines(cli, usgFmt_).ommit_outermost_group_surrounders(true).str(); + } + else { //group itself does have docstring + bool anyDocInside = std::any_of(cli.begin(), cli.end(), + [](const pattern& p){ return !p.doc().empty(); }); - str::trim(lbl); - handle_spacing(os, paragraph::param, indentLvl); - print_entry(os, lbl, cli.doc()); + if(anyDocInside) { //group docstring as title, then child entries + handle_spacing(os, paragraph::group, indentLvl); + os << cli.doc(); + for(const auto& p : cli) { + print_doc(os, p, indentLvl + 1); } } + else { //group label first then group docstring + auto lbl = usage_lines(cli, usgFmt_) + .ommit_outermost_group_surrounders(true).str(); + + str::trim(lbl); + handle_spacing(os, paragraph::param, indentLvl); + print_entry(os, lbl, cli.doc()); + } } + } + - /***************************************************************/ /** + /***************************************************************//** * * @brief writes documentation text for one group or parameter * *******************************************************************/ - template - void print_doc(detail::formatting_ostream &os, const pattern &ptrn, int indentLvl) const - { - if(ptrn.is_group()) - { - print_doc(os, ptrn.as_group(), indentLvl); - } - else - { - const auto &p = ptrn.as_param(); - if(!filter_(p)) return; + template + void print_doc(detail::formatting_ostream& os, + const pattern& ptrn, int indentLvl) const + { + if(ptrn.is_group()) { + print_doc(os, ptrn.as_group(), indentLvl); + } + else { + const auto& p = ptrn.as_param(); + if(!filter_(p)) return; - handle_spacing(os, paragraph::param, indentLvl); - print_entry(os, param_label(p, fmt_), p.doc()); - } + handle_spacing(os, paragraph::param, indentLvl); + print_entry(os, param_label(p, fmt_), p.doc()); } + } - /***************************************************************/ /** + /***************************************************************//** * * @brief handles line and paragraph spacings * *******************************************************************/ - template - void handle_spacing(detail::formatting_ostream &os, paragraph p, int indentLvl) const - { - const auto oldIndent = os.first_column(); - const auto indent = fmt_.first_column() + indentLvl * fmt_.indent_size(); + template + void handle_spacing(detail::formatting_ostream& os, + paragraph p, int indentLvl) const + { + const auto oldIndent = os.first_column(); + const auto indent = fmt_.first_column() + indentLvl * fmt_.indent_size(); - if(os.total_non_blank_lines() < 1) - { - os.first_column(indent); - return; - } + if(os.total_non_blank_lines() < 1) { + os.first_column(indent); + return; + } - if(os.paragraph_lines() > 1 || indent < oldIndent) - { - os.wrap_hard(fmt_.paragraph_spacing() + 1); - } - else - { - os.wrap_hard(); - } + if(os.paragraph_lines() > 1 || indent < oldIndent) { + os.wrap_hard(fmt_.paragraph_spacing() + 1); + } else { + os.wrap_hard(); + } - if(p == paragraph::group) - { - if(os.blank_lines_before_paragraph() < fmt_.paragraph_spacing()) - { - os.wrap_hard(fmt_.paragraph_spacing() - os.blank_lines_before_paragraph()); - } - } - else if(os.blank_lines_before_paragraph() < fmt_.line_spacing()) - { - os.wrap_hard(fmt_.line_spacing() - os.blank_lines_before_paragraph()); + if(p == paragraph::group) { + if(os.blank_lines_before_paragraph() < fmt_.paragraph_spacing()) { + os.wrap_hard(fmt_.paragraph_spacing() - os.blank_lines_before_paragraph()); } - os.first_column(indent); } + else if(os.blank_lines_before_paragraph() < fmt_.line_spacing()) { + os.wrap_hard(fmt_.line_spacing() - os.blank_lines_before_paragraph()); + } + os.first_column(indent); + } - /*********************************************************************/ /** + /*********************************************************************//** * * @brief prints one entry = label + docstring * ************************************************************************/ - template - void print_entry(detail::formatting_ostream &os, const string &label, const string &docstr) const - { - if(label.empty()) return; + template + void print_entry(detail::formatting_ostream& os, + const string& label, const string& docstr) const + { + if(label.empty()) return; - os << label; + os << label; - if(!docstr.empty()) - { - if(os.current_column() >= fmt_.doc_column()) os.wrap_soft(); - const auto oldcol = os.first_column(); - os.first_column(fmt_.doc_column()); - os << docstr; - os.first_column(oldcol); - } + if(!docstr.empty()) { + if(os.current_column() >= fmt_.doc_column()) os.wrap_soft(); + const auto oldcol = os.first_column(); + os.first_column(fmt_.doc_column()); + os << docstr; + os.first_column(oldcol); } + } - /*********************************************************************/ /** + + /*********************************************************************//** * * @brief makes label for one parameter * ************************************************************************/ - static doc_string param_label(const parameter ¶m, const doc_formatting &fmt) - { - doc_string lbl; + static doc_string + param_label(const parameter& param, const doc_formatting& fmt) + { + doc_string lbl; - if(param.repeatable()) lbl += fmt.repeat_prefix(); + if(param.repeatable()) lbl += fmt.repeat_prefix(); - const auto &flags = param.flags(); - if(!flags.empty()) - { - lbl += flags[0]; - const int n = std::min(fmt.max_flags_per_param_in_doc(), int(flags.size())); - for(int i = 1; i < n; ++i) - { - lbl += fmt.flag_separator() + flags[i]; - } + const auto& flags = param.flags(); + if(!flags.empty()) { + lbl += flags[0]; + const int n = std::min(fmt.max_flags_per_param_in_doc(), + int(flags.size())); + for(int i = 1; i < n; ++i) { + lbl += fmt.flag_separator() + flags[i]; } - else if(!param.label().empty() || !fmt.empty_label().empty()) - { - lbl += fmt.label_prefix(); - if(!param.label().empty()) - { - lbl += param.label(); - } - else - { - lbl += fmt.empty_label(); - } - lbl += fmt.label_postfix(); + } + else if(!param.label().empty() || !fmt.empty_label().empty()) { + lbl += fmt.label_prefix(); + if(!param.label().empty()) { + lbl += param.label(); + } else { + lbl += fmt.empty_label(); } + lbl += fmt.label_postfix(); + } - if(param.repeatable()) lbl += fmt.repeat_postfix(); + if(param.repeatable()) lbl += fmt.repeat_postfix(); - return lbl; - } - }; + return lbl; + } - /*************************************************************************/ /** +}; + + + + +/*************************************************************************//** * * @brief stores strings for man page sections * *****************************************************************************/ - class man_page - { +class man_page +{ +public: + //--------------------------------------------------------------- + using string = doc_string; + + //--------------------------------------------------------------- + /** @brief man page section */ + class section { public: - //--------------------------------------------------------------- using string = doc_string; - //--------------------------------------------------------------- - /** @brief man page section */ - class section - { - public: - using string = doc_string; + section(string stitle, string scontent): + title_{std::move(stitle)}, content_{std::move(scontent)} + {} - section(string stitle, string scontent) - : title_{ std::move(stitle) } - , content_{ std::move(scontent) } - {} + const string& title() const noexcept { return title_; } + const string& content() const noexcept { return content_; } - const string &title() const noexcept - { - return title_; - } - const string &content() const noexcept - { - return content_; - } + private: + string title_; + string content_; + }; - private: - string title_; - string content_; - }; +private: + using section_store = std::vector
; - private: - using section_store = std::vector
; +public: + //--------------------------------------------------------------- + using value_type = section; + using const_iterator = section_store::const_iterator; + using size_type = section_store::size_type; - public: - //--------------------------------------------------------------- - using value_type = section; - using const_iterator = section_store::const_iterator; - using size_type = section_store::size_type; - //--------------------------------------------------------------- - man_page &append_section(string title, string content) - { - sections_.emplace_back(std::move(title), std::move(content)); - return *this; - } - //----------------------------------------------------- - man_page &prepend_section(string title, string content) - { - sections_.emplace(sections_.begin(), std::move(title), std::move(content)); - return *this; - } + //--------------------------------------------------------------- + man_page& + append_section(string title, string content) + { + sections_.emplace_back(std::move(title), std::move(content)); + return *this; + } + //----------------------------------------------------- + man_page& + prepend_section(string title, string content) + { + sections_.emplace(sections_.begin(), + std::move(title), std::move(content)); + return *this; + } - //--------------------------------------------------------------- - const section &operator[](size_type index) const noexcept - { - return sections_[index]; - } - //--------------------------------------------------------------- - size_type size() const noexcept - { - return sections_.size(); - } + //--------------------------------------------------------------- + const section& operator [] (size_type index) const noexcept { + return sections_[index]; + } - bool empty() const noexcept - { - return sections_.empty(); - } + //--------------------------------------------------------------- + size_type size() const noexcept { return sections_.size(); } - //--------------------------------------------------------------- - const_iterator begin() const noexcept - { - return sections_.begin(); - } - const_iterator end() const noexcept - { - return sections_.end(); - } + bool empty() const noexcept { return sections_.empty(); } - //--------------------------------------------------------------- - man_page &program_name(const string &n) - { - progName_ = n; - return *this; - } - man_page &program_name(string &&n) - { - progName_ = std::move(n); - return *this; - } - const string &program_name() const noexcept - { - return progName_; - } - //--------------------------------------------------------------- - man_page §ion_row_spacing(int rows) - { - sectionSpc_ = rows > 0 ? rows : 0; - return *this; - } - int section_row_spacing() const noexcept - { - return sectionSpc_; - } + //--------------------------------------------------------------- + const_iterator begin() const noexcept { return sections_.begin(); } + const_iterator end() const noexcept { return sections_.end(); } + + + //--------------------------------------------------------------- + man_page& program_name(const string& n) { + progName_ = program_basename(n); + return *this; + } + man_page& program_name(string&& n) { + progName_ = program_basename(n); + return *this; + } + const string& program_name() const noexcept { + return progName_; + } + + static const string program_basename(const string& progname) { + // clip off the exe path, i.e. execute equivalent of basename(progname, ".exe") + auto sep_pos = progname.find_last_of(":/\\"); + auto end_pos = progname.size(); + const doc_string exe = ".exe"; + if (exe.size() < progname.size() && std::equal(exe.rbegin(), exe.rend(), progname.rbegin())) + { + end_pos -= exe.size(); + } + if (sep_pos == doc_string::npos) + { + sep_pos = 0; + } + else + { + sep_pos++; + } + + if (sep_pos > 0 || end_pos < progname.size()) + { + return progname.substr(sep_pos, end_pos - sep_pos); + } + return progname; + } + + //--------------------------------------------------------------- + man_page& section_row_spacing(int rows) { + sectionSpc_ = rows > 0 ? rows : 0; + return *this; + } + int section_row_spacing() const noexcept { return sectionSpc_; } - private: - int sectionSpc_ = 1; - section_store sections_; - string progName_; - }; - /*************************************************************************/ /** +private: + int sectionSpc_ = 1; + section_store sections_; + string progName_; +}; + + + +/*************************************************************************//** * * @brief generates man sections from command line parameters * with sections "synopsis" and "options" * *****************************************************************************/ - inline man_page make_man_page(const group &cli, - doc_string progname = "", - const doc_formatting &fmt = doc_formatting{}) - { - man_page man; - man.append_section("SYNOPSIS", usage_lines(cli, progname, fmt).str()); - man.append_section("OPTIONS", documentation(cli, fmt).str()); - return man; - } +inline man_page +make_man_page(const group& cli, + doc_string progname = "", + const doc_formatting& fmt = doc_formatting{}) +{ + man_page man; + man.program_name(progname); + man.append_section("SYNOPSIS", usage_lines(cli, man.program_name(),fmt).str()); + man.append_section("OPTIONS", documentation(cli,fmt).str()); + return man; +} + + - /*************************************************************************/ /** +/*************************************************************************//** * * @brief generates man page based on command line parameters * *****************************************************************************/ - template - OStream &operator<<(OStream &os, const man_page &man) - { - bool first = true; - const auto secSpc = doc_string(man.section_row_spacing() + 1, '\n'); - for(const auto §ion : man) - { - if(!section.content().empty()) - { - if(first) - first = false; - else - os << secSpc; - if(!section.title().empty()) os << section.title() << '\n'; - os << section.content(); - } +template +OStream& +operator << (OStream& os, const man_page& man) +{ + bool first = true; + const auto secSpc = doc_string(man.section_row_spacing() + 1, '\n'); + for(const auto& section : man) { + if(!section.content().empty()) { + if(first) first = false; else os << secSpc; + if(!section.title().empty()) os << section.title() << '\n'; + os << section.content(); } - os << '\n'; - return os; } + os << '\n'; + return os; +} + - /*************************************************************************/ /** + + + +/*************************************************************************//** * * @brief printing methods for debugging command line interfaces * *****************************************************************************/ - namespace debug - { - /*************************************************************************/ /** +namespace debug { + + +/*************************************************************************//** * * @brief prints first flag or value label of a parameter * *****************************************************************************/ - inline doc_string doc_label(const parameter &p) - { - if(!p.flags().empty()) return p.flags().front(); - if(!p.label().empty()) return p.label(); - return doc_string{ "" }; - } +inline doc_string doc_label(const parameter& p) +{ + if(!p.flags().empty()) return p.flags().front(); + if(!p.label().empty()) return p.label(); + return doc_string{""}; +} - inline doc_string doc_label(const group &) - { - return ""; - } +inline doc_string doc_label(const group&) +{ + return ""; +} + +inline doc_string doc_label(const pattern& p) +{ + return p.is_group() ? doc_label(p.as_group()) : doc_label(p.as_param()); +} - inline doc_string doc_label(const pattern &p) - { - return p.is_group() ? doc_label(p.as_group()) : doc_label(p.as_param()); - } - /*************************************************************************/ /** +/*************************************************************************//** * * @brief prints parsing result * *****************************************************************************/ - template - void print(OStream &os, const parsing_result &result) - { - for(const auto &m : result) - { - os << "#" << m.index() << " " << m.arg() << " -> "; - auto p = m.param(); - if(p) - { - os << doc_label(*p) << " \t"; - if(m.repeat() > 0) - { - os << (m.bad_repeat() ? "[bad repeat " : "[repeat ") << m.repeat() << "]"; - } - if(m.blocked()) os << " [blocked]"; - if(m.conflict()) os << " [conflict]"; - os << '\n'; - } - else - { - os << " [unmapped]\n"; - } - } +template +void print(OStream& os, const parsing_result& result) +{ + for(const auto& m : result) { + os << "#" << m.index() << " " << m.arg() << " -> "; + auto p = m.param(); + if(p) { + os << doc_label(*p) << " \t"; + if(m.repeat() > 0) { + os << (m.bad_repeat() ? "[bad repeat " : "[repeat ") + << m.repeat() << "]"; + } + if(m.blocked()) os << " [blocked]"; + if(m.conflict()) os << " [conflict]"; + os << '\n'; + } + else { + os << " [unmapped]\n"; + } + } - for(const auto &m : result.missing()) - { - auto p = m.param(); - if(p) - { - os << doc_label(*p) << " \t"; - os << " [missing after " << m.after_index() << "]\n"; - } - } + for(const auto& m : result.missing()) { + auto p = m.param(); + if(p) { + os << doc_label(*p) << " \t"; + os << " [missing after " << m.after_index() << "]\n"; } + } +} - /*************************************************************************/ /** + +/*************************************************************************//** * * @brief prints parameter label and some properties * *****************************************************************************/ - template - void print(OStream &os, const parameter &p) - { - if(p.greedy()) os << '!'; - if(p.blocking()) os << '~'; - if(!p.required()) os << '['; - os << doc_label(p); - if(p.repeatable()) os << "..."; - if(!p.required()) os << "]"; - } +template +void print(OStream& os, const parameter& p) +{ + if(p.greedy()) os << '!'; + if(p.blocking()) os << '~'; + if(!p.required()) os << '['; + os << doc_label(p); + if(p.repeatable()) os << "..."; + if(!p.required()) os << "]"; +} - //------------------------------------------------------------------- - template - void print(OStream &os, const group &g, int level = 0); - /*************************************************************************/ /** +//------------------------------------------------------------------- +template +void print(OStream& os, const group& g, int level = 0); + + +/*************************************************************************//** * * @brief prints group or parameter; uses indentation * *****************************************************************************/ - template - void print(OStream &os, const pattern ¶m, int level = 0) - { - if(param.is_group()) - { - print(os, param.as_group(), level); - } - else - { - os << doc_string(4 * level, ' '); - print(os, param.as_param()); - } - } +template +void print(OStream& os, const pattern& param, int level = 0) +{ + if(param.is_group()) { + print(os, param.as_group(), level); + } + else { + os << doc_string(4*level, ' '); + print(os, param.as_param()); + } +} + - /*************************************************************************/ /** +/*************************************************************************//** * * @brief prints group and its contents; uses indentation * *****************************************************************************/ - template - void print(OStream &os, const group &g, int level) - { - auto indent = doc_string(4 * level, ' '); - os << indent; - if(g.blocking()) os << '~'; - if(g.joinable()) os << 'J'; - os << (g.exclusive() ? "(|\n" : "(\n"); - for(const auto &p : g) - { - print(os, p, level + 1); - } - os << '\n' << indent << (g.exclusive() ? "|)" : ")"); - if(g.repeatable()) os << "..."; - os << '\n'; - } +template +void print(OStream& os, const group& g, int level) +{ + auto indent = doc_string(4*level, ' '); + os << indent; + if(g.blocking()) os << '~'; + if(g.joinable()) os << 'J'; + os << (g.exclusive() ? "(|\n" : "(\n"); + for(const auto& p : g) { + print(os, p, level+1); + } + os << '\n' << indent << (g.exclusive() ? "|)" : ")"); + if(g.repeatable()) os << "..."; + os << '\n'; +} + - } // namespace debug +} // namespace debug } //namespace clipp #endif diff --git a/source/Applications/Advanced/HandEyeCalibration/HandEyeCalibration/HandEyeCalibration.cpp b/source/Applications/Advanced/HandEyeCalibration/HandEyeCalibration/HandEyeCalibration.cpp index dff44ce4..ce36565c 100644 --- a/source/Applications/Advanced/HandEyeCalibration/HandEyeCalibration/HandEyeCalibration.cpp +++ b/source/Applications/Advanced/HandEyeCalibration/HandEyeCalibration/HandEyeCalibration.cpp @@ -1,7 +1,5 @@ /* Perform Hand-Eye calibration. - -Note: This example uses experimental SDK features, which may be modified, moved, or deleted in the future without notice. */ #include @@ -9,7 +7,6 @@ Note: This example uses experimental SDK features, which may be modified, moved, #include #include #include -#include #include #include @@ -115,10 +112,10 @@ int main() { const auto robotPose = enterRobotPose(currentPoseId); - const auto frame = Zivid::Experimental::Calibration::captureCalibrationBoard(camera); + const auto frame = Zivid::Calibration::captureCalibrationBoard(camera); std::cout << "Detecting checkerboard in point cloud" << std::endl; - const auto detectionResult = Zivid::Calibration::detectFeaturePoints(frame.pointCloud()); + const auto detectionResult = Zivid::Calibration::detectCalibrationBoard(frame); if(detectionResult.valid()) { diff --git a/source/Applications/Advanced/MultiCamera/MultiCameraCalibration/MultiCameraCalibration.cpp b/source/Applications/Advanced/MultiCamera/MultiCameraCalibration/MultiCameraCalibration.cpp index 08a2c2f7..12085d82 100644 --- a/source/Applications/Advanced/MultiCamera/MultiCameraCalibration/MultiCameraCalibration.cpp +++ b/source/Applications/Advanced/MultiCamera/MultiCameraCalibration/MultiCameraCalibration.cpp @@ -84,7 +84,7 @@ int main(int argc, char **argv) std::cout << "Capturing frame with camera: " << serial << std::endl; const auto frame = assistedCapture(camera); std::cout << "Detecting checkerboard in point cloud" << std::endl; - const auto detectionResult = Zivid::Calibration::detectFeaturePoints(frame.pointCloud()); + const auto detectionResult = Zivid::Calibration::detectCalibrationBoard(frame); if(detectionResult) { detectionResults.push_back(detectionResult); diff --git a/source/Applications/Advanced/MultiCamera/MultiCameraCalibrationFromZDF/MultiCameraCalibrationFromZDF.cpp b/source/Applications/Advanced/MultiCamera/MultiCameraCalibrationFromZDF/MultiCameraCalibrationFromZDF.cpp index 808865c3..2b688f48 100644 --- a/source/Applications/Advanced/MultiCamera/MultiCameraCalibrationFromZDF/MultiCameraCalibrationFromZDF.cpp +++ b/source/Applications/Advanced/MultiCamera/MultiCameraCalibrationFromZDF/MultiCameraCalibrationFromZDF.cpp @@ -47,7 +47,7 @@ int main(int argc, char **argv) const auto serial = frame.cameraInfo().serialNumber().toString(); std::cout << "Detecting checkerboard in point cloud..." << std::endl; - const auto detectionResult = Zivid::Calibration::detectFeaturePoints(frame.pointCloud()); + const auto detectionResult = Zivid::Calibration::detectCalibrationBoard(frame); if(detectionResult) { detectionResults.push_back(detectionResult); diff --git a/source/Applications/Advanced/MultiCamera/MultiCameraTutorial.md b/source/Applications/Advanced/MultiCamera/MultiCameraTutorial.md index 67f66823..a3fde4ec 100644 --- a/source/Applications/Advanced/MultiCamera/MultiCameraTutorial.md +++ b/source/Applications/Advanced/MultiCamera/MultiCameraTutorial.md @@ -39,7 +39,7 @@ auto cameras = zivid.cameras(); ### Capture calibration object -We are now ready to capture the calibration object. We assume that all cameras will get good captures of the calibration object with Capture Assistant. You may use Zivid Studio to quickly verify that the calibration object is in view. The detection API ([Zivid::Calibration::detectFeaturePoints][detect_feature_points-url]) will notify the user if the quality is not good enough for calibration. +We are now ready to capture the calibration object. We assume that all cameras will get good captures of the calibration object with Capture Assistant. You may use Zivid Studio to quickly verify that the calibration object is in view. The detection API ([Zivid::Calibration::detectCalibrationBoard][detect_calibration_board-url]) will notify the user if the quality is not good enough for calibration. Capture in the sample is performed with Capture Assistant, this was covered in the [Capture Tutorial][capture_tutorial_capture_assistant-url]. The assisted capture has been wrapped in the function `assistedCapture` ([go to source][capture_with_ca-url]). @@ -55,10 +55,10 @@ const auto frame = Zivid::Frame(fileName); ### Detect checkerboard feature points -The calibration object we use in this tutorial is a checkerboard. Before we can run calibration, we must detect feature points from the checkerboard from all cameras ([go to source][detect_feature_points-url]). +The calibration object we use in this tutorial is a checkerboard. Before we can run calibration, we must detect feature points from the checkerboard from all cameras ([go to source][detect_calibration_board-url]). ```cpp -const auto detectionResult = Zivid::Calibration::detectFeaturePoints(frame.pointCloud()); +const auto detectionResult = Zivid::Calibration::detectCalibrationBoard(frame); ``` We may, at this point, verify that the capture had good enough quality. `detectionResult` is of a type that can be tested directly. It overloads the bool operator to provide this information. When it passes the quality test, we save the detection result and the serial number of the camera used ([go to source][verify_checkerboard_capture_quality-url]). @@ -326,7 +326,7 @@ This tutorial shows how to use the Zivid SDK to calibrate multiple cameras and u [calibration_object-url]: https://support.zivid.com/latest/academy/applications/hand-eye/calibration-object.html [connect_all_cameras-url]: MultiCameraCalibration/MultiCameraCalibration.cpp#L48 [capture_tutorial_capture_assistant-url]: ../../../../Camera/Basic/CaptureTutorial.md#capture-assistant -[detect_feature_points-url]: MultiCameraCalibration/MultiCameraCalibration.cpp#L69 +[detect_calibration_board-url]: MultiCameraCalibration/MultiCameraCalibration.cpp#L69 [capture_with_ca-url]: MultiCameraCalibration/MultiCameraCalibration.cpp#L67 [verify_checkerboard_capture_quality-url]: MultiCameraCalibration/MultiCameraCalibration.cpp#L70-L79 [calibrate-url]: MultiCameraCalibration/MultiCameraCalibration.cpp#L83 diff --git a/source/Applications/Advanced/ROIBoxViaArucoMarker/ROIBoxViaArucoMarker.cpp b/source/Applications/Advanced/ROIBoxViaArucoMarker/ROIBoxViaArucoMarker.cpp index 28ea37e6..f4b30733 100644 --- a/source/Applications/Advanced/ROIBoxViaArucoMarker/ROIBoxViaArucoMarker.cpp +++ b/source/Applications/Advanced/ROIBoxViaArucoMarker/ROIBoxViaArucoMarker.cpp @@ -1,8 +1,7 @@ /* Filter the point cloud based on a ROI box given relative to the ArUco marker on a Zivid Calibration Board. -The ZFC file for this sample can be downloaded from https://support.zivid.com/en/latest/api-reference/samples/sample-data.html. -This sample depends on ArUco libraries in OpenCV with extra modules (https://github.com/opencv/opencv_contrib). +The ZFC file for this sample can be downloaded from https://support.zivid.com/en/latest/api-reference/samples/sample-data.html. */ #include @@ -13,8 +12,6 @@ This sample depends on ArUco libraries in OpenCV with extra modules (https://git #include #include -#include - #include #include #include @@ -25,180 +22,6 @@ This sample depends on ArUco libraries in OpenCV with extra modules (https://git namespace { - struct Line2d - { - float slope; - float intercept; - }; - - float clamp(const float n, const float lower, const float upper) - { - return std::max(lower, std::min(n, upper)); - } - - Line2d fitLine(const cv::Point2f &point1, const cv::Point2f &point2) - { - // Fitting a line y=a*x + b to 2 points - const auto slope{ (point2.y - point1.y) / (point2.x - point1.x) }; - const auto intercept{ point1.y - slope * point1.x }; - return { slope, intercept }; - }; - - cv::Point2f estimate2DMarkerCenter(const std::vector &markerCorners) - { - const auto &markerCorner0 = markerCorners[0]; - const auto &markerCorner1 = markerCorners[1]; - const auto &markerCorner2 = markerCorners[2]; - const auto &markerCorner3 = markerCorners[3]; - - // Fitting line between two diagonal marker corners - const auto backDiagonal{ fitLine(markerCorner2, markerCorner0) }; - const auto forwardDiagonal{ fitLine(markerCorner3, markerCorner1) }; - - // Finding intersection of the two lines - // a1*x + b1 = a2*x + b2 - // x = (b2-b1) / (a1-a2) - // y = a1*x + b1 - const auto xCoordinate = - (forwardDiagonal.intercept - backDiagonal.intercept) / (backDiagonal.slope - forwardDiagonal.slope); - const auto yCoordinate = backDiagonal.slope * xCoordinate + backDiagonal.intercept; - auto markerCenter = cv::Point2f(xCoordinate, yCoordinate); - - return markerCenter; - } - - std::vector estimate3DMarkerPoints( - const Zivid::PointCloud &pointCloud, - const std::vector &markerPoints2D) - { - if(markerPoints2D.empty()) - { - return {}; - } - - const float width = pointCloud.width(); - const float height = pointCloud.height(); - const auto points = pointCloud.copyPointsXYZ(); - - std::vector markerPoints3D; - markerPoints3D.reserve(markerPoints2D.size()); - - for(const auto &point2D : markerPoints2D) - { - // Estimating the 3D center/corners of the marker using bilinear interpolation - // See https://en.wikipedia.org/wiki/Bilinear_interpolation - const auto x = point2D.x; - const auto y = point2D.y; - - // Getting pixel coordinates for the four known pixels next to the interpolation point - const auto xRoundedDown = clamp(std::floor(x), 0, width - 1); - const auto xRoundedUp = clamp(std::ceil(x), 0, width - 1); - const auto yRoundedDown = clamp(std::floor(y), 0, height - 1); - const auto yRoundedUp = clamp(std::ceil(y), 0, height - 1); - - // Getting 3D coordinates for the four points next to the interpolation point - const auto q11 = points(yRoundedDown, xRoundedDown); - const auto q12 = points(yRoundedUp, xRoundedDown); - const auto q21 = points(yRoundedDown, xRoundedUp); - const auto q22 = points(yRoundedUp, xRoundedUp); - - // Linear interpolation in x direction - // f(x,y1) = (x2 - x)/(x2 - x1) * f(q11) + (x - x1)/(x2 - x1) * f(q21) - const cv::Point3f fxy1{ (xRoundedUp - x) / (xRoundedUp - xRoundedDown) * q11.x - + (x - xRoundedDown) / (xRoundedUp - xRoundedDown) * q21.x, - (xRoundedUp - x) / (xRoundedUp - xRoundedDown) * q11.y - + (x - xRoundedDown) / (xRoundedUp - xRoundedDown) * q21.y, - (xRoundedUp - x) / (xRoundedUp - xRoundedDown) * q11.z - + (x - xRoundedDown) / (xRoundedUp - xRoundedDown) * q21.z }; - // f(x,y2) = (x2 - x)/(x2 - x1) * f(q12) + (x - x1)/(x2 - x1) * f(q22) - const cv::Point3f fxy2{ (xRoundedUp - x) / (xRoundedUp - xRoundedDown) * q12.x - + (x - xRoundedDown) / (xRoundedUp - xRoundedDown) * q22.x, - (xRoundedUp - x) / (xRoundedUp - xRoundedDown) * q12.y - + (x - xRoundedDown) / (xRoundedUp - xRoundedDown) * q22.y, - (xRoundedUp - x) / (xRoundedUp - xRoundedDown) * q12.z - + (x - xRoundedDown) / (xRoundedUp - xRoundedDown) * q22.z }; - - // Linear interpolation in y direction - // f(x,y) = (y2 - y)/(y2 - y1) * f(x,y1) + (y - y1)/(y2 - y1) * f(x,y2) - markerPoints3D.emplace_back(cv::Point3f{ (yRoundedUp - y) / (yRoundedUp - yRoundedDown) * fxy1.x - + (y - yRoundedDown) / (yRoundedUp - yRoundedDown) * fxy2.x, - (yRoundedUp - y) / (yRoundedUp - yRoundedDown) * fxy1.y - + (y - yRoundedDown) / (yRoundedUp - yRoundedDown) * fxy2.y, - (yRoundedUp - y) / (yRoundedUp - yRoundedDown) * fxy1.z - + (y - yRoundedDown) / (yRoundedUp - yRoundedDown) * fxy2.z }); - } - - return markerPoints3D; - } - - Zivid::Matrix4x4 transformationMatrix(const cv::Matx33f &rotationMatrix, const cv::Vec3f &translationVector) - { - auto transformMatrix = Zivid::Matrix4x4{ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 }; - - for(size_t row = 0; row < rotationMatrix.rows; row++) - { - for(size_t col = 0; col < rotationMatrix.cols; col++) - { - transformMatrix(row, col) = rotationMatrix(row, col); - } - } - - transformMatrix(0, 3) = translationVector[0]; - transformMatrix(1, 3) = translationVector[1]; - transformMatrix(2, 3) = translationVector[2]; - - return transformMatrix; - } - - Zivid::Matrix4x4 estimateArUcoMarkerPose( - const Zivid::PointCloud &pointCloud, - const std::vector &markerCorners) - { - // Extracting 2D corners and estimating 2D center - const auto center2D = estimate2DMarkerCenter(markerCorners); - - // Estimating 3D corners and center from 2D data - const auto corners3D = estimate3DMarkerPoints(pointCloud, markerCorners); - const auto center3D = estimate3DMarkerPoints(pointCloud, { center2D })[0]; - - // Extracting origin and calculating normal vectors for x-, y- and z-axis - const auto origin = cv::Vec3f(center3D.x, center3D.y, center3D.z); - - const auto xAxis = cv::Vec3f( - corners3D[1].x - corners3D[0].x, corners3D[1].y - corners3D[0].y, corners3D[1].z - corners3D[0].z); - - const auto yAxis = cv::Vec3f( - corners3D[2].x - corners3D[1].x, corners3D[2].y - corners3D[1].y, corners3D[2].z - corners3D[1].z); - - const auto u = xAxis / cv::norm(xAxis, cv::NORM_L2); - const auto v = yAxis / cv::norm(yAxis, cv::NORM_L2); - const auto normal = u.cross(v); - const auto unitNormal = normal / cv::norm(normal, cv::NORM_L2); - - auto rotationMatrix = cv::Mat(3, 3, CV_32F); - for(int i = 0; i < 3; ++i) - { - rotationMatrix.at(i, 0) = u[i]; - rotationMatrix.at(i, 1) = v[i]; - rotationMatrix.at(i, 2) = unitNormal[i]; - } - - return transformationMatrix(rotationMatrix, origin); - } - - cv::Mat pointCloudToGray(const Zivid::PointCloud &pointCloud) - { - const auto image = pointCloud.copyImageRGBA(); - const auto rgba = cv::Mat( - pointCloud.height(), - pointCloud.width(), - CV_8UC4, - const_cast(static_cast(image.data()))); - cv::Mat gray; - cv::cvtColor(rgba, gray, cv::COLOR_RGBA2GRAY); - return gray; - } - std::vector zividToEigen(const std::vector &zividPoints) { std::vector eigenPoints(zividPoints.size()); @@ -305,31 +128,25 @@ int main() const Zivid::PointXYZ pointBInArUcoFrame = roiBoxLowerLeftCornerInArUcoFrame; std::cout << "Configuring ArUco marker" << std::endl; - const auto markerDictionary = cv::aruco::getPredefinedDictionary(cv::aruco::DICT_4X4_100); - std::vector markerIds; - std::vector> markerCorners; - cv::Ptr detectorParameters = cv::aruco::DetectorParameters::create(); - detectorParameters->cornerRefinementMethod = cv::aruco::CORNER_REFINE_SUBPIX; - - std::cout << "Converting to OpenCV image format" << std::endl; - const auto grayImage = pointCloudToGray(pointCloud); + const auto markerDictionary = Zivid::Calibration::MarkerDictionary::aruco4x4_50; + std::vector markerId = { 1 }; - std::cout << "Detecting ArUco Marker" << std::endl; - cv::aruco::detectMarkers(grayImage, markerDictionary, markerCorners, markerIds, detectorParameters); + std::cout << "Detecting ArUco marker" << std::endl; + const auto detectionResult = Zivid::Calibration::detectMarkers(originalFrame, markerId, markerDictionary); - if(markerIds.empty()) + if(!detectionResult.valid()) { std::cout << "No ArUco markers detected" << std::endl; return EXIT_FAILURE; } std::cout << "Estimating pose of detected ArUco marker" << std::endl; - const auto transformMarkerToCamera = estimateArUcoMarkerPose(pointCloud, markerCorners[0]); + const auto transformCameraToMarker = detectionResult.detectedMarkers()[0].pose().toMatrix(); std::cout << "Transforming the ROI base frame points to the camera frame" << std::endl; const auto roiPointsInCameraFrame = transformPoints( std::vector{ pointOInArUcoFrame, pointAInArUcoFrame, pointBInArUcoFrame }, - transformMarkerToCamera); + transformCameraToMarker); std::cout << "Setting the ROI" << std::endl; settings.set(Zivid::Settings::RegionOfInterest{ diff --git a/source/Applications/Advanced/ROIBoxViaCheckerboard/ROIBoxViaCheckerboard.cpp b/source/Applications/Advanced/ROIBoxViaCheckerboard/ROIBoxViaCheckerboard.cpp index eb12c001..0c743fda 100644 --- a/source/Applications/Advanced/ROIBoxViaCheckerboard/ROIBoxViaCheckerboard.cpp +++ b/source/Applications/Advanced/ROIBoxViaCheckerboard/ROIBoxViaCheckerboard.cpp @@ -127,14 +127,14 @@ int main() const Zivid::PointXYZ pointBInCheckerboardFrame = roiBoxLowerLeftCornerInCheckerboardFrame; std::cout << "Detecting and estimating pose of the Zivid checkerboard in the camera frame" << std::endl; - const auto detectionResult = Zivid::Calibration::detectFeaturePoints(originalFrame.pointCloud()); - const auto transformCheckerboardToCamera = detectionResult.pose().toMatrix(); + const auto detectionResult = Zivid::Calibration::detectCalibrationBoard(originalFrame); + const auto transformCameraToCheckerboard = detectionResult.pose().toMatrix(); std::cout << "Transforming the ROI base frame points to the camera frame" << std::endl; const auto roiPointsInCameraFrame = transformPoints( std::vector{ pointOInCheckerboardFrame, pointAInCheckerboardFrame, pointBInCheckerboardFrame }, - transformCheckerboardToCamera); + transformCameraToCheckerboard); std::cout << "Setting the ROI" << std::endl; settings.set(Zivid::Settings::RegionOfInterest{ diff --git a/source/Applications/Advanced/ReprojectPoints/ReprojectPoints.cpp b/source/Applications/Advanced/ReprojectPoints/ReprojectPoints.cpp index fb7e9d15..4f3fa578 100644 --- a/source/Applications/Advanced/ReprojectPoints/ReprojectPoints.cpp +++ b/source/Applications/Advanced/ReprojectPoints/ReprojectPoints.cpp @@ -6,13 +6,11 @@ The checkerboard pose is determined first and then used to estimate the coordina in the camera frame. These points are then passed to the API to get the corresponding projector pixels. The projector pixel coordinates are then used to draw markers at the correct locations before displaying the image using the projector. - -Note: This example uses experimental SDK features, which may be modified, moved, or deleted in the future without notice. */ #include +#include #include -#include #include #include @@ -82,7 +80,7 @@ int main() auto camera = zivid.connectCamera(); std::cout << "Capturing and estimating pose of the Zivid checkerboard in the camera frame" << std::endl; - const auto detectionResult = Zivid::Experimental::Calibration::detectFeaturePoints(camera); + const auto detectionResult = Zivid::Calibration::detectCalibrationBoard(camera); if(!detectionResult.valid()) { throw std::runtime_error("Calibration board not detected!"); diff --git a/source/Applications/Advanced/TransformPointCloudViaArucoMarker/TransformPointCloudViaArucoMarker.cpp b/source/Applications/Advanced/TransformPointCloudViaArucoMarker/TransformPointCloudViaArucoMarker.cpp index 047e68b6..c9457b78 100644 --- a/source/Applications/Advanced/TransformPointCloudViaArucoMarker/TransformPointCloudViaArucoMarker.cpp +++ b/source/Applications/Advanced/TransformPointCloudViaArucoMarker/TransformPointCloudViaArucoMarker.cpp @@ -1,14 +1,12 @@ /* -Transform a point cloud from camera to ArUco Marker coordinate frame by estimating the marker's pose from the -point cloud. The ZDF file for this sample can be found under the main instructions for Zivid samples. +Transform a point cloud from camera to ArUco marker coordinate frame by estimating the marker's pose from the point cloud. -This sample depends on ArUco libraries in OpenCV with extra modules (https://github.com/opencv/opencv_contrib). +The ZDF file for this sample can be found under the main instructions for Zivid samples. */ #include #include -#include #include #include #include @@ -30,187 +28,41 @@ struct cv::traits::Type namespace { - struct Line2d - { - float slope; - float intercept; - }; - - float clamp(const float n, const float lower, const float upper) - { - return std::max(lower, std::min(n, upper)); - } - - Line2d fitLine(const cv::Point2f &point1, const cv::Point2f &point2) - { - // Fitting a line y=a*x + b to 2 points - const auto slope{ (point2.y - point1.y) / (point2.x - point1.x) }; - const auto intercept{ point1.y - slope * point1.x }; - return { slope, intercept }; - }; - - cv::Point2f estimate2DMarkerCenter(const std::vector &markerCorners) + cv::Mat pointCloudToColorBGRA(const Zivid::PointCloud &pointCloud) { - const auto &markerCorner0 = markerCorners[0]; - const auto &markerCorner1 = markerCorners[1]; - const auto &markerCorner2 = markerCorners[2]; - const auto &markerCorner3 = markerCorners[3]; - - // Fitting line between two diagonal marker corners - const auto backDiagonal{ fitLine(markerCorner2, markerCorner0) }; - const auto forwardDiagonal{ fitLine(markerCorner3, markerCorner1) }; - - // Finding intersection of the two lines - // a1*x + b1 = a2*x + b2 - // x = (b2-b1) / (a1-a2) - // y = a1*x + b1 - const auto xCoordinate = - (forwardDiagonal.intercept - backDiagonal.intercept) / (backDiagonal.slope - forwardDiagonal.slope); - const auto yCoordinate = backDiagonal.slope * xCoordinate + backDiagonal.intercept; - auto markerCenter = cv::Point2f(xCoordinate, yCoordinate); + auto bgra = cv::Mat(pointCloud.height(), pointCloud.width(), CV_8UC4); + pointCloud.copyData(&(*bgra.begin())); - return markerCenter; + return bgra; } - std::vector estimate3DMarkerPoints( - const Zivid::PointCloud &pointCloud, - const std::vector &markerPoints2D) + void displayBGR(const cv::Mat &bgr, const std::string &bgrName) { - if(markerPoints2D.empty()) - { - return {}; - } - - const float width = pointCloud.width(); - const float height = pointCloud.height(); - const auto points = pointCloud.copyPointsXYZ(); - - std::vector markerPoints3D; - markerPoints3D.reserve(markerPoints2D.size()); - - for(const auto &point2D : markerPoints2D) - { - // Estimating the 3D center/corners of the marker using bilinear interpolation - // See https://en.wikipedia.org/wiki/Bilinear_interpolation - const auto x = point2D.x; - const auto y = point2D.y; - - // Getting pixel coordinates for the four known pixels next to the interpolation point - const auto xRoundedDown = clamp(std::floor(x), 0, width - 1); - const auto xRoundedUp = clamp(std::ceil(x), 0, width - 1); - const auto yRoundedDown = clamp(std::floor(y), 0, height - 1); - const auto yRoundedUp = clamp(std::ceil(y), 0, height - 1); - - // Getting 3D coordinates for the four points next to the interpolation point - const auto q11 = points(yRoundedDown, xRoundedDown); - const auto q12 = points(yRoundedUp, xRoundedDown); - const auto q21 = points(yRoundedDown, xRoundedUp); - const auto q22 = points(yRoundedUp, xRoundedUp); - - // Linear interpolation in x direction - // f(x,y1) = (x2 - x)/(x2 - x1) * f(q11) + (x - x1)/(x2 - x1) * f(q21) - const cv::Point3f fxy1{ (xRoundedUp - x) / (xRoundedUp - xRoundedDown) * q11.x - + (x - xRoundedDown) / (xRoundedUp - xRoundedDown) * q21.x, - (xRoundedUp - x) / (xRoundedUp - xRoundedDown) * q11.y - + (x - xRoundedDown) / (xRoundedUp - xRoundedDown) * q21.y, - (xRoundedUp - x) / (xRoundedUp - xRoundedDown) * q11.z - + (x - xRoundedDown) / (xRoundedUp - xRoundedDown) * q21.z }; - // f(x,y2) = (x2 - x)/(x2 - x1) * f(q12) + (x - x1)/(x2 - x1) * f(q22) - const cv::Point3f fxy2{ (xRoundedUp - x) / (xRoundedUp - xRoundedDown) * q12.x - + (x - xRoundedDown) / (xRoundedUp - xRoundedDown) * q22.x, - (xRoundedUp - x) / (xRoundedUp - xRoundedDown) * q12.y - + (x - xRoundedDown) / (xRoundedUp - xRoundedDown) * q22.y, - (xRoundedUp - x) / (xRoundedUp - xRoundedDown) * q12.z - + (x - xRoundedDown) / (xRoundedUp - xRoundedDown) * q22.z }; - - // Linear interpolation in y direction - // f(x,y) = (y2 - y)/(y2 - y1) * f(x,y1) + (y - y1)/(y2 - y1) * f(x,y2) - markerPoints3D.emplace_back(cv::Point3f{ (yRoundedUp - y) / (yRoundedUp - yRoundedDown) * fxy1.x - + (y - yRoundedDown) / (yRoundedUp - yRoundedDown) * fxy2.x, - (yRoundedUp - y) / (yRoundedUp - yRoundedDown) * fxy1.y - + (y - yRoundedDown) / (yRoundedUp - yRoundedDown) * fxy2.y, - (yRoundedUp - y) / (yRoundedUp - yRoundedDown) * fxy1.z - + (y - yRoundedDown) / (yRoundedUp - yRoundedDown) * fxy2.z }); - } - - return markerPoints3D; + cv::namedWindow(bgrName, cv::WINDOW_AUTOSIZE); + cv::imshow(bgrName, bgr); + cv::waitKey(0); } - Zivid::Matrix4x4 transformationMatrix(const cv::Matx33f &rotationMatrix, const cv::Vec3f &translationVector) + cv::Mat drawDetectedMarker( + const cv::Mat &bgraImage, + const Zivid::Calibration::DetectionResultFiducialMarkers &detectionResult) { - auto transformMatrix = Zivid::Matrix4x4{ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 }; - - for(size_t row = 0; row < rotationMatrix.rows; row++) + const auto detectedMarkerCorners = detectionResult.detectedMarkers()[0].cornersInPixelCoordinates(); + std::vector markerCorners; + markerCorners.reserve(detectedMarkerCorners.size()); + for(const auto &corner : detectedMarkerCorners) { - for(size_t col = 0; col < rotationMatrix.cols; col++) - { - transformMatrix(row, col) = rotationMatrix(row, col); - } + markerCorners.emplace_back(corner.x, corner.y); } - transformMatrix(0, 3) = translationVector[0]; - transformMatrix(1, 3) = translationVector[1]; - transformMatrix(2, 3) = translationVector[2]; - - return transformMatrix; - } - - Zivid::Matrix4x4 estimateArUcoMarkerPose( - const Zivid::PointCloud &pointCloud, - const std::vector &markerCorners) - { - // Extracting 2D corners and estimateing 2D center - const auto center2D = estimate2DMarkerCenter(markerCorners); - - // Estimating 3D corners and center from 2D data - const auto corners3D = estimate3DMarkerPoints(pointCloud, markerCorners); - const auto center3D = estimate3DMarkerPoints(pointCloud, { center2D })[0]; - - // Extracting origin and calculating normal vectors for x-, y- and z-axis - const auto origin = cv::Vec3f(center3D.x, center3D.y, center3D.z); - - const auto xAxis = cv::Vec3f( - corners3D[2].x - corners3D[1].x, corners3D[2].y - corners3D[1].y, corners3D[2].z - corners3D[1].z); - - const auto yAxis = cv::Vec3f( - corners3D[0].x - corners3D[1].x, corners3D[0].y - corners3D[1].y, corners3D[0].z - corners3D[1].z); - - const auto u = xAxis / cv::norm(xAxis, cv::NORM_L2); - const auto v = yAxis / cv::norm(yAxis, cv::NORM_L2); - const auto normal = u.cross(v); - const auto unitNormal = normal / cv::norm(normal, cv::NORM_L2); - - auto rotationMatrix = cv::Mat(3, 3, CV_32F); - for(int i = 0; i < 3; ++i) + cv::Mat bgr; + cv::cvtColor(bgraImage, bgr, cv::COLOR_BGRA2BGR); + for(size_t i = 0; i < markerCorners.size(); ++i) { - rotationMatrix.at(i, 0) = u[i]; - rotationMatrix.at(i, 1) = v[i]; - rotationMatrix.at(i, 2) = unitNormal[i]; + cv::line(bgr, markerCorners[i], markerCorners[(i + 1) % markerCorners.size()], cv::Scalar(0, 255, 0), 2); } - return transformationMatrix(rotationMatrix, origin); - } - - cv::Mat pointCloudToColorBGRA(const Zivid::PointCloud &pointCloud) - { - auto bgra = cv::Mat(pointCloud.height(), pointCloud.width(), CV_8UC4); - pointCloud.copyData(&(*bgra.begin())); - - return bgra; - } - - cv::Mat colorBGRAToGray(const cv::Mat &bgra) - { - cv::Mat gray; - cv::cvtColor(bgra, gray, cv::COLOR_BGRA2GRAY); - return gray; - } - - void displayBGR(const cv::Mat &bgr, const std::string &bgrName) - { - cv::namedWindow(bgrName, cv::WINDOW_AUTOSIZE); - cv::imshow(bgrName, bgr); - cv::waitKey(0); + return bgr; } } // namespace @@ -226,46 +78,44 @@ int main() const auto frame = Zivid::Frame(arucoMarkerFile); auto pointCloud = frame.pointCloud(); - std::cout << "Converting to OpenCV image format" << std::endl; - const auto bgraImage = pointCloudToColorBGRA(pointCloud); - const auto grayImage = colorBGRAToGray(bgraImage); - std::cout << "Configuring ArUco marker" << std::endl; - const auto markerDictionary = cv::aruco::getPredefinedDictionary(cv::aruco::DICT_4X4_100); - std::vector markerIds; - std::vector> markerCorners; - cv::Ptr detectorParameters = cv::aruco::DetectorParameters::create(); - detectorParameters->cornerRefinementMethod = cv::aruco::CORNER_REFINE_SUBPIX; + const auto markerDictionary = Zivid::Calibration::MarkerDictionary::aruco4x4_50; + std::vector markerId = { 1 }; - std::cout << "Detecting ArUco Marker" << std::endl; - cv::aruco::detectMarkers(grayImage, markerDictionary, markerCorners, markerIds, detectorParameters); - - std::cout << "Displaying detected ArUco marker" << std::endl; - cv::Mat bgr; - cv::cvtColor(bgraImage, bgr, cv::COLOR_BGRA2BGR); - cv::aruco::drawDetectedMarkers(bgr, markerCorners); - displayBGR(bgr, "ArucoMarkerDetected"); + std::cout << "Detecting ArUco marker" << std::endl; + const auto detectionResult = Zivid::Calibration::detectMarkers(frame, markerId, markerDictionary); - if(markerIds.empty()) + if(!detectionResult.valid()) { std::cout << "No ArUco markers detected" << std::endl; - return EXIT_SUCCESS; + return EXIT_FAILURE; } + std::cout << "Converting to OpenCV image format" << std::endl; + const auto bgraImage = pointCloudToColorBGRA(pointCloud); + + std::cout << "Displaying detected ArUco marker" << std::endl; + const auto bgr = drawDetectedMarker(bgraImage, detectionResult); + displayBGR(bgr, "ArucoMarkerDetected"); + const auto bgrImageFile = "ArucoMarkerDetected.png"; std::cout << "Saving 2D color image with detected ArUco marker to file: " << bgrImageFile << std::endl; cv::imwrite(bgrImageFile, bgr); std::cout << "Estimating pose of detected ArUco marker" << std::endl; - const auto transformMarkerToCamera = estimateArUcoMarkerPose(pointCloud, markerCorners[0]); - std::cout << "Camera pose in ArUco marker frame:" << std::endl; - std::cout << transformMarkerToCamera << std::endl; - const auto transformCameraToMarker = transformMarkerToCamera.inverse(); + const auto transformCameraToMarker = detectionResult.detectedMarkers()[0].pose().toMatrix(); std::cout << "ArUco marker pose in camera frame:" << std::endl; std::cout << transformCameraToMarker << std::endl; + std::cout << "Camera pose in ArUco marker frame:" << std::endl; + const auto transformMarkerToCamera = transformCameraToMarker.inverse(); + std::cout << transformMarkerToCamera << std::endl; + + const auto transformFile = "ArUcoMarkerToCameraTransform.yaml"; + std::cout << "Saving a YAML file with Inverted ArUco marker pose to file: " << transformFile << std::endl; + transformMarkerToCamera.save(transformFile); std::cout << "Transforming point cloud from camera frame to ArUco marker frame" << std::endl; - pointCloud.transform(transformCameraToMarker); + pointCloud.transform(transformMarkerToCamera); const auto arucoMarkerTransformedFile = "CalibrationBoardInArucoMarkerOrigin.zdf"; std::cout << "Saving transformed point cloud to file: " << arucoMarkerTransformedFile << std::endl; diff --git a/source/Applications/Advanced/TransformPointCloudViaCheckerboard/TransformPointCloudViaCheckerboard.cpp b/source/Applications/Advanced/TransformPointCloudViaCheckerboard/TransformPointCloudViaCheckerboard.cpp index 1342231b..2c08b135 100644 --- a/source/Applications/Advanced/TransformPointCloudViaCheckerboard/TransformPointCloudViaCheckerboard.cpp +++ b/source/Applications/Advanced/TransformPointCloudViaCheckerboard/TransformPointCloudViaCheckerboard.cpp @@ -118,7 +118,6 @@ namespace CoordinateSystemPoints getCoordinateSystemPoints(const Zivid::Frame &frame, const Zivid::Matrix4x4 &checkerboardPose, float size_of_axis) { - const auto pointCloud = frame.pointCloud(); const auto intrinsics = Zivid::Experimental::Calibration::estimateIntrinsics(frame); const auto cvCameraMatrix = zividCameraMatrixToOpenCVCameraMatrix(intrinsics.cameraMatrix()); const auto cvDistCoeffs = zividDistortionCoefficientsToOpenCVDistortionCoefficients(intrinsics.distortion()); @@ -171,7 +170,7 @@ int main() auto pointCloud = frame.pointCloud(); std::cout << "Detecting and estimating pose of the Zivid checkerboard in the camera frame" << std::endl; - const auto detectionResult = Zivid::Calibration::detectFeaturePoints(frame.pointCloud()); + const auto detectionResult = Zivid::Calibration::detectCalibrationBoard(frame); const auto transformCameraToCheckerboard = detectionResult.pose().toMatrix(); std::cout << transformCameraToCheckerboard << std::endl; std::cout << "Camera pose in checkerboard frame:" << std::endl; diff --git a/source/Applications/PointCloudTutorial.md b/source/Applications/PointCloudTutorial.md index 024ed46c..290cba79 100644 --- a/source/Applications/PointCloudTutorial.md +++ b/source/Applications/PointCloudTutorial.md @@ -158,54 +158,57 @@ We present examples for the two memory allocation options using OpenCV. If you are only concerned about e.g. RGB color data of the point cloud, you can copy only that data to the CPU memory. -``` sourceCode cpp -auto pointCloud = frame.pointCloud(); -std::cout << "Copying data with Zivid API from the GPU into the memory location allocated by OpenCV" - << std::endl; -pointCloud.copyData(&(*bgraUserAllocated.begin())); - -std::cout << "Displaying image" << std::endl; -cv::imshow("BGRA image User Allocated", bgraUserAllocated); -cv::waitKey(0); +([go to +source](https://github.com/zivid/zivid-cpp-samples/tree/master//source/Camera/Advanced/AllocateMemoryForPointCloudData/AllocateMemoryForPointCloudData.cpp#L70-L93)) +``` sourceCode cpp +std::cout << "Capturing frame" << std::endl; +frame = camera.capture(settings); pointCloud = frame.pointCloud(); - std::cout << "Copying colors with Zivid API from GPU to CPU" << std::endl; auto colors = pointCloud.copyColorsBGRA(); std::cout << "Casting the data pointer as a void*, since this is what the OpenCV matrix constructor requires." -``` - -**Copy selected data from GPU to CPU memory (user-allocated)** - -In the above example, ownership of the data was held by the returned -`Zivid::Array2D<>` objects. Alternatively, you may provide a -pre-allocated memory buffer to `Zivid::PointCloud::copyData(dataPtr)`. -The type of `dataPtr` defines what shall be copied (`PointXYZ`, -`ColorRGBA`, etc.). + << std::endl; +auto *dataPtrZividAllocated = const_cast(static_cast(colors.data())); -Now let us look at the exact same use case as above. However, this time, -we allow OpenCV to allocate the necessary storage. Then we ask the Zivid -API to copy data directly from the GPU into this memory location. +std::cout << "Wrapping this block of data in an OpenCV matrix. This is possible since the layout of \n" + << "Zivid::ColorBGRA exactly matches the layout of CV_8UC4. No copying occurs in this step." + << std::endl; +const cv::Mat bgraZividAllocated(colors.height(), colors.width(), CV_8UC4, dataPtrZividAllocated); -``` sourceCode cpp -std::cout << "Creating settings" << std::endl; -auto settings = Zivid::Settings{ Zivid::Settings::Acquisitions{ Zivid::Settings::Acquisition{} } }; -std::cout << "Getting camera resolution" << std::endl; -const auto resolution = Zivid::Experimental::SettingsInfo::resolution(camera.info(), settings); +std::cout << "Displaying image" << std::endl; +cv::imshow("BGRA image Zivid Allocated", bgraZividAllocated); +cv::waitKey(0); + .. rubric:: Copy selected data from GPU to CPU memory (user-allocated) -std::cout << "Camera resolution:" << std::endl; -std::cout << "Height: " << resolution.height() << std::endl; -std::cout << "Width: " << resolution.width() << std::endl; + In the above example, ownership of the data was held by the returned :code:`Zivid::Array2D<>` objects. + Alternatively, you may provide a pre-allocated memory buffer to :code:`Zivid::PointCloud::copyData(dataPtr)`. + The type of :code:`dataPtr` defines what shall be copied (:code:`PointXYZ`, :code:`ColorRGBA`, etc.). -// Copy selected data from GPU to system memory (User-allocated) + Now let us look at the exact same use case as above. + However, this time, we allow OpenCV to allocate the necessary storage. + Then we ask the Zivid API to copy data directly from the GPU into this memory location. ``` ([go to -source](https://github.com/zivid/zivid-cpp-samples/tree/master//source/Applications/Advanced/HandEyeCalibration/UtilizeHandEyeCalibration/UtilizeHandEyeCalibration.cpp#L234)) +source](https://github.com/zivid/zivid-cpp-samples/tree/master//source/Camera/Advanced/AllocateMemoryForPointCloudData/AllocateMemoryForPointCloudData.cpp#L52-L66)) ``` sourceCode cpp -pointCloud.transform(transformBaseToCamera); +std::cout << "Allocating the necessary storage with OpenCV API based on resolution info before any capturing" + << std::endl; +auto bgraUserAllocated = cv::Mat(resolution.height(), resolution.width(), CV_8UC4); +std::cout << "Capturing frame" << std::endl; +auto frame = camera.capture(settings); +auto pointCloud = frame.pointCloud(); + +std::cout << "Copying data with Zivid API from the GPU into the memory location allocated by OpenCV" + << std::endl; +pointCloud.copyData(&(*bgraUserAllocated.begin())); + +std::cout << "Displaying image" << std::endl; +cv::imshow("BGRA image User Allocated", bgraUserAllocated); +cv::waitKey(0); ``` ## Transform diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index f96e217c..6a514082 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5 FATAL_ERROR) +cmake_minimum_required(VERSION 3.5...3.29 FATAL_ERROR) project(ZividCppSamples) @@ -12,12 +12,11 @@ include(CompilerOptions) set(CMAKE_MAP_IMPORTED_CONFIG_MINSIZEREL "MinSizeRel;Release;") set(CMAKE_MAP_IMPORTED_CONFIG_RELWITHDEBINFO "RelWithDebInfo;Release;") -set(ZIVID_VERSION 2.12.0) +set(ZIVID_VERSION 2.13.0) option(USE_EIGEN3 "Enable samples which depend on Eigen 3" ON) option(USE_OPENCV "Enable samples which depend on OpenCV" ON) option(USE_PCL "Enable samples which depend on Point Cloud Library (PCL)" ON) -option(USE_ARUCO "Enable samples which depend on ArUco (OpenCV with extra modules)" OFF) option(USE_HALCON "Enable samples which depend on Halcon" OFF) set(SAMPLES @@ -41,6 +40,7 @@ set(SAMPLES Camera/InfoUtilOther/CaptureWithDiagnostics Camera/InfoUtilOther/GetCameraIntrinsics Camera/InfoUtilOther/FirmwareUpdater + Camera/InfoUtilOther/NetworkConfiguration Camera/InfoUtilOther/CameraInfo Camera/InfoUtilOther/SettingsInfo Camera/InfoUtilOther/FrameInfo @@ -137,10 +137,6 @@ set(Thread_DEPENDING MultiCameraCaptureSequentiallyWithInterleavedProcessing ZividBenchmark ) -set(ArUco_DEPENDING - TransformPointCloudViaArucoMarker - ROIBoxViaArucoMarker -) set(Halcon_DEPENDING CaptureHalconViaGenICam CaptureHalconViaZivid @@ -197,29 +193,12 @@ if(USE_OPENCV) highgui calib3d ) - if(USE_ARUCO) - find_package( - OpenCV - 4.1.0 - COMPONENTS - ${OPENCV_COMMON_MODULES} - aruco + find_package(OpenCV 4.1.0 COMPONENTS ${OPENCV_COMMON_MODULES}) + if(NOT OpenCV_FOUND) + message( + FATAL_ERROR + "OpenCV not found. Please point OpenCV_DIR to the directory of your OpenCV installation (containing the file OpenCVConfig.cmake), or disable the OpenCV samples with -DUSE_OPENCV=OFF." ) - if(NOT OpenCV_FOUND) - message( - FATAL_ERROR - "OpenCV not found. Please point OpenCV_DIR to the directory of your OpenCV installation (containing the file OpenCVConfig.cmake). If you don't have OpenCV with extra modules (OpenCV_Contrib), disable the ArUco samples with -DUSE_ARUCO=OFF." - ) - endif() - else() - disable_samples("ArUco") - find_package(OpenCV 4.1.0 COMPONENTS ${OPENCV_COMMON_MODULES}) - if(NOT OpenCV_FOUND) - message( - FATAL_ERROR - "OpenCV not found. Please point OpenCV_DIR to the directory of your OpenCV installation (containing the file OpenCVConfig.cmake), or disable the OpenCV samples with -DUSE_OPENCV=OFF." - ) - endif() endif() else() disable_samples("OpenCV") diff --git a/source/Camera/Basic/CaptureTutorial.md b/source/Camera/Basic/CaptureTutorial.md index c58fdfee..4cd06da3 100644 --- a/source/Camera/Basic/CaptureTutorial.md +++ b/source/Camera/Basic/CaptureTutorial.md @@ -455,9 +455,6 @@ frame.save(dataFilePLY); We can get 2D color image from a 3D capture. -([go to -source](https://github.com/zivid/zivid-cpp-samples/tree/master//source/Applications/Advanced/ROIBoxViaArucoMarker/ROIBoxViaArucoMarker.cpp#L191)) - ``` sourceCode cpp const auto image = pointCloud.copyImageRGBA(); ``` diff --git a/source/Camera/InfoUtilOther/NetworkConfiguration/NetworkConfiguration.cpp b/source/Camera/InfoUtilOther/NetworkConfiguration/NetworkConfiguration.cpp new file mode 100644 index 00000000..886b4405 --- /dev/null +++ b/source/Camera/InfoUtilOther/NetworkConfiguration/NetworkConfiguration.cpp @@ -0,0 +1,108 @@ +/* +Uses Zivid API to change the IP address of the Zivid camera. + +*/ + +#include + +#include +#include + +namespace +{ + bool confirm(const std::string &message) + { + while(true) + { + std::cout << message << " [Y/n] "; + std::string input; + std::getline(std::cin, input); + if(input == "y" || input == "Y" || input == "yes" || input == "Yes" || input == "YES") + { + return true; + } + if(input == "n" || input == "N" || input == "no" || input == "No" || input == "NO") + { + return false; + } + std::cout << "Invalid input. Please enter 'Y' or 'n'." << std::endl; + } + } + +} // namespace + +int main() +{ + try + { + Zivid::Application zivid; + + auto cameras = zivid.cameras(); + + if(cameras.empty()) + { + throw std::runtime_error("Failed to connect to camera. No cameras found."); + } + + auto camera = cameras[0]; + auto originalConfig = camera.networkConfiguration(); + + std::cout << "Current network configuration of camera " << camera.info().serialNumber() << ":" << std::endl; + std::cout << originalConfig << std::endl << std::endl; + + auto mode = Zivid::NetworkConfiguration::IPV4::Mode::manual; + auto address = originalConfig.ipv4().address(); + auto subnetMask = originalConfig.ipv4().subnetMask(); + + if(confirm("Do you want to use DHCP?")) + { + mode = Zivid::NetworkConfiguration::IPV4::Mode::dhcp; + } + else + { + std::string inputAddress; + std::cout << "Enter IPv4 Address [" << originalConfig.ipv4().address() << "]: "; + std::getline(std::cin, inputAddress); + if(!inputAddress.empty()) + { + address = Zivid::NetworkConfiguration::IPV4::Address{ inputAddress }; + } + + std::string inputSubnetMask; + std::cout << "Enter new Subnet mask [" << originalConfig.ipv4().subnetMask() << "]: "; + std::getline(std::cin, inputSubnetMask); + if(!inputSubnetMask.empty()) + { + subnetMask = Zivid::NetworkConfiguration::IPV4::SubnetMask{ inputSubnetMask }; + } + } + + Zivid::NetworkConfiguration newConfig(Zivid::NetworkConfiguration::IPV4(mode, address, subnetMask)); + + std::cout << "\nNew network configuration:" << std::endl; + std::cout << newConfig << std::endl; + if(!confirm( + "Do you want to apply the new network configuration to camera " + camera.info().serialNumber().toString() + + "?")) + { + return EXIT_SUCCESS; + } + + std::cout << "Applying network configuration..." << std::endl; + camera.applyNetworkConfiguration(newConfig); + + std::cout << "Updated network configuration of camera " << camera.info().serialNumber() << ":" << std::endl; + std::cout << camera.networkConfiguration() << std::endl << std::endl; + + std::cout << "Camera status is '" << camera.state().status() << "'" << std::endl; + } + + catch(const std::exception &e) + { + std::cerr << "Error: " << Zivid::toString(e) << std::endl; + std::cout << "Press enter to exit." << std::endl; + std::cin.get(); + return EXIT_FAILURE; + } + return EXIT_SUCCESS; +} diff --git a/source/Camera/InfoUtilOther/Warmup/Warmup.cpp b/source/Camera/InfoUtilOther/Warmup/Warmup.cpp index 9fab5c23..7ce8cf16 100644 --- a/source/Camera/InfoUtilOther/Warmup/Warmup.cpp +++ b/source/Camera/InfoUtilOther/Warmup/Warmup.cpp @@ -8,7 +8,7 @@ Short example of a basic way to warm up the camera with specified time and captu #include #include -using HighResClock = std::chrono::high_resolution_clock; +using SteadyClock = std::chrono::steady_clock; using Duration = std::chrono::nanoseconds; int main() @@ -33,13 +33,13 @@ int main() std::cout << "Starting warm up for: " << warmupTime.count() << " minutes" << std::endl; - const auto beforeWarmup = HighResClock::now(); + const auto beforeWarmup = SteadyClock::now(); - while(HighResClock::now() - beforeWarmup < warmupTime) + while(SteadyClock::now() - beforeWarmup < warmupTime) { - const auto beforeCapture = HighResClock::now(); + const auto beforeCapture = SteadyClock::now(); camera.capture(settings); - const auto afterCapture = HighResClock::now(); + const auto afterCapture = SteadyClock::now(); const auto captureTime = afterCapture - beforeCapture; if(captureTime < captureCycle) { @@ -51,7 +51,7 @@ int main() << "Please increase the desired capture cycle." << std::endl; } - const auto remainingTime = warmupTime - (HighResClock::now() - beforeWarmup); + const auto remainingTime = warmupTime - (SteadyClock::now() - beforeWarmup); const auto remainingTimeMinutes = std::chrono::duration_cast(remainingTime); const auto remainingTimeSeconds = diff --git a/source/Camera/InfoUtilOther/ZividBenchmark/ZividBenchmark.cpp b/source/Camera/InfoUtilOther/ZividBenchmark/ZividBenchmark.cpp index cf663225..d238d7e4 100644 --- a/source/Camera/InfoUtilOther/ZividBenchmark/ZividBenchmark.cpp +++ b/source/Camera/InfoUtilOther/ZividBenchmark/ZividBenchmark.cpp @@ -19,7 +19,7 @@ namespace { const int printWidth = 56; - using HighResClock = std::chrono::high_resolution_clock; + using SteadyClock = std::chrono::steady_clock; using Duration = std::chrono::nanoseconds; Duration computeAverageDuration(const std::vector &durations) @@ -361,9 +361,9 @@ namespace template FrameAndCaptureTime captureAndMeasure(Zivid::Camera &camera, const SettingsT &settings) { - const auto before = HighResClock::now(); + const auto before = SteadyClock::now(); const auto frame = camera.capture(settings); - const auto after = HighResClock::now(); + const auto after = SteadyClock::now(); return { std::move(frame), (after - before) }; } @@ -373,19 +373,19 @@ namespace template<> Duration useFrame(const Zivid::Frame &frame) { - const auto before = HighResClock::now(); + const auto before = SteadyClock::now(); const auto pointCloud = frame.pointCloud(); const auto data = pointCloud.copyData(); - const auto after = HighResClock::now(); + const auto after = SteadyClock::now(); return (after - before); } template<> Duration useFrame(const Zivid::Frame2D &frame2D) { - const auto before = HighResClock::now(); + const auto before = SteadyClock::now(); const auto image = frame2D.imageRGBA(); - const auto after = HighResClock::now(); + const auto after = SteadyClock::now(); return (after - before); } @@ -406,14 +406,14 @@ namespace for(size_t i = 0; i < numFrames; i++) { dummyCapture2D(camera, settings2D); - const auto before = HighResClock::now(); + const auto before = SteadyClock::now(); const auto frame2dAndCaptureTime = captureAndMeasure(camera, settings2D); std::future userThread = std::async(std::launch::async, useFrame, std::ref(frame2dAndCaptureTime.frame)); const auto frameAndCaptureTime = captureAndMeasure(camera, settings); const auto processTime = useFrame(frameAndCaptureTime.frame); const auto processTime2D = userThread.get(); - const auto after = HighResClock::now(); + const auto after = SteadyClock::now(); captureDurations2D.push_back(frame2dAndCaptureTime.captureTime); captureDurations.push_back(frameAndCaptureTime.captureTime); @@ -456,14 +456,14 @@ namespace for(size_t i = 0; i < numFrames; i++) { dummyCapture3D(camera, settings); - const auto before = HighResClock::now(); + const auto before = SteadyClock::now(); const auto frameAndCaptureTime = captureAndMeasure(camera, settings); std::future userThread = std::async(std::launch::async, useFrame, std::ref(frameAndCaptureTime.frame)); const auto frame2dAndCaptureTime = captureAndMeasure(camera, settings2D); const auto processTime2D = useFrame(frame2dAndCaptureTime.frame); const auto processTime = userThread.get(); - const auto after = HighResClock::now(); + const auto after = SteadyClock::now(); captureDurations2D.push_back(frame2dAndCaptureTime.captureTime); captureDurations.push_back(frameAndCaptureTime.captureTime); @@ -509,10 +509,10 @@ namespace for(size_t i = 0; i < numFrames; i++) { dummyCapture3D(camera, settings); - const auto before = HighResClock::now(); + const auto before = SteadyClock::now(); const auto frameAndCaptureTime = captureAndMeasure(camera, settings); const auto processTime = useFrame(frameAndCaptureTime.frame); - const auto after = HighResClock::now(); + const auto after = SteadyClock::now(); captureDurations.push_back(frameAndCaptureTime.captureTime); processDurations.push_back(processTime); @@ -539,11 +539,11 @@ namespace for(size_t i = 0; i < numConnects; i++) { - const auto beforeConnect = HighResClock::now(); + const auto beforeConnect = SteadyClock::now(); camera.connect(); - const auto afterConnect = HighResClock::now(); + const auto afterConnect = SteadyClock::now(); camera.disconnect(); - const auto afterDisconnect = HighResClock::now(); + const auto afterDisconnect = SteadyClock::now(); connectDurations.push_back(afterConnect - beforeConnect); disconnectDurations.push_back(afterDisconnect - afterConnect); @@ -581,16 +581,16 @@ namespace for(size_t i = 0; i < numFrames; i++) { - const auto beforeCapture2D = HighResClock::now(); + const auto beforeCapture2D = SteadyClock::now(); const auto frame2D = camera.capture(settings2D); - const auto afterCapture2D = HighResClock::now(); + const auto afterCapture2D = SteadyClock::now(); const auto frame = camera.capture(settings); - const auto afterCapture = HighResClock::now(); + const auto afterCapture = SteadyClock::now(); const auto image = frame2D.imageRGBA(); - const auto afterProcess2D = HighResClock::now(); + const auto afterProcess2D = SteadyClock::now(); const auto pointCloud = frame.pointCloud(); const auto data = pointCloud.copyData(); - const auto afterProcess = HighResClock::now(); + const auto afterProcess = SteadyClock::now(); captureDurations2D.push_back(afterCapture2D - beforeCapture2D); captureDurations.push_back(afterCapture - afterCapture2D); @@ -642,16 +642,16 @@ namespace for(size_t i = 0; i < numFrames; i++) { - const auto beforeCapture = HighResClock::now(); + const auto beforeCapture = SteadyClock::now(); const auto frame = camera.capture(settings); - const auto afterCapture = HighResClock::now(); + const auto afterCapture = SteadyClock::now(); const auto frame2D = camera.capture(settings2D); - const auto afterCapture2D = HighResClock::now(); + const auto afterCapture2D = SteadyClock::now(); const auto pointCloud = frame.pointCloud(); const auto data = pointCloud.copyData(); - const auto afterProcess = HighResClock::now(); + const auto afterProcess = SteadyClock::now(); const auto image = frame2D.imageRGBA(); - const auto afterProcess2D = HighResClock::now(); + const auto afterProcess2D = SteadyClock::now(); captureDurations.push_back(afterCapture - beforeCapture); captureDurations2D.push_back(afterCapture2D - afterCapture); @@ -696,12 +696,12 @@ namespace for(size_t i = 0; i < numFrames; i++) { - const auto beforeCapture = HighResClock::now(); + const auto beforeCapture = SteadyClock::now(); const auto frame = camera.capture(settings); - const auto afterCapture = HighResClock::now(); + const auto afterCapture = SteadyClock::now(); const auto pointCloud = frame.pointCloud(); const auto data = pointCloud.copyData(); - const auto afterProcess = HighResClock::now(); + const auto afterProcess = SteadyClock::now(); captureDurations.push_back(afterCapture - beforeCapture); processDurations.push_back(afterProcess - afterCapture); @@ -738,9 +738,9 @@ namespace for(size_t i = 0; i < numFrames; i++) { - const auto beforeSuggestSettings = HighResClock::now(); + const auto beforeSuggestSettings = SteadyClock::now(); const auto settings{ Zivid::CaptureAssistant::suggestSettings(camera, suggestSettingsParameters) }; - const auto afterSuggestSettings = HighResClock::now(); + const auto afterSuggestSettings = SteadyClock::now(); suggestSettingsDurations.push_back(afterSuggestSettings - beforeSuggestSettings); } @@ -825,11 +825,11 @@ namespace for(size_t i = 0; i < numFrames; i++) { - const auto beforeCapture = HighResClock::now(); + const auto beforeCapture = SteadyClock::now(); // The 2D capture API returns after the 2D image is available in CPU memory. // All the acquisition, processing, and copying happen inside this function call. const auto frame2D = camera.capture(settings); - const auto afterCapture = HighResClock::now(); + const auto afterCapture = SteadyClock::now(); captureDurations.push_back(afterCapture - beforeCapture); } @@ -853,19 +853,19 @@ namespace Duration copyDataTime(Zivid::Frame &frame) { auto pointCloud = frame.pointCloud(); - const auto beforeCopyData = HighResClock::now(); + const auto beforeCopyData = SteadyClock::now(); pointCloud.copyData(); - const auto afterCopyData = HighResClock::now(); + const auto afterCopyData = SteadyClock::now(); return afterCopyData - beforeCopyData; } Duration copyDataTime(Zivid::Frame2D &frame2D) { - const auto beforeCopyData = HighResClock::now(); + const auto beforeCopyData = SteadyClock::now(); // The method to get the image from the Frame2D object returns the image right away. // The image object holds a handle to the image data in CPU memory. frame2D.imageRGBA(); - const auto afterCopyData = HighResClock::now(); + const auto afterCopyData = SteadyClock::now(); return afterCopyData - beforeCopyData; } @@ -929,9 +929,9 @@ namespace std::vector durationsPerFormat; for(size_t j = 0; j < numFrames; j++) { - const auto beforeSave = HighResClock::now(); + const auto beforeSave = SteadyClock::now(); frame.save(dataFile); - const auto afterSave = HighResClock::now(); + const auto afterSave = SteadyClock::now(); durationsPerFormat.push_back(afterSave - beforeSave); } diff --git a/source/Camera/Maintenance/CorrectCameraInField/CorrectCameraInField.cpp b/source/Camera/Maintenance/CorrectCameraInField/CorrectCameraInField.cpp index 738afd73..54b2bfcc 100644 --- a/source/Camera/Maintenance/CorrectCameraInField/CorrectCameraInField.cpp +++ b/source/Camera/Maintenance/CorrectCameraInField/CorrectCameraInField.cpp @@ -12,6 +12,7 @@ time that camera captures a new point cloud. Note: This example uses experimental SDK features, which may be modified, moved, or deleted in the future without notice. */ +#include #include #include @@ -55,7 +56,7 @@ namespace if(yesNoPrompt("Capture (y) or finish (n)?")) { std::cout << "Capturing calibration board" << std::endl; - const auto detectionResult = Zivid::Experimental::Calibration::detectFeaturePoints(camera); + const auto detectionResult = Zivid::Calibration::detectCalibrationBoard(camera); const auto input = Zivid::Experimental::Calibration::InfieldCorrectionInput{ detectionResult }; if(input.valid()) diff --git a/source/Camera/Maintenance/VerifyCameraInField/VerifyCameraInField.cpp b/source/Camera/Maintenance/VerifyCameraInField/VerifyCameraInField.cpp index 80fd9ea8..aa79c5ee 100644 --- a/source/Camera/Maintenance/VerifyCameraInField/VerifyCameraInField.cpp +++ b/source/Camera/Maintenance/VerifyCameraInField/VerifyCameraInField.cpp @@ -8,6 +8,7 @@ shock in shipping or handling. If so, look at the CorrectCameraInField sample. Note: This example uses experimental SDK features, which may be modified, moved, or deleted in the future without notice. */ +#include #include #include @@ -38,7 +39,7 @@ int main() // Gather data std::cout << "Capturing calibration board" << std::endl; - const auto detectionResult = Zivid::Experimental::Calibration::detectFeaturePoints(camera); + const auto detectionResult = Zivid::Calibration::detectCalibrationBoard(camera); // Prepare data and check that it is appropriate for infield verification const auto input = Zivid::Experimental::Calibration::InfieldCorrectionInput{ detectionResult }; diff --git a/source/Camera/Maintenance/VerifyCameraInFieldFromZDF/VerifyCameraInFieldFromZDF.cpp b/source/Camera/Maintenance/VerifyCameraInFieldFromZDF/VerifyCameraInFieldFromZDF.cpp index 32ac4d88..fae17eb5 100644 --- a/source/Camera/Maintenance/VerifyCameraInFieldFromZDF/VerifyCameraInFieldFromZDF.cpp +++ b/source/Camera/Maintenance/VerifyCameraInFieldFromZDF/VerifyCameraInFieldFromZDF.cpp @@ -17,6 +17,7 @@ used in production. In addition, you can send these ZDF files to Zivid Customer Note: This example uses experimental SDK features, which may be modified, moved, or deleted in the future without notice. */ +#include #include #include @@ -37,7 +38,7 @@ int main() // offline infield verification std::cout << "Capturing calibration board" << std::endl; - const auto frame = Zivid::Experimental::Calibration::captureCalibrationBoard(camera); + const auto frame = Zivid::Calibration::captureCalibrationBoard(camera); const auto dataFile = "FrameWithCalibrationBoard.zdf"; std::cout << "Saving frame to file: " << dataFile << ", for later use in offline infield verification" @@ -51,7 +52,7 @@ int main() const auto loadedFrame = Zivid::Frame(dataFile); std::cout << "Detecting calibration board" << std::endl; - const auto detectionResult = Zivid::Experimental::Calibration::detectFeaturePoints(loadedFrame); + const auto detectionResult = Zivid::Calibration::detectCalibrationBoard(loadedFrame); const auto input = Zivid::Experimental::Calibration::InfieldCorrectionInput{ detectionResult }; if(!input.valid())