diff --git a/README.md b/README.md index ebc5e05e..6c8c5ddd 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # C++ samples -This repository contains cpp code samples for Zivid SDK v2.13.1. For +This repository contains cpp code samples for Zivid SDK v2.11.1. For tested compatibility with earlier SDK versions, please check out [accompanying releases](https://github.com/zivid/zivid-cpp-samples/tree/master/../../releases). @@ -55,7 +55,8 @@ from the camera can be used. - **Advanced** - [AllocateMemoryForPointCloudData](https://github.com/zivid/zivid-cpp-samples/tree/master/source/Camera/Advanced/AllocateMemoryForPointCloudData/AllocateMemoryForPointCloudData.cpp) - Two methods to copy point cloud data from GPU memory to CPU memory, to be consumed by OpenCV. - - [Capture2DAnd3D](https://github.com/zivid/zivid-cpp-samples/tree/master/source/Camera/Advanced/Capture2DAnd3D/Capture2DAnd3D.cpp) - Capture 2D and 3D separately with the Zivid camera. + - [Capture2DAnd3D](https://github.com/zivid/zivid-cpp-samples/tree/master/source/Camera/Advanced/Capture2DAnd3D/Capture2DAnd3D.cpp) - Capture 2D and then 3D using various capture strategies, + optimizing for both 2D quality and 2D acquisition speed. - [CaptureHalconViaGenICam](https://github.com/zivid/zivid-cpp-samples/tree/master/source/Camera/Advanced/CaptureHalconViaGenICam/CaptureHalconViaGenICam.cpp) - Capture and save a point cloud, with colors, using GenICam interface and Halcon C++ SDK. - [CaptureHalconViaZivid](https://github.com/zivid/zivid-cpp-samples/tree/master/source/Camera/Advanced/CaptureHalconViaZivid/CaptureHalconViaZivid.cpp) - Capture a point cloud, with colors, using Zivid SDK, @@ -71,9 +72,6 @@ from the camera can be used. - [MultiCameraCaptureSequentiallyWithInterleavedProcessing](https://github.com/zivid/zivid-cpp-samples/tree/master/source/Camera/Advanced/MultiCameraCaptureSequentiallyWithInterleavedProcessing/MultiCameraCaptureSequentiallyWithInterleavedProcessing.cpp) - Capture point clouds with multiple cameras sequentially with interleaved processing. - **InfoUtilOther** - - [AutomaticNetworkConfigurationForCameras](https://github.com/zivid/zivid-cpp-samples/tree/master/source/Camera/InfoUtilOther/AutomaticNetworkConfigurationForCameras/AutomaticNetworkConfigurationForCameras.cpp) - Automatically set the IP addresses of any number of - cameras to be in the same subnet as the provided IP address - of the network interface. - [CameraInfo](https://github.com/zivid/zivid-cpp-samples/tree/master/source/Camera/InfoUtilOther/CameraInfo/CameraInfo.cpp) - List connected cameras and print camera version and state information for each connected camera. - [CameraUserData](https://github.com/zivid/zivid-cpp-samples/tree/master/source/Camera/InfoUtilOther/CameraUserData/CameraUserData.cpp) - Store user data on the Zivid camera. @@ -83,12 +81,10 @@ 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. - - [ZividBenchmark](https://github.com/zivid/zivid-cpp-samples/tree/master/source/Camera/InfoUtilOther/ZividBenchmark/ZividBenchmark.cpp) - Zividbenchmark is a sample that will test the average + - [ZividBenchmark](https://github.com/zivid/zivid-cpp-samples/tree/master/source/Camera/InfoUtilOther/ZividBenchmark/ZividBenchmark.cpp) - Zividbenchmarks is a sample that will test the average speed of different operations on your computer. - **Maintenance** - [CorrectCameraInField](https://github.com/zivid/zivid-cpp-samples/tree/master/source/Camera/Maintenance/CorrectCameraInField/CorrectCameraInField.cpp) - Correct the dimension trueness of a Zivid camera. @@ -132,9 +128,8 @@ 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. @@ -142,7 +137,7 @@ from the camera can be used. - [PoseConversions](https://github.com/zivid/zivid-cpp-samples/tree/master/source/Applications/Advanced/HandEyeCalibration/PoseConversions/PoseConversions.cpp) - Convert to/from Transformation Matrix (Rotation Matrix + Translation Vector) - [UtilizeHandEyeCalibration](https://github.com/zivid/zivid-cpp-samples/tree/master/source/Applications/Advanced/HandEyeCalibration/UtilizeHandEyeCalibration/UtilizeHandEyeCalibration.cpp) - Transform single data point or entire point cloud from - camera to robot base reference frame using Hand-Eye + camera frame to robot base frame using Hand-Eye calibration - **MultiCamera** - [MultiCameraCalibration](https://github.com/zivid/zivid-cpp-samples/tree/master/source/Applications/Advanced/MultiCamera/MultiCameraCalibration/MultiCameraCalibration.cpp) - Use captures of a calibration object to generate @@ -233,8 +228,7 @@ this: 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, 24.04. +Zivid and HALCON are compatible with Windows 10 and Ubuntu 20.04, 22.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 e9396e8a..88f34583 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,5 @@ function install_www_deb { rm -r $TMP_DIR || 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 +install_www_deb "https://downloads.zivid.com/sdk/releases/2.11.1+de9b5dae-1/u${VERSION_ID:0:2}/zivid-telicam-driver_3.0.1.1-3_amd64.deb" || exit +install_www_deb "https://downloads.zivid.com/sdk/releases/2.11.1+de9b5dae-1/u${VERSION_ID:0:2}/zivid_2.11.1+de9b5dae-1_amd64.deb" || exit diff --git a/source/3rd-party/clipp/include/clipp.h b/source/3rd-party/clipp/include/clipp.h index a1148f31..f01b42e7 100644 --- a/source/3rd-party/clipp/include/clipp.h +++ b/source/3rd-party/clipp/include/clipp.h @@ -31,788 +31,728 @@ #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_string = std::string; -using doc_string = std::string; + using arg_index = int; -using arg_list = std::vector; + using arg_string = std::string; + using doc_string = std::string; + using arg_list = std::vector; - -/*************************************************************************//** + /*************************************************************************/ /** * * @brief tristate * *****************************************************************************/ -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); } - + 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); + } -/*************************************************************************//** + /*************************************************************************/ /** * * @brief (start,size) index range * *****************************************************************************/ -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 match length & position within subject string */ - explicit constexpr - subrange(size_type pos, size_type len) noexcept : - at_{pos}, length_{len} - {} + class subrange + { + public: + using size_type = arg_string::size_type; - /** @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 default: no match */ + explicit constexpr subrange() noexcept + : at_{ arg_string::npos } + , length_{ 0 } + {} - /** @brief returns true, if query string is a prefix of the subject string */ - constexpr bool prefix() const noexcept { - return at_ == 0; - } + /** @brief match length & position within subject string */ + explicit constexpr subrange(size_type pos, size_type len) noexcept + : at_{ pos } + , length_{ len } + {} - /** @brief returns true, if query is a substring of the query string */ - constexpr explicit operator bool () const noexcept { - return at_ != arg_string::npos; - } + /** @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_; + } -private: - size_type at_; - size_type 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 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_; + }; -/*************************************************************************//** + /*************************************************************************/ /** * * @brief match predicates * *****************************************************************************/ -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); - } -} + using match_predicate = std::function; + using match_function = std::function; -/*************************************************************************//** + /*************************************************************************/ /** * * @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()...), -#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(int) -> decltype( + std::declval()(std::declval()...), + std::integral_constant::type>::value>{}); + template + constexpr auto check_is_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_callable_without_arg(int) + -> decltype(std::declval()(), + std::integral_constant::type>::value>{}); -template -constexpr auto -check_is_void_callable(long) -> std::false_type; + template + constexpr auto check_is_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(int) + -> decltype(std::declval()(std::declval()...), std::true_type{}); -template -constexpr auto -check_is_void_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_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(0)) -{}; + template + struct is_callable : decltype(check_is_callable_without_arg(0)) + {}; -template -struct is_callable : - decltype(check_is_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)) + {}; -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(long) -> std::false_type; - -template -struct has_size_getter : - decltype(check_has_size_getter(0)) -{}; + template + constexpr auto check_has_size_getter(int) -> decltype(std::declval().size(), std::true_type{}); -} // namespace traits + template + constexpr auto check_has_size_getter(long) -> std::false_type; + 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(; txt::isspace(*s); ++s); - if(!s[0] || s[0] == '-') return false; - return true; -} - + 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; + } -/*************************************************************************//** + /*************************************************************************/ /** * * @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 -struct limits_clamped { - static T from(const V& v) { 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); + } + }; -/*************************************************************************//** + /*************************************************************************/ /** * * @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 bool from(const char* s) { - if(!s) return false; - 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 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 bool from(const char *s) + { + if(!s) return false; + return static_cast(s); + } + }; -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 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 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 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 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 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 short int from(const char* s) { - return clamped_on_limits(std::strtoll(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 int from(const char* s) { - 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 long 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 long long int from(const char* s) { - return (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 float from(const char* s) { - return (std::strtof(s,nullptr)); - } -}; + 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 double from(const char* s) { - return (std::strtod(s,nullptr)); - } -}; + template<> + struct make + { + static inline long long int from(const char *s) + { + return (std::strtoll(s, nullptr, 10)); + } + }; -template<> -struct make { - static inline long double from(const char* s) { - return (std::strtold(s,nullptr)); - } -}; + template<> + struct make + { + static inline float from(const char *s) + { + return (std::strtof(s, nullptr)); + } + }; -template<> -struct make { - static inline std::string from(const char* s) { - return std::string(s); - } -}; + 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 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)} - {} - - void operator () () const { - if(t_) *t_ = v_; - } - -private: - T* t_; - V v_; -}; + 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_; + }; -/*************************************************************************//** + /*************************************************************************/ /** * * @brief flips bools * *****************************************************************************/ -class flip_bool -{ -public: - explicit constexpr - flip_bool(bool& target) noexcept : - b_{&target} - {} - - void operator () () const { - if(b_) *b_ = !*b_; - } - -private: - bool* b_; -}; + class flip_bool + { + public: + explicit constexpr flip_bool(bool &target) noexcept + : b_{ &target } + {} + void operator()() const + { + if(b_) *b_ = !*b_; + } + private: + bool *b_; + }; -/*************************************************************************//** + /*************************************************************************/ /** * * @brief increments using operator ++ * *****************************************************************************/ -template -class increment -{ -public: - explicit constexpr - increment(T& target) noexcept : t_{std::addressof(target)} {} - - void operator () () const { - if(t_) ++(*t_); - } - -private: - T* t_; -}; + template + class increment + { + public: + explicit constexpr increment(T &target) noexcept + : t_{ std::addressof(target) } + {} + void operator()() const + { + if(t_) ++(*t_); + } + private: + T *t_; + }; -/*************************************************************************//** + /*************************************************************************/ /** * * @brief decrements using operator -- * *****************************************************************************/ -template -class decrement -{ -public: - explicit constexpr - decrement(T& target) noexcept : t_{std::addressof(target)} {} - - void operator () () const { - if(t_) --(*t_); - } - -private: - T* t_; -}; + template + class decrement + { + public: + explicit constexpr decrement(T &target) noexcept + : t_{ std::addressof(target) } + {} + void operator()() const + { + if(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)} - {} - - void operator () () const { - if(t_) (*t_) += by_; - } - -private: - T* t_; - T 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_; + }; -/*************************************************************************//** + /*************************************************************************/ /** * * @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)} {} - - void operator () (const char* s) const { - if(t_ && s) *t_ = detail::make::from(s); - } + template + class map_arg_to + { + public: + explicit constexpr map_arg_to(T &target) noexcept + : t_{ std::addressof(target) } + {} -private: - T* t_; -}; + void operator()(const char *s) const + { + if(t_ && s) *t_ = detail::make::from(s); + } + 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)); - } - -private: - std::vector* t_; -}; + void operator()(const char *s) const + { + if(t_ && s) t_->push_back(detail::make::from(s)); + } + 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} {} - - void operator () (const char* s) const { - if(t_ && s) *t_ = true; - } - -private: - bool* t_; -}; - - -} // namespace detail - - + 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 -/*************************************************************************//** + /*************************************************************************/ /** * * @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; - - s.erase( - std::find_if_not(s.rbegin(), s.rend(), - [](char c) { return txt::isspace(c);} ).base(), - s.end() ); -} + 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()); + } -/*************************************************************************//** + /*************************************************************************/ /** * * @brief removes leading whitespace from string * *****************************************************************************/ -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 txt::isspace(c);}) - ); -} + 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); })); + } -/*************************************************************************//** + /*************************************************************************/ /** * * @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; - - s.erase(std::remove_if(s.begin(), s.end(), - [](char c) { return txt::isspace(c); }), - s.end() ); -} + 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()); + } -/*************************************************************************//** + /*************************************************************************/ /** * * @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 @@ -821,46 +761,41 @@ has_postfix(const std::basic_string& subject, * 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< - typename std::decay::type>(), - "elements of input range must have a ::size() member function"); + static_assert(traits::has_size_getter::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); + //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); } - } - return strs[0].substr(0, m); -} - - -/*************************************************************************//** + /*************************************************************************/ /** * * @brief returns longest substring range that could be found in 'arg' * @@ -868,35 +803,31 @@ longest_common_prefix(const InputRange& strs) * @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(); + 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 }; } - } - return subrange{i,n}; -} - - -/*************************************************************************//** + /*************************************************************************/ /** * * @brief returns longest prefix range that could be found in 'arg' * @@ -904,208 +835,206 @@ longest_substring_match(const std::basic_string& arg, * @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(); + 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 }; } - } - 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; - - str::trim(s); - if(s.empty()) return subrange{}; + 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; - auto i = s.find_first_of("0123456789+-"); - if(i == string_t::npos) { - i = s.find(decimalPoint); - if(i == string_t::npos) return subrange{}; - } + str::trim(s); + if(s.empty()) 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] != '-' && !txt::isdigit(s[j])) break; - } - else if(!txt::isdigit(s[j])) { - break; + auto i = s.find_first_of("0123456789+-"); + if(i == string_t::npos) + { + i = s.find(decimalPoint); + if(i == string_t::npos) 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}; -} + 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; + } + } + } + //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; - - str::trim(s); - if(s.empty()) return subrange{}; - - auto i = s.find_first_of("0123456789+-"); - if(i == string_t::npos) return subrange{}; + template + subrange first_integer_match(std::basic_string s, C digitSeparator = C(',')) + { + using string_t = std::basic_string; - 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; - } - } + str::trim(s); + if(s.empty()) return subrange{}; - //if length == 1 then must be a digit - if(j-i == 1 && !txt::isdigit(s[i])) return subrange{}; + auto i = s.find_first_of("0123456789+-"); + if(i == string_t::npos) return subrange{}; - return subrange{i,j-i}; -} + 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; + } + } + //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 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); - - return (match && match.length() == candidate.size()); -} - + 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()); + } -/*************************************************************************//** + /*************************************************************************/ /** * * @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()); -} - -} // namespace str - - - - + 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 -/*************************************************************************//** + /*************************************************************************/ /** * * @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; @@ -1114,1498 +1043,1469 @@ set(T& target, V value) { * 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_(); } - private: - simple_action 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_; + }; -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)); } - - /** @brief adds an action that has an operator()() */ - Derived& operator () (simple_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 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"); - - return call(clipp::set(t)); - } + template + Derived &set(Target &t) + { + static_assert(!std::is_pointer::value, "parameter target type must not be a pointer"); - /** @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))); - } + 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 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); - } - - //TODO remove ugly empty param list overload - Derived& - target() { - 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); + } - //--------------------------------------------------------------- - /** @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 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 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 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 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; - - - //--------------------------------------------------------------- - /** @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 sets documentations string */ - Derived& doc(doc_string&& txt) { - doc_ = std::move(txt); - return *static_cast(this); - } - - - //--------------------------------------------------------------- - /** @brief returns if a group/parameter is repeatable */ - bool repeatable() const noexcept { - return repeatable_; - } - - /** @brief sets repeatability of group/parameter */ - Derived& repeatable(bool yes) noexcept { - repeatable_ = yes; - return *static_cast(this); - } + template + class token + { + public: + //--------------------------------------------------------------- + using doc_string = clipp::doc_string; + //--------------------------------------------------------------- + /** @brief returns documentation string */ + const doc_string &doc() const noexcept + { + return doc_; + } - //--------------------------------------------------------------- - /** @brief returns if a group/parameter is blocking/positional */ - bool blocking() const noexcept { - return blocking_; - } + /** @brief sets documentations string */ + Derived &doc(const doc_string &txt) + { + doc_ = txt; + return *static_cast(this); + } - /** @brief determines, if a group/parameter is blocking/positional */ - Derived& blocking(bool yes) noexcept { - blocking_ = yes; - return *static_cast(this); - } + /** @brief sets documentations string */ + Derived &doc(doc_string &&txt) + { + doc_ = std::move(txt); + return *static_cast(this); + } + //--------------------------------------------------------------- + /** @brief returns if a group/parameter is repeatable */ + bool repeatable() const noexcept + { + return repeatable_; + } -private: - //--------------------------------------------------------------- - doc_string doc_; - bool repeatable_ = false; - bool blocking_ = false; -}; + /** @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 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 % (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%(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))); + } -/*************************************************************************//** + /*************************************************************************/ /** * * @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))); -} - - - -} // namespace detail - + 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 -/*************************************************************************//** + /*************************************************************************/ /** * * @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 txt::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 std::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 txt::isalpha(c); }); -} - - + inline bool alphabetic(const arg_string &s) + { + return std::all_of(s.begin(), s.end(), [](char c) { return std::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)...} - {} - - 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_)); - } + class none_of + { + public: + none_of(arg_list strs) + : excluded_{ std::move(strs) } + {} -private: - arg_list excluded_; -}; + 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_)); + } + 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} - {} - - subrange operator () (const arg_string& s) const { - return str::first_number_match(s, separator_, decpoint_, exp_); - } - -private: - char decpoint_; - char separator_; - char exp_; -}; + 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_); + } + 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} {} - - subrange operator () (const arg_string& s) const { - return str::first_integer_match(s, separator_); - } - -private: - char separator_; -}; + 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_; + }; -/*************************************************************************//** + /*************************************************************************/ /** * * @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} {} - - 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_; -}; + 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; + } + 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)} {} - - subrange operator () (const arg_string& s) const { - return str::substring_match(s, str_); - } - -private: - arg_string 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_); + } + 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)} {} - - bool operator () (const arg_string& s) const { - return s.find(prefix_) == 0; - } - -private: - arg_string prefix_; -}; + 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_; + }; -/*************************************************************************//** + /*************************************************************************/ /** * * @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)} {} - - bool operator () (const arg_string& s) const { - return s.find(prefix_) != 0; - } - -private: - arg_string prefix_; -}; - + class prefix_not + { + public: + explicit prefix_not(arg_string p) + : prefix_{ std::move(p) } + {} -/** @brief alias for prefix_not */ -using noprefix = prefix_not; + bool operator()(const arg_string &s) const + { + return s.find(prefix_) != 0; + } + private: + arg_string prefix_; + }; + /** @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} - {} - - explicit - length(std::size_t min, std::size_t max): - min_{min}, max_{max} - {} + class length + { + public: + explicit length(std::size_t exact) + : min_{ exact } + , max_{ exact } + {} - bool operator () (const arg_string& s) const { - return s.size() >= min_ && s.size() <= max_; - } + explicit length(std::size_t min, std::size_t max) + : min_{ min } + , max_{ max } + {} -private: - std::size_t min_; - std::size_t max_; -}; + bool operator()(const arg_string &s) const + { + return s.size() >= min_ && s.size() <= 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}; -} - - -} // namespace match - - - + inline length max_length(std::size_t max) + { + return length{ 0, max }; + } + } // 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)} {} + 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{}; - } + subrange operator()(const arg_string &arg) const + { + return match_(arg) ? subrange{ 0, arg.size() } : subrange{}; + } - private: - match_predicate match_; - }; + private: + match_predicate match_; + }; -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)...); - } + 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 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 "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 + //----------------------------------------------------- + /** @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} - {} - - - //--------------------------------------------------------------- - /** @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; - } + 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 should match greedily */ - bool - greedy() const noexcept { - return greedy_; - } + /** @brief determines if a parameter is required */ + parameter &required(bool yes) noexcept + { + required_ = yes; + return *this; + } - /** @brief determines if a parameter should match greedily */ - parameter& - greedy(bool yes) noexcept { - greedy_ = yes; - return *this; - } + //--------------------------------------------------------------- + /** @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 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()}; + if(std::find(flags_.begin(), flags_.end(), arg) != flags_.end()) + { + return subrange{ 0, arg.size() }; + } + return str::longest_prefix_match(arg, flags_); } - return str::longest_prefix_match(arg, flags_); } - } + //--------------------------------------------------------------- + /** @brief access range of flag strings */ + const arg_list &flags() const noexcept + { + return 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 custom match operation */ - const match_function& - matcher() const noexcept { - return matcher_; - } + //--------------------------------------------------------------- + /** @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 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); + for(auto &f : p.flags_) + { + if(f.find(prefix) != 0) f.insert(0, prefix); + } + return p; } - 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; + //--------------------------------------------------------------- + /** @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()); + 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)); - } - - //--------------------------------------------------------------- - 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)...); - } - - arg_list flags_; - match_function matcher_; - doc_string label_; - bool required_ = false; - bool greedy_ = false; -}; + 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); + } + 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; + }; -/*************************************************************************//** + /*************************************************************************/ /** * * @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 @@ -2615,1217 +2515,1331 @@ class group : * nasty problems associated with it and the implementation * becomes bloated and needlessly complicated. */ - template - struct child_t { - enum class type : char {param, group}; - public: + template + struct child_t + { + enum class type : char + { + param, + group + }; - explicit - child_t(const Param& v) : m_{v}, type_{type::param} {} - child_t( Param&& v) noexcept : m_{std::move(v)}, type_{type::param} {} + 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 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; + 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; } - 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; + 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; } - 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; } - - 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; } - - private: - void destroy_content() { - switch(type_) { - default: - case type::param: m_.param.~Param(); break; - case type::group: m_.group.~Group(); break; + bool is_param() const noexcept + { + return type_ == type::param; + } + bool is_group() const noexcept + { + return type_ == type::group; } - } - union data { - data() {} + Param &as_param() noexcept + { + return m_.param; + } + Group &as_group() noexcept + { + return m_.group; + } - data(const Param& v) : param{v} {} - data( Param&& v) noexcept : param{std::move(v)} {} + const Param &as_param() const noexcept + { + return m_.param; + } + const Group &as_group() const noexcept + { + return m_.group; + } - data(const Group& g) : group{g} {} - data( Group&& g) noexcept : group{std::move(g)} {} - ~data() {} + private: + void destroy_content() + { + switch(type_) + { + default: + case type::param: m_.param.~Param(); break; + case type::group: m_.group.~Group(); break; + } + } - Param param; - Group group; + 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_; }; - data m_; - type type_; - }; - - -public: - //--------------------------------------------------------------- - using child = child_t; - using value_type = child; - -private: - using children_store = std::vector; + public: + //--------------------------------------------------------------- + using child = child_t; + using value_type = child; -public: - using const_iterator = children_store::const_iterator; - using iterator = children_store::iterator; - using size_type = children_store::size_type; + private: + using children_store = std::vector; + public: + using const_iterator = children_store::const_iterator; + using iterator = children_store::iterator; + using size_type = children_store::size_type; - //--------------------------------------------------------------- - /** + //--------------------------------------------------------------- + /** * @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_; + class depth_first_traverser + { public: - int level() const noexcept { return level_; } - const child* param() const noexcept { return context_.parent ? &(*context_.cur) : nullptr; } - }; + //----------------------------------------------------- + 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); + } + }; - depth_first_traverser() = default; + depth_first_traverser() = default; - explicit - depth_first_traverser(const group& cur): stack_{} { - if(!cur.empty()) stack_.emplace_back(cur); - } + explicit depth_first_traverser(const group &cur) + : stack_{} + { + if(!cur.empty()) stack_.emplace_back(cur); + } - explicit operator bool() const noexcept { - return !stack_.empty(); - } + explicit operator bool() const noexcept + { + return !stack_.empty(); + } - int level() const noexcept { - return int(stack_.size()); - } + int level() const noexcept + { + return int(stack_.size()); + } - bool is_first_in_parent() const noexcept { - if(stack_.empty()) return false; - return (stack_.back().cur == stack_.back().parent->begin()); - } + 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); - } + bool is_last_in_parent() const noexcept + { + if(stack_.empty()) return false; + return (stack_.back().cur + 1 == stack_.back().end); + } - 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; + 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; } - 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 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 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 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(); }); - } + /** @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(); + }); + } - /** @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 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(); }); - } + /** @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_; - } + 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 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 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 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; + /** @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; + 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; - } - 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 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 child& - operator * () const noexcept { - return *stack_.back().cur; - } + const group *root() const noexcept + { + return stack_.empty() ? nullptr : stack_.front().parent; + } - const child* - operator -> () const noexcept { - return &(*stack_.back().cur); - } + /** @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 group& - parent() const noexcept { - return *(stack_.back().parent); - } + const child &operator*() const noexcept + { + return *stack_.back().cur; + } + const child *operator->() const noexcept + { + return &(*stack_.back().cur); + } - /** @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()); + const group &parent() const noexcept + { + return *(stack_.back().parent); } - else { - next_sibling(); + + /** @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; } - return *this; - } - /** @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(); + /** @brief go to next sibling of current */ + depth_first_traverser &next_sibling() + { 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(); + 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; } - 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(); - } - - inline friend bool operator == (const depth_first_traverser& a, - const depth_first_traverser& b) - { - if(a.stack_.empty() || b.stack_.empty()) return false; + depth_first_traverser &skip_alternatives() + { + if(stack_.empty()) return *this; - //parents not the same -> different position - if(a.stack_.back().parent != b.stack_.back().parent) return false; + //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; + } - 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); - } + return *this; + } - memento - undo_point() const { - memento m; - m.level_ = int(stack_.size()); - if(!stack_.empty()) m.context_ = stack_.back(); - return m; - } + void invalidate() + { + stack_.clear(); + } - 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_; + 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); } - else if(stack_.empty() && m.level_ == 1) { - stack_.push_back(m.context_); + inline friend bool operator!=(const depth_first_traverser &a, const depth_first_traverser &b) + { + return !(a == b); } - } - - private: - context_list stack_; - }; + memento undo_point() const + { + memento m; + m.level_ = int(stack_.size()); + if(!stack_.empty()) m.context_ = stack_.back(); + return m; + } - //--------------------------------------------------------------- - 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)...); - } + 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_); + } + } - template - explicit - group(parameter param, Params... params): - children_{}, exclusive_{false}, joinable_{false}, scoped_{true} - { - push_back(std::move(param), std::move(params)...); - } + private: + context_list stack_; + }; - 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() = 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)...); + } - //----------------------------------------------------- - group(const group&) = default; - group(group&&) = default; + 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& operator = (const group&) = default; - group& operator = (group&&) = default; + //----------------------------------------------------- + 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 + //--------------------------------------------------------------- + /** @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_; - } - - - //--------------------------------------------------------------- - /** @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(); }); - } - + bool scoped() const noexcept + { + return scoped_; + } - //--------------------------------------------------------------- - /** @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 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 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 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 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 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){ + //--------------------------------------------------------------- + /** @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&& 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 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 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 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 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 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 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 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 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 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 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 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 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 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 + //--------------------------------------------------------------- + /** @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}; - } - - - //--------------------------------------------------------------- - /** @brief returns recursive parameter count */ - size_type param_count() const { - size_type c = 0; - for(const auto& n : children_) { - c += n.param_count(); + depth_first_traverser begin_dfs() const noexcept + { + return depth_first_traverser{ *this }; } - return c; - } + //--------------------------------------------------------------- + /** @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 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 + /** @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; + 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; - } + 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); - } - + //--------------------------------------------------------------- + /** @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)); + 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 , (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,(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); + } -/*************************************************************************//** + /*************************************************************************/ /** * * @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 | (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|(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); + } -/*************************************************************************//** + /*************************************************************************/ /** * * @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) {} - -template -void set_blocking(bool yes, P& p, Ps&... ps) { - p.blocking(yes); - set_blocking(yes, ps...); -} + 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...); + } + } // 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, group b) -{ - a.blocking(true); - return group{std::move(a), std::move(b)}.scoped(true); -} - -//--------------------------------------------------------- -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())) + inline group operator&(parameter a, parameter b) { - 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); + a.blocking(true); + b.blocking(true); + return group{ std::move(a), std::move(b) }.scoped(true); } -} -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())) + //--------------------------------------------------------- + inline group operator&(parameter a, group b) { - 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); + a.blocking(true); + return group{ std::move(a), std::move(b) }.scoped(true); } -} + //--------------------------------------------------------- + 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); + } + } + 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())) + { + 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); -} - -//------------------------------------------------------------------- -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); -} + inline group joinable(group g) + { + return g.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(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(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 @@ -3834,85 +3848,72 @@ repeatable(group g) { * 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(group 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); + } -/*************************************************************************//** + /*************************************************************************/ /** * * @brief makes a parameter greedy (match with top priority) * *****************************************************************************/ -inline parameter -greedy(parameter p) { - return p.greedy(true); -} - -inline parameter -operator ! (parameter p) { - return greedy(p); -} - + inline parameter greedy(parameter p) + { + return p.greedy(true); + } + 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&& params) -{ - return std::move(with_prefix(prefix, params)); -} - - -template -inline group -with_prefix(arg_string prefix, Param&& param, Params&&... params) -{ - return with_prefix(prefix, group{std::forward(param), - std::forward(params)...}); -} + 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)... }); + } -/*************************************************************************//** + /*************************************************************************/ /** * * @brief recursively prepends a prefix to all flags * @@ -3920,98 +3921,87 @@ with_prefix(arg_string prefix, Param&& param, Params&&... params) * @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 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 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)); } - return g; -} - -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)...}); -} + //------------------------------------------------------------------- + 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; + } + 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)... }); + } -/*************************************************************************//** + /*************************************************************************/ /** * * @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&& 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)...}); -} + 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)... }); + } -/*************************************************************************//** + /*************************************************************************/ /** * * @brief recursively prepends a suffix to all flags * @@ -4019,69 +4009,57 @@ with_suffix(arg_string suffix, Param&& param, Params&&... params) * @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&& 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)...}); -} - - - - - + 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)... }); + } -/*************************************************************************//** + /*************************************************************************/ /** * * @brief parsing implementation details * *****************************************************************************/ -namespace detail { - - -/*************************************************************************//** + namespace detail + { + /*************************************************************************/ /** * * @brief DFS traverser that keeps track of 'scopes' * scope = all parameters that are either bounded by @@ -4089,2998 +4067,3363 @@ namespace detail { * 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} - {} - - const dfs_traverser& base() const noexcept { return pos_; } - const dfs_traverser& last_match() const noexcept { return lastMatch_; } + 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 } + {} - const group& parent() const noexcept { return pos_.parent(); } + const dfs_traverser &base() const noexcept + { + return pos_; + } + const dfs_traverser &last_match() const noexcept + { + return lastMatch_; + } - 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 group &parent() const noexcept + { + return pos_.parent(); + } - const pattern* operator ->() const noexcept { return pos_.operator->(); } - const pattern& operator *() const noexcept { return *pos_; } + 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 pattern* ptr() const noexcept { return pos_.operator->(); } + const pattern *operator->() const noexcept + { + return pos_.operator->(); + } + const pattern &operator*() const noexcept + { + return *pos_; + } - explicit operator bool() const noexcept { return bool(pos_); } + const pattern *ptr() const noexcept + { + return pos_.operator->(); + } - bool joinable() const noexcept { return pos_.joinable(); } - arg_string common_flag_prefix() const { return pos_.common_flag_prefix(); } + explicit operator bool() const noexcept + { + return bool(pos_); + } - void ignore_blocking(bool yes) { ignoreBlocks_ = yes; } + bool joinable() const noexcept + { + return pos_.joinable(); + } + arg_string common_flag_prefix() const + { + return pos_.common_flag_prefix(); + } - void invalidate() { - pos_.invalidate(); - } + void ignore_blocking(bool yes) + { + ignoreBlocks_ = yes; + } - bool matched() const noexcept { - return (pos_ == lastMatch_); - } + void invalidate() + { + pos_.invalidate(); + } - bool start_of_repeat_group() const noexcept { return repeatGroupStarted_; } + bool matched() const noexcept + { + return (pos_ == lastMatch_); + } - //----------------------------------------------------- - scoped_dfs_traverser& - next_sibling() { pos_.next_sibling(); return *this; } + bool start_of_repeat_group() const noexcept + { + return repeatGroupStarted_; + } - scoped_dfs_traverser& - next_after_siblings() { pos_.next_after_siblings(); return *this; } + //----------------------------------------------------- + scoped_dfs_traverser &next_sibling() + { + pos_.next_sibling(); + return *this; + } + scoped_dfs_traverser &next_after_siblings() + { + pos_.next_after_siblings(); + return *this; + } - //----------------------------------------------------- - scoped_dfs_traverser& - operator ++ () - { - if(!pos_) return *this; + //----------------------------------------------------- + scoped_dfs_traverser &operator++() + { + if(!pos_) return *this; - if(pos_.is_last_in_path()) { - return_to_outermost_scope(); - 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(); + //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_; + } } - if(!pos_) return_to_outermost_scope(); - } else { - ++pos_; + else + { + ++pos_; + } + check_if_left_scope(); + return *this; } - } else { - ++pos_; - } - check_if_left_scope(); - return *this; - } - //----------------------------------------------------- - void next_after_match(scoped_dfs_traverser match) - { - if(!match || ignoreBlocks_) return; + //----------------------------------------------------- + void next_after_match(scoped_dfs_traverser match) + { + if(!match || ignoreBlocks_) return; - check_repeat_group_start(match); + check_repeat_group_start(match); - lastMatch_ = match.base(); + lastMatch_ = match.base(); - // if there is a blocking ancestor -> go back to it - if(!match->blocking()) { - match.pos_.back_to_ancestor(match.innermost_blocking_group()); - } + // if there is a blocking ancestor -> go back to it + if(!match->blocking()) + { + match.pos_.back_to_ancestor(match.innermost_blocking_group()); + } - //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 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(match->blocking()) { - if(match.pos_.is_alternative()) { - //discard other alternatives - match.pos_.skip_alternatives(); - } + if(match->blocking()) + { + if(match.pos_.is_alternative()) + { + //discard other alternatives + match.pos_.skip_alternatives(); + } + + 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(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()); + 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_; } - else { //stay at match position - pos_ = std::move(match.pos_); + else + { + if(match.pos_.level() < pos_.level()) + { + return_to_level(match.pos_.level()); + } + posAfterLastMatch_ = pos_; } + repeatGroupContinues_ = repeat_group_continues(); } - 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.pos_.level() > pos_.level()) { - scopes_.push(pos_.undo_point()); - pos_ = std::move(match.pos_); + 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(match.pos_.level() < pos_.level()) { - return_to_level(match.pos_.level()); + else if(lastMatch_.innermost_repeat_group() != newrg) + { + repeatGroupStarted_ = true; } - else { - pos_ = std::move(match.pos_); + 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() && scopes_.top().param() == &(*pos_.root()->begin())); + } + repeatGroupContinues_ = repeatGroupStarted_; } - posAfterLastMatch_ = pos_; - } - else { - if(match.pos_.level() < pos_.level()) { - return_to_level(match.pos_.level()); - } - posAfterLastMatch_ = 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 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; - } + //----------------------------------------------------- + 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()) { + //----------------------------------------------------- + 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()) + { pos_.undo(scopes_.top()); scopes_.pop(); } - posAfterLastMatch_.invalidate(); + repeatGroupContinues_ = repeat_group_continues(); } - } - 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(); - 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(); - - 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(); - } - }; + //----------------------------------------------------- + void return_to_outermost_scope() + { + posAfterLastMatch_.invalidate(); - dfs_traverser pos_; - dfs_traverser lastMatch_; - dfs_traverser posAfterLastMatch_; - std::stack scopes_; - bool ignoreBlocks_ = false; - bool repeatGroupStarted_ = false; - bool repeatGroupContinues_ = false; -}; + 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(); + 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_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_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_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; - - match_t() = default; - - match_t(arg_string s, scoped_dfs_traverser p): - str_{std::move(s)}, pos_{std::move(p)} - {} + class match_t + { + public: + using size_type = arg_string::size_type; - size_type length() const noexcept { return str_.size(); } + match_t() = default; - const arg_string& str() const noexcept { return str_; } - const scoped_dfs_traverser& pos() const noexcept { return pos_; } + match_t(arg_string s, scoped_dfs_traverser p) + : str_{ std::move(s) } + , pos_{ std::move(p) } + {} - explicit operator bool() const noexcept { return bool(pos_); } + size_type length() const noexcept + { + return str_.size(); + } -private: - arg_string str_; - scoped_dfs_traverser 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_); + } + 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& 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)}; + 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) }; + } + } } + ++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& 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)}; + 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 }; + } + } } } + ++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& 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)}; + 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) }; + } + } } + ++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} - {} - - const parameter* param() const noexcept { return param_; } - - arg_index after_index() const noexcept { return aftIndex_; } + class missing_event + { + public: + explicit missing_event(const parameter *p, arg_index after) + : param_{ p } + , aftIndex_{ after } + {} - private: - const parameter* param_; - arg_index aftIndex_; - }; + const parameter *param() const noexcept + { + return param_; + } - //----------------------------------------------------- - using missing_events = std::vector; - using arg_mappings = std::vector; + arg_index after_index() const noexcept + { + return aftIndex_; + } + private: + const parameter *param_; + arg_index aftIndex_; + }; -private: - struct miss_candidate { - miss_candidate(dfs_traverser p, arg_index idx, - bool firstInRepeatGroup = false): - pos{std::move(p)}, index{idx}, - startsRepeatGroup{firstInRepeatGroup} - {} + //----------------------------------------------------- + using missing_events = std::vector; + using arg_mappings = std::vector; - 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 } + {} + dfs_traverser pos; + arg_index index; + bool startsRepeatGroup; + }; + using miss_candidates = std::vector; -public: - //--------------------------------------------------------------- - /** @brief initializes parser with a command line interface + 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_; - //--------------------------------------------------------------- - /** @brief processes one command line argument */ - bool operator() (const arg_string& arg) - { - ++eaten_; - ++index_; + if(!valid()) return false; - if(!valid()) return false; + if(!blocked_ && try_match(arg)) return true; - if(!blocked_ && try_match(arg)) return true; + if(try_match_blocked(arg)) return false; - if(try_match_blocked(arg)) return false; + //skipping of blocking & required patterns is not allowed + if(!blocked_ && !pos_.matched() && pos_->required() && pos_->blocking()) + { + blocked_ = true; + } - //skipping of blocking & required patterns is not allowed - if(!blocked_ && !pos_.matched() && pos_->required() && pos_->blocking()) { - blocked_ = true; + add_nomatch(arg); + return false; } - add_nomatch(arg); - return false; - } - - - //--------------------------------------------------------------- - /** @brief returns range of argument -> parameter mappings */ - const arg_mappings& args() const { - return args_; - } + //--------------------------------------------------------------- + /** @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); + /** @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; } - return misses; - } - /** @brief returns number of processed command line arguments */ - arg_index parse_count() const noexcept { return eaten_; } + /** @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; + 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; + } - //--------------------------------------------------------------- - /** @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; - } + //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; + } - //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; } - return false; - } - - //--------------------------------------------------------------- - bool try_match_blocked(parser&& parse, const arg_string& arg) - { - const auto nold = int(parse.args_.size()); + //--------------------------------------------------------------- + bool try_match_blocked(parser &&parse, const arg_string &arg) + { + const auto nold = int(parse.args_.size()); - parse.pos_.ignore_blocking(true); + parse.pos_.ignore_blocking(true); - if(!parse.try_match(arg)) return false; + 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; + for(auto i = parse.args_.begin() + nold; i != parse.args_.end(); ++i) + { + args_.push_back(*i); + args_.back().blocked_ = true; + } + return 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; - for(const auto& m : matches) add_match(m); - return true; - } - - //----------------------------------------------------- - /** @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 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 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 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 + //----------------------------------------------------- + /** @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); - - if(!match) return false; + 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; - 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()) + while(!arg.empty()) { - arg.insert(0,prefix); - } + auto match = detail::longest_prefix_match(parse.pos_, arg, select); - parse.add_match(match); - matches.push_back(std::move(match)); - } + if(!match) return false; - if(matches.empty()) return false; + 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(!parse.missCand_.empty()) return false; - for(const auto& a : parse.args_) if(a.any_error()) return false; + parse.add_match(match); + matches.push_back(std::move(match)); + } - //replay matches onto *this - for(const auto& m : matches) add_match(m); - return true; - } + if(!arg.empty() || matches.empty()) 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(); - if(accept(g)) return true; - return false; + 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; } - auto pos = start; - while(pos) { - if(pos->is_group() && pos->as_group().joinable()) { - const auto& g = pos->as_group(); + //----------------------------------------------------- + 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; - pos.next_sibling(); + return false; } - else { - ++pos; + + 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; + } } + 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); + //--------------------------------------------------------------- + void add_match(const match_t &match) + { + const auto &pos = match.pos(); + if(!pos || !pos->is_param()) return; - 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)); + pos_.next_after_match(pos); - add_miss_candidates_after(pos); - clean_miss_candidates_for(pos.base()); - discard_alternative_miss_candidates(pos.base()); + 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; + //----------------------------------------------------- + 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; + //----------------------------------------------------- + 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; } - } - //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; - } - } 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; }); - - if(i != args_.rend()) return i->repeat() + 1; - return 0; - } + //----------------------------------------------------- + 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; + } + } + } + } - //--------------------------------------------------------------- - const group* root_; - scoped_dfs_traverser pos_; - arg_index index_; - arg_index eaten_; - arg_mappings args_; - miss_candidates missCand_; - bool blocked_; -}; + //--------------------------------------------------------------- + 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_; + }; -/*************************************************************************//** + /*************************************************************************/ /** * * @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(); } - -private: - //----------------------------------------------------- - arg_mappings arg2param_; - missing_events missing_; -}; - - - + iterator end() const noexcept + { + return arg2param_.end(); + } -namespace detail { -namespace { + private: + //----------------------------------------------------- + arg_mappings arg2param_; + missing_events missing_; + }; -/*************************************************************************//** + 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) -{ - //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) == '-')) + void sanitize_args(arg_list &args) { - --fstDigit; - } + //e.g. {"-o12", ".34"} -> {"-o", "12.34"} - //prepend digits from previous arg - i->insert(begin(*i), fstDigit, end(prv)); + if(args.empty()) return; - //erase digits in previous arg - prv.erase(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; + } + //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& 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()); - } - } - - for(auto m : res.missing()) { - if(m.param()) m.param()->notify_missing(m.after_index()); - } -} + void execute_actions(const parsing_result &res) + { + for(const auto &m : res) + { + if(m.param()) + { + const auto ¶m = *(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()); + } + } + 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); - - execute_actions(result); - - return result; -} - -} //anonymous namespace -} // namespace detail + 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); + return result; + } + } //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); -} - - -/*************************************************************************//** - * - * @brief parses the standard array of command line arguments; omits argv[0] - * - *****************************************************************************/ -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); -} + 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) -{ - return parse(argc, (const char **)argv, cli, offset); -} - - - - + 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); + } -/*************************************************************************//** + /*************************************************************************/ /** * * @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_; } - - /** @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; - } + 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_; + } -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 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; + } + 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 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 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 that no documentation text must exceed; + /** @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; * (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_; } + 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& param_separator(const string& sep) { - paramSep_ = sep; - return *this; - } - const string& param_separator() const noexcept { return paramSep_; } + /** @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 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 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 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_; } - - /** @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 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 maximum number of flags per parameter to be printed + /** @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 * 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& paragraph_spacing(int lines) { - paragraphSpc_ = lines > 0 ? lines : 0; - return *this; - } - int paragraph_spacing() const noexcept { return paragraphSpc_; } + doc_formatting ¶graph_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_; - } + 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; + }; -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 { - -/*************************************************************************//** + 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} - {} - - - //--------------------------------------------------------------- - const OStream& base() const noexcept { return os_; } - OStream& base() noexcept { return os_; } + 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 } + {} - bool good() const { return os_.good(); } + //--------------------------------------------------------------- + const OStream &base() const noexcept + { + return os_; + } + OStream &base() noexcept + { + return os_; + } + 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_; } + //--------------------------------------------------------------- + /** @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 determines the rightmost border of the text body */ - formatting_ostream& last_column(int c) { - lastCol_ = c < 0 ? 0 : c; - return *this; - } + /** @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 last_column() const noexcept + { + return lastCol_; + } - int text_width() const noexcept { - return lastCol_ - firstCol_; - } + int text_width() const noexcept + { + return lastCol_ - firstCol_; + } - /** @brief additional indentation for the 2nd, 3rd, ... line of + /** @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& paragraph_spacing(int lines) { - paragraphSpacing_ = lines; - return *this; - } - int paragraph_spacing() const noexcept { - return paragraphSpacing_; - } + /** @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 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_; - } - - /** @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_; - } - - - //--------------------------------------------------------------- - /* @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_; - } + formatting_ostream &min_paragraph_lines_for_spacing(int lines) + { + paragraphSpacingThreshold_ = lines; + return *this; + } + int min_paragraph_lines_for_spacing() const noexcept + { + return paragraphSpacingThreshold_; + } - /* @brief go to new line, and start a new paragraph */ - void wrap_hard(int times = 1) { - if(times < 1) return; + /** @brief if set to true, newline characters will be ignored */ + formatting_ostream &ignore_newline_chars(bool yes) + { + ignoreInputNls_ = yes; + return *this; + } - if(paragraph_spacing() > 0 && - paragraph_lines() >= min_paragraph_lines_for_spacing()) - { - times = paragraph_spacing() + 1; - } + bool ignore_newline_chars() const noexcept + { + return ignoreInputNls_; + } - 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 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_; + } - //--------------------------------------------------------------- - 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_; - } + /* @brief go to new line, and start a new paragraph */ + void wrap_hard(int times = 1) + { + if(times < 1) return; - int current_column() const noexcept { - return curCol_; - } + if(paragraph_spacing() > 0 && paragraph_lines() >= min_paragraph_lines_for_spacing()) + { + times = paragraph_spacing() + 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_; - } + 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; + } + //--------------------------------------------------------------- + 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_; + } - //--------------------------------------------------------------- - template - friend formatting_ostream& - operator << (formatting_ostream& os, const T& x) { - os.write(x); - return os; - } + int current_column() const noexcept + { + return curCol_; + } - void flush() { - os_.flush(); - } + 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_; + } + //--------------------------------------------------------------- + template + friend formatting_ostream &operator<<(formatting_ostream &os, const T &x) + { + os.write(x); + return os; + } -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 flush() + { + os_.flush(); + } - void fix_indent() { - if(left_of_text_area()) { - const auto fst = current_line_begin(); - write_spaces(fst - curCol_); - curCol_ = fst; - } - } + 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_); + } - template - bool only_whitespace(Iter first, Iter last) const { - return last == std::find_if_not(first, last, - [](char_type c) { return txt::isspace(c); }); - } + void fix_indent() + { + if(left_of_text_area()) + { + const auto fst = current_line_begin(); + write_spaces(fst - curCol_); + curCol_ = fst; + } + } - /** @brief write any object */ - template - void write(const T& x) { - std::ostringstream ss; - ss << x; - write(std::move(ss).str()); - } + 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 a stringstream */ - void write(const std::ostringstream& s) { - write(s.str()); - } + /** @brief write any object */ + template + void write(const T &x) + { + std::ostringstream ss; + ss << x; + write(std::move(ss).str()); + } - /** @brief write a string */ - void write(const string_type& s) { - write(s.begin(), s.end()); - } + /** @brief write a stringstream */ + void write(const std::ostringstream &s) + { + write(s.str()); + } - /** @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); + /** @brief write a string */ + void write(const string_type &s) + { + write(s.begin(), s.end()); } - } - 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 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 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_; - } - } + /** @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; - OStream& os_; - int curCol_; - int firstCol_; - int lastCol_; - int hangingIndent_; - int paragraphSpacing_; - int paragraphSpacingThreshold_; - int curBlankLines_; - int curParagraphLines_; - int totalNonBlankLines_; - bool ignoreInputNls_; -}; + 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 !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 -{ -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)) + class usage_lines { - if(!prefix_.empty()) prefix_ += ' '; - } - - 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_; - } - - 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(); - } - + public: + using string = doc_string; -private: - using stream_t = detail::formatting_ostream<>; - const group& cli_; - doc_formatting fmt_; - string prefix_; - bool ommitOutermostSurrounders_ = false; + 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) + {} - //----------------------------------------------------- - 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; + usage_lines &ommit_outermost_group_surrounders(bool yes) + { + ommitOutermostSurrounders_ = yes; + return *this; + } + bool ommit_outermost_group_surrounders() const + { + return ommitOutermostSurrounders_; + } - bool is_singleton() const noexcept { - return linestart && pos.is_last_in_path(); + template + inline friend OStream &operator<<(OStream &os, const usage_lines &p) + { + p.write(os); + return os; } - bool is_alternative() const noexcept { - return pos.parent().exclusive(); + + 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; + + //----------------------------------------------------- + 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()); - - auto hindent = int(prefix_.size()); - if(fos.first_column() + hindent >= int(0.4 * fos.text_width())) { - hindent = fmt_.indent_size(); - } - fos.hanging_indent(hindent); + template + void write(OStream &os) const + { + detail::formatting_ostream fos(os); + fos.first_column(fmt_.first_column()); + fos.last_column(fmt_.last_column()); - fos.paragraph_spacing(fmt_.paragraph_spacing()); - fos.min_paragraph_lines_for_spacing(2); - fos.ignore_newline_chars(fmt_.ignore_newline_chars()); + auto hindent = int(prefix_.size()); + if(fos.first_column() + hindent >= int(0.4 * fos.text_width())) + { + hindent = fmt_.indent_size(); + } + fos.hanging_indent(hindent); - context cur; - cur.pos = cli_.begin_dfs(); - cur.linestart = true; - cur.level = cur.pos.level(); - cur.outermost = &cli_; + fos.paragraph_spacing(fmt_.paragraph_spacing()); + fos.min_paragraph_lines_for_spacing(2); + fos.ignore_newline_chars(fmt_.ignore_newline_chars()); - write(fos, cur, prefix_); - } + context cur; + cur.pos = cli_.begin_dfs(); + cur.linestart = true; + cur.level = cur.pos.level(); + cur.outermost = &cli_; + 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; - - std::ostringstream buf; - if(cur.linestart) buf << prefix; - const auto initPos = buf.tellp(); + template + void write(OStream &os, context cur, string prefix) const + { + if(!cur.pos) return; - cur.level = cur.pos.level(); + std::ostringstream buf; + if(cur.linestart) buf << prefix; + const auto initPos = buf.tellp(); - 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); + cur.level = cur.pos.level(); - 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) { + 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 { - buf << param_label(cur.pos->as_param(), cur); - ++cur.pos; + else + { + //don't visit siblings of starter node + cur.pos.skip_siblings(); } check_end_group(buf, cur); - } while(cur.pos); - os << buf.str(); - } + 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(); + } - /***************************************************************//** + /***************************************************************/ /** * * @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; - - 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(); - }); + 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; - 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'); + 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'; } - ++cur.line; - os << '\n'; } + cur.pos.next_sibling(); //do not descend into members } - 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.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(); + 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(); } - 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(); + 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 ""; } - 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(); - return lbl; - } + if(showopt) lbl += fmt_.optional_postfix(); + if(showrep) lbl += fmt_.repeat_postfix(); + 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 ""; - - const bool showOpt = g.all_optional() && - !(ommitOutermostSurrounders_ && cur.outermost == &g); + if(!flagsonly) return ""; - auto pfx = g.common_flag_prefix(); - if(pfx.empty()) return ""; + const bool showOpt = g.all_optional() && !(ommitOutermostSurrounders_ && cur.outermost == &g); - 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; - }); + auto pfx = g.common_flag_prefix(); + if(pfx.empty()) return ""; - if(allSingleChar) { + 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; - for(const auto& p : g) { + 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; + }); - return ""; - } + 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; + std::pair group_surrounders(const group &group, const context &cur) const + { + string prefix; + string postfix; - const bool isOutermost = &group == cur.outermost; - if(isOutermost && ommitOutermostSurrounders_) - return {string{}, string{}}; + const bool isOutermost = &group == cur.outermost; + if(isOutermost && ommitOutermostSurrounders_) return { string{}, string{} }; - if(group.exclusive()) { - if(group.all_optional()) { - prefix = fmt_.optional_prefix(); + 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(); - 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(); - } - - 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(); - } + 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 -{ -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)} + class documentation { - //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, const param_filter& filter) : - documentation{cli, doc_formatting{}, - [filter](const parameter& p) { return filter(p); }} - {} + 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()); + } - template - inline friend OStream& operator << (OStream& os, const documentation& p) { - p.write(os); - return os; - } + documentation(const group &cli, filter_function filter) + : documentation{ cli, doc_formatting{}, std::move(filter) } + {} - string str() const { - std::ostringstream os; - write(os); - return os.str(); - } + 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; + } -private: - using dfs_traverser = group::depth_first_traverser; + string str() const + { + std::ostringstream os; + write(os); + return os.str(); + } - 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; - - //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(); }); + template + void print_doc(detail::formatting_ostream &os, const group &cli, int indentLvl = 0) const + { + if(cli.empty()) return; - 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); + //if group itself doesn't have docstring + if(cli.doc().empty()) + { + for(const auto &p : cli) + { + print_doc(os, p, indentLvl); } } - 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(); }); + + 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()); + 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()); + 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()); + } + 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& param, const doc_formatting& fmt) - { - doc_string lbl; + static doc_string param_label(const parameter ¶m, 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(); + 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(); } - lbl += fmt.label_postfix(); - } - - if(param.repeatable()) lbl += fmt.repeat_postfix(); - - return lbl; - } - -}; - + if(param.repeatable()) lbl += fmt.repeat_postfix(); + return lbl; + } + }; -/*************************************************************************//** + /*************************************************************************/ /** * * @brief stores strings for man page sections * *****************************************************************************/ -class man_page -{ -public: - //--------------------------------------------------------------- - using string = doc_string; - - //--------------------------------------------------------------- - /** @brief man page section */ - class section { + class man_page + { public: + //--------------------------------------------------------------- using string = doc_string; - 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_; } - - private: - string title_; - string content_; - }; - -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; - - - //--------------------------------------------------------------- - 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; - } + //--------------------------------------------------------------- + /** @brief man page section */ + class section + { + public: + using string = doc_string; + section(string stitle, string scontent) + : title_{ std::move(stitle) } + , content_{ std::move(scontent) } + {} - //--------------------------------------------------------------- - const section& operator [] (size_type index) const noexcept { - return sections_[index]; - } + const string &title() const noexcept + { + return title_; + } + const string &content() const noexcept + { + return content_; + } - //--------------------------------------------------------------- - size_type size() const noexcept { return sections_.size(); } + private: + string title_; + string content_; + }; - bool empty() const noexcept { return sections_.empty(); } + 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; - //--------------------------------------------------------------- - const_iterator begin() const noexcept { return sections_.begin(); } - const_iterator end() const noexcept { return sections_.end(); } + //--------------------------------------------------------------- + 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]; + } - //--------------------------------------------------------------- - 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_; - } + //--------------------------------------------------------------- + size_type size() const noexcept + { + return sections_.size(); + } - 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_; } + bool empty() const noexcept + { + return sections_.empty(); + } + //--------------------------------------------------------------- + const_iterator begin() const noexcept + { + return sections_.begin(); + } + const_iterator end() const noexcept + { + return sections_.end(); + } -private: - int sectionSpc_ = 1; - section_store sections_; - string progName_; -}; + //--------------------------------------------------------------- + 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_; + } + 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.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; -} - - + 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; + } -/*************************************************************************//** + /*************************************************************************/ /** * * @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& section : 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 §ion : 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 group&) -{ - return ""; -} + 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 pattern& p) -{ - return p.is_group() ? doc_label(p.as_group()) : doc_label(p.as_param()); -} + 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()); + } -/*************************************************************************//** + /*************************************************************************/ /** * * @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 group& g, int level = 0); + 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); -/*************************************************************************//** + /*************************************************************************/ /** * * @brief prints group or parameter; uses indentation * *****************************************************************************/ -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()); - } -} - + 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()); + } + } -/*************************************************************************//** + /*************************************************************************/ /** * * @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 d57182fd..5cf74237 100644 --- a/source/Applications/Advanced/HandEyeCalibration/HandEyeCalibration/HandEyeCalibration.cpp +++ b/source/Applications/Advanced/HandEyeCalibration/HandEyeCalibration/HandEyeCalibration.cpp @@ -29,7 +29,7 @@ namespace CommandType enterCommand() { - std::cout << "Enter command, p (to add robot pose) or c (to perform calibration): "; + std::cout << "Enter command, p (to add robot pose) or c (to perform calibration):"; const auto command = getInput(); if(command == "P" || command == "p") @@ -63,99 +63,47 @@ namespace Zivid::Frame assistedCapture(Zivid::Camera &camera) { - const auto parameters = Zivid::CaptureAssistant::SuggestSettingsParameters{ + const auto suggestSettingsParameters = Zivid::CaptureAssistant::SuggestSettingsParameters{ Zivid::CaptureAssistant::SuggestSettingsParameters::AmbientLightFrequency::none, Zivid::CaptureAssistant::SuggestSettingsParameters::MaxCaptureTime{ std::chrono::milliseconds{ 800 } } }; - const auto settings = Zivid::CaptureAssistant::suggestSettings(camera, parameters); + const auto settings = Zivid::CaptureAssistant::suggestSettings(camera, suggestSettingsParameters); return camera.capture(settings); } - std::string markersToString(const std::vector &markerIds) - { - std::ostringstream oss; - for(const auto &id : markerIds) - { - oss << id << " "; - } - return oss.str(); - } - - void handleAddPose( - size_t ¤tPoseId, - std::vector &handEyeInput, - Zivid::Camera &camera, - const std::string &calibrationObject) + Zivid::Calibration::HandEyeOutput performCalibration( + const std::vector &handEyeInput) { - const auto robotPose = enterRobotPose(currentPoseId); - - std::cout << "Detecting calibration object in point cloud" << std::endl; - if(calibrationObject == "c") - { - const auto frame = Zivid::Calibration::captureCalibrationBoard(camera); - const auto detectionResult = Zivid::Calibration::detectCalibrationBoard(frame); - - if(detectionResult.valid()) - { - std::cout << "Calibration board detected " << std::endl; - handEyeInput.emplace_back(robotPose, detectionResult); - currentPoseId++; - } - else - { - std::cout - << "Failed to detect calibration board, ensure that the entire board is in the view of the camera" - << std::endl; - } - } - else if(calibrationObject == "m") + while(true) { - const auto frame = assistedCapture(camera); - - auto markerDictionary = Zivid::Calibration::MarkerDictionary::aruco4x4_50; - std::vector markerIds = { 1, 2, 3 }; - - std::cout << "Detecting arUco marker IDs " << markersToString(markerIds) << "from the dictionary " - << markerDictionary << std::endl; - auto detectionResult = Zivid::Calibration::detectMarkers(frame, markerIds, markerDictionary); - - if(detectionResult.valid()) + std::cout << "Enter type of calibration, eth (for eye-to-hand) or eih (for eye-in-hand): "; + const auto calibrationType = getInput(); + if(calibrationType == "eth" || calibrationType == "ETH") { - std::cout << "ArUco marker(s) detected: " << detectionResult.detectedMarkers().size() << std::endl; - handEyeInput.emplace_back(robotPose, detectionResult); - currentPoseId++; + std::cout << "Performing eye-to-hand calibration" << std::endl; + return Zivid::Calibration::calibrateEyeToHand(handEyeInput); } - else + if(calibrationType == "eih" || calibrationType == "EIH") { - std::cout - << "Failed to detect any ArUco markers, ensure that at least one ArUco marker is in the view of the camera" - << std::endl; + std::cout << "Performing eye-in-hand calibration" << std::endl; + return Zivid::Calibration::calibrateEyeInHand(handEyeInput); } + std::cout << "Entered uknown method" << std::endl; } } +} // namespace - std::vector readHandEyeInputs(Zivid::Camera &camera) +int main() +{ + try { - size_t currentPoseId{ 0 }; - bool calibrate{ false }; - - std::string calibrationObject; - while(true) - { - std::cout - << "Enter calibration object you are using, m (for ArUco marker(s)) or c (for Zivid checkerboard): " - << std::endl; - calibrationObject = getInput(); - if(calibrationObject == "m" || calibrationObject == "c") - { - break; - } - } + Zivid::Application zivid; - std::cout << "Zivid primarily operates with a (4x4) transformation matrix. To convert" << std::endl; - std::cout << "from axis-angle, rotation vector, roll-pitch-yaw, or quaternion, check out" << std::endl; - std::cout << "our PoseConversions sample." << std::endl; + std::cout << "Connecting to camera" << std::endl; + auto camera{ zivid.connectCamera() }; + size_t currentPoseId{ 0 }; + bool calibrate{ false }; std::vector handEyeInput; do { @@ -165,7 +113,25 @@ namespace { try { - handleAddPose(currentPoseId, handEyeInput, camera, calibrationObject); + const auto robotPose = enterRobotPose(currentPoseId); + + const auto frame = assistedCapture(camera); + + std::cout << "Detecting checkerboard in point cloud" << std::endl; + const auto detectionResult = Zivid::Calibration::detectFeaturePoints(frame.pointCloud()); + + if(detectionResult.valid()) + { + std::cout << "Calibration board detected " << std::endl; + handEyeInput.emplace_back(Zivid::Calibration::HandEyeInput{ robotPose, detectionResult }); + currentPoseId++; + } + else + { + std::cout + << "Failed to detect calibration board, ensure that the entire board is in the view of the camera" + << std::endl; + } } catch(const std::exception &e) { @@ -186,52 +152,9 @@ namespace } } } while(!calibrate); - return handEyeInput; - } - - Zivid::Calibration::HandEyeOutput performCalibration( - const std::vector &handEyeInput) - { - while(true) - { - std::cout << "Enter type of calibration, eth (for eye-to-hand) or eih (for eye-in-hand): "; - const auto calibrationType = getInput(); - if(calibrationType == "eth" || calibrationType == "ETH") - { - std::cout << "Performing eye-to-hand calibration with " << handEyeInput.size() << " dataset pairs" - << std::endl; - std::cout << "The resulting transform is the camera pose in robot base frame" << std::endl; - return Zivid::Calibration::calibrateEyeToHand(handEyeInput); - } - if(calibrationType == "eih" || calibrationType == "EIH") - { - std::cout << "Performing eye-in-hand calibration with " << handEyeInput.size() << " dataset pairs" - << std::endl; - std::cout << "The resulting transform is the camera pose in flange (end-effector) frame" << std::endl; - return Zivid::Calibration::calibrateEyeInHand(handEyeInput); - } - std::cout << "Entered uknown method" << std::endl; - } - } -} // namespace - -int main() -{ - try - { - Zivid::Application zivid; - - std::cout << "Connecting to camera" << std::endl; - auto camera{ zivid.connectCamera() }; - - const auto handEyeInput{ readHandEyeInputs(camera) }; const auto calibrationResult{ performCalibration(handEyeInput) }; - std::cout << "Zivid primarily operates with a (4x4) transformation matrix. To convert" << std::endl; - std::cout << "to axis-angle, rotation vector, roll-pitch-yaw, or quaternion, check out" << std::endl; - std::cout << "our PoseConversions sample." << std::endl; - if(calibrationResult.valid()) { std::cout << "Hand-Eye calibration OK\n" diff --git a/source/Applications/Advanced/HandEyeCalibration/UtilizeHandEyeCalibration/UtilizeHandEyeCalibration.cpp b/source/Applications/Advanced/HandEyeCalibration/UtilizeHandEyeCalibration/UtilizeHandEyeCalibration.cpp index ade136ad..2b40c35d 100644 --- a/source/Applications/Advanced/HandEyeCalibration/UtilizeHandEyeCalibration/UtilizeHandEyeCalibration.cpp +++ b/source/Applications/Advanced/HandEyeCalibration/UtilizeHandEyeCalibration/UtilizeHandEyeCalibration.cpp @@ -1,25 +1,25 @@ /* -Transform single data point or entire point cloud from camera to robot base reference frame using Hand-Eye calibration +Transform single data point or entire point cloud from camera frame to robot base frame using Hand-Eye calibration matrix. This example shows how to utilize the result of Hand-Eye calibration to transform either (picking) point coordinates -or the entire point cloud from the camera to the robot base reference frame. +or the entire point cloud from the camera frame to the robot base frame. For both Eye-To-Hand and Eye-In-Hand, there is a Zivid gem placed approx. 500 mm away from the robot base (see below). -The (picking) point is the Zivid gem centroid, defined as image coordinates in the camera reference frame and hard-coded +The (picking) point is the Zivid gem centroid, defined as image coordinates in the camera frame and hard-coded in this code example. Open the ZDF files in Zivid Studio to inspect the gem's 2D and corresponding 3D coordinates. Eye-To-Hand - ZDF file: ZividGemEyeToHand.zdf - 2D image coordinates: (1035,255) - Corresponding 3D coordinates: (37.77 -145.92 1227.1) -- Corresponding 3D coordinates (robot base reference frame): (-12.4 514.37 -21.79) +- Corresponding 3D coordinates (robot base frame): (-12.4 514.37 -21.79) Eye-In-Hand: - ZDF file: ZividGemEyeInHand.zdf - 2D image coordinates: (1460,755) -- Corresponding 3D coordinates (camera reference frame): (83.95 28.84 305.7) -- Corresponding 3D coordinates (robot base reference frame): (531.03 -5.44 164.6) +- Corresponding 3D coordinates (camera frame): (83.95 28.84 305.7) +- Corresponding 3D coordinates (robot base frame): (531.03 -5.44 164.6) For verification, check that the Zivid gem centroid 3D coordinates are the same as above after the transformation. @@ -139,14 +139,14 @@ int main() { fileName = "/ZividGemEyeToHand.zdf"; - // The (picking) point is defined as image coordinates in camera reference frame. It is hard-coded - // for the ZividGemEyeToHand.zdf (1035,255) X: 37.77 Y: -145.92 Z: 1227.1 + // The (picking) point is defined as image coordinates in camera frame. It is hard-coded for the + // ZividGemEyeToHand.zdf (1035,255) X: 37.77 Y: -145.92 Z: 1227.1 imageCoordinateX = 1035; imageCoordinateY = 255; const auto eyeToHandTransformFile = "/EyeToHandTransform.yaml"; - std::cout << "Reading camera pose in robot base reference frame (result of eye-to-hand calibration" + std::cout << "Reading camera pose in robot base frame (result of eye-to-hand calibration" << std::endl; transformBaseToCamera.load(std::string(ZIVID_SAMPLE_DATA_DIR) + eyeToHandTransformFile); @@ -157,25 +157,24 @@ int main() { fileName = "/ZividGemEyeInHand.zdf"; - // The (picking) point is defined as image coordinates in camera reference frame. It is hard-coded - // for the ZividGemEyeInHand.zdf (1460,755) X: 83.95 Y: 28.84 Z: 305.7 + // The (picking) point is defined as image coordinates in camera frame. It is hard-coded for the + // ZividGemEyeInHand.zdf (1460,755) X: 83.95 Y: 28.84 Z: 305.7 imageCoordinateX = 1357; imageCoordinateY = 666; const auto eyeInHandTransformFile = "/EyeInHandTransform.yaml"; const auto robotTransformFile = "/RobotTransform.yaml"; - std::cout - << "Reading camera pose in end-effector reference frame (result of eye-in-hand calibration)" - << std::endl; + std::cout << "Reading camera pose in end-effector frame (result of eye-in-hand calibration)" + << std::endl; Zivid::Matrix4x4 transformEndEffectorToCamera( std::string(ZIVID_SAMPLE_DATA_DIR) + eyeInHandTransformFile); - std::cout << "Reading end-effector pose in robot base reference frame" << std::endl; + std::cout << "Reading end-effector pose in robot base frame" << std::endl; Zivid::Matrix4x4 transformBaseToEndEffector( std::string(ZIVID_SAMPLE_DATA_DIR) + robotTransformFile); - std::cout << "Computing camera pose in robot base reference frame" << std::endl; + std::cout << "Computing camera pose in robot base frame" << std::endl; transformBaseToCamera = eigenToZivid( zividToEigen(transformBaseToEndEffector) * zividToEigen(transformEndEffectorToCamera)); @@ -191,7 +190,7 @@ int main() } const auto dataFile = std::string(ZIVID_SAMPLE_DATA_DIR) + fileName; - std::cout << "Reading point cloud from file: " << dataFile << std::endl; + std::cout << "Reading ZDF frame from file: " << dataFile << std::endl; const auto frame = Zivid::Frame(dataFile); auto pointCloud = frame.pointCloud(); @@ -212,16 +211,16 @@ int main() xyz(imageCoordinateY, imageCoordinateX).z, xyz(imageCoordinateY, imageCoordinateX).w); - std::cout << "Point coordinates in camera reference frame: " << pointInCameraFrame.x() << " " + std::cout << "Point coordinates in camera frame: " << pointInCameraFrame.x() << " " << pointInCameraFrame.y() << " " << pointInCameraFrame.z() << std::endl; // Converting to Eigen matrix for easier computation const Eigen::Affine3f transformBaseToCameraEigen = zividToEigen(transformBaseToCamera); - std::cout << "Transforming (picking) point from camera to robot base reference frame" << std::endl; + std::cout << "Transforming (picking) point from camera to robot base frame" << std::endl; const Eigen::Vector4f pointInBaseFrame = transformBaseToCameraEigen * pointInCameraFrame; - std::cout << "Point coordinates in robot base reference frame: " << pointInBaseFrame.x() << " " + std::cout << "Point coordinates in robot base frame: " << pointInBaseFrame.x() << " " << pointInBaseFrame.y() << " " << pointInBaseFrame.z() << std::endl; loopContinue = false; @@ -234,7 +233,7 @@ int main() pointCloud.transform(transformBaseToCamera); const auto saveFile = "ZividGemTransformed.zdf"; - std::cout << "Saving point cloud to file: " << saveFile << std::endl; + std::cout << "Saving frame to file: " << saveFile << std::endl; frame.save(saveFile); loopContinue = false; diff --git a/source/Applications/Advanced/MultiCamera/MultiCameraCalibration/MultiCameraCalibration.cpp b/source/Applications/Advanced/MultiCamera/MultiCameraCalibration/MultiCameraCalibration.cpp index 12085d82..4b636eed 100644 --- a/source/Applications/Advanced/MultiCamera/MultiCameraCalibration/MultiCameraCalibration.cpp +++ b/source/Applications/Advanced/MultiCamera/MultiCameraCalibration/MultiCameraCalibration.cpp @@ -4,7 +4,6 @@ Use captures of a calibration object to generate transformation matrices to a si #include -#include #include #include #include @@ -20,26 +19,6 @@ namespace const auto settings = Zivid::CaptureAssistant::suggestSettings(camera, parameters); return camera.capture(settings); } - - std::vector connectToAllAvailableCameras(const std::vector &cameras) - { - std::vector connectedCameras; - for(auto camera : cameras) - { - if(camera.state().status() == Zivid::CameraState::Status::available) - { - std::cout << "Connecting to camera: " << camera.info().serialNumber() << std::endl; - camera.connect(); - connectedCameras.push_back(camera); - } - else - { - std::cout << "Camera " << camera.info().serialNumber() << "is not available. " - << "Camera status: " << camera.state().status() << std::endl; - } - } - return connectedCameras; - } } // namespace int main(int argc, char **argv) @@ -67,24 +46,27 @@ int main(int argc, char **argv) std::cout << "Finding cameras" << std::endl; auto cameras = zivid.cameras(); - std::cout << "Number of cameras found: " << cameras.size() << std::endl; - - auto connectedCameras = connectToAllAvailableCameras(cameras); - if(connectedCameras.size() < 2) + if(cameras.size() < 2) { throw std::runtime_error("At least two cameras need to be connected"); } - std::cout << "Number of connected cameras: " << connectedCameras.size() << std::endl; + std::cout << "Number of cameras found: " << cameras.size() << std::endl; + for(auto &camera : cameras) + { + std::cout << "Connecting to camera: " << camera.info().serialNumber() << std::endl; + camera.connect(); + } auto detectionResults = std::vector(); auto serialNumbers = std::vector(); - for(auto &camera : connectedCameras) + + for(auto &camera : cameras) { const auto serial = camera.info().serialNumber().toString(); 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::detectCalibrationBoard(frame); + const auto detectionResult = Zivid::Calibration::detectFeaturePoints(frame.pointCloud()); 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 2b688f48..0c8b6dd6 100644 --- a/source/Applications/Advanced/MultiCamera/MultiCameraCalibrationFromZDF/MultiCameraCalibrationFromZDF.cpp +++ b/source/Applications/Advanced/MultiCamera/MultiCameraCalibrationFromZDF/MultiCameraCalibrationFromZDF.cpp @@ -4,7 +4,6 @@ Use captures of a calibration object to generate transformation matrices to a si #include -#include #include #include #include @@ -47,7 +46,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::detectCalibrationBoard(frame); + const auto detectionResult = Zivid::Calibration::detectFeaturePoints(frame.pointCloud()); if(detectionResult) { detectionResults.push_back(detectionResult); diff --git a/source/Applications/Advanced/MultiCamera/MultiCameraTutorial.md b/source/Applications/Advanced/MultiCamera/MultiCameraTutorial.md index a3fde4ec..67f66823 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::detectCalibrationBoard][detect_calibration_board-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::detectFeaturePoints][detect_feature_points-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_calibration_board-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_feature_points-url]). ```cpp -const auto detectionResult = Zivid::Calibration::detectCalibrationBoard(frame); +const auto detectionResult = Zivid::Calibration::detectFeaturePoints(frame.pointCloud()); ``` 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_calibration_board-url]: MultiCameraCalibration/MultiCameraCalibration.cpp#L69 +[detect_feature_points-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/MultiCamera/StitchByTransformation/StitchByTransformation.cpp b/source/Applications/Advanced/MultiCamera/StitchByTransformation/StitchByTransformation.cpp index 213e4edf..1429752a 100644 --- a/source/Applications/Advanced/MultiCamera/StitchByTransformation/StitchByTransformation.cpp +++ b/source/Applications/Advanced/MultiCamera/StitchByTransformation/StitchByTransformation.cpp @@ -1,7 +1,5 @@ /* Use transformation matrices from Multi-Camera calibration to transform point clouds into single coordinate frame, from connected cameras. - -Note: This example uses experimental SDK features, which may be modified, moved, or deleted in the future without notice. */ #include @@ -68,26 +66,6 @@ namespace return transformsMappedToCameras; } - std::vector connectToAllAvailableCameras(const std::vector &cameras) - { - std::vector connectedCameras; - for(auto camera : cameras) - { - if(camera.state().status() == Zivid::CameraState::Status::available) - { - std::cout << "Connecting to camera: " << camera.info().serialNumber() << std::endl; - camera.connect(); - connectedCameras.push_back(camera); - } - else - { - std::cout << "Camera " << camera.info().serialNumber() << "is not available. " - << "Camera status: " << camera.state().status() << std::endl; - } - } - return connectedCameras; - } - const auto rgbList = std::array{ 0xFFB300, // Vivid Yellow 0x803E75, // Strong Purple @@ -138,9 +116,14 @@ int main(int argc, char **argv) auto cameras = zivid.cameras(); std::cout << "Number of cameras found: " << cameras.size() << std::endl; - auto connectedCameras = connectToAllAvailableCameras(cameras); + for(auto &camera : cameras) + { + std::cout << "Connecting camera: " << camera.info().serialNumber() << std::endl; + camera.connect(); + } + const auto transformsMappedToCameras = - getTransformationMatricesFromYAML(transformationMatricesfileList, connectedCameras); + getTransformationMatricesFromYAML(transformationMatricesfileList, cameras); // Capture from all cameras auto frames = std::vector(); diff --git a/source/Applications/Advanced/ProjectAndFindMarker/ProjectAndFindMarker.cpp b/source/Applications/Advanced/ProjectAndFindMarker/ProjectAndFindMarker.cpp index fe7045c6..a6c978c2 100644 --- a/source/Applications/Advanced/ProjectAndFindMarker/ProjectAndFindMarker.cpp +++ b/source/Applications/Advanced/ProjectAndFindMarker/ProjectAndFindMarker.cpp @@ -8,7 +8,7 @@ allowing us to find the 3D coordinates relative to the camera. #include #include #include -#include +#include #include @@ -172,11 +172,12 @@ namespace Zivid::CaptureAssistant::SuggestSettingsParameters::MaxCaptureTime{ std::chrono::milliseconds{ 1200 } } }; auto settings = Zivid::CaptureAssistant::suggestSettings(camera, suggestSettingsParameters); - const auto processing = - Zivid::Settings::Processing{ Zivid::Settings::Processing::Filters::Reflection::Removal::Enabled::yes, - Zivid::Settings::Processing::Filters::Reflection::Removal::Mode::global, - Zivid::Settings::Processing::Filters::Smoothing::Gaussian::Enabled::yes, - Zivid::Settings::Processing::Filters::Smoothing::Gaussian::Sigma{ 1.5 } }; + const auto processing = Zivid::Settings::Processing{ + Zivid::Settings::Processing::Filters::Reflection::Removal::Enabled::yes, + Zivid::Settings::Processing::Filters::Reflection::Removal::Experimental::Mode::global, + Zivid::Settings::Processing::Filters::Smoothing::Gaussian::Enabled::yes, + Zivid::Settings::Processing::Filters::Smoothing::Gaussian::Sigma{ 1.5 } + }; settings.set(processing); settings.set(Zivid::Settings::Sampling::Pixel::all); @@ -202,7 +203,7 @@ int main() auto camera = zivid.connectCamera(); std::cout << "Retrieving the projector resolution that the camera supports" << std::endl; - const auto projectorResolution = Zivid::Projection::projectorResolution(camera); + const auto projectorResolution = Zivid::Experimental::Projection::projectorResolution(camera); std::cout << "Creating a projector image with resolution: " << projectorResolution.toString() << std::endl; const int cvMatArrayType = CV_8UC4; @@ -227,7 +228,7 @@ int main() projectorImage.save(projectorImageFile); std::cout << "Displaying the projector image" << std::endl; - auto projectedImageHandle = Zivid::Projection::showImage(camera, projectorImage); + auto projectedImageHandle = Zivid::Experimental::Projection::showImage(camera, projectorImage); std::cout << "Press enter to continue..."; std::cin.get(); diff --git a/source/Applications/Advanced/ROIBoxViaArucoMarker/ROIBoxViaArucoMarker.cpp b/source/Applications/Advanced/ROIBoxViaArucoMarker/ROIBoxViaArucoMarker.cpp index f4b30733..30b5cbf1 100644 --- a/source/Applications/Advanced/ROIBoxViaArucoMarker/ROIBoxViaArucoMarker.cpp +++ b/source/Applications/Advanced/ROIBoxViaArucoMarker/ROIBoxViaArucoMarker.cpp @@ -1,9 +1,11 @@ /* 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). */ +#include #include #include @@ -12,6 +14,8 @@ The ZFC file for this sample can be downloaded from https://support.zivid.com/en #include #include +#include + #include #include #include @@ -22,6 +26,180 @@ The ZFC file for this sample can be downloaded from https://support.zivid.com/en 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()); @@ -128,25 +306,31 @@ int main() const Zivid::PointXYZ pointBInArUcoFrame = roiBoxLowerLeftCornerInArUcoFrame; std::cout << "Configuring ArUco marker" << std::endl; - const auto markerDictionary = Zivid::Calibration::MarkerDictionary::aruco4x4_50; - std::vector markerId = { 1 }; + 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); - std::cout << "Detecting ArUco marker" << std::endl; - const auto detectionResult = Zivid::Calibration::detectMarkers(originalFrame, markerId, markerDictionary); + std::cout << "Detecting ArUco Marker" << std::endl; + cv::aruco::detectMarkers(grayImage, markerDictionary, markerCorners, markerIds, detectorParameters); - if(!detectionResult.valid()) + if(markerIds.empty()) { std::cout << "No ArUco markers detected" << std::endl; return EXIT_FAILURE; } std::cout << "Estimating pose of detected ArUco marker" << std::endl; - const auto transformCameraToMarker = detectionResult.detectedMarkers()[0].pose().toMatrix(); + const auto transformMarkerToCamera = estimateArUcoMarkerPose(pointCloud, markerCorners[0]); std::cout << "Transforming the ROI base frame points to the camera frame" << std::endl; const auto roiPointsInCameraFrame = transformPoints( std::vector{ pointOInArUcoFrame, pointAInArUcoFrame, pointBInArUcoFrame }, - transformCameraToMarker); + transformMarkerToCamera); 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 0c743fda..eef0088d 100644 --- a/source/Applications/Advanced/ROIBoxViaCheckerboard/ROIBoxViaCheckerboard.cpp +++ b/source/Applications/Advanced/ROIBoxViaCheckerboard/ROIBoxViaCheckerboard.cpp @@ -4,7 +4,7 @@ Filter the point cloud based on a ROI box given relative to the Zivid Calibratio The ZFC file for this sample can be downloaded from https://support.zivid.com/en/latest/api-reference/samples/sample-data.html. */ -#include +#include #include #include @@ -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::detectCalibrationBoard(originalFrame); - const auto transformCameraToCheckerboard = detectionResult.pose().toMatrix(); + const auto detectionResult = Zivid::Calibration::detectFeaturePoints(pointCloud); + const auto transformCheckerboardToCamera = 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 }, - transformCameraToCheckerboard); + transformCheckerboardToCamera); 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 4f3fa578..1f05251e 100644 --- a/source/Applications/Advanced/ReprojectPoints/ReprojectPoints.cpp +++ b/source/Applications/Advanced/ReprojectPoints/ReprojectPoints.cpp @@ -7,11 +7,10 @@ in the camera frame. These points are then passed to the API to get the correspo The projector pixel coordinates are then used to draw markers at the correct locations before displaying the image using the projector. */ - #include -#include #include -#include +#include +#include #include @@ -80,7 +79,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::Calibration::detectCalibrationBoard(camera); + const auto detectionResult = Zivid::Experimental::Calibration::detectFeaturePoints(camera); if(!detectionResult.valid()) { throw std::runtime_error("Calibration board not detected!"); @@ -98,10 +97,10 @@ int main() const auto pointsInCameraFrame = transformGridToCalibrationBoard(grid, transformCameraToCheckerboard); std::cout << "Getting projector pixels (2D) corresponding to points (3D) in the camera frame" << std::endl; - const auto projectorPixels = Zivid::Projection::pixelsFrom3DPoints(camera, pointsInCameraFrame); + const auto projectorPixels = Zivid::Experimental::Projection::pixelsFrom3DPoints(camera, pointsInCameraFrame); std::cout << "Retrieving the projector resolution that the camera supports" << std::endl; - const auto projectorResolution = Zivid::Projection::projectorResolution(camera); + const auto projectorResolution = Zivid::Experimental::Projection::projectorResolution(camera); std::cout << "Creating a blank projector image with resolution: " << projectorResolution.toString() << std::endl; @@ -129,7 +128,7 @@ int main() { // A Local Scope to handle the projected image lifetime - auto projectedImageHandle = Zivid::Projection::showImage(camera, projectorImage); + auto projectedImageHandle = Zivid::Experimental::Projection::showImage(camera, projectorImage); const Zivid::Settings2D settings2D{ Zivid::Settings2D::Acquisitions{ Zivid::Settings2D::Acquisition{ Zivid::Settings2D::Acquisition::Brightness{ 0.0 }, diff --git a/source/Applications/Advanced/TransformPointCloudViaArucoMarker/TransformPointCloudViaArucoMarker.cpp b/source/Applications/Advanced/TransformPointCloudViaArucoMarker/TransformPointCloudViaArucoMarker.cpp index c9457b78..24d36b17 100644 --- a/source/Applications/Advanced/TransformPointCloudViaArucoMarker/TransformPointCloudViaArucoMarker.cpp +++ b/source/Applications/Advanced/TransformPointCloudViaArucoMarker/TransformPointCloudViaArucoMarker.cpp @@ -1,12 +1,15 @@ /* -Transform a point cloud from camera to ArUco marker coordinate frame by estimating the marker's pose from the point cloud. +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. -The ZDF file for this sample can be found under the main instructions for Zivid samples. +This sample depends on ArUco libraries in OpenCV with extra modules (https://github.com/opencv/opencv_contrib). */ +#include #include #include +#include #include #include #include @@ -28,41 +31,187 @@ struct cv::traits::Type namespace { - cv::Mat pointCloudToColorBGRA(const Zivid::PointCloud &pointCloud) + struct Line2d { - auto bgra = cv::Mat(pointCloud.height(), pointCloud.width(), CV_8UC4); - pointCloud.copyData(&(*bgra.begin())); + float slope; + float intercept; + }; - return bgra; + float clamp(const float n, const float lower, const float upper) + { + return std::max(lower, std::min(n, upper)); } - void displayBGR(const cv::Mat &bgr, const std::string &bgrName) + Line2d fitLine(const cv::Point2f &point1, const cv::Point2f &point2) { - cv::namedWindow(bgrName, cv::WINDOW_AUTOSIZE); - cv::imshow(bgrName, bgr); - cv::waitKey(0); + // 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; } - cv::Mat drawDetectedMarker( - const cv::Mat &bgraImage, - const Zivid::Calibration::DetectionResultFiducialMarkers &detectionResult) + std::vector estimate3DMarkerPoints( + const Zivid::PointCloud &pointCloud, + const std::vector &markerPoints2D) { - const auto detectedMarkerCorners = detectionResult.detectedMarkers()[0].cornersInPixelCoordinates(); - std::vector markerCorners; - markerCorners.reserve(detectedMarkerCorners.size()); - for(const auto &corner : detectedMarkerCorners) + if(markerPoints2D.empty()) { - markerCorners.emplace_back(corner.x, corner.y); + return {}; } - cv::Mat bgr; - cv::cvtColor(bgraImage, bgr, cv::COLOR_BGRA2BGR); - for(size_t i = 0; i < markerCorners.size(); ++i) + 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++) { - cv::line(bgr, markerCorners[i], markerCorners[(i + 1) % markerCorners.size()], cv::Scalar(0, 255, 0), 2); + for(size_t col = 0; col < rotationMatrix.cols; col++) + { + transformMatrix(row, col) = rotationMatrix(row, col); + } } - return bgr; + 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) + { + rotationMatrix.at(i, 0) = u[i]; + rotationMatrix.at(i, 1) = v[i]; + rotationMatrix.at(i, 2) = unitNormal[i]; + } + + 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); } } // namespace @@ -78,44 +227,46 @@ 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 = Zivid::Calibration::MarkerDictionary::aruco4x4_50; - std::vector markerId = { 1 }; + 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 << "Detecting ArUco marker" << std::endl; - const auto detectionResult = Zivid::Calibration::detectMarkers(frame, markerId, markerDictionary); + 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"); - if(!detectionResult.valid()) + if(markerIds.empty()) { std::cout << "No ArUco markers detected" << std::endl; - return EXIT_FAILURE; + return EXIT_SUCCESS; } - 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 transformCameraToMarker = detectionResult.detectedMarkers()[0].pose().toMatrix(); - std::cout << "ArUco marker pose in camera frame:" << std::endl; - std::cout << transformCameraToMarker << std::endl; + const auto transformMarkerToCamera = estimateArUcoMarkerPose(pointCloud, markerCorners[0]); 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); + const auto transformCameraToMarker = transformMarkerToCamera.inverse(); + std::cout << "ArUco marker pose in camera frame:" << std::endl; + std::cout << transformCameraToMarker << std::endl; std::cout << "Transforming point cloud from camera frame to ArUco marker frame" << std::endl; - pointCloud.transform(transformMarkerToCamera); + pointCloud.transform(transformCameraToMarker); 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 2c08b135..d8782dd0 100644 --- a/source/Applications/Advanced/TransformPointCloudViaCheckerboard/TransformPointCloudViaCheckerboard.cpp +++ b/source/Applications/Advanced/TransformPointCloudViaCheckerboard/TransformPointCloudViaCheckerboard.cpp @@ -2,11 +2,8 @@ Transform a point cloud from camera to checkerboard (Zivid Calibration Board) coordinate frame by getting checkerboard pose from the API. The ZDF file for this sample can be found under the main instructions for Zivid samples. - -Note: This example uses experimental SDK features, which may be modified, moved, or deleted in the future without notice. */ -#include #include #include @@ -118,6 +115,7 @@ 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()); @@ -170,7 +168,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::detectCalibrationBoard(frame); + const auto detectionResult = Zivid::Calibration::detectFeaturePoints(frame.pointCloud()); 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/Basic/Visualization/CaptureFromFileCameraVis3D/CaptureFromFileCameraVis3D.cpp b/source/Applications/Basic/Visualization/CaptureFromFileCameraVis3D/CaptureFromFileCameraVis3D.cpp index cd1591d9..cb3fdbf3 100644 --- a/source/Applications/Basic/Visualization/CaptureFromFileCameraVis3D/CaptureFromFileCameraVis3D.cpp +++ b/source/Applications/Basic/Visualization/CaptureFromFileCameraVis3D/CaptureFromFileCameraVis3D.cpp @@ -41,14 +41,15 @@ int main(int argc, char **argv) auto camera = zivid.createFileCamera(fileCamera); std::cout << "Configuring settings" << std::endl; - const auto settings = Zivid::Settings{ Zivid::Settings::Acquisitions{ Zivid::Settings::Acquisition{} }, - Zivid::Settings::Processing::Filters::Smoothing::Gaussian::Enabled::yes, - Zivid::Settings::Processing::Filters::Smoothing::Gaussian::Sigma{ 1.5 }, - Zivid::Settings::Processing::Filters::Reflection::Removal::Enabled::yes, - Zivid::Settings::Processing::Filters::Reflection::Removal::Mode::global, - Zivid::Settings::Processing::Color::Balance::Red{ 1 }, - Zivid::Settings::Processing::Color::Balance::Green{ 1 }, - Zivid::Settings::Processing::Color::Balance::Blue{ 1 } }; + const auto settings = + Zivid::Settings{ Zivid::Settings::Acquisitions{ Zivid::Settings::Acquisition{} }, + Zivid::Settings::Processing::Filters::Smoothing::Gaussian::Enabled::yes, + Zivid::Settings::Processing::Filters::Smoothing::Gaussian::Sigma{ 1.5 }, + Zivid::Settings::Processing::Filters::Reflection::Removal::Enabled::yes, + Zivid::Settings::Processing::Filters::Reflection::Removal::Experimental::Mode::global, + Zivid::Settings::Processing::Color::Balance::Red{ 1 }, + Zivid::Settings::Processing::Color::Balance::Green{ 1 }, + Zivid::Settings::Processing::Color::Balance::Blue{ 1 } }; std::cout << "Capturing frame" << std::endl; const auto frame = camera.capture(settings); diff --git a/source/Applications/Basic/Visualization/ProjectImageStartAndStop/ProjectImageStartAndStop.cpp b/source/Applications/Basic/Visualization/ProjectImageStartAndStop/ProjectImageStartAndStop.cpp index 75aa63fc..5ced654b 100644 --- a/source/Applications/Basic/Visualization/ProjectImageStartAndStop/ProjectImageStartAndStop.cpp +++ b/source/Applications/Basic/Visualization/ProjectImageStartAndStop/ProjectImageStartAndStop.cpp @@ -9,7 +9,7 @@ How to stop the image projection is demonstrated in three different ways: */ #include -#include +#include #include #include @@ -29,7 +29,7 @@ namespace void projecting(Zivid::Camera &camera, const Zivid::Image &projectorImageFunctionScope) { - auto projectedImageHandle = Zivid::Projection::showImage(camera, projectorImageFunctionScope); + auto projectedImageHandle = Zivid::Experimental::Projection::showImage(camera, projectorImageFunctionScope); std::cout << "Press enter to stop projecting by leaving a function scope" << std::endl; std::cin.get(); @@ -48,13 +48,13 @@ int main() auto camera = zivid.connectCamera(); std::cout << "Retrieving the projector resolution that the camera supports" << std::endl; - const auto projectorResolution = Zivid::Projection::projectorResolution(camera); + const auto projectorResolution = Zivid::Experimental::Projection::projectorResolution(camera); const auto redColor = Zivid::ColorBGRA(0, 0, 255, 255); auto projectorImage = createProjectorImage(projectorResolution, redColor); - auto projectedImageHandle = Zivid::Projection::showImage(camera, projectorImage); + auto projectedImageHandle = Zivid::Experimental::Projection::showImage(camera, projectorImage); std::cout << "Press enter to stop projecting using the \".stop()\" function." << std::endl; std::cin.get(); @@ -63,7 +63,7 @@ int main() const auto greenColor = Zivid::ColorBGRA(0, 255, 0, 255); { projectorImage = createProjectorImage(projectorResolution, greenColor); - projectedImageHandle = Zivid::Projection::showImage(camera, projectorImage); + projectedImageHandle = Zivid::Experimental::Projection::showImage(camera, projectorImage); std::cout << "Press enter to stop projecting by leaving a local scope" << std::endl; std::cin.get(); @@ -75,7 +75,7 @@ int main() const auto zividPinkColor = Zivid::ColorBGRA(114, 52, 237, 255); projectorImage = createProjectorImage(projectorResolution, zividPinkColor); - projectedImageHandle = Zivid::Projection::showImage(camera, projectorImage); + projectedImageHandle = Zivid::Experimental::Projection::showImage(camera, projectorImage); std::cout << "Press enter to stop projecting by performing a 3D capture" << std::endl; std::cin.get(); diff --git a/source/Applications/Basic/Visualization/ReadAndProjectImage/ReadAndProjectImage.cpp b/source/Applications/Basic/Visualization/ReadAndProjectImage/ReadAndProjectImage.cpp index ccd50c68..48636618 100644 --- a/source/Applications/Basic/Visualization/ReadAndProjectImage/ReadAndProjectImage.cpp +++ b/source/Applications/Basic/Visualization/ReadAndProjectImage/ReadAndProjectImage.cpp @@ -6,7 +6,8 @@ The image for this sample can be found under the main instructions for Zivid sam #include #include -#include +#include +#include #include @@ -57,7 +58,8 @@ namespace // intentional fallthrough case Zivid::CameraInfo::Model::ValueType::zividOnePlusMedium: // intentional fallthrough - case Zivid::CameraInfo::Model::ValueType::zividOnePlusLarge: break; + case Zivid::CameraInfo::Model::ValueType::zividOnePlusLarge: + throw std::invalid_argument("Projecting images is not supported for Zivid One+ cameras"); } throw std::invalid_argument("Invalid camera model"); } @@ -80,7 +82,7 @@ int main() std::cout << "input image size: " << inputImage.size() << std::endl; std::cout << "Retrieving the projector resolution that the camera supports" << std::endl; - const auto projectorResolution = Zivid::Projection::projectorResolution(camera); + const auto projectorResolution = Zivid::Experimental::Projection::projectorResolution(camera); std::cout << "Resizing input image to fit projector resolution: " << projectorResolution.toString() << std::endl; @@ -94,7 +96,7 @@ int main() { // A Local Scope to handle the projected image lifetime - auto projectedImageHandle = Zivid::Projection::showImage(camera, projectorImage); + auto projectedImageHandle = Zivid::Experimental::Projection::showImage(camera, projectorImage); const Zivid::Settings2D settings2D{ Zivid::Settings2D::Acquisitions{ Zivid::Settings2D::Acquisition{ Zivid::Settings2D::Acquisition::Brightness{ 0.0 }, @@ -121,7 +123,8 @@ int main() { // A Local Scope to handle the projected image lifetime - auto projectedImageHandle = Zivid::Projection::showImage(camera, projectorImageForGivenCamera); + auto projectedImageHandle = + Zivid::Experimental::Projection::showImage(camera, projectorImageForGivenCamera); std::cout << "Press enter to stop projecting..." << std::endl; std::cin.get(); diff --git a/source/Applications/PointCloudTutorial.md b/source/Applications/PointCloudTutorial.md index 290cba79..e5cca355 100644 --- a/source/Applications/PointCloudTutorial.md +++ b/source/Applications/PointCloudTutorial.md @@ -33,12 +33,10 @@ data. Tip: -> If you prefer watching a video, our webinar [Getting your point cloud -> ready for your -> application](https://www.zivid.com/webinars-page?wchannelid=ffpqbqc7sg&wmediaid=h66zph71vo) -> covers the Point Cloud Tutorial. - -**Prerequisites** +If you prefer watching a video, our webinar [Getting your point cloud +ready for your +application](https://www.zivid.com/webinars-page?wchannelid=ffpqbqc7sg&wmediaid=h66zph71vo) +covers the Point Cloud Tutorial. .. rubric:: Prerequisites - Install [Zivid Software](https://support.zivid.com/latest//getting-started/software-installation.html). @@ -103,13 +101,11 @@ from GPU memory. Note: -`Zivid::Camera::capture()` method returns at some moment in time after -the camera completes capturing raw images. The handle from -`Zivid::Frame::pointCloud()` is available instantly. However, the actual -point cloud data becomes available only after the processing on the GPU -is finished. Any calls to data-copy functions (section below) will block -and wait for processing to finish before proceeding with the requested -copy operation. +The handle from `Zivid::Frame::pointCloud()` is available instantly. +However, the actual point cloud data becomes available only after the +processing on the GPU is finished. Any calls to data-copy functions +(section below) will block and wait for processing to finish before +proceeding with the requested copy operation. For detailed explanation, see [Point Cloud Capture Process](https://support.zivid.com/latest/academy/camera/point-cloud-capture-process.html). @@ -220,7 +216,7 @@ frame or, e.g., [scale the point cloud by transforming it from mm to m](https://support.zivid.com/latest//academy/applications/transform/transform-millimeters-to-meters.html). ([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/Applications/Advanced/HandEyeCalibration/UtilizeHandEyeCalibration/UtilizeHandEyeCalibration.cpp#L233)) ``` sourceCode cpp pointCloud.transform(transformBaseToCamera); @@ -238,11 +234,10 @@ the point cloud. Note: -> [Monochrome -> Capture](https://support.zivid.com/latest/academy/camera/monochrome-capture.html) -> is a hardware-based subsample method that reduces the resolution of -> the point cloud during capture while also reducing the acquisition and -> capture time. +[Monochrome +Capture](https://support.zivid.com/latest/academy/camera/monochrome-capture.html) +is a hardware-based subsample method that reduces the resolution of the +point cloud during capture while also reducing the capture time. ----- diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index fb5aea3d..2cfe6e8c 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5...3.29 FATAL_ERROR) +cmake_minimum_required(VERSION 3.5 FATAL_ERROR) project(ZividCppSamples) @@ -12,11 +12,12 @@ include(CompilerOptions) set(CMAKE_MAP_IMPORTED_CONFIG_MINSIZEREL "MinSizeRel;Release;") set(CMAKE_MAP_IMPORTED_CONFIG_RELWITHDEBINFO "RelWithDebInfo;Release;") -set(ZIVID_VERSION 2.13.0) +set(ZIVID_VERSION 2.10.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 @@ -36,12 +37,10 @@ set(SAMPLES Camera/Advanced/AllocateMemoryForPointCloudData Camera/Advanced/CaptureHalconViaGenICam Camera/Advanced/CaptureHalconViaZivid - Camera/InfoUtilOther/AutomaticNetworkConfigurationForCameras Camera/InfoUtilOther/CameraUserData Camera/InfoUtilOther/CaptureWithDiagnostics Camera/InfoUtilOther/GetCameraIntrinsics Camera/InfoUtilOther/FirmwareUpdater - Camera/InfoUtilOther/NetworkConfiguration Camera/InfoUtilOther/CameraInfo Camera/InfoUtilOther/SettingsInfo Camera/InfoUtilOther/FrameInfo @@ -121,7 +120,6 @@ set(Visualization_DEPENDING ROIBoxViaCheckerboard ) set(Clipp_DEPENDING - AutomaticNetworkConfigurationForCameras CameraUserData MultiCameraCalibrationFromZDF StitchByTransformation @@ -139,6 +137,10 @@ set(Thread_DEPENDING MultiCameraCaptureSequentiallyWithInterleavedProcessing ZividBenchmark ) +set(ArUco_DEPENDING + TransformPointCloudViaArucoMarker + ROIBoxViaArucoMarker +) set(Halcon_DEPENDING CaptureHalconViaGenICam CaptureHalconViaZivid @@ -195,12 +197,29 @@ if(USE_OPENCV) highgui calib3d ) - 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(USE_ARUCO) + find_package( + OpenCV + 4.1.0 + COMPONENTS + ${OPENCV_COMMON_MODULES} + aruco ) + 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/Advanced/Capture2DAnd3D/Capture2DAnd3D.cpp b/source/Camera/Advanced/Capture2DAnd3D/Capture2DAnd3D.cpp index cbe0c04f..dba70faa 100644 --- a/source/Camera/Advanced/Capture2DAnd3D/Capture2DAnd3D.cpp +++ b/source/Camera/Advanced/Capture2DAnd3D/Capture2DAnd3D.cpp @@ -1,13 +1,11 @@ /* -Capture 2D and 3D separately with the Zivid camera. - -Capture separate 2D image with subsampling2x2. Then capture 3D with subsampling4x4 -and upsampling2x2 to match resolution of 2D. -Then use color from 2D when visualizing the 3D point cloud. +Capture 2D and then 3D using various capture strategies, optimizing for both 2D quality and 2D acquisition speed. */ +#include #include +#include #include #include #include @@ -20,7 +18,92 @@ Then use color from 2D when visualizing the 3D point cloud. namespace { - void displayPointCloud(const Zivid::PointCloud &pointCloud, const cv::Mat &bgra) + cv::Mat mapBGR(const Zivid::Experimental::PixelMapping &pixelMapping, const cv::Mat &fullResolutionBGR) + { + std::cout << "Pixel mapping: " << pixelMapping << std::endl; + cv::Mat mappedBGR( + fullResolutionBGR.rows / pixelMapping.rowStride(), + fullResolutionBGR.cols / pixelMapping.colStride(), + CV_8UC3); + std::cout << "Mapped width: " << mappedBGR.cols << ", height: " << mappedBGR.rows << std::endl; + for(size_t row = 0; row < static_cast(fullResolutionBGR.rows - pixelMapping.rowOffset()); + row += pixelMapping.rowStride()) + { + for(size_t col = 0; col < static_cast(fullResolutionBGR.cols - pixelMapping.colOffset()); + col += pixelMapping.colStride()) + { + mappedBGR.at(row / pixelMapping.rowStride(), col / pixelMapping.colStride()) = + fullResolutionBGR.at(row + pixelMapping.rowOffset(), col + pixelMapping.colOffset()); + } + } + return mappedBGR; + } + + Zivid::Settings::Sampling::Pixel stringToPixelSetting(const std::string &pixelsToSample) + { + const auto validValues = Zivid::Settings::Sampling::Pixel::validValues(); + for(const auto &value : validValues) + { + const auto pixelSetting = Zivid::Settings::Sampling::Pixel{ value }; + if(pixelSetting.toString() == pixelsToSample) + { + return pixelSetting; + } + } + + std::stringstream errorMsg; + errorMsg << "Invalid pixel value. Use one of:"; + for(const auto &value : validValues) + { + errorMsg << " " << Zivid::Settings::Sampling::Pixel{ value }.toString(); + } + throw std::runtime_error(errorMsg.str()); + } + + void displayBGR(const std::vector &bgrs, const std::vector &bgrNames) + { + if(bgrs.empty() || bgrNames.empty() || bgrs.size() != bgrNames.size()) + { + std::cerr << "Error: Invalid input data." << std::endl; + return; + } + + const int titleMargin = 60; + int totalRows = bgrs[0].rows; + const int separationMargin = 40; + int combinedWidth = 0; + for(const auto &bgr : bgrs) + { + totalRows = std::max(totalRows, bgr.rows); + combinedWidth += bgr.cols + separationMargin; + } + totalRows += titleMargin; + + cv::Mat combinedImage(totalRows, combinedWidth, bgrs[0].type()); + + int x_offset = 0; + for(size_t i = 0; i < bgrs.size(); ++i) + { + cv::Mat roi(combinedImage, cv::Rect(x_offset, titleMargin, bgrs[i].cols, bgrs[i].rows)); + bgrs[i].copyTo(roi); + + x_offset += bgrs[i].cols + separationMargin; + + cv::putText( + combinedImage, + bgrNames[i], + cv::Point(x_offset - bgrs[i].cols, 20), + cv::FONT_HERSHEY_SIMPLEX, + 0.5, + cv::Scalar(255, 255, 255), + 1, + cv::LINE_AA); + } + cv::imshow("Combined Image", combinedImage); + cv::waitKey(0); + } + + void displayPointCloud(const Zivid::PointCloud &pointCloud, const cv::Mat &mappedBGR) { const auto xyz = pointCloud.copyData(); @@ -31,12 +114,15 @@ namespace pclPointCloud.points.resize(xyz.size()); for(size_t i = 0; i < xyz.size(); ++i) { - pclPointCloud.points[i].x = xyz(i).x; // NOLINT(cppcoreguidelines-pro-type-union-access) - pclPointCloud.points[i].y = xyz(i).y; // NOLINT(cppcoreguidelines-pro-type-union-access) - pclPointCloud.points[i].z = xyz(i).z; // NOLINT(cppcoreguidelines-pro-type-union-access) - pclPointCloud.points[i].b = bgra.at(i)[0]; // NOLINT(cppcoreguidelines-pro-type-union-access) - pclPointCloud.points[i].g = bgra.at(i)[1]; // NOLINT(cppcoreguidelines-pro-type-union-access) - pclPointCloud.points[i].r = bgra.at(i)[2]; // NOLINT(cppcoreguidelines-pro-type-union-access) + pclPointCloud.points[i].x = xyz(i).x; // NOLINT(cppcoreguidelines-pro-type-union-access) + pclPointCloud.points[i].y = xyz(i).y; // NOLINT(cppcoreguidelines-pro-type-union-access) + pclPointCloud.points[i].z = xyz(i).z; // NOLINT(cppcoreguidelines-pro-type-union-access) + pclPointCloud.points[i].b = + mappedBGR.at(i)[0]; // NOLINT(cppcoreguidelines-pro-type-union-access) + pclPointCloud.points[i].g = + mappedBGR.at(i)[1]; // NOLINT(cppcoreguidelines-pro-type-union-access) + pclPointCloud.points[i].r = + mappedBGR.at(i)[2]; // NOLINT(cppcoreguidelines-pro-type-union-access) } auto viewer = pcl::visualization::PCLVisualizer("Viewer"); @@ -58,58 +144,99 @@ namespace } } // namespace -int main() +int main(int argc, char **argv) { try { + std::stringstream pixelSettingDoc; + pixelSettingDoc << "Select which pixels to sample. Valid options:"; + const auto validValues = Zivid::Settings::Sampling::Pixel::validValues(); + for(const auto &value : validValues) + { + pixelSettingDoc << " '" << Zivid::Settings::Sampling::Pixel{ value }.toString() << "'"; + } + + auto pixelsToSample = Zivid::Settings::Sampling::Pixel::all; + auto cli = + ((clipp::option("-p", "--pixels-to-sample") + & clipp::value("pixelsToSample") >> + [&](const std::string &value) { pixelsToSample = stringToPixelSetting(value); }) + % pixelSettingDoc.str()); + + if(!parse(argc, argv, cli)) + { + auto fmt = clipp::doc_formatting{}; + //.alternatives_min_split_size(1).surround_labels("\"", "\""); + std::cout << "SYNOPSIS:" << std::endl; + std::cout << clipp::usage_lines(cli, "Capture2DAnd3D", fmt) << std::endl; + std::cout << "OPTIONS:" << std::endl; + std::cout << clipp::documentation(cli) << std::endl; + throw std::runtime_error{ "Invalid usage" }; + } + Zivid::Application zivid; std::cout << "Connecting to camera" << std::endl; auto camera = zivid.connectCamera(); std::cout << "Configuring 2D settings" << std::endl; - const auto settings2D = Zivid::Settings2D{ - Zivid::Settings2D::Acquisitions{ Zivid::Settings2D::Acquisition{} }, - Zivid::Settings2D::Sampling::Pixel::blueSubsample2x2, - }; + const auto settings2D = + Zivid::Settings2D{ Zivid::Settings2D::Acquisitions{ Zivid::Settings2D::Acquisition{} } }; std::cout << "Configuring 3D settings" << std::endl; auto settings = Zivid::Settings{ - Zivid::Settings::Engine::phase, + Zivid::Settings::Experimental::Engine::phase, Zivid::Settings::Acquisitions{ Zivid::Settings::Acquisition{} }, - Zivid::Settings::Sampling::Pixel::blueSubsample4x4, - Zivid::Settings::Sampling::Color::disabled, - Zivid::Settings::Processing::Resampling::Mode::upsample2x2, + Zivid::Settings::Sampling::Pixel{ pixelsToSample }, + Zivid::Settings::Sampling::Color{ Zivid::Settings::Sampling::Color::disabled }, }; - if((camera.info().model() == Zivid::CameraInfo::Model::zividTwo) - || (camera.info().model() == Zivid::CameraInfo::Model::zividTwoL100)) + const auto cameraModel = camera.info().model().value(); + if(pixelsToSample == Zivid::Settings::Sampling::Pixel::all + && (cameraModel == Zivid::CameraInfo::Model::ValueType::zivid2PlusM130 + || cameraModel == Zivid::CameraInfo::Model::ValueType::zivid2PlusM60 + || cameraModel == Zivid::CameraInfo::Model::ValueType::zivid2PlusL110)) { - std::cout - << camera.info().modelName() - << " does not support 4x4 subsampling. This sample is written to show how combinations of Sampling::Pixel and Processing::Resampling::Mode." - << std::endl; - settings = settings.copyWith( - Zivid::Settings::Sampling::Pixel::blueSubsample2x2, - Zivid::Settings::Processing::Resampling::Mode::disabled); + // For 2+, we must lower Brightness from the default 2.5 to 2.2, when using `all` mode. + // This code can be removed by changing the Config.yml option 'Camera/Power/Limit'. + for(auto &a : settings.acquisitions()) + { + a.set(Zivid::Settings::Acquisition::Brightness{ 2.2 }); + } } std::cout << "Capturing 2D frame" << std::endl; const auto frame2D = camera.capture(settings2D); std::cout << "Getting BGRA image" << std::endl; const auto image = frame2D.imageBGRA(); - const cv::Mat bgra( + const cv::Mat fullResolutionBGRA( image.height(), image.width(), CV_8UC4, // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast) const_cast(static_cast(image.data()))); + cv::Mat fullResolutionBGR; + cv::cvtColor(fullResolutionBGRA, fullResolutionBGR, cv::COLOR_BGRA2BGR); + const auto pixelMapping = Zivid::Experimental::Calibration::pixelMapping(camera, settings); + const auto mappedBGR = (pixelsToSample.value() == Zivid::Settings::Sampling::Pixel::ValueType::all) + ? fullResolutionBGR + : mapBGR(pixelMapping, fullResolutionBGR); + if(pixelsToSample.value() == Zivid::Settings::Sampling::Pixel::ValueType::all) + { + displayBGR({ fullResolutionBGR }, { "Full resolution 2D" }); + } + else + { + std::ostringstream mappedTitle; + mappedTitle << pixelsToSample << " RGB from 2D capture"; + displayBGR({ fullResolutionBGR, mappedBGR }, { "Full resolution 2D", mappedTitle.str() }); + } std::cout << "Capturing frame" << std::endl; const auto frame = camera.capture(settings); const auto pointCloud = frame.pointCloud(); std::cout << "Visualizing point cloud" << std::endl; - displayPointCloud(pointCloud, bgra); + displayPointCloud(pointCloud, mappedBGR); } catch(const std::exception &e) { diff --git a/source/Camera/Advanced/CaptureHDRLoop/CaptureHDRLoop.cpp b/source/Camera/Advanced/CaptureHDRLoop/CaptureHDRLoop.cpp index 9b8b107a..51902df0 100644 --- a/source/Camera/Advanced/CaptureHDRLoop/CaptureHDRLoop.cpp +++ b/source/Camera/Advanced/CaptureHDRLoop/CaptureHDRLoop.cpp @@ -14,6 +14,10 @@ namespace std::string settingsFolder(const Zivid::Camera &camera) { const auto modelName = camera.info().modelName().value(); + if(modelName.find("Zivid One+") == 0) + { + return "zividOne"; + } if(modelName.find("Zivid 2+") == 0) { return "zivid2Plus"; diff --git a/source/Camera/Advanced/MultiCameraCaptureInParallel/MultiCameraCaptureInParallel.cpp b/source/Camera/Advanced/MultiCameraCaptureInParallel/MultiCameraCaptureInParallel.cpp index bcda17e2..aa56c6c5 100644 --- a/source/Camera/Advanced/MultiCameraCaptureInParallel/MultiCameraCaptureInParallel.cpp +++ b/source/Camera/Advanced/MultiCameraCaptureInParallel/MultiCameraCaptureInParallel.cpp @@ -40,27 +40,6 @@ namespace return data; } - - std::vector connectToAllAvailableCameras(const std::vector &cameras) - { - std::vector connectedCameras; - for(auto camera : cameras) - { - if(camera.state().status() == Zivid::CameraState::Status::available) - { - std::cout << "Connecting to camera: " << camera.info().serialNumber() << std::endl; - camera.connect(); - connectedCameras.push_back(camera); - } - else - { - std::cout << "Camera " << camera.info().serialNumber() << "is not available. " - << "Camera status: " << camera.state().status() << std::endl; - } - } - return connectedCameras; - } - } // namespace int main() @@ -73,11 +52,15 @@ int main() auto cameras = zivid.cameras(); std::cout << "Number of cameras found: " << cameras.size() << std::endl; - auto connectedCameras = connectToAllAvailableCameras(cameras); + for(auto &camera : cameras) + { + std::cout << "Connecting to camera: " << camera.info().serialNumber().value() << std::endl; + camera.connect(); + } std::vector> futureFrames; - for(auto &camera : connectedCameras) + for(auto &camera : cameras) { std::cout << "Starting to capture (in a separate thread) with camera: " << camera.info().serialNumber().value() << std::endl; @@ -86,9 +69,9 @@ int main() std::vector frames; - for(size_t i = 0; i < connectedCameras.size(); ++i) + for(size_t i = 0; i < cameras.size(); ++i) { - std::cout << "Waiting for camera " << connectedCameras[i].info().serialNumber() << " to finish capturing" + std::cout << "Waiting for camera " << cameras[i].info().serialNumber() << " to finish capturing" << std::endl; const auto frame = futureFrames[i].get(); frames.push_back(frame); diff --git a/source/Camera/Advanced/MultiCameraCaptureSequentially/MultiCameraCaptureSequentially.cpp b/source/Camera/Advanced/MultiCameraCaptureSequentially/MultiCameraCaptureSequentially.cpp index e8a7eecf..b6950b72 100644 --- a/source/Camera/Advanced/MultiCameraCaptureSequentially/MultiCameraCaptureSequentially.cpp +++ b/source/Camera/Advanced/MultiCameraCaptureSequentially/MultiCameraCaptureSequentially.cpp @@ -25,26 +25,6 @@ namespace return data; } - - std::vector connectToAllAvailableCameras(const std::vector &cameras) - { - std::vector connectedCameras; - for(auto camera : cameras) - { - if(camera.state().status() == Zivid::CameraState::Status::available) - { - std::cout << "Connecting to camera: " << camera.info().serialNumber() << std::endl; - camera.connect(); - connectedCameras.push_back(camera); - } - else - { - std::cout << "Camera " << camera.info().serialNumber() << "is not available. " - << "Camera status: " << camera.state().status() << std::endl; - } - } - return connectedCameras; - } } // namespace int main() @@ -57,11 +37,15 @@ int main() auto cameras = zivid.cameras(); std::cout << "Number of cameras found: " << cameras.size() << std::endl; - auto connectedCameras = connectToAllAvailableCameras(cameras); + for(auto &camera : cameras) + { + std::cout << "Connecting to camera: " << camera.info().serialNumber().value() << std::endl; + camera.connect(); + } std::vector frames; - for(auto &camera : connectedCameras) + for(auto &camera : cameras) { std::cout << "Capturing frame with camera: " << camera.info().serialNumber() << std::endl; const auto parameters = Zivid::CaptureAssistant::SuggestSettingsParameters{ diff --git a/source/Camera/Advanced/MultiCameraCaptureSequentiallyWithInterleavedProcessing/MultiCameraCaptureSequentiallyWithInterleavedProcessing.cpp b/source/Camera/Advanced/MultiCameraCaptureSequentiallyWithInterleavedProcessing/MultiCameraCaptureSequentiallyWithInterleavedProcessing.cpp index a167ad2e..f1ff83b2 100644 --- a/source/Camera/Advanced/MultiCameraCaptureSequentiallyWithInterleavedProcessing/MultiCameraCaptureSequentiallyWithInterleavedProcessing.cpp +++ b/source/Camera/Advanced/MultiCameraCaptureSequentiallyWithInterleavedProcessing/MultiCameraCaptureSequentiallyWithInterleavedProcessing.cpp @@ -25,26 +25,6 @@ namespace return data; } - - std::vector connectToAllAvailableCameras(const std::vector &cameras) - { - std::vector connectedCameras; - for(auto camera : cameras) - { - if(camera.state().status() == Zivid::CameraState::Status::available) - { - std::cout << "Connecting to camera: " << camera.info().serialNumber() << std::endl; - camera.connect(); - connectedCameras.push_back(camera); - } - else - { - std::cout << "Camera " << camera.info().serialNumber() << "is not available. " - << "Camera status: " << camera.state().status() << std::endl; - } - } - return connectedCameras; - } } // namespace int main() @@ -57,11 +37,15 @@ int main() auto cameras = zivid.cameras(); std::cout << "Number of cameras found: " << cameras.size() << std::endl; - auto connectedCameras = connectToAllAvailableCameras(cameras); + for(auto &camera : cameras) + { + std::cout << "Connecting to camera: " << camera.info().serialNumber().value() << std::endl; + camera.connect(); + } std::vector>> futureData; - for(auto &camera : connectedCameras) + for(auto &camera : cameras) { std::cout << "Capturing frame with camera: " << camera.info().serialNumber() << std::endl; const auto parameters = Zivid::CaptureAssistant::SuggestSettingsParameters{ diff --git a/source/Camera/Basic/CaptureAssistant/CaptureAssistant.cpp b/source/Camera/Basic/CaptureAssistant/CaptureAssistant.cpp index beea00e9..66661266 100644 --- a/source/Camera/Basic/CaptureAssistant/CaptureAssistant.cpp +++ b/source/Camera/Basic/CaptureAssistant/CaptureAssistant.cpp @@ -29,11 +29,12 @@ int main() std::cout << "Manually configuring processing settings (Capture Assistant only suggests acquisition settings)" << std::endl; - const auto processing = - Zivid::Settings::Processing{ Zivid::Settings::Processing::Filters::Reflection::Removal::Enabled::yes, - Zivid::Settings::Processing::Filters::Reflection::Removal::Mode::global, - Zivid::Settings::Processing::Filters::Smoothing::Gaussian::Enabled::yes, - Zivid::Settings::Processing::Filters::Smoothing::Gaussian::Sigma{ 1.5 } }; + const auto processing = Zivid::Settings::Processing{ + Zivid::Settings::Processing::Filters::Reflection::Removal::Enabled::yes, + Zivid::Settings::Processing::Filters::Reflection::Removal::Experimental::Mode::global, + Zivid::Settings::Processing::Filters::Smoothing::Gaussian::Enabled::yes, + Zivid::Settings::Processing::Filters::Smoothing::Gaussian::Sigma{ 1.5 } + }; settings.set(processing); std::cout << "Capturing frame" << std::endl; diff --git a/source/Camera/Basic/CaptureFromFileCamera/CaptureFromFileCamera.cpp b/source/Camera/Basic/CaptureFromFileCamera/CaptureFromFileCamera.cpp index 0ece8fd9..67fb5f67 100644 --- a/source/Camera/Basic/CaptureFromFileCamera/CaptureFromFileCamera.cpp +++ b/source/Camera/Basic/CaptureFromFileCamera/CaptureFromFileCamera.cpp @@ -40,14 +40,15 @@ int main(int argc, char **argv) auto camera = zivid.createFileCamera(fileCamera); std::cout << "Configuring settings" << std::endl; - const auto settings = Zivid::Settings{ Zivid::Settings::Acquisitions{ Zivid::Settings::Acquisition{} }, - Zivid::Settings::Processing::Filters::Smoothing::Gaussian::Enabled::yes, - Zivid::Settings::Processing::Filters::Smoothing::Gaussian::Sigma{ 1.5 }, - Zivid::Settings::Processing::Filters::Reflection::Removal::Enabled::yes, - Zivid::Settings::Processing::Filters::Reflection::Removal::Mode::global, - Zivid::Settings::Processing::Color::Balance::Red{ 1 }, - Zivid::Settings::Processing::Color::Balance::Green{ 1 }, - Zivid::Settings::Processing::Color::Balance::Blue{ 1 } }; + const auto settings = + Zivid::Settings{ Zivid::Settings::Acquisitions{ Zivid::Settings::Acquisition{} }, + Zivid::Settings::Processing::Filters::Smoothing::Gaussian::Enabled::yes, + Zivid::Settings::Processing::Filters::Smoothing::Gaussian::Sigma{ 1.5 }, + Zivid::Settings::Processing::Filters::Reflection::Removal::Enabled::yes, + Zivid::Settings::Processing::Filters::Reflection::Removal::Experimental::Mode::global, + Zivid::Settings::Processing::Color::Balance::Red{ 1 }, + Zivid::Settings::Processing::Color::Balance::Green{ 1 }, + Zivid::Settings::Processing::Color::Balance::Blue{ 1 } }; std::cout << "Capturing frame" << std::endl; const auto frame = camera.capture(settings); diff --git a/source/Camera/Basic/CaptureHDRCompleteSettings/CaptureHDRCompleteSettings.cpp b/source/Camera/Basic/CaptureHDRCompleteSettings/CaptureHDRCompleteSettings.cpp index 4f0ec7c7..e406ee37 100644 --- a/source/Camera/Basic/CaptureHDRCompleteSettings/CaptureHDRCompleteSettings.cpp +++ b/source/Camera/Basic/CaptureHDRCompleteSettings/CaptureHDRCompleteSettings.cpp @@ -27,7 +27,16 @@ namespace { case Zivid::CameraInfo::Model::ValueType::zividOnePlusSmall: case Zivid::CameraInfo::Model::ValueType::zividOnePlusMedium: - case Zivid::CameraInfo::Model::ValueType::zividOnePlusLarge: break; + case Zivid::CameraInfo::Model::ValueType::zividOnePlusLarge: + { + const std::vector apertures{ 8.0, 4.0, 1.4 }; + const std::vector gains{ 1.0, 1.0, 2.0 }; + const std::vector exposureTimes{ microseconds{ 6500 }, + microseconds{ 10000 }, + microseconds{ 40000 } }; + const std::vector brightnesses{ 1.8, 1.8, 1.8 }; + return { apertures, gains, exposureTimes, brightnesses }; + } case Zivid::CameraInfo::Model::ValueType::zividTwo: case Zivid::CameraInfo::Model::ValueType::zividTwoL100: { @@ -67,9 +76,9 @@ int main() std::cout << "Configuring settings for capture:" << std::endl; Zivid::Settings settings{ - Zivid::Settings::Engine::phase, + Zivid::Settings::Experimental::Engine::phase, Zivid::Settings::Sampling::Color::rgb, - Zivid::Settings::Sampling::Pixel::blueSubsample2x2, + Zivid::Settings::Sampling::Pixel::all, Zivid::Settings::RegionOfInterest::Box::Enabled::yes, Zivid::Settings::RegionOfInterest::Box::PointO{ 1000, 1000, 1000 }, Zivid::Settings::RegionOfInterest::Box::PointA{ 1000, -1000, 1000 }, @@ -86,7 +95,7 @@ int main() Zivid::Settings::Processing::Filters::Outlier::Removal::Enabled::yes, Zivid::Settings::Processing::Filters::Outlier::Removal::Threshold{ 5.0 }, Zivid::Settings::Processing::Filters::Reflection::Removal::Enabled::yes, - Zivid::Settings::Processing::Filters::Reflection::Removal::Mode::global, + Zivid::Settings::Processing::Filters::Reflection::Removal::Experimental::Mode::global, Zivid::Settings::Processing::Filters::Cluster::Removal::Enabled::yes, Zivid::Settings::Processing::Filters::Cluster::Removal::MaxNeighborDistance{ 10 }, Zivid::Settings::Processing::Filters::Cluster::Removal::MinArea{ 100 }, @@ -94,10 +103,9 @@ int main() Zivid::Settings::Processing::Filters::Experimental::ContrastDistortion::Correction::Strength{ 0.4 }, Zivid::Settings::Processing::Filters::Experimental::ContrastDistortion::Removal::Enabled::no, Zivid::Settings::Processing::Filters::Experimental::ContrastDistortion::Removal::Threshold{ 0.5 }, - Zivid::Settings::Processing::Filters::Hole::Repair::Enabled::yes, - Zivid::Settings::Processing::Filters::Hole::Repair::HoleSize{ 0.2 }, - Zivid::Settings::Processing::Filters::Hole::Repair::Strictness{ 1 }, - Zivid::Settings::Processing::Resampling::Mode::upsample2x2, + Zivid::Settings::Processing::Filters::Experimental::HoleFilling::Enabled::yes, + Zivid::Settings::Processing::Filters::Experimental::HoleFilling::HoleSize{ 0.2 }, + Zivid::Settings::Processing::Filters::Experimental::HoleFilling::Strictness{ 1 }, Zivid::Settings::Processing::Color::Balance::Red{ 1.0 }, Zivid::Settings::Processing::Color::Balance::Green{ 1.0 }, Zivid::Settings::Processing::Color::Balance::Blue{ 1.0 }, diff --git a/source/Camera/Basic/CaptureTutorial.md b/source/Camera/Basic/CaptureTutorial.md index 4cd06da3..2b123f27 100644 --- a/source/Camera/Basic/CaptureTutorial.md +++ b/source/Camera/Basic/CaptureTutorial.md @@ -14,7 +14,6 @@ tutorial see: [**Connect**](#Connect) | [**Configure**](#Configure) | [**Capture**](#Capture) | -[**Save**](#Save) | [**Multithreading**](#Multithreading) | [**Conclusion**](#Conclusion) @@ -34,12 +33,11 @@ MATLAB](https://github.com/zivid/zivid-matlab-samples/blob/master/source/Camera/ Tip: -> If you prefer watching a video, our webinar [Making 3D captures easy - -> A tour of Zivid Studio and Zivid -> SDK](https://www.zivid.com/webinars-page?wchannelid=ffpqbqc7sg&wmediaid=ce68dbjldk) -> covers the same content as the Capture Tutorial. - -**Prerequisites** +If you prefer watching a video, our webinar [Making 3D captures easy - A +tour of Zivid Studio and Zivid +SDK](https://www.zivid.com/webinars-page?wchannelid=ffpqbqc7sg&wmediaid=ce68dbjldk) +covers the same content as the Capture Tutorial. .. rubric:: +Prerequisites - Install [Zivid Software](https://support.zivid.com/latest//getting-started/software-installation.html). @@ -92,7 +90,7 @@ auto camera = zivid.connectCamera(Zivid::CameraInfo::SerialNumber{ "2020C0DE" }) Note: -> The serial number of your camera is shown in the Zivid Studio. +The serial number of your camera is shown in the Zivid Studio. ----- @@ -142,17 +140,18 @@ The acquisition settings should be initialized like shown below, but you are free to alter the processing settings. ([go to -source](https://github.com/zivid/zivid-cpp-samples/tree/master//source/Camera/Basic/CaptureFromFileCamera/CaptureFromFileCamera.cpp#L43-L50)) +source](https://github.com/zivid/zivid-cpp-samples/tree/master//source/Camera/Basic/CaptureFromFileCamera/CaptureFromFileCamera.cpp#L43-L51)) ``` sourceCode cpp -const auto settings = Zivid::Settings{ Zivid::Settings::Acquisitions{ Zivid::Settings::Acquisition{} }, - Zivid::Settings::Processing::Filters::Smoothing::Gaussian::Enabled::yes, - Zivid::Settings::Processing::Filters::Smoothing::Gaussian::Sigma{ 1.5 }, - Zivid::Settings::Processing::Filters::Reflection::Removal::Enabled::yes, - Zivid::Settings::Processing::Filters::Reflection::Removal::Mode::global, - Zivid::Settings::Processing::Color::Balance::Red{ 1 }, - Zivid::Settings::Processing::Color::Balance::Green{ 1 }, - Zivid::Settings::Processing::Color::Balance::Blue{ 1 } }; +const auto settings = + Zivid::Settings{ Zivid::Settings::Acquisitions{ Zivid::Settings::Acquisition{} }, + Zivid::Settings::Processing::Filters::Smoothing::Gaussian::Enabled::yes, + Zivid::Settings::Processing::Filters::Smoothing::Gaussian::Sigma{ 1.5 }, + Zivid::Settings::Processing::Filters::Reflection::Removal::Enabled::yes, + Zivid::Settings::Processing::Filters::Reflection::Removal::Experimental::Mode::global, + Zivid::Settings::Processing::Color::Balance::Red{ 1 }, + Zivid::Settings::Processing::Color::Balance::Green{ 1 }, + Zivid::Settings::Processing::Color::Balance::Blue{ 1 } }; ``` You can read more about the file camera option in [File @@ -211,8 +210,8 @@ There are only two parameters to configure with Capture Assistant: Another option is to configure settings manually. For more information about what each settings does, please see [Camera Settings](https://support.zivid.com/latest/reference-articles/camera-settings.html). -Then, the next step it's [Capturing High Quality Point -Clouds](https://support.zivid.com/latest/academy/camera/capturing-high-quality-point-clouds.html) +Note that Zivid 2 has a set of [standard +settings](https://support.zivid.com/latest//reference-articles/standard-acquisition-settings-zivid-two.html). #### Single Acquisition @@ -248,14 +247,14 @@ for(const auto aperture : { 11.31, 5.66, 2.83 }) Fully configured settings are demonstrated below. ([go to -source](https://github.com/zivid/zivid-cpp-samples/tree/master//source/Camera/Basic/CaptureHDRCompleteSettings/CaptureHDRCompleteSettings.cpp#L68-L132)) +source](https://github.com/zivid/zivid-cpp-samples/tree/master//source/Camera/Basic/CaptureHDRCompleteSettings/CaptureHDRCompleteSettings.cpp#L77-L140)) ``` sourceCode cpp std::cout << "Configuring settings for capture:" << std::endl; Zivid::Settings settings{ - Zivid::Settings::Engine::phase, + Zivid::Settings::Experimental::Engine::phase, Zivid::Settings::Sampling::Color::rgb, - Zivid::Settings::Sampling::Pixel::blueSubsample2x2, + Zivid::Settings::Sampling::Pixel::all, Zivid::Settings::RegionOfInterest::Box::Enabled::yes, Zivid::Settings::RegionOfInterest::Box::PointO{ 1000, 1000, 1000 }, Zivid::Settings::RegionOfInterest::Box::PointA{ 1000, -1000, 1000 }, @@ -272,7 +271,7 @@ Zivid::Settings settings{ Zivid::Settings::Processing::Filters::Outlier::Removal::Enabled::yes, Zivid::Settings::Processing::Filters::Outlier::Removal::Threshold{ 5.0 }, Zivid::Settings::Processing::Filters::Reflection::Removal::Enabled::yes, - Zivid::Settings::Processing::Filters::Reflection::Removal::Mode::global, + Zivid::Settings::Processing::Filters::Reflection::Removal::Experimental::Mode::global, Zivid::Settings::Processing::Filters::Cluster::Removal::Enabled::yes, Zivid::Settings::Processing::Filters::Cluster::Removal::MaxNeighborDistance{ 10 }, Zivid::Settings::Processing::Filters::Cluster::Removal::MinArea{ 100 }, @@ -280,10 +279,9 @@ Zivid::Settings settings{ Zivid::Settings::Processing::Filters::Experimental::ContrastDistortion::Correction::Strength{ 0.4 }, Zivid::Settings::Processing::Filters::Experimental::ContrastDistortion::Removal::Enabled::no, Zivid::Settings::Processing::Filters::Experimental::ContrastDistortion::Removal::Threshold{ 0.5 }, - Zivid::Settings::Processing::Filters::Hole::Repair::Enabled::yes, - Zivid::Settings::Processing::Filters::Hole::Repair::HoleSize{ 0.2 }, - Zivid::Settings::Processing::Filters::Hole::Repair::Strictness{ 1 }, - Zivid::Settings::Processing::Resampling::Mode::upsample2x2, + Zivid::Settings::Processing::Filters::Experimental::HoleFilling::Enabled::yes, + Zivid::Settings::Processing::Filters::Experimental::HoleFilling::HoleSize{ 0.2 }, + Zivid::Settings::Processing::Filters::Experimental::HoleFilling::Strictness{ 1 }, Zivid::Settings::Processing::Color::Balance::Red{ 1.0 }, Zivid::Settings::Processing::Color::Balance::Green{ 1.0 }, Zivid::Settings::Processing::Color::Balance::Blue{ 1.0 }, @@ -347,7 +345,7 @@ Check out for recommended .yml files tuned for your application. ([go to -source](https://github.com/zivid/zivid-cpp-samples/tree/master//source/Camera/Basic/CaptureHDRCompleteSettings/CaptureHDRCompleteSettings.cpp#L144-L149)) +source](https://github.com/zivid/zivid-cpp-samples/tree/master//source/Camera/Basic/CaptureHDRCompleteSettings/CaptureHDRCompleteSettings.cpp#L152-L157)) ``` sourceCode cpp const auto settingsFile = "Settings.yml"; @@ -360,7 +358,7 @@ const auto settingsFromFile = Zivid::Settings(settingsFile); You can also save settings to .yml file. ([go to -source](https://github.com/zivid/zivid-cpp-samples/tree/master//source/Camera/Basic/CaptureHDRCompleteSettings/CaptureHDRCompleteSettings.cpp#L144-L146)) +source](https://github.com/zivid/zivid-cpp-samples/tree/master//source/Camera/Basic/CaptureHDRCompleteSettings/CaptureHDRCompleteSettings.cpp#L152-L154)) ``` sourceCode cpp const auto settingsFile = "Settings.yml"; @@ -417,7 +415,16 @@ source](https://github.com/zivid/zivid-cpp-samples/tree/master//source/Camera/Ba const auto frame2D = camera.capture(settings2D); ``` -## Save +----- + +Caution\!: + +> Zivid One+ camera has a time penalty when changing the capture mode +> (2D and 3D) if the 2D capture settings use brightness \> 0. + +You can read more about it in [2D and 3D switching +limitation](https://support.zivid.com/latest//support/2d-3d-switching-limitation.html). +Save ---- We can now save our results. @@ -433,10 +440,9 @@ frame.save(dataFile); Tip: -> You can open and view `Frame.zdf` file in [Zivid -> Studio](https://support.zivid.com/latest//getting-started/studio-guide.html). - -### Export +You can open and view `Frame.zdf` file in [Zivid +Studio](https://support.zivid.com/latest//getting-started/studio-guide.html). +Export ^^^^^^ The API detects which format to use. See [Point Cloud](https://support.zivid.com/latest//reference-articles/point-cloud-structure-and-output-formats.html) @@ -455,6 +461,9 @@ 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#L192)) + ``` sourceCode cpp const auto image = pointCloud.copyImageRGBA(); ``` diff --git a/source/Camera/Basic/CaptureWithSettingsFromYML/CaptureWithSettingsFromYML.cpp b/source/Camera/Basic/CaptureWithSettingsFromYML/CaptureWithSettingsFromYML.cpp index 26ba3d32..0639889c 100644 --- a/source/Camera/Basic/CaptureWithSettingsFromYML/CaptureWithSettingsFromYML.cpp +++ b/source/Camera/Basic/CaptureWithSettingsFromYML/CaptureWithSettingsFromYML.cpp @@ -13,6 +13,10 @@ namespace std::string settingsFolder(const Zivid::Camera &camera) { const auto modelName = camera.info().modelName().value(); + if(modelName.find("Zivid One+") == 0) + { + return "zividOne"; + } if(modelName.find("Zivid 2+") == 0) { return "zivid2Plus"; diff --git a/source/Camera/InfoUtilOther/AutomaticNetworkConfigurationForCameras/AutomaticNetworkConfigurationForCameras.cpp b/source/Camera/InfoUtilOther/AutomaticNetworkConfigurationForCameras/AutomaticNetworkConfigurationForCameras.cpp deleted file mode 100644 index 2dd21b9c..00000000 --- a/source/Camera/InfoUtilOther/AutomaticNetworkConfigurationForCameras/AutomaticNetworkConfigurationForCameras.cpp +++ /dev/null @@ -1,110 +0,0 @@ -/* -Automatically set the IP addresses of any number of cameras to be in the same subnet as the provided IP address of the network interface. -*/ - -#include -#include -#include -#include -#include -#include - -namespace -{ - void assertUserInput(const std::string &ipAddress, const std::string &subnetMask) - { - (void)Zivid::NetworkConfiguration::IPV4::Address{ ipAddress }; - (void)Zivid::NetworkConfiguration::IPV4::SubnetMask{ subnetMask }; - } - - std::vector splitUserInput(const std::string &str, char delimiter) - { - std::vector parts; - std::istringstream userInput(str); - for(std::string part; std::getline(userInput, part, delimiter);) - { - parts.push_back(part); - } - return parts; - } - - std::tuple parseOptions(int argc, char **argv) - { - std::string ipAddress; - std::string subnetMask = "255.255.255.0"; - - auto cli = clipp::group( - clipp::required("--interface-ipv4") & clipp::value("IP address of the PC network interface", ipAddress), - clipp::option("--subnet-mask") & clipp::value("Network subnet mask (default: 255.255.255.0)", subnetMask)); - - if(!clipp::parse(argc, argv, cli)) - { - auto fmt = clipp::doc_formatting{}.alternatives_min_split_size(1).surround_labels("\"", "\""); - std::cout << "SYNOPSIS:" << std::endl; - std::cout << clipp::usage_lines(cli, argv[0], fmt) << std::endl; - std::cout << "OPTIONS:" << std::endl; - std::cout << clipp::documentation(cli) << std::endl; - throw std::runtime_error("Command-line parsing failed"); - } - - assertUserInput(ipAddress, subnetMask); - - return std::make_tuple(ipAddress, subnetMask); - } -} // namespace - -int main(int argc, char **argv) -{ - try - { - std::tuple options = parseOptions(argc, argv); - std::string ipAddress = std::get<0>(options); - std::string subnetMask = std::get<1>(options); - - assertUserInput(ipAddress, subnetMask); - - auto ipAddressOctets = splitUserInput(ipAddress, '.'); - - Zivid::Application zivid; - auto cameras = zivid.cameras(); - if(cameras.empty()) - { - throw std::runtime_error("Failed to connect to camera. No cameras found."); - } - - int lastIpAddressOctet = std::stoi(ipAddressOctets[3]); - std::string remainingIpAddressOctets = ipAddressOctets[0] + "." + ipAddressOctets[1] + "." + ipAddressOctets[2]; - - // defines the last octet of the ip address of the first Zivid camera. Eg.: x.x.x.2 - int nextIpAddressLastOctet = 2; - - for(auto &camera : cameras) - { - if(nextIpAddressLastOctet == lastIpAddressOctet) - { - nextIpAddressLastOctet += 1; - } - - Zivid::NetworkConfiguration newConfig(Zivid::NetworkConfiguration::IPV4( - Zivid::NetworkConfiguration::IPV4::Mode::manual, - Zivid::NetworkConfiguration::IPV4::Address( - remainingIpAddressOctets + "." + std::to_string(nextIpAddressLastOctet)), - Zivid::NetworkConfiguration::IPV4::SubnetMask(subnetMask))); - - nextIpAddressLastOctet += 1; - - std::cout << "Applying network configuration to camera " << camera.info().serialNumber() << std::endl; - camera.applyNetworkConfiguration(newConfig); - std::cout << "New " << camera.networkConfiguration() << "\n" << std::endl; - } - } - catch(const std::exception &e) - { - std::cerr << "Error: " << e.what() << 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/CaptureWithDiagnostics/CaptureWithDiagnostics.cpp b/source/Camera/InfoUtilOther/CaptureWithDiagnostics/CaptureWithDiagnostics.cpp index 6ce9a9cb..5c31af61 100644 --- a/source/Camera/InfoUtilOther/CaptureWithDiagnostics/CaptureWithDiagnostics.cpp +++ b/source/Camera/InfoUtilOther/CaptureWithDiagnostics/CaptureWithDiagnostics.cpp @@ -17,6 +17,10 @@ namespace std::string settingsFolder(const Zivid::Camera &camera) { const auto modelName = camera.info().modelName().value(); + if(modelName.find("Zivid One+") == 0) + { + return "zividOne"; + } if(modelName.find("Zivid 2+") == 0) { return "zivid2Plus"; diff --git a/source/Camera/InfoUtilOther/GetCameraIntrinsics/GetCameraIntrinsics.cpp b/source/Camera/InfoUtilOther/GetCameraIntrinsics/GetCameraIntrinsics.cpp index 570d0cb3..3b4a4d58 100644 --- a/source/Camera/InfoUtilOther/GetCameraIntrinsics/GetCameraIntrinsics.cpp +++ b/source/Camera/InfoUtilOther/GetCameraIntrinsics/GetCameraIntrinsics.cpp @@ -112,7 +112,7 @@ int main() for(const auto aperture : { 11.31, 5.66, 2.83 }) { const auto settings = - Zivid::Settings{ Zivid::Settings::Engine::phase, + Zivid::Settings{ Zivid::Settings::Experimental::Engine::phase, Zivid::Settings::Acquisitions{ Zivid::Settings::Acquisition{ Zivid::Settings::Acquisition::Aperture{ aperture } } }, Zivid::Settings::Processing::Filters::Outlier::Removal::Enabled::yes, @@ -132,7 +132,7 @@ int main() != supportedSamplingPixelValues.end()) { const auto settingsSubsampled = - Zivid::Settings{ Zivid::Settings::Engine::phase, + Zivid::Settings{ Zivid::Settings::Experimental::Engine::phase, Zivid::Settings::Acquisitions{ Zivid::Settings::Acquisition{} }, Zivid::Settings::Sampling::Pixel::blueSubsample2x2 }; const std::string fixedIntrinsicsForSubsampledSettingsPath = "FixedIntrinsicsSubsampledBlue2x2.yml"; diff --git a/source/Camera/InfoUtilOther/NetworkConfiguration/NetworkConfiguration.cpp b/source/Camera/InfoUtilOther/NetworkConfiguration/NetworkConfiguration.cpp deleted file mode 100644 index 886b4405..00000000 --- a/source/Camera/InfoUtilOther/NetworkConfiguration/NetworkConfiguration.cpp +++ /dev/null @@ -1,108 +0,0 @@ -/* -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 7ce8cf16..9fab5c23 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 SteadyClock = std::chrono::steady_clock; +using HighResClock = std::chrono::high_resolution_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 = SteadyClock::now(); + const auto beforeWarmup = HighResClock::now(); - while(SteadyClock::now() - beforeWarmup < warmupTime) + while(HighResClock::now() - beforeWarmup < warmupTime) { - const auto beforeCapture = SteadyClock::now(); + const auto beforeCapture = HighResClock::now(); camera.capture(settings); - const auto afterCapture = SteadyClock::now(); + const auto afterCapture = HighResClock::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 - (SteadyClock::now() - beforeWarmup); + const auto remainingTime = warmupTime - (HighResClock::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 d238d7e4..80e2cdec 100644 --- a/source/Camera/InfoUtilOther/ZividBenchmark/ZividBenchmark.cpp +++ b/source/Camera/InfoUtilOther/ZividBenchmark/ZividBenchmark.cpp @@ -1,8 +1,6 @@ /* -Zividbenchmark is a sample that will test the average speed of different operations on your computer. +Zividbenchmarks is a sample that will test the average speed of different operations on your computer. It will provide the mean and median for connects, disconnects, single imaging, HDR and filtering. - -Note: This example uses experimental SDK features, which may be modified, moved, or deleted in the future without notice. */ #include @@ -19,7 +17,7 @@ namespace { const int printWidth = 56; - using SteadyClock = std::chrono::steady_clock; + using HighResClock = std::chrono::high_resolution_clock; using Duration = std::chrono::nanoseconds; Duration computeAverageDuration(const std::vector &durations) @@ -295,23 +293,21 @@ namespace Zivid::Camera getFirstCamera(Zivid::Application &zivid) { const auto cameras = zivid.cameras(); - for(const auto &camera : cameras) + if(cameras.size() != 1) { - if(camera.state().status() == Zivid::CameraState::Status::available) - { - std::cout << "Available camera: " << camera.info().serialNumber() << std::endl; - printZividInfo(camera, zivid); - return camera; - } - std::cout << "Camera " << camera.info().serialNumber() << "is not available. " - << "Camera status: " << camera.state().status() << std::endl; + throw std::runtime_error("At least one camera needs to be connected"); } - throw std::runtime_error("At least one camera needs to be available"); + printZividInfo(cameras.at(0), zivid); + return cameras.at(0); } - std::chrono::microseconds getMinExposureTime() + std::chrono::microseconds getMinExposureTime(const std::string &modelName) { - return std::chrono::microseconds{ 1677 }; + if(modelName.substr(0, 14) == "Zivid One Plus") + { + return std::chrono::microseconds{ 6500 }; // Min for Zivid One Plus + } + return std::chrono::microseconds{ 1677 }; // Min for Zivid 2 and Zivid 2+ } Zivid::Settings::Acquisition @@ -336,7 +332,7 @@ namespace throw std::runtime_error("Unequal input vector size"); } - Zivid::Settings settings{ Zivid::Settings::Engine::phase, + Zivid::Settings settings{ Zivid::Settings::Experimental::Engine::phase, Zivid::Settings::Processing::Filters::Smoothing::Gaussian::Enabled{ enableGaussian }, Zivid::Settings::Processing::Filters::Smoothing::Gaussian::Sigma{ 1.5 }, Zivid::Settings::Processing::Filters::Noise::Removal::Enabled{ true }, @@ -361,9 +357,9 @@ namespace template FrameAndCaptureTime captureAndMeasure(Zivid::Camera &camera, const SettingsT &settings) { - const auto before = SteadyClock::now(); + const auto before = HighResClock::now(); const auto frame = camera.capture(settings); - const auto after = SteadyClock::now(); + const auto after = HighResClock::now(); return { std::move(frame), (after - before) }; } @@ -373,19 +369,19 @@ namespace template<> Duration useFrame(const Zivid::Frame &frame) { - const auto before = SteadyClock::now(); + const auto before = HighResClock::now(); const auto pointCloud = frame.pointCloud(); const auto data = pointCloud.copyData(); - const auto after = SteadyClock::now(); + const auto after = HighResClock::now(); return (after - before); } template<> Duration useFrame(const Zivid::Frame2D &frame2D) { - const auto before = SteadyClock::now(); + const auto before = HighResClock::now(); const auto image = frame2D.imageRGBA(); - const auto after = SteadyClock::now(); + const auto after = HighResClock::now(); return (after - before); } @@ -406,14 +402,14 @@ namespace for(size_t i = 0; i < numFrames; i++) { dummyCapture2D(camera, settings2D); - const auto before = SteadyClock::now(); + const auto before = HighResClock::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 = SteadyClock::now(); + const auto after = HighResClock::now(); captureDurations2D.push_back(frame2dAndCaptureTime.captureTime); captureDurations.push_back(frameAndCaptureTime.captureTime); @@ -456,14 +452,14 @@ namespace for(size_t i = 0; i < numFrames; i++) { dummyCapture3D(camera, settings); - const auto before = SteadyClock::now(); + const auto before = HighResClock::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 = SteadyClock::now(); + const auto after = HighResClock::now(); captureDurations2D.push_back(frame2dAndCaptureTime.captureTime); captureDurations.push_back(frameAndCaptureTime.captureTime); @@ -509,10 +505,10 @@ namespace for(size_t i = 0; i < numFrames; i++) { dummyCapture3D(camera, settings); - const auto before = SteadyClock::now(); + const auto before = HighResClock::now(); const auto frameAndCaptureTime = captureAndMeasure(camera, settings); const auto processTime = useFrame(frameAndCaptureTime.frame); - const auto after = SteadyClock::now(); + const auto after = HighResClock::now(); captureDurations.push_back(frameAndCaptureTime.captureTime); processDurations.push_back(processTime); @@ -539,11 +535,11 @@ namespace for(size_t i = 0; i < numConnects; i++) { - const auto beforeConnect = SteadyClock::now(); + const auto beforeConnect = HighResClock::now(); camera.connect(); - const auto afterConnect = SteadyClock::now(); + const auto afterConnect = HighResClock::now(); camera.disconnect(); - const auto afterDisconnect = SteadyClock::now(); + const auto afterDisconnect = HighResClock::now(); connectDurations.push_back(afterConnect - beforeConnect); disconnectDurations.push_back(afterDisconnect - afterConnect); @@ -581,16 +577,16 @@ namespace for(size_t i = 0; i < numFrames; i++) { - const auto beforeCapture2D = SteadyClock::now(); + const auto beforeCapture2D = HighResClock::now(); const auto frame2D = camera.capture(settings2D); - const auto afterCapture2D = SteadyClock::now(); + const auto afterCapture2D = HighResClock::now(); const auto frame = camera.capture(settings); - const auto afterCapture = SteadyClock::now(); + const auto afterCapture = HighResClock::now(); const auto image = frame2D.imageRGBA(); - const auto afterProcess2D = SteadyClock::now(); + const auto afterProcess2D = HighResClock::now(); const auto pointCloud = frame.pointCloud(); const auto data = pointCloud.copyData(); - const auto afterProcess = SteadyClock::now(); + const auto afterProcess = HighResClock::now(); captureDurations2D.push_back(afterCapture2D - beforeCapture2D); captureDurations.push_back(afterCapture - afterCapture2D); @@ -642,16 +638,16 @@ namespace for(size_t i = 0; i < numFrames; i++) { - const auto beforeCapture = SteadyClock::now(); + const auto beforeCapture = HighResClock::now(); const auto frame = camera.capture(settings); - const auto afterCapture = SteadyClock::now(); + const auto afterCapture = HighResClock::now(); const auto frame2D = camera.capture(settings2D); - const auto afterCapture2D = SteadyClock::now(); + const auto afterCapture2D = HighResClock::now(); const auto pointCloud = frame.pointCloud(); const auto data = pointCloud.copyData(); - const auto afterProcess = SteadyClock::now(); + const auto afterProcess = HighResClock::now(); const auto image = frame2D.imageRGBA(); - const auto afterProcess2D = SteadyClock::now(); + const auto afterProcess2D = HighResClock::now(); captureDurations.push_back(afterCapture - beforeCapture); captureDurations2D.push_back(afterCapture2D - afterCapture); @@ -696,12 +692,12 @@ namespace for(size_t i = 0; i < numFrames; i++) { - const auto beforeCapture = SteadyClock::now(); + const auto beforeCapture = HighResClock::now(); const auto frame = camera.capture(settings); - const auto afterCapture = SteadyClock::now(); + const auto afterCapture = HighResClock::now(); const auto pointCloud = frame.pointCloud(); const auto data = pointCloud.copyData(); - const auto afterProcess = SteadyClock::now(); + const auto afterProcess = HighResClock::now(); captureDurations.push_back(afterCapture - beforeCapture); processDurations.push_back(afterProcess - afterCapture); @@ -738,9 +734,9 @@ namespace for(size_t i = 0; i < numFrames; i++) { - const auto beforeSuggestSettings = SteadyClock::now(); + const auto beforeSuggestSettings = HighResClock::now(); const auto settings{ Zivid::CaptureAssistant::suggestSettings(camera, suggestSettingsParameters) }; - const auto afterSuggestSettings = SteadyClock::now(); + const auto afterSuggestSettings = HighResClock::now(); suggestSettingsDurations.push_back(afterSuggestSettings - beforeSuggestSettings); } @@ -825,11 +821,11 @@ namespace for(size_t i = 0; i < numFrames; i++) { - const auto beforeCapture = SteadyClock::now(); + const auto beforeCapture = HighResClock::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 = SteadyClock::now(); + const auto afterCapture = HighResClock::now(); captureDurations.push_back(afterCapture - beforeCapture); } @@ -853,19 +849,19 @@ namespace Duration copyDataTime(Zivid::Frame &frame) { auto pointCloud = frame.pointCloud(); - const auto beforeCopyData = SteadyClock::now(); + const auto beforeCopyData = HighResClock::now(); pointCloud.copyData(); - const auto afterCopyData = SteadyClock::now(); + const auto afterCopyData = HighResClock::now(); return afterCopyData - beforeCopyData; } Duration copyDataTime(Zivid::Frame2D &frame2D) { - const auto beforeCopyData = SteadyClock::now(); + const auto beforeCopyData = HighResClock::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 = SteadyClock::now(); + const auto afterCopyData = HighResClock::now(); return afterCopyData - beforeCopyData; } @@ -929,9 +925,9 @@ namespace std::vector durationsPerFormat; for(size_t j = 0; j < numFrames; j++) { - const auto beforeSave = SteadyClock::now(); + const auto beforeSave = HighResClock::now(); frame.save(dataFile); - const auto afterSave = SteadyClock::now(); + const auto afterSave = HighResClock::now(); durationsPerFormat.push_back(afterSave - beforeSave); } @@ -975,7 +971,7 @@ int main(int argc, char **argv) const size_t numFramesSave = 10; const size_t numCopies = 10; - const std::chrono::microseconds exposureTime = getMinExposureTime(); + const std::chrono::microseconds exposureTime = getMinExposureTime(camera.info().modelName().toString()); const std::vector oneExposureTime{ exposureTime }; const std::vector twoExposureTimes{ exposureTime, exposureTime }; const std::vector threeExposureTimes{ exposureTime, exposureTime, exposureTime }; diff --git a/source/Camera/Maintenance/CorrectCameraInField/CorrectCameraInField.cpp b/source/Camera/Maintenance/CorrectCameraInField/CorrectCameraInField.cpp index 54b2bfcc..738afd73 100644 --- a/source/Camera/Maintenance/CorrectCameraInField/CorrectCameraInField.cpp +++ b/source/Camera/Maintenance/CorrectCameraInField/CorrectCameraInField.cpp @@ -12,7 +12,6 @@ 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 @@ -56,7 +55,7 @@ namespace if(yesNoPrompt("Capture (y) or finish (n)?")) { std::cout << "Capturing calibration board" << std::endl; - const auto detectionResult = Zivid::Calibration::detectCalibrationBoard(camera); + const auto detectionResult = Zivid::Experimental::Calibration::detectFeaturePoints(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 aa79c5ee..80fd9ea8 100644 --- a/source/Camera/Maintenance/VerifyCameraInField/VerifyCameraInField.cpp +++ b/source/Camera/Maintenance/VerifyCameraInField/VerifyCameraInField.cpp @@ -8,7 +8,6 @@ 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 @@ -39,7 +38,7 @@ int main() // Gather data std::cout << "Capturing calibration board" << std::endl; - const auto detectionResult = Zivid::Calibration::detectCalibrationBoard(camera); + const auto detectionResult = Zivid::Experimental::Calibration::detectFeaturePoints(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 fae17eb5..32ac4d88 100644 --- a/source/Camera/Maintenance/VerifyCameraInFieldFromZDF/VerifyCameraInFieldFromZDF.cpp +++ b/source/Camera/Maintenance/VerifyCameraInFieldFromZDF/VerifyCameraInFieldFromZDF.cpp @@ -17,7 +17,6 @@ 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 @@ -38,7 +37,7 @@ int main() // offline infield verification std::cout << "Capturing calibration board" << std::endl; - const auto frame = Zivid::Calibration::captureCalibrationBoard(camera); + const auto frame = Zivid::Experimental::Calibration::captureCalibrationBoard(camera); const auto dataFile = "FrameWithCalibrationBoard.zdf"; std::cout << "Saving frame to file: " << dataFile << ", for later use in offline infield verification" @@ -52,7 +51,7 @@ int main() const auto loadedFrame = Zivid::Frame(dataFile); std::cout << "Detecting calibration board" << std::endl; - const auto detectionResult = Zivid::Calibration::detectCalibrationBoard(loadedFrame); + const auto detectionResult = Zivid::Experimental::Calibration::detectFeaturePoints(loadedFrame); const auto input = Zivid::Experimental::Calibration::InfieldCorrectionInput{ detectionResult }; if(!input.valid()) diff --git a/source/cmake/CompilerOptions.cmake b/source/cmake/CompilerOptions.cmake index 3e28dc9c..989242fd 100644 --- a/source/cmake/CompilerOptions.cmake +++ b/source/cmake/CompilerOptions.cmake @@ -1,9 +1,6 @@ set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_EXTENSIONS OFF) -# Warnings are disabled by default to ensure that the samples are as portable as possible across compiler versions. It -# is recommended to enable warnings if you plan to modify the samples or use this project as a basis for your -# development project. option(WARNINGS "Enable compiler warnings" OFF) if(WARNINGS) @@ -58,19 +55,27 @@ elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") endif() set(WARNINGS_THAT_SHOULD_BE_IGNORED # WHY it is ok to ignore - 4244 # Narrowing conversions: Too strict and noisy for this code base. - 4267 # Conversion: Happens a lot in these samples, would complicate them too much to handle manually. - 4702 # Unreachable code: Ignoring because they happen in external headers despite `/external:W0`. + 4702 # Got unreachable warnings from external, even though they are generallly ignored by /experimental:external + 4710 # If the compiler decides to not inline a function, that's their decision + 4711 # If the compiler decides to inline a function, that's their decision + 4571 # Just a non-interesting informational warning about msvc changing behaviour in 7.1 + 4267 # Conversion: Happens a lot in these samples, would complicate them too much to handle manually + 4365 # Conversion: Happens a lot in these samples, would complicate them too much to handle manually + 4388 # Signed/unsigned comparison: Happens a lot in these samples, would complicate them too much to handle manually + 5045 # Informs that the compiler will insert Spectre mitigation for memory load if /Qspectre switch is specified + 4244 # Narrowing conversions: Too strict and noisy for this code base + 4242 # Narrowing conversions: Too strict and noisy for this code base + 4820 # The type and order of elements caused the compiler to ad padding to the end of a struct + 4868 # compiler may not enforce left-to-right evaluation order in braced initializer list + 4355 # #include causes the following: 'this': used in base member initializer list 4996 # Complains about use of `gmtime`. Ignoring since most samples use a single thread. + 4514 # Complains about unused inline functions in header files. Too strict and noisy for this code base. ) foreach(WARNING ${WARNINGS_THAT_SHOULD_BE_IGNORED}) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd${WARNING}") endforeach() - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4") - - if(CMAKE_VERSION VERSION_LESS 3.24 OR CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 19.29.30036.3) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /experimental:external /external:anglebrackets /external:W0") - endif() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Wall") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /experimental:external /external:anglebrackets /external:W0") else() set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W0") endif()