diff --git a/README.md b/README.md index fe6555c9..125bff5f 100644 --- a/README.md +++ b/README.md @@ -555,3 +555,4 @@ Some helper code is licensed under MIT license. * pugixml: MIT license. https://github.com/zeux/pugixml * nanoflann: 2-clause BSD license. https://github.com/jlblancoc/nanoflann * tinymeshutils: MIT license. https://github.com/syoyo/tinymeshutils +* criterion(for benchmark): MIT license. https://github.com/p-ranav/criterion diff --git a/benchmarks/criterion.LICENSE b/benchmarks/criterion.LICENSE new file mode 100644 index 00000000..ba66ff8a --- /dev/null +++ b/benchmarks/criterion.LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Pranav + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/benchmarks/criterion.hpp b/benchmarks/criterion.hpp new file mode 100644 index 00000000..9d2ec5dd --- /dev/null +++ b/benchmarks/criterion.hpp @@ -0,0 +1,7168 @@ + +#pragma once + +namespace indicators { +enum class Color { grey, red, green, yellow, blue, magenta, cyan, white, unspecified }; +} + +#pragma once +namespace indicators { +enum class FontStyle { bold, dark, italic, underline, blink, reverse, concealed, crossed }; +} + +#pragma once + +namespace indicators { +enum class ProgressType { incremental, decremental }; +} + +//! +//! termcolor +//! ~~~~~~~~~ +//! +//! termcolor is a header-only c++ library for printing colored messages +//! to the terminal. Written just for fun with a help of the Force. +//! +//! :copyright: (c) 2013 by Ihor Kalnytskyi +//! :license: BSD, see LICENSE for details +//! + +#ifndef TERMCOLOR_HPP_ +#define TERMCOLOR_HPP_ + +// the following snippet of code detects the current OS and +// defines the appropriate macro that is used to wrap some +// platform specific things +#if defined(_WIN32) || defined(_WIN64) +#define TERMCOLOR_OS_WINDOWS +#elif defined(__APPLE__) +#define TERMCOLOR_OS_MACOS +#elif defined(__unix__) || defined(__unix) +#define TERMCOLOR_OS_LINUX +#else +#error unsupported platform +#endif + +// This headers provides the `isatty()`/`fileno()` functions, +// which are used for testing whether a standart stream refers +// to the terminal. As for Windows, we also need WinApi funcs +// for changing colors attributes of the terminal. +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) +#include +#elif defined(TERMCOLOR_OS_WINDOWS) +#if defined(_MSC_VER) +#if !defined(NOMINMAX) +#define NOMINMAX +#endif +#include +#include +#endif +#endif + +#include +#include + +namespace termcolor { +// Forward declaration of the `_internal` namespace. +// All comments are below. +namespace _internal { +// An index to be used to access a private storage of I/O streams. See +// colorize / nocolorize I/O manipulators for details. +static int colorize_index = std::ios_base::xalloc(); + +inline FILE *get_standard_stream(const std::ostream &stream); +inline bool is_colorized(std::ostream &stream); +inline bool is_atty(const std::ostream &stream); + +#if defined(TERMCOLOR_OS_WINDOWS) +inline void win_change_attributes(std::ostream &stream, int foreground, int background = -1); +#endif +} // namespace _internal + +inline std::ostream &colorize(std::ostream &stream) { + stream.iword(_internal::colorize_index) = 1L; + return stream; +} + +inline std::ostream &nocolorize(std::ostream &stream) { + stream.iword(_internal::colorize_index) = 0L; + return stream; +} + +inline std::ostream &reset(std::ostream &stream) { + if (_internal::is_colorized(stream)) { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\033[00m"; +#elif defined(TERMCOLOR_OS_WINDOWS) + _internal::win_change_attributes(stream, -1, -1); +#endif + } + return stream; +} + +inline std::ostream &bold(std::ostream &stream) { + if (_internal::is_colorized(stream)) { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\033[1m"; +#elif defined(TERMCOLOR_OS_WINDOWS) +#endif + } + return stream; +} + +inline std::ostream &dark(std::ostream &stream) { + if (_internal::is_colorized(stream)) { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\033[2m"; +#elif defined(TERMCOLOR_OS_WINDOWS) +#endif + } + return stream; +} + +inline std::ostream &italic(std::ostream &stream) { + if (_internal::is_colorized(stream)) { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\033[3m"; +#elif defined(TERMCOLOR_OS_WINDOWS) +#endif + } + return stream; +} + +inline std::ostream &underline(std::ostream &stream) { + if (_internal::is_colorized(stream)) { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\033[4m"; +#elif defined(TERMCOLOR_OS_WINDOWS) +#endif + } + return stream; +} + +inline std::ostream &blink(std::ostream &stream) { + if (_internal::is_colorized(stream)) { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\033[5m"; +#elif defined(TERMCOLOR_OS_WINDOWS) +#endif + } + return stream; +} + +inline std::ostream &reverse(std::ostream &stream) { + if (_internal::is_colorized(stream)) { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\033[7m"; +#elif defined(TERMCOLOR_OS_WINDOWS) +#endif + } + return stream; +} + +inline std::ostream &concealed(std::ostream &stream) { + if (_internal::is_colorized(stream)) { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\033[8m"; +#elif defined(TERMCOLOR_OS_WINDOWS) +#endif + } + return stream; +} + +inline std::ostream &crossed(std::ostream &stream) { + if (_internal::is_colorized(stream)) { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\033[9m"; +#elif defined(TERMCOLOR_OS_WINDOWS) +#endif + } + return stream; +} + +template inline std::ostream &color(std::ostream &stream) { + if (_internal::is_colorized(stream)) { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + char command[12]; + std::snprintf(command, sizeof(command), "\033[38;5;%dm", code); + stream << command; +#elif defined(TERMCOLOR_OS_WINDOWS) +#endif + } + return stream; +} + +template inline std::ostream &on_color(std::ostream &stream) { + if (_internal::is_colorized(stream)) { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + char command[12]; + std::snprintf(command, sizeof(command), "\033[48;5;%dm", code); + stream << command; +#elif defined(TERMCOLOR_OS_WINDOWS) +#endif + } + return stream; +} + +template inline std::ostream &color(std::ostream &stream) { + if (_internal::is_colorized(stream)) { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + char command[20]; + std::snprintf(command, sizeof(command), "\033[38;2;%d;%d;%dm", r, g, b); + stream << command; +#elif defined(TERMCOLOR_OS_WINDOWS) +#endif + } + return stream; +} + +template inline std::ostream &on_color(std::ostream &stream) { + if (_internal::is_colorized(stream)) { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + char command[20]; + std::snprintf(command, sizeof(command), "\033[48;2;%d;%d;%dm", r, g, b); + stream << command; +#elif defined(TERMCOLOR_OS_WINDOWS) +#endif + } + return stream; +} + +inline std::ostream &grey(std::ostream &stream) { + if (_internal::is_colorized(stream)) { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\033[30m"; +#elif defined(TERMCOLOR_OS_WINDOWS) + _internal::win_change_attributes(stream, + 0 // grey (black) + ); +#endif + } + return stream; +} + +inline std::ostream &red(std::ostream &stream) { + if (_internal::is_colorized(stream)) { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\033[31m"; +#elif defined(TERMCOLOR_OS_WINDOWS) + _internal::win_change_attributes(stream, FOREGROUND_RED); +#endif + } + return stream; +} + +inline std::ostream &green(std::ostream &stream) { + if (_internal::is_colorized(stream)) { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\033[32m"; +#elif defined(TERMCOLOR_OS_WINDOWS) + _internal::win_change_attributes(stream, FOREGROUND_GREEN); +#endif + } + return stream; +} + +inline std::ostream &yellow(std::ostream &stream) { + if (_internal::is_colorized(stream)) { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\033[33m"; +#elif defined(TERMCOLOR_OS_WINDOWS) + _internal::win_change_attributes(stream, FOREGROUND_GREEN | FOREGROUND_RED); +#endif + } + return stream; +} + +inline std::ostream &blue(std::ostream &stream) { + if (_internal::is_colorized(stream)) { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\033[34m"; +#elif defined(TERMCOLOR_OS_WINDOWS) + _internal::win_change_attributes(stream, FOREGROUND_BLUE); +#endif + } + return stream; +} + +inline std::ostream &magenta(std::ostream &stream) { + if (_internal::is_colorized(stream)) { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\033[35m"; +#elif defined(TERMCOLOR_OS_WINDOWS) + _internal::win_change_attributes(stream, FOREGROUND_BLUE | FOREGROUND_RED); +#endif + } + return stream; +} + +inline std::ostream &cyan(std::ostream &stream) { + if (_internal::is_colorized(stream)) { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\033[36m"; +#elif defined(TERMCOLOR_OS_WINDOWS) + _internal::win_change_attributes(stream, FOREGROUND_BLUE | FOREGROUND_GREEN); +#endif + } + return stream; +} + +inline std::ostream &white(std::ostream &stream) { + if (_internal::is_colorized(stream)) { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\033[37m"; +#elif defined(TERMCOLOR_OS_WINDOWS) + _internal::win_change_attributes(stream, FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED); +#endif + } + return stream; +} + +inline std::ostream &on_grey(std::ostream &stream) { + if (_internal::is_colorized(stream)) { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\033[40m"; +#elif defined(TERMCOLOR_OS_WINDOWS) + _internal::win_change_attributes(stream, -1, + 0 // grey (black) + ); +#endif + } + return stream; +} + +inline std::ostream &on_red(std::ostream &stream) { + if (_internal::is_colorized(stream)) { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\033[41m"; +#elif defined(TERMCOLOR_OS_WINDOWS) + _internal::win_change_attributes(stream, -1, BACKGROUND_RED); +#endif + } + return stream; +} + +inline std::ostream &on_green(std::ostream &stream) { + if (_internal::is_colorized(stream)) { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\033[42m"; +#elif defined(TERMCOLOR_OS_WINDOWS) + _internal::win_change_attributes(stream, -1, BACKGROUND_GREEN); +#endif + } + return stream; +} + +inline std::ostream &on_yellow(std::ostream &stream) { + if (_internal::is_colorized(stream)) { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\033[43m"; +#elif defined(TERMCOLOR_OS_WINDOWS) + _internal::win_change_attributes(stream, -1, BACKGROUND_GREEN | BACKGROUND_RED); +#endif + } + return stream; +} + +inline std::ostream &on_blue(std::ostream &stream) { + if (_internal::is_colorized(stream)) { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\033[44m"; +#elif defined(TERMCOLOR_OS_WINDOWS) + _internal::win_change_attributes(stream, -1, BACKGROUND_BLUE); +#endif + } + return stream; +} + +inline std::ostream &on_magenta(std::ostream &stream) { + if (_internal::is_colorized(stream)) { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\033[45m"; +#elif defined(TERMCOLOR_OS_WINDOWS) + _internal::win_change_attributes(stream, -1, BACKGROUND_BLUE | BACKGROUND_RED); +#endif + } + return stream; +} + +inline std::ostream &on_cyan(std::ostream &stream) { + if (_internal::is_colorized(stream)) { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\033[46m"; +#elif defined(TERMCOLOR_OS_WINDOWS) + _internal::win_change_attributes(stream, -1, BACKGROUND_GREEN | BACKGROUND_BLUE); +#endif + } + return stream; +} + +inline std::ostream &on_white(std::ostream &stream) { + if (_internal::is_colorized(stream)) { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\033[47m"; +#elif defined(TERMCOLOR_OS_WINDOWS) + _internal::win_change_attributes(stream, -1, + BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_RED); +#endif + } + + return stream; +} + +//! Since C++ hasn't a way to hide something in the header from +//! the outer access, I have to introduce this namespace which +//! is used for internal purpose and should't be access from +//! the user code. +namespace _internal { +//! Since C++ hasn't a true way to extract stream handler +//! from the a given `std::ostream` object, I have to write +//! this kind of hack. +inline FILE *get_standard_stream(const std::ostream &stream) { + if (&stream == &std::cout) + return stdout; + else if ((&stream == &std::cerr) || (&stream == &std::clog)) + return stderr; + + return nullptr; +} + +// Say whether a given stream should be colorized or not. It's always +// true for ATTY streams and may be true for streams marked with +// colorize flag. +inline bool is_colorized(std::ostream &stream) { + return is_atty(stream) || static_cast(stream.iword(colorize_index)); +} + +//! Test whether a given `std::ostream` object refers to +//! a terminal. +inline bool is_atty(const std::ostream &stream) { + FILE *std_stream = get_standard_stream(stream); + + // Unfortunately, fileno() ends with segmentation fault + // if invalid file descriptor is passed. So we need to + // handle this case gracefully and assume it's not a tty + // if standard stream is not detected, and 0 is returned. + if (!std_stream) + return false; + +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + return ::isatty(fileno(std_stream)); +#elif defined(TERMCOLOR_OS_WINDOWS) + return ::_isatty(_fileno(std_stream)); +#endif +} + +#if defined(TERMCOLOR_OS_WINDOWS) +//! Change Windows Terminal colors attribute. If some +//! parameter is `-1` then attribute won't changed. +inline void win_change_attributes(std::ostream &stream, int foreground, int background) { + // yeah, i know.. it's ugly, it's windows. + static WORD defaultAttributes = 0; + + // Windows doesn't have ANSI escape sequences and so we use special + // API to change Terminal output color. That means we can't + // manipulate colors by means of "std::stringstream" and hence + // should do nothing in this case. + if (!_internal::is_atty(stream)) + return; + + // get terminal handle + HANDLE hTerminal = INVALID_HANDLE_VALUE; + if (&stream == &std::cout) + hTerminal = GetStdHandle(STD_OUTPUT_HANDLE); + else if (&stream == &std::cerr) + hTerminal = GetStdHandle(STD_ERROR_HANDLE); + + // save default terminal attributes if it unsaved + if (!defaultAttributes) { + CONSOLE_SCREEN_BUFFER_INFO info; + if (!GetConsoleScreenBufferInfo(hTerminal, &info)) + return; + defaultAttributes = info.wAttributes; + } + + // restore all default settings + if (foreground == -1 && background == -1) { + SetConsoleTextAttribute(hTerminal, defaultAttributes); + return; + } + + // get current settings + CONSOLE_SCREEN_BUFFER_INFO info; + if (!GetConsoleScreenBufferInfo(hTerminal, &info)) + return; + + if (foreground != -1) { + info.wAttributes &= ~(info.wAttributes & 0x0F); + info.wAttributes |= static_cast(foreground); + } + + if (background != -1) { + info.wAttributes &= ~(info.wAttributes & 0xF0); + info.wAttributes |= static_cast(background); + } + + SetConsoleTextAttribute(hTerminal, info.wAttributes); +} +#endif // TERMCOLOR_OS_WINDOWS + +} // namespace _internal + +} // namespace termcolor + +#undef TERMCOLOR_OS_WINDOWS +#undef TERMCOLOR_OS_MACOS +#undef TERMCOLOR_OS_LINUX + +#endif // TERMCOLOR_HPP_ + +#pragma once +#include + +#if defined(_WIN32) +#include + +namespace indicators { + +static inline std::pair terminal_size() { + CONSOLE_SCREEN_BUFFER_INFO csbi; + int cols, rows; + GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi); + cols = csbi.srWindow.Right - csbi.srWindow.Left + 1; + rows = csbi.srWindow.Bottom - csbi.srWindow.Top + 1; + return {static_cast(rows), static_cast(cols)}; +} + +size_t terminal_width() { return terminal_size().second; } + +} // namespace indicators + +#else + +#include //ioctl() and TIOCGWINSZ +#include // for STDOUT_FILENO + +namespace indicators { + +static inline std::pair terminal_size() { + struct winsize size; + ioctl(STDOUT_FILENO, TIOCGWINSZ, &size); + return {static_cast(size.ws_row), static_cast(size.ws_col)}; +} + +static inline size_t terminal_width() { return terminal_size().second; } + +} // namespace indicators + +#endif + +/* +Activity Indicators for Modern C++ +https://github.com/p-ranav/indicators + +Licensed under the MIT License . +SPDX-License-Identifier: MIT +Copyright (c) 2019 Dawid Pilarski . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ +#pragma once + +#include +// #include +// #include +// #include +#include +#include +#include +#include +#include + +namespace indicators { + +namespace details { + +template struct if_else; + +template <> struct if_else { using type = std::true_type; }; + +template <> struct if_else { using type = std::false_type; }; + +template struct if_else_type; + +template struct if_else_type { + using type = True; +}; + +template struct if_else_type { + using type = False; +}; + +template struct conjuction; + +template <> struct conjuction<> : std::true_type {}; + +template +struct conjuction + : if_else_type>::type {}; + +template struct disjunction; + +template <> struct disjunction<> : std::false_type {}; + +template +struct disjunction + : if_else_type>::type {}; + +enum class ProgressBarOption { + bar_width = 0, + prefix_text, + postfix_text, + start, + end, + fill, + lead, + remainder, + max_postfix_text_len, + completed, + show_percentage, + show_elapsed_time, + show_remaining_time, + saved_start_time, + foreground_color, + spinner_show, + spinner_states, + font_styles, + hide_bar_when_complete, + min_progress, + max_progress, + progress_type, + stream +}; + +template struct Setting { + template ::value>::type> + explicit Setting(Args &&... args) : value(std::forward(args)...) {} + Setting(const Setting &) = default; + Setting(Setting &&) = default; + + static constexpr auto id = Id; + using type = T; + + T value{}; +}; + +template struct is_setting : std::false_type {}; + +template struct is_setting> : std::true_type {}; + +template +struct are_settings : if_else...>::value>::type {}; + +template <> struct are_settings<> : std::true_type {}; + +template struct is_setting_from_tuple; + +template struct is_setting_from_tuple> : std::true_type {}; + +template +struct is_setting_from_tuple> + : if_else...>::value>::type {}; + +template +struct are_settings_from_tuple + : if_else...>::value>::type {}; + +template struct always_true { static constexpr auto value = true; }; + +template Default &&get_impl(Default &&def) { + return std::forward(def); +} + +template +auto get_impl(Default && /*def*/, T &&first, Args &&... /*tail*/) -> + typename std::enable_if<(std::decay::type::id == Id), + decltype(std::forward(first))>::type { + return std::forward(first); +} + +template +auto get_impl(Default &&def, T && /*first*/, Args &&... tail) -> + typename std::enable_if<(std::decay::type::id != Id), + decltype(get_impl(std::forward(def), + std::forward(tail)...))>::type { + return get_impl(std::forward(def), std::forward(tail)...); +} + +template ::value, void>::type> +auto get(Default &&def, Args &&... args) + -> decltype(details::get_impl(std::forward(def), std::forward(args)...)) { + return details::get_impl(std::forward(def), std::forward(args)...); +} + +template using StringSetting = Setting; + +template using IntegerSetting = Setting; + +template using BooleanSetting = Setting; + +template struct option_idx; + +template +struct option_idx, counter> + : if_else_type<(Id == T::id), std::integral_constant, + option_idx, counter + 1>>::type {}; + +template struct option_idx, counter> { + static_assert(always_true<(ProgressBarOption)Id>::value, "No such option was found"); +}; + +template +auto get_value(Settings &&settings) + -> decltype((std::get::type>::value>( + std::declval()))) { + return std::get::type>::value>( + std::forward(settings)); +} + +} // namespace details + +namespace option { +using BarWidth = details::IntegerSetting; +using PrefixText = details::StringSetting; +using PostfixText = details::StringSetting; +using Start = details::StringSetting; +using End = details::StringSetting; +using Fill = details::StringSetting; +using Lead = details::StringSetting; +using Remainder = details::StringSetting; +using MaxPostfixTextLen = details::IntegerSetting; +using Completed = details::BooleanSetting; +using ShowPercentage = details::BooleanSetting; +using ShowElapsedTime = details::BooleanSetting; +using ShowRemainingTime = details::BooleanSetting; +using SavedStartTime = details::BooleanSetting; +using ForegroundColor = details::Setting; +using ShowSpinner = details::BooleanSetting; +using SpinnerStates = + details::Setting, details::ProgressBarOption::spinner_states>; +using HideBarWhenComplete = + details::BooleanSetting; +using FontStyles = + details::Setting, details::ProgressBarOption::font_styles>; +using MinProgress = details::IntegerSetting; +using MaxProgress = details::IntegerSetting; +using ProgressType = details::Setting; +using Stream = details::Setting; +} // namespace option +} // namespace indicators + +#pragma once + +#if defined(_MSC_VER) +#if !defined(NOMINMAX) +#define NOMINMAX +#endif +#include +#include +#else +#include +#endif + +namespace indicators { + +#if defined(_MSC_VER) + +static inline void show_console_cursor(bool const show) { + HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE); + + CONSOLE_CURSOR_INFO cursorInfo; + + GetConsoleCursorInfo(out, &cursorInfo); + cursorInfo.bVisible = show; // set the cursor visibility + SetConsoleCursorInfo(out, &cursorInfo); +} + +#else + +static inline void show_console_cursor(bool const show) { + std::fputs(show ? "\033[?25h" : "\033[?25l", stdout); +} + +#endif + +} // namespace indicators +#pragma once + +#if defined(_MSC_VER) +#if !defined(NOMINMAX) +#define NOMINMAX +#endif +#include +#include +#else +#include +#endif + +namespace indicators { + +#ifdef _MSC_VER + +static inline void move(int x, int y) { + auto hStdout = GetStdHandle(STD_OUTPUT_HANDLE); + if (!hStdout) + return; + + CONSOLE_SCREEN_BUFFER_INFO csbiInfo; + GetConsoleScreenBufferInfo(hStdout, &csbiInfo); + + COORD cursor; + + cursor.X = csbiInfo.dwCursorPosition.X + x; + cursor.Y = csbiInfo.dwCursorPosition.Y + y; + SetConsoleCursorPosition(hStdout, cursor); +} + +static inline void move_up(int lines) { move(0, -lines); } +static inline void move_down(int lines) { move(0, -lines); } +static inline void move_right(int cols) { move(cols, 0); } +static inline void move_left(int cols) { move(-cols, 0); } + +#else + +static inline void move_up(int lines) { std::cout << "\033[" << lines << "A"; } +static inline void move_down(int lines) { std::cout << "\033[" << lines << "B"; } +static inline void move_right(int cols) { std::cout << "\033[" << cols << "C"; } +static inline void move_left(int cols) { std::cout << "\033[" << cols << "D"; } + +#endif + +} // namespace indicators +#pragma once + +// #include + +#include +#if __has_include() +#include +#define INDICATORS_HAVE_CODECVT 1 +#endif +#include +#include +#include +#include + +namespace unicode { + +#if INDICATORS_HAVE_CODECVT +namespace details { + +/* + * This is an implementation of wcwidth() and wcswidth() (defined in + * IEEE Std 1002.1-2001) for Unicode. + * + * http://www.opengroup.org/onlinepubs/007904975/functions/wcwidth.html + * http://www.opengroup.org/onlinepubs/007904975/functions/wcswidth.html + * + * In fixed-width output devices, Latin characters all occupy a single + * "cell" position of equal width, whereas ideographic CJK characters + * occupy two such cells. Interoperability between terminal-line + * applications and (teletype-style) character terminals using the + * UTF-8 encoding requires agreement on which character should advance + * the cursor by how many cell positions. No established formal + * standards exist at present on which Unicode character shall occupy + * how many cell positions on character terminals. These routines are + * a first attempt of defining such behavior based on simple rules + * applied to data provided by the Unicode Consortium. + * + * For some graphical characters, the Unicode standard explicitly + * defines a character-cell width via the definition of the East Asian + * FullWidth (F), Wide (W), Half-width (H), and Narrow (Na) classes. + * In all these cases, there is no ambiguity about which width a + * terminal shall use. For characters in the East Asian Ambiguous (A) + * class, the width choice depends purely on a preference of backward + * compatibility with either historic CJK or Western practice. + * Choosing single-width for these characters is easy to justify as + * the appropriate long-term solution, as the CJK practice of + * displaying these characters as double-width comes from historic + * implementation simplicity (8-bit encoded characters were displayed + * single-width and 16-bit ones double-width, even for Greek, + * Cyrillic, etc.) and not any typographic considerations. + * + * Much less clear is the choice of width for the Not East Asian + * (Neutral) class. Existing practice does not dictate a width for any + * of these characters. It would nevertheless make sense + * typographically to allocate two character cells to characters such + * as for instance EM SPACE or VOLUME INTEGRAL, which cannot be + * represented adequately with a single-width glyph. The following + * routines at present merely assign a single-cell width to all + * neutral characters, in the interest of simplicity. This is not + * entirely satisfactory and should be reconsidered before + * establishing a formal standard in this area. At the moment, the + * decision which Not East Asian (Neutral) characters should be + * represented by double-width glyphs cannot yet be answered by + * applying a simple rule from the Unicode database content. Setting + * up a proper standard for the behavior of UTF-8 character terminals + * will require a careful analysis not only of each Unicode character, + * but also of each presentation form, something the author of these + * routines has avoided to do so far. + * + * http://www.unicode.org/unicode/reports/tr11/ + * + * Markus Kuhn -- 2007-05-26 (Unicode 5.0) + * + * Permission to use, copy, modify, and distribute this software + * for any purpose and without fee is hereby granted. The author + * disclaims all warranties with regard to this software. + * + * Latest version: http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c + */ + +struct interval { + int first; + int last; +}; + +/* auxiliary function for binary search in interval table */ +static inline int bisearch(wchar_t ucs, const struct interval *table, int max) { + int min = 0; + int mid; + + if (ucs < table[0].first || ucs > table[max].last) + return 0; + while (max >= min) { + mid = (min + max) / 2; + if (ucs > table[mid].last) + min = mid + 1; + else if (ucs < table[mid].first) + max = mid - 1; + else + return 1; + } + + return 0; +} + +/* The following two functions define the column width of an ISO 10646 + * character as follows: + * + * - The null character (U+0000) has a column width of 0. + * + * - Other C0/C1 control characters and DEL will lead to a return + * value of -1. + * + * - Non-spacing and enclosing combining characters (general + * category code Mn or Me in the Unicode database) have a + * column width of 0. + * + * - SOFT HYPHEN (U+00AD) has a column width of 1. + * + * - Other format characters (general category code Cf in the Unicode + * database) and ZERO WIDTH SPACE (U+200B) have a column width of 0. + * + * - Hangul Jamo medial vowels and final consonants (U+1160-U+11FF) + * have a column width of 0. + * + * - Spacing characters in the East Asian Wide (W) or East Asian + * Full-width (F) category as defined in Unicode Technical + * Report #11 have a column width of 2. + * + * - All remaining characters (including all printable + * ISO 8859-1 and WGL4 characters, Unicode control characters, + * etc.) have a column width of 1. + * + * This implementation assumes that wchar_t characters are encoded + * in ISO 10646. + */ + +static inline int mk_wcwidth(wchar_t ucs) { + /* sorted list of non-overlapping intervals of non-spacing characters */ + /* generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c" */ + static const struct interval combining[] = { + {0x0300, 0x036F}, {0x0483, 0x0486}, {0x0488, 0x0489}, {0x0591, 0x05BD}, + {0x05BF, 0x05BF}, {0x05C1, 0x05C2}, {0x05C4, 0x05C5}, {0x05C7, 0x05C7}, + {0x0600, 0x0603}, {0x0610, 0x0615}, {0x064B, 0x065E}, {0x0670, 0x0670}, + {0x06D6, 0x06E4}, {0x06E7, 0x06E8}, {0x06EA, 0x06ED}, {0x070F, 0x070F}, + {0x0711, 0x0711}, {0x0730, 0x074A}, {0x07A6, 0x07B0}, {0x07EB, 0x07F3}, + {0x0901, 0x0902}, {0x093C, 0x093C}, {0x0941, 0x0948}, {0x094D, 0x094D}, + {0x0951, 0x0954}, {0x0962, 0x0963}, {0x0981, 0x0981}, {0x09BC, 0x09BC}, + {0x09C1, 0x09C4}, {0x09CD, 0x09CD}, {0x09E2, 0x09E3}, {0x0A01, 0x0A02}, + {0x0A3C, 0x0A3C}, {0x0A41, 0x0A42}, {0x0A47, 0x0A48}, {0x0A4B, 0x0A4D}, + {0x0A70, 0x0A71}, {0x0A81, 0x0A82}, {0x0ABC, 0x0ABC}, {0x0AC1, 0x0AC5}, + {0x0AC7, 0x0AC8}, {0x0ACD, 0x0ACD}, {0x0AE2, 0x0AE3}, {0x0B01, 0x0B01}, + {0x0B3C, 0x0B3C}, {0x0B3F, 0x0B3F}, {0x0B41, 0x0B43}, {0x0B4D, 0x0B4D}, + {0x0B56, 0x0B56}, {0x0B82, 0x0B82}, {0x0BC0, 0x0BC0}, {0x0BCD, 0x0BCD}, + {0x0C3E, 0x0C40}, {0x0C46, 0x0C48}, {0x0C4A, 0x0C4D}, {0x0C55, 0x0C56}, + {0x0CBC, 0x0CBC}, {0x0CBF, 0x0CBF}, {0x0CC6, 0x0CC6}, {0x0CCC, 0x0CCD}, + {0x0CE2, 0x0CE3}, {0x0D41, 0x0D43}, {0x0D4D, 0x0D4D}, {0x0DCA, 0x0DCA}, + {0x0DD2, 0x0DD4}, {0x0DD6, 0x0DD6}, {0x0E31, 0x0E31}, {0x0E34, 0x0E3A}, + {0x0E47, 0x0E4E}, {0x0EB1, 0x0EB1}, {0x0EB4, 0x0EB9}, {0x0EBB, 0x0EBC}, + {0x0EC8, 0x0ECD}, {0x0F18, 0x0F19}, {0x0F35, 0x0F35}, {0x0F37, 0x0F37}, + {0x0F39, 0x0F39}, {0x0F71, 0x0F7E}, {0x0F80, 0x0F84}, {0x0F86, 0x0F87}, + {0x0F90, 0x0F97}, {0x0F99, 0x0FBC}, {0x0FC6, 0x0FC6}, {0x102D, 0x1030}, + {0x1032, 0x1032}, {0x1036, 0x1037}, {0x1039, 0x1039}, {0x1058, 0x1059}, + {0x1160, 0x11FF}, {0x135F, 0x135F}, {0x1712, 0x1714}, {0x1732, 0x1734}, + {0x1752, 0x1753}, {0x1772, 0x1773}, {0x17B4, 0x17B5}, {0x17B7, 0x17BD}, + {0x17C6, 0x17C6}, {0x17C9, 0x17D3}, {0x17DD, 0x17DD}, {0x180B, 0x180D}, + {0x18A9, 0x18A9}, {0x1920, 0x1922}, {0x1927, 0x1928}, {0x1932, 0x1932}, + {0x1939, 0x193B}, {0x1A17, 0x1A18}, {0x1B00, 0x1B03}, {0x1B34, 0x1B34}, + {0x1B36, 0x1B3A}, {0x1B3C, 0x1B3C}, {0x1B42, 0x1B42}, {0x1B6B, 0x1B73}, + {0x1DC0, 0x1DCA}, {0x1DFE, 0x1DFF}, {0x200B, 0x200F}, {0x202A, 0x202E}, + {0x2060, 0x2063}, {0x206A, 0x206F}, {0x20D0, 0x20EF}, {0x302A, 0x302F}, + {0x3099, 0x309A}, {0xA806, 0xA806}, {0xA80B, 0xA80B}, {0xA825, 0xA826}, + {0xFB1E, 0xFB1E}, {0xFE00, 0xFE0F}, {0xFE20, 0xFE23}, {0xFEFF, 0xFEFF}, + {0xFFF9, 0xFFFB}, {0x10A01, 0x10A03}, {0x10A05, 0x10A06}, {0x10A0C, 0x10A0F}, + {0x10A38, 0x10A3A}, {0x10A3F, 0x10A3F}, {0x1D167, 0x1D169}, {0x1D173, 0x1D182}, + {0x1D185, 0x1D18B}, {0x1D1AA, 0x1D1AD}, {0x1D242, 0x1D244}, {0xE0001, 0xE0001}, + {0xE0020, 0xE007F}, {0xE0100, 0xE01EF}}; + + /* test for 8-bit control characters */ + if (ucs == 0) + return 0; + if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0)) + return -1; + + /* binary search in table of non-spacing characters */ + if (bisearch(ucs, combining, sizeof(combining) / sizeof(struct interval) - 1)) + return 0; + + /* if we arrive here, ucs is not a combining or C0/C1 control character */ + + return 1 + (ucs >= 0x1100 && + (ucs <= 0x115f || /* Hangul Jamo init. consonants */ + ucs == 0x2329 || ucs == 0x232a || + (ucs >= 0x2e80 && ucs <= 0xa4cf && ucs != 0x303f) || /* CJK ... Yi */ + (ucs >= 0xac00 && ucs <= 0xd7a3) || /* Hangul Syllables */ + (ucs >= 0xf900 && ucs <= 0xfaff) || /* CJK Compatibility Ideographs */ + (ucs >= 0xfe10 && ucs <= 0xfe19) || /* Vertical forms */ + (ucs >= 0xfe30 && ucs <= 0xfe6f) || /* CJK Compatibility Forms */ + (ucs >= 0xff00 && ucs <= 0xff60) || /* Fullwidth Forms */ + (ucs >= 0xffe0 && ucs <= 0xffe6) || (ucs >= 0x20000 && ucs <= 0x2fffd) || + (ucs >= 0x30000 && ucs <= 0x3fffd))); +} + +static inline int mk_wcswidth(const wchar_t *pwcs, size_t n) { + int w, width = 0; + + for (; *pwcs && n-- > 0; pwcs++) + if ((w = mk_wcwidth(*pwcs)) < 0) + return -1; + else + width += w; + + return width; +} + +/* + * The following functions are the same as mk_wcwidth() and + * mk_wcswidth(), except that spacing characters in the East Asian + * Ambiguous (A) category as defined in Unicode Technical Report #11 + * have a column width of 2. This variant might be useful for users of + * CJK legacy encodings who want to migrate to UCS without changing + * the traditional terminal character-width behaviour. It is not + * otherwise recommended for general use. + */ +static inline int mk_wcwidth_cjk(wchar_t ucs) { + /* sorted list of non-overlapping intervals of East Asian Ambiguous + * characters, generated by "uniset +WIDTH-A -cat=Me -cat=Mn -cat=Cf c" */ + static const struct interval ambiguous[] = { + {0x00A1, 0x00A1}, {0x00A4, 0x00A4}, {0x00A7, 0x00A8}, {0x00AA, 0x00AA}, {0x00AE, 0x00AE}, + {0x00B0, 0x00B4}, {0x00B6, 0x00BA}, {0x00BC, 0x00BF}, {0x00C6, 0x00C6}, {0x00D0, 0x00D0}, + {0x00D7, 0x00D8}, {0x00DE, 0x00E1}, {0x00E6, 0x00E6}, {0x00E8, 0x00EA}, {0x00EC, 0x00ED}, + {0x00F0, 0x00F0}, {0x00F2, 0x00F3}, {0x00F7, 0x00FA}, {0x00FC, 0x00FC}, {0x00FE, 0x00FE}, + {0x0101, 0x0101}, {0x0111, 0x0111}, {0x0113, 0x0113}, {0x011B, 0x011B}, {0x0126, 0x0127}, + {0x012B, 0x012B}, {0x0131, 0x0133}, {0x0138, 0x0138}, {0x013F, 0x0142}, {0x0144, 0x0144}, + {0x0148, 0x014B}, {0x014D, 0x014D}, {0x0152, 0x0153}, {0x0166, 0x0167}, {0x016B, 0x016B}, + {0x01CE, 0x01CE}, {0x01D0, 0x01D0}, {0x01D2, 0x01D2}, {0x01D4, 0x01D4}, {0x01D6, 0x01D6}, + {0x01D8, 0x01D8}, {0x01DA, 0x01DA}, {0x01DC, 0x01DC}, {0x0251, 0x0251}, {0x0261, 0x0261}, + {0x02C4, 0x02C4}, {0x02C7, 0x02C7}, {0x02C9, 0x02CB}, {0x02CD, 0x02CD}, {0x02D0, 0x02D0}, + {0x02D8, 0x02DB}, {0x02DD, 0x02DD}, {0x02DF, 0x02DF}, {0x0391, 0x03A1}, {0x03A3, 0x03A9}, + {0x03B1, 0x03C1}, {0x03C3, 0x03C9}, {0x0401, 0x0401}, {0x0410, 0x044F}, {0x0451, 0x0451}, + {0x2010, 0x2010}, {0x2013, 0x2016}, {0x2018, 0x2019}, {0x201C, 0x201D}, {0x2020, 0x2022}, + {0x2024, 0x2027}, {0x2030, 0x2030}, {0x2032, 0x2033}, {0x2035, 0x2035}, {0x203B, 0x203B}, + {0x203E, 0x203E}, {0x2074, 0x2074}, {0x207F, 0x207F}, {0x2081, 0x2084}, {0x20AC, 0x20AC}, + {0x2103, 0x2103}, {0x2105, 0x2105}, {0x2109, 0x2109}, {0x2113, 0x2113}, {0x2116, 0x2116}, + {0x2121, 0x2122}, {0x2126, 0x2126}, {0x212B, 0x212B}, {0x2153, 0x2154}, {0x215B, 0x215E}, + {0x2160, 0x216B}, {0x2170, 0x2179}, {0x2190, 0x2199}, {0x21B8, 0x21B9}, {0x21D2, 0x21D2}, + {0x21D4, 0x21D4}, {0x21E7, 0x21E7}, {0x2200, 0x2200}, {0x2202, 0x2203}, {0x2207, 0x2208}, + {0x220B, 0x220B}, {0x220F, 0x220F}, {0x2211, 0x2211}, {0x2215, 0x2215}, {0x221A, 0x221A}, + {0x221D, 0x2220}, {0x2223, 0x2223}, {0x2225, 0x2225}, {0x2227, 0x222C}, {0x222E, 0x222E}, + {0x2234, 0x2237}, {0x223C, 0x223D}, {0x2248, 0x2248}, {0x224C, 0x224C}, {0x2252, 0x2252}, + {0x2260, 0x2261}, {0x2264, 0x2267}, {0x226A, 0x226B}, {0x226E, 0x226F}, {0x2282, 0x2283}, + {0x2286, 0x2287}, {0x2295, 0x2295}, {0x2299, 0x2299}, {0x22A5, 0x22A5}, {0x22BF, 0x22BF}, + {0x2312, 0x2312}, {0x2460, 0x24E9}, {0x24EB, 0x254B}, {0x2550, 0x2573}, {0x2580, 0x258F}, + {0x2592, 0x2595}, {0x25A0, 0x25A1}, {0x25A3, 0x25A9}, {0x25B2, 0x25B3}, {0x25B6, 0x25B7}, + {0x25BC, 0x25BD}, {0x25C0, 0x25C1}, {0x25C6, 0x25C8}, {0x25CB, 0x25CB}, {0x25CE, 0x25D1}, + {0x25E2, 0x25E5}, {0x25EF, 0x25EF}, {0x2605, 0x2606}, {0x2609, 0x2609}, {0x260E, 0x260F}, + {0x2614, 0x2615}, {0x261C, 0x261C}, {0x261E, 0x261E}, {0x2640, 0x2640}, {0x2642, 0x2642}, + {0x2660, 0x2661}, {0x2663, 0x2665}, {0x2667, 0x266A}, {0x266C, 0x266D}, {0x266F, 0x266F}, + {0x273D, 0x273D}, {0x2776, 0x277F}, {0xE000, 0xF8FF}, {0xFFFD, 0xFFFD}, {0xF0000, 0xFFFFD}, + {0x100000, 0x10FFFD}}; + + /* binary search in table of non-spacing characters */ + if (bisearch(ucs, ambiguous, sizeof(ambiguous) / sizeof(struct interval) - 1)) + return 2; + + return mk_wcwidth(ucs); +} + +static inline int mk_wcswidth_cjk(const wchar_t *pwcs, size_t n) { + int w, width = 0; + + for (; *pwcs && n-- > 0; pwcs++) + if ((w = mk_wcwidth_cjk(*pwcs)) < 0) + return -1; + else + width += w; + + return width; +} + +// convert UTF-8 string to wstring +static inline std::wstring utf8_decode(const std::string &str) { + std::wstring_convert> myconv; + return myconv.from_bytes(str); +} + +// convert wstring to UTF-8 string +static inline std::string utf8_encode(const std::wstring &str) { + std::wstring_convert> myconv; + return myconv.to_bytes(str); +} + +} // namespace details + +static inline int display_width(const std::string &input) { + using namespace unicode::details; + return mk_wcswidth(utf8_decode(input).c_str(), input.size()); +} + +static inline int display_width(const std::wstring &input) { + return details::mk_wcswidth(input.c_str(), input.size()); +} + +#else + +static inline int display_width(const std::string &input) { return input.length(); } + +static inline int display_width(const std::wstring &input) { return input.length(); } + +#endif + +} // namespace unicode + +// #include +// #include + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace indicators { +namespace details { + +inline void set_stream_color(std::ostream &os, Color color) { + switch (color) { + case Color::grey: + os << termcolor::grey; + break; + case Color::red: + os << termcolor::red; + break; + case Color::green: + os << termcolor::green; + break; + case Color::yellow: + os << termcolor::yellow; + break; + case Color::blue: + os << termcolor::blue; + break; + case Color::magenta: + os << termcolor::magenta; + break; + case Color::cyan: + os << termcolor::cyan; + break; + case Color::white: + os << termcolor::white; + break; + default: + assert(false); + } +} + +inline void set_font_style(std::ostream &os, FontStyle style) { + switch (style) { + case FontStyle::bold: + os << termcolor::bold; + break; + case FontStyle::dark: + os << termcolor::dark; + break; + case FontStyle::italic: + os << termcolor::italic; + break; + case FontStyle::underline: + os << termcolor::underline; + break; + case FontStyle::blink: + os << termcolor::blink; + break; + case FontStyle::reverse: + os << termcolor::reverse; + break; + case FontStyle::concealed: + os << termcolor::concealed; + break; + case FontStyle::crossed: + os << termcolor::crossed; + break; + default: + break; + } +} + +inline std::ostream &write_duration(std::ostream &os, std::chrono::nanoseconds ns) { + using namespace std; + using namespace std::chrono; + using days = duration>; + char fill = os.fill(); + os.fill('0'); + auto d = duration_cast(ns); + ns -= d; + auto h = duration_cast(ns); + ns -= h; + auto m = duration_cast(ns); + ns -= m; + auto s = duration_cast(ns); + if (d.count() > 0) + os << setw(2) << d.count() << "d:"; + if (h.count() > 0) + os << setw(2) << h.count() << "h:"; + os << setw(2) << m.count() << "m:" << setw(2) << s.count() << 's'; + os.fill(fill); + return os; +} + +class BlockProgressScaleWriter { +public: + BlockProgressScaleWriter(std::ostream &os, size_t bar_width) : os(os), bar_width(bar_width) {} + + std::ostream &write(float progress) { + std::string fill_text{"█"}; + std::vector lead_characters{" ", "▏", "▎", "▍", "▌", "▋", "▊", "▉"}; + auto value = std::min(1.0f, std::max(0.0f, progress / 100.0f)); + auto whole_width = std::floor(value * bar_width); + auto remainder_width = fmod((value * bar_width), 1.0f); + auto part_width = std::floor(remainder_width * lead_characters.size()); + std::string lead_text = lead_characters[size_t(part_width)]; + if ((bar_width - whole_width - 1) < 0) + lead_text = ""; + for (size_t i = 0; i < whole_width; ++i) + os << fill_text; + os << lead_text; + for (size_t i = 0; i < (bar_width - whole_width - 1); ++i) + os << " "; + return os; + } + +private: + std::ostream &os; + size_t bar_width = 0; +}; + +class ProgressScaleWriter { +public: + ProgressScaleWriter(std::ostream &os, size_t bar_width, const std::string &fill, + const std::string &lead, const std::string &remainder) + : os(os), bar_width(bar_width), fill(fill), lead(lead), remainder(remainder) {} + + std::ostream &write(float progress) { + auto pos = static_cast(progress * bar_width / 100.0); + for (size_t i = 0, current_display_width = 0; i < bar_width;) { + std::string next; + + if (i < pos) { + next = fill; + current_display_width = unicode::display_width(fill); + } else if (i == pos) { + next = lead; + current_display_width = unicode::display_width(lead); + } else { + next = remainder; + current_display_width = unicode::display_width(remainder); + } + + i += current_display_width; + + if (i > bar_width) { + // `next` is larger than the allowed bar width + // fill with empty space instead + os << std::string((bar_width - (i - current_display_width)), ' '); + break; + } + + os << next; + } + return os; + } + +private: + std::ostream &os; + size_t bar_width = 0; + std::string fill; + std::string lead; + std::string remainder; +}; + +class IndeterminateProgressScaleWriter { +public: + IndeterminateProgressScaleWriter(std::ostream &os, size_t bar_width, const std::string &fill, + const std::string &lead) + : os(os), bar_width(bar_width), fill(fill), lead(lead) {} + + std::ostream &write(size_t progress) { + for (size_t i = 0; i < bar_width;) { + std::string next; + size_t current_display_width = 0; + + if (i < progress) { + next = fill; + current_display_width = unicode::display_width(fill); + } else if (i == progress) { + next = lead; + current_display_width = unicode::display_width(lead); + } else { + next = fill; + current_display_width = unicode::display_width(fill); + } + + i += current_display_width; + + if (i > bar_width) { + // `next` is larger than the allowed bar width + // fill with empty space instead + os << std::string((bar_width - (i - current_display_width)), ' '); + break; + } + + os << next; + } + return os; + } + +private: + std::ostream &os; + size_t bar_width = 0; + std::string fill; + std::string lead; +}; + +} // namespace details +} // namespace indicators +#pragma once + +// #include + +#include +#include +#include +#include +// #include +// #include +// #include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace indicators { + +class ProgressBar { + using Settings = + std::tuple; + +public: + template ::type...>::value, + void *>::type = nullptr> + explicit ProgressBar(Args &&... args) + : settings_( + details::get(option::BarWidth{100}, + std::forward(args)...), + details::get(option::PrefixText{}, + std::forward(args)...), + details::get(option::PostfixText{}, + std::forward(args)...), + details::get(option::Start{"["}, + std::forward(args)...), + details::get(option::End{"]"}, + std::forward(args)...), + details::get(option::Fill{"="}, + std::forward(args)...), + details::get(option::Lead{">"}, + std::forward(args)...), + details::get(option::Remainder{" "}, + std::forward(args)...), + details::get( + option::MaxPostfixTextLen{0}, std::forward(args)...), + details::get(option::Completed{false}, + std::forward(args)...), + details::get(option::ShowPercentage{false}, + std::forward(args)...), + details::get( + option::ShowElapsedTime{false}, std::forward(args)...), + details::get( + option::ShowRemainingTime{false}, std::forward(args)...), + details::get( + option::SavedStartTime{false}, std::forward(args)...), + details::get( + option::ForegroundColor{Color::unspecified}, std::forward(args)...), + details::get( + option::FontStyles{std::vector{}}, std::forward(args)...), + details::get(option::MinProgress{0}, + std::forward(args)...), + details::get(option::MaxProgress{100}, + std::forward(args)...), + details::get( + option::ProgressType{ProgressType::incremental}, std::forward(args)...), + details::get(option::Stream{std::cout}, + std::forward(args)...)) { + + // if progress is incremental, start from min_progress + // else start from max_progress + const auto type = get_value(); + if (type == ProgressType::incremental) + progress_ = get_value(); + else + progress_ = get_value(); + } + + template + void set_option(details::Setting &&setting) { + static_assert(!std::is_same( + std::declval()))>::type>::value, + "Setting has wrong type!"); + std::lock_guard lock(mutex_); + get_value() = std::move(setting).value; + } + + template + void set_option(const details::Setting &setting) { + static_assert(!std::is_same( + std::declval()))>::type>::value, + "Setting has wrong type!"); + std::lock_guard lock(mutex_); + get_value() = setting.value; + } + + void set_option( + const details::Setting &setting) { + std::lock_guard lock(mutex_); + get_value() = setting.value; + if (setting.value.length() > get_value()) { + get_value() = setting.value.length(); + } + } + + void + set_option(details::Setting &&setting) { + std::lock_guard lock(mutex_); + get_value() = std::move(setting).value; + auto &new_value = get_value(); + if (new_value.length() > get_value()) { + get_value() = new_value.length(); + } + } + + void set_progress(size_t new_progress) { + { + std::lock_guard lock(mutex_); + progress_ = new_progress; + } + + save_start_time(); + print_progress(); + } + + void tick() { + { + std::lock_guard lock{mutex_}; + const auto type = get_value(); + if (type == ProgressType::incremental) + progress_ += 1; + else + progress_ -= 1; + } + save_start_time(); + print_progress(); + } + + size_t current() { + std::lock_guard lock{mutex_}; + return std::min(progress_, size_t(get_value())); + } + + bool is_completed() const { return get_value(); } + + void mark_as_completed() { + get_value() = true; + print_progress(); + } + +private: + template + auto get_value() -> decltype((details::get_value(std::declval()).value)) { + return details::get_value(settings_).value; + } + + template + auto get_value() const + -> decltype((details::get_value(std::declval()).value)) { + return details::get_value(settings_).value; + } + + size_t progress_{0}; + Settings settings_; + std::chrono::nanoseconds elapsed_; + std::chrono::time_point start_time_point_; + std::mutex mutex_; + + template friend class MultiProgress; + template friend class DynamicProgress; + std::atomic multi_progress_mode_{false}; + + void save_start_time() { + auto &show_elapsed_time = get_value(); + auto &saved_start_time = get_value(); + auto &show_remaining_time = get_value(); + if ((show_elapsed_time || show_remaining_time) && !saved_start_time) { + start_time_point_ = std::chrono::high_resolution_clock::now(); + saved_start_time = true; + } + } + + std::pair get_prefix_text() { + std::stringstream os; + os << get_value(); + const auto result = os.str(); + const auto result_size = unicode::display_width(result); + return {result, result_size}; + } + + std::pair get_postfix_text() { + std::stringstream os; + const auto max_progress = get_value(); + + if (get_value()) { + os << " " + << std::min(static_cast(static_cast(progress_) / max_progress * 100), + size_t(100)) + << "%"; + } + + auto &saved_start_time = get_value(); + + if (get_value()) { + os << " ["; + if (saved_start_time) + details::write_duration(os, elapsed_); + else + os << "00:00s"; + } + + if (get_value()) { + if (get_value()) + os << "<"; + else + os << " ["; + + if (saved_start_time) { + auto eta = std::chrono::nanoseconds( + progress_ > 0 ? static_cast(elapsed_.count() * max_progress / progress_) + : 0); + auto remaining = eta > elapsed_ ? (eta - elapsed_) : (elapsed_ - eta); + details::write_duration(os, remaining); + } else { + os << "00:00s"; + } + + os << "]"; + } else { + if (get_value()) + os << "]"; + } + + os << " " << get_value(); + + const auto result = os.str(); + const auto result_size = unicode::display_width(result); + return {result, result_size}; + } + +public: + void print_progress(bool from_multi_progress = false) { + std::lock_guard lock{mutex_}; + + auto &os = get_value(); + + const auto type = get_value(); + const auto min_progress = get_value(); + const auto max_progress = get_value(); + if (multi_progress_mode_ && !from_multi_progress) { + if ((type == ProgressType::incremental && progress_ >= max_progress) || + (type == ProgressType::decremental && progress_ <= min_progress)) { + get_value() = true; + } + return; + } + auto now = std::chrono::high_resolution_clock::now(); + if (!get_value()) + elapsed_ = std::chrono::duration_cast(now - start_time_point_); + + if (get_value() != Color::unspecified) + details::set_stream_color(os, get_value()); + + for (auto &style : get_value()) + details::set_font_style(os, style); + + const auto prefix_pair = get_prefix_text(); + const auto prefix_text = prefix_pair.first; + const auto prefix_length = prefix_pair.second; + os << prefix_text; + + os << get_value(); + + details::ProgressScaleWriter writer{os, get_value(), + get_value(), + get_value(), + get_value()}; + writer.write(double(progress_) / double(max_progress) * 100.0f); + + os << get_value(); + + const auto postfix_pair = get_postfix_text(); + const auto postfix_text = postfix_pair.first; + const auto postfix_length = postfix_pair.second; + os << postfix_text; + + // Get length of prefix text and postfix text + const auto start_length = get_value().size(); + const auto bar_width = get_value(); + const auto end_length = get_value().size(); + const auto terminal_width = terminal_size().second; + // prefix + bar_width + postfix should be <= terminal_width + const int remaining = + terminal_width - (prefix_length + start_length + bar_width + end_length + postfix_length); + if (remaining > 0) { + os << std::string(remaining, ' ') << "\r"; + } else if (remaining < 0) { + // Do nothing. Maybe in the future truncate postfix with ... + } + os.flush(); + + if ((type == ProgressType::incremental && progress_ >= max_progress) || + (type == ProgressType::decremental && progress_ <= min_progress)) { + get_value() = true; + } + if (get_value() && + !from_multi_progress) // Don't std::endl if calling from MultiProgress + os << termcolor::reset << std::endl; + } +}; + +} // namespace indicators + +#pragma once + +// #include +// #include + +#include +#include +#include +// #include +// #include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace indicators { + +class BlockProgressBar { + using Settings = std::tuple; + +public: + template ::type...>::value, + void *>::type = nullptr> + explicit BlockProgressBar(Args &&... args) + : settings_(details::get( + option::ForegroundColor{Color::unspecified}, std::forward(args)...), + details::get(option::BarWidth{100}, + std::forward(args)...), + details::get(option::Start{"["}, + std::forward(args)...), + details::get(option::End{"]"}, + std::forward(args)...), + details::get( + option::PrefixText{""}, std::forward(args)...), + details::get( + option::PostfixText{""}, std::forward(args)...), + details::get( + option::ShowPercentage{true}, std::forward(args)...), + details::get( + option::ShowElapsedTime{false}, std::forward(args)...), + details::get( + option::ShowRemainingTime{false}, std::forward(args)...), + details::get(option::Completed{false}, + std::forward(args)...), + details::get( + option::SavedStartTime{false}, std::forward(args)...), + details::get( + option::MaxPostfixTextLen{0}, std::forward(args)...), + details::get( + option::FontStyles{std::vector{}}, std::forward(args)...), + details::get( + option::MaxProgress{100}, std::forward(args)...), + details::get(option::Stream{std::cout}, + std::forward(args)...)) {} + + template + void set_option(details::Setting &&setting) { + static_assert(!std::is_same( + std::declval()))>::type>::value, + "Setting has wrong type!"); + std::lock_guard lock(mutex_); + get_value() = std::move(setting).value; + } + + template + void set_option(const details::Setting &setting) { + static_assert(!std::is_same( + std::declval()))>::type>::value, + "Setting has wrong type!"); + std::lock_guard lock(mutex_); + get_value() = setting.value; + } + + void set_option( + const details::Setting &setting) { + std::lock_guard lock(mutex_); + get_value() = setting.value; + if (setting.value.length() > get_value()) { + get_value() = setting.value.length(); + } + } + + void + set_option(details::Setting &&setting) { + std::lock_guard lock(mutex_); + get_value() = std::move(setting).value; + auto &new_value = get_value(); + if (new_value.length() > get_value()) { + get_value() = new_value.length(); + } + } + + void set_progress(float value) { + if (is_completed()) + return; + { + std::lock_guard lock{mutex_}; + progress_ = value; + } + save_start_time(); + print_progress(); + } + + void tick() { + if (is_completed()) + return; + { + std::lock_guard lock{mutex_}; + progress_ += 1; + } + save_start_time(); + print_progress(); + } + + size_t current() { + std::lock_guard lock{mutex_}; + return std::min(static_cast(progress_), + size_t(get_value())); + } + + bool is_completed() const { return get_value(); } + + void mark_as_completed() { + if (!get_value()) + print_progress(); + get_value() = true; + } + +private: + template + auto get_value() -> decltype((details::get_value(std::declval()).value)) { + return details::get_value(settings_).value; + } + + template + auto get_value() const + -> decltype((details::get_value(std::declval()).value)) { + return details::get_value(settings_).value; + } + + Settings settings_; + float progress_{0.0}; + std::chrono::time_point start_time_point_; + std::mutex mutex_; + + template friend class MultiProgress; + template friend class DynamicProgress; + std::atomic multi_progress_mode_{false}; + + void save_start_time() { + auto &show_elapsed_time = get_value(); + auto &saved_start_time = get_value(); + auto &show_remaining_time = get_value(); + if ((show_elapsed_time || show_remaining_time) && !saved_start_time) { + start_time_point_ = std::chrono::high_resolution_clock::now(); + saved_start_time = true; + } + } + + std::pair get_prefix_text() { + std::stringstream os; + os << get_value(); + const auto result = os.str(); + const auto result_size = unicode::display_width(result); + return {result, result_size}; + } + + std::pair get_postfix_text() { + std::stringstream os; + const auto max_progress = get_value(); + auto now = std::chrono::high_resolution_clock::now(); + auto elapsed = std::chrono::duration_cast(now - start_time_point_); + + if (get_value()) { + os << " " << std::min(static_cast(progress_ / max_progress * 100.0), size_t(100)) + << "%"; + } + + auto &saved_start_time = get_value(); + + if (get_value()) { + os << " ["; + if (saved_start_time) + details::write_duration(os, elapsed); + else + os << "00:00s"; + } + + if (get_value()) { + if (get_value()) + os << "<"; + else + os << " ["; + + if (saved_start_time) { + auto eta = std::chrono::nanoseconds( + progress_ > 0 ? static_cast(elapsed.count() * max_progress / progress_) : 0); + auto remaining = eta > elapsed ? (eta - elapsed) : (elapsed - eta); + details::write_duration(os, remaining); + } else { + os << "00:00s"; + } + + os << "]"; + } else { + if (get_value()) + os << "]"; + } + + os << " " << get_value(); + + const auto result = os.str(); + const auto result_size = unicode::display_width(result); + return {result, result_size}; + } + +public: + void print_progress(bool from_multi_progress = false) { + std::lock_guard lock{mutex_}; + + auto &os = get_value(); + + const auto max_progress = get_value(); + if (multi_progress_mode_ && !from_multi_progress) { + if (progress_ > max_progress) { + get_value() = true; + } + return; + } + + if (get_value() != Color::unspecified) + details::set_stream_color(os, get_value()); + + for (auto &style : get_value()) + details::set_font_style(os, style); + + const auto prefix_pair = get_prefix_text(); + const auto prefix_text = prefix_pair.first; + const auto prefix_length = prefix_pair.second; + os << prefix_text; + + os << get_value(); + + details::BlockProgressScaleWriter writer{os, + get_value()}; + writer.write(progress_ / max_progress * 100); + + os << get_value(); + + const auto postfix_pair = get_postfix_text(); + const auto postfix_text = postfix_pair.first; + const auto postfix_length = postfix_pair.second; + os << postfix_text; + + // Get length of prefix text and postfix text + const auto start_length = get_value().size(); + const auto bar_width = get_value(); + const auto end_length = get_value().size(); + const auto terminal_width = terminal_size().second; + // prefix + bar_width + postfix should be <= terminal_width + const int remaining = + terminal_width - (prefix_length + start_length + bar_width + end_length + postfix_length); + if (remaining > 0) { + os << std::string(remaining, ' ') << "\r"; + } else if (remaining < 0) { + // Do nothing. Maybe in the future truncate postfix with ... + } + os.flush(); + + if (progress_ == max_progress) { + get_value() = true; + } + if (get_value() && + !from_multi_progress) // Don't std::endl if calling from MultiProgress + os << termcolor::reset << std::endl; + } +}; + +} // namespace indicators + +#pragma once + +// #include + +#include +#include +#include +#include +// #include +// #include +// #include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace indicators { + +class IndeterminateProgressBar { + using Settings = + std::tuple; + + enum class Direction { forward, backward }; + + Direction direction_{Direction::forward}; + +public: + template ::type...>::value, + void *>::type = nullptr> + explicit IndeterminateProgressBar(Args &&... args) + : settings_(details::get(option::BarWidth{100}, + std::forward(args)...), + details::get( + option::PrefixText{}, std::forward(args)...), + details::get( + option::PostfixText{}, std::forward(args)...), + details::get(option::Start{"["}, + std::forward(args)...), + details::get(option::End{"]"}, + std::forward(args)...), + details::get(option::Fill{"."}, + std::forward(args)...), + details::get(option::Lead{"<==>"}, + std::forward(args)...), + details::get( + option::MaxPostfixTextLen{0}, std::forward(args)...), + details::get(option::Completed{false}, + std::forward(args)...), + details::get( + option::ForegroundColor{Color::unspecified}, std::forward(args)...), + details::get( + option::FontStyles{std::vector{}}, std::forward(args)...), + details::get(option::Stream{std::cout}, + std::forward(args)...)) { + // starts with [<==>...........] + // progress_ = 0 + + // ends with [...........<==>] + // ^^^^^^^^^^^^^^^^^ bar_width + // ^^^^^^^^^^^^ (bar_width - len(lead)) + // progress_ = bar_width - len(lead) + progress_ = 0; + max_progress_ = get_value() - + get_value().size() + + get_value().size() + + get_value().size(); + } + + template + void set_option(details::Setting &&setting) { + static_assert(!std::is_same( + std::declval()))>::type>::value, + "Setting has wrong type!"); + std::lock_guard lock(mutex_); + get_value() = std::move(setting).value; + } + + template + void set_option(const details::Setting &setting) { + static_assert(!std::is_same( + std::declval()))>::type>::value, + "Setting has wrong type!"); + std::lock_guard lock(mutex_); + get_value() = setting.value; + } + + void set_option( + const details::Setting &setting) { + std::lock_guard lock(mutex_); + get_value() = setting.value; + if (setting.value.length() > get_value()) { + get_value() = setting.value.length(); + } + } + + void + set_option(details::Setting &&setting) { + std::lock_guard lock(mutex_); + get_value() = std::move(setting).value; + auto &new_value = get_value(); + if (new_value.length() > get_value()) { + get_value() = new_value.length(); + } + } + + void tick() { + { + std::lock_guard lock{mutex_}; + if (get_value()) + return; + + progress_ += (direction_ == Direction::forward) ? 1 : -1; + if (direction_ == Direction::forward && progress_ == max_progress_) { + // time to go back + direction_ = Direction::backward; + } else if (direction_ == Direction::backward && progress_ == 0) { + direction_ = Direction::forward; + } + } + print_progress(); + } + + bool is_completed() { return get_value(); } + + void mark_as_completed() { + get_value() = true; + print_progress(); + } + +private: + template + auto get_value() -> decltype((details::get_value(std::declval()).value)) { + return details::get_value(settings_).value; + } + + template + auto get_value() const + -> decltype((details::get_value(std::declval()).value)) { + return details::get_value(settings_).value; + } + + size_t progress_{0}; + size_t max_progress_; + Settings settings_; + std::chrono::nanoseconds elapsed_; + std::mutex mutex_; + + template friend class MultiProgress; + template friend class DynamicProgress; + std::atomic multi_progress_mode_{false}; + + std::pair get_prefix_text() { + std::stringstream os; + os << get_value(); + const auto result = os.str(); + const auto result_size = unicode::display_width(result); + return {result, result_size}; + } + + std::pair get_postfix_text() { + std::stringstream os; + os << " " << get_value(); + + const auto result = os.str(); + const auto result_size = unicode::display_width(result); + return {result, result_size}; + } + +public: + void print_progress(bool from_multi_progress = false) { + std::lock_guard lock{mutex_}; + + auto &os = get_value(); + + if (multi_progress_mode_ && !from_multi_progress) { + return; + } + if (get_value() != Color::unspecified) + details::set_stream_color(os, get_value()); + + for (auto &style : get_value()) + details::set_font_style(os, style); + + const auto prefix_pair = get_prefix_text(); + const auto prefix_text = prefix_pair.first; + const auto prefix_length = prefix_pair.second; + os << prefix_text; + + os << get_value(); + + details::IndeterminateProgressScaleWriter writer{ + os, get_value(), + get_value(), + get_value()}; + writer.write(progress_); + + os << get_value(); + + const auto postfix_pair = get_postfix_text(); + const auto postfix_text = postfix_pair.first; + const auto postfix_length = postfix_pair.second; + os << postfix_text; + + // Get length of prefix text and postfix text + const auto start_length = get_value().size(); + const auto bar_width = get_value(); + const auto end_length = get_value().size(); + const auto terminal_width = terminal_size().second; + // prefix + bar_width + postfix should be <= terminal_width + const int remaining = + terminal_width - (prefix_length + start_length + bar_width + end_length + postfix_length); + if (remaining > 0) { + os << std::string(remaining, ' ') << "\r"; + } else if (remaining < 0) { + // Do nothing. Maybe in the future truncate postfix with ... + } + os.flush(); + + if (get_value() && + !from_multi_progress) // Don't std::endl if calling from MultiProgress + os << termcolor::reset << std::endl; + } +}; + +} // namespace indicators + +#pragma once +#include +#include +#include +#include +#include + +// #include +// #include +// #include + +namespace indicators { + +template class MultiProgress { +public: + template ::type> + explicit MultiProgress(Indicators &... bars) { + bars_ = {bars...}; + for (auto &bar : bars_) { + bar.get().multi_progress_mode_ = true; + } + } + + template + typename std::enable_if<(index >= 0 && index < count), void>::type set_progress(size_t value) { + if (!bars_[index].get().is_completed()) + bars_[index].get().set_progress(value); + print_progress(); + } + + template + typename std::enable_if<(index >= 0 && index < count), void>::type set_progress(float value) { + if (!bars_[index].get().is_completed()) + bars_[index].get().set_progress(value); + print_progress(); + } + + template + typename std::enable_if<(index >= 0 && index < count), void>::type tick() { + if (!bars_[index].get().is_completed()) + bars_[index].get().tick(); + print_progress(); + } + + template + typename std::enable_if<(index >= 0 && index < count), bool>::type is_completed() const { + return bars_[index].get().is_completed(); + } + +private: + std::atomic started_{false}; + std::mutex mutex_; + std::vector> bars_; + + bool _all_completed() { + bool result{true}; + for (size_t i = 0; i < count; ++i) + result &= bars_[i].get().is_completed(); + return result; + } + +public: + void print_progress() { + std::lock_guard lock{mutex_}; + if (started_) + move_up(count); + for (auto &bar : bars_) { + bar.get().print_progress(true); + std::cout << "\n"; + } + std::cout << termcolor::reset; + if (!started_) + started_ = true; + } +}; + +} // namespace indicators + +#pragma once +#include +#include +// #include +// #include +// #include +#include +#include +#include + +namespace indicators { + +template class DynamicProgress { + using Settings = std::tuple; + +public: + template explicit DynamicProgress(Indicators &... bars) { + bars_ = {bars...}; + for (auto &bar : bars_) { + bar.get().multi_progress_mode_ = true; + ++total_count_; + ++incomplete_count_; + } + } + + Indicator &operator[](size_t index) { + print_progress(); + std::lock_guard lock{mutex_}; + return bars_[index].get(); + } + + size_t push_back(Indicator &bar) { + std::lock_guard lock{mutex_}; + bar.multi_progress_mode_ = true; + bars_.push_back(bar); + return bars_.size() - 1; + } + + template + void set_option(details::Setting &&setting) { + static_assert(!std::is_same( + std::declval()))>::type>::value, + "Setting has wrong type!"); + std::lock_guard lock(mutex_); + get_value() = std::move(setting).value; + } + + template + void set_option(const details::Setting &setting) { + static_assert(!std::is_same( + std::declval()))>::type>::value, + "Setting has wrong type!"); + std::lock_guard lock(mutex_); + get_value() = setting.value; + } + +private: + Settings settings_; + std::atomic started_{false}; + std::mutex mutex_; + std::vector> bars_; + std::atomic total_count_{0}; + std::atomic incomplete_count_{0}; + + template + auto get_value() -> decltype((details::get_value(std::declval()).value)) { + return details::get_value(settings_).value; + } + + template + auto get_value() const + -> decltype((details::get_value(std::declval()).value)) { + return details::get_value(settings_).value; + } + +public: + void print_progress() { + std::lock_guard lock{mutex_}; + auto &hide_bar_when_complete = get_value(); + if (hide_bar_when_complete) { + // Hide completed bars + if (started_) { + for (size_t i = 0; i < incomplete_count_; ++i) + std::cout << "\033[A\r\033[K" << std::flush; + } + incomplete_count_ = 0; + for (auto &bar : bars_) { + if (!bar.get().is_completed()) { + bar.get().print_progress(true); + std::cout << "\n"; + ++incomplete_count_; + } + } + if (!started_) + started_ = true; + } else { + // Don't hide any bars + if (started_) { + for (size_t i = 0; i < total_count_; ++i) + std::cout << "\x1b[A"; + } + for (auto &bar : bars_) { + bar.get().print_progress(true); + std::cout << "\n"; + } + if (!started_) + started_ = true; + } + total_count_ = bars_.size(); + std::cout << termcolor::reset; + } +}; + +} // namespace indicators + +#pragma once + +// #include + +#include +#include +#include +#include +// #include +// #include +#include +#include +#include +#include +#include +#include +#include + +namespace indicators { + +class ProgressSpinner { + using Settings = + std::tuple; + +public: + template ::type...>::value, + void *>::type = nullptr> + explicit ProgressSpinner(Args &&... args) + : settings_( + details::get( + option::ForegroundColor{Color::unspecified}, std::forward(args)...), + details::get(option::PrefixText{}, + std::forward(args)...), + details::get(option::PostfixText{}, + std::forward(args)...), + details::get(option::ShowPercentage{true}, + std::forward(args)...), + details::get( + option::ShowElapsedTime{false}, std::forward(args)...), + details::get( + option::ShowRemainingTime{false}, std::forward(args)...), + details::get(option::ShowSpinner{true}, + std::forward(args)...), + details::get( + option::SavedStartTime{false}, std::forward(args)...), + details::get(option::Completed{false}, + std::forward(args)...), + details::get( + option::MaxPostfixTextLen{0}, std::forward(args)...), + details::get( + option::SpinnerStates{ + std::vector{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"}}, + std::forward(args)...), + details::get( + option::FontStyles{std::vector{}}, std::forward(args)...), + details::get(option::MaxProgress{100}, + std::forward(args)...), + details::get(option::Stream{std::cout}, + std::forward(args)...)) {} + + template + void set_option(details::Setting &&setting) { + static_assert(!std::is_same( + std::declval()))>::type>::value, + "Setting has wrong type!"); + std::lock_guard lock(mutex_); + get_value() = std::move(setting).value; + } + + template + void set_option(const details::Setting &setting) { + static_assert(!std::is_same( + std::declval()))>::type>::value, + "Setting has wrong type!"); + std::lock_guard lock(mutex_); + get_value() = setting.value; + } + + void set_option( + const details::Setting &setting) { + std::lock_guard lock(mutex_); + get_value() = setting.value; + if (setting.value.length() > get_value()) { + get_value() = setting.value.length(); + } + } + + void + set_option(details::Setting &&setting) { + std::lock_guard lock(mutex_); + get_value() = std::move(setting).value; + auto &new_value = get_value(); + if (new_value.length() > get_value()) { + get_value() = new_value.length(); + } + } + + void set_progress(size_t value) { + { + std::lock_guard lock{mutex_}; + progress_ = value; + } + save_start_time(); + print_progress(); + } + + void tick() { + { + std::lock_guard lock{mutex_}; + progress_ += 1; + } + save_start_time(); + print_progress(); + } + + size_t current() { + std::lock_guard lock{mutex_}; + return std::min(progress_, size_t(get_value())); + } + + bool is_completed() const { return get_value(); } + + void mark_as_completed() { + get_value() = true; + print_progress(); + } + +private: + Settings settings_; + size_t progress_{0}; + size_t index_{0}; + std::chrono::time_point start_time_point_; + std::mutex mutex_; + + template + auto get_value() -> decltype((details::get_value(std::declval()).value)) { + return details::get_value(settings_).value; + } + + template + auto get_value() const + -> decltype((details::get_value(std::declval()).value)) { + return details::get_value(settings_).value; + } + + void save_start_time() { + auto &show_elapsed_time = get_value(); + auto &show_remaining_time = get_value(); + auto &saved_start_time = get_value(); + if ((show_elapsed_time || show_remaining_time) && !saved_start_time) { + start_time_point_ = std::chrono::high_resolution_clock::now(); + saved_start_time = true; + } + } + +public: + void print_progress() { + std::lock_guard lock{mutex_}; + + auto &os = get_value(); + + const auto max_progress = get_value(); + auto now = std::chrono::high_resolution_clock::now(); + auto elapsed = std::chrono::duration_cast(now - start_time_point_); + + if (get_value() != Color::unspecified) + details::set_stream_color(os, get_value()); + + for (auto &style : get_value()) + details::set_font_style(os, style); + + os << get_value(); + if (get_value()) + os << get_value() + [index_ % get_value().size()]; + if (get_value()) { + os << " " << std::size_t(progress_ / double(max_progress) * 100) << "%"; + } + + if (get_value()) { + os << " ["; + details::write_duration(os, elapsed); + } + + if (get_value()) { + if (get_value()) + os << "<"; + else + os << " ["; + auto eta = std::chrono::nanoseconds( + progress_ > 0 ? static_cast(elapsed.count() * max_progress / progress_) : 0); + auto remaining = eta > elapsed ? (eta - elapsed) : (elapsed - eta); + details::write_duration(os, remaining); + os << "]"; + } else { + if (get_value()) + os << "]"; + } + + if (get_value() == 0) + get_value() = 10; + os << " " << get_value() + << std::string(get_value(), ' ') << "\r"; + os.flush(); + index_ += 1; + if (progress_ > max_progress) { + get_value() = true; + } + if (get_value()) + os << termcolor::reset << std::endl; + } +}; + +} // namespace indicators + + +#pragma once +#include +#include +#include +#include +#include + +namespace criterion { + +struct benchmark_config { + static inline std::tuple<> empty_tuple{}; + std::string name; + using Fn = std::function &, // teardown time stamp + void *parameters)>; // benchmark parameters + Fn fn; + std::string parameterized_instance_name = ""; + void *parameters = (void *)(&empty_tuple); + + enum class benchmark_reporting_type { console }; + benchmark_reporting_type reporting_type = benchmark_reporting_type::console; +}; + +} // namespace criterion + +#pragma once +#include +// #include +#include +#include + +namespace criterion { + +struct benchmark_result { + std::string name; + std::size_t num_warmup_runs; + std::size_t num_runs; + std::size_t num_iterations; + + long double lowest_rsd; // Lowest relative standard deviation (RSD) + long double lowest_rsd_mean; // mean @ lowest RSD + std::size_t lowest_rsd_index; // which run had the lowest RSD best estimate + + long double warmup_execution_time; // execution time during warmup + long double mean_execution_time; // global mean execution time + long double fastest_execution_time; // global best execution time + long double slowest_execution_time; // global worst execution time + + long double average_iteration_performance; // iterations per second in the average case + long double fastest_iteration_performance; // iterations per second in the fastest case + long double slowest_iteration_performance; // iterations per second in the slowest case + + std::string to_csv() const { + std::stringstream os; + + os << '"' << name << "\","; + os << std::fixed << std::setprecision(2) << num_warmup_runs << ',' << num_runs * num_iterations + << ',' << mean_execution_time << ',' << fastest_execution_time << ',' + << slowest_execution_time << ',' << lowest_rsd_mean << ',' << lowest_rsd << ',' + << lowest_rsd_index << ',' << average_iteration_performance << ',' + << fastest_iteration_performance << ',' << slowest_iteration_performance; + + return os.str(); + } + + std::string to_json() const { + std::stringstream os; + os << std::fixed << std::setprecision(2) << " {\n" + << " \"name\": \"" << name << "\",\n" + << " \"warmup_runs\": " << num_warmup_runs << ",\n" + << " \"iterations\": " << num_runs * num_iterations << ",\n" + << " \"mean_execution_time\": " << mean_execution_time << ",\n" + << " \"fastest_execution_time\": " << fastest_execution_time << ",\n" + << " \"slowest_execution_time\": " << slowest_execution_time << ",\n" + << " \"lowest_rsd_execution_time\": " << lowest_rsd_mean << ",\n" + << " \"lowest_rsd_percentage\": " << lowest_rsd << ",\n" + << " \"lowest_rsd_index\": " << lowest_rsd_index << ",\n" + << " \"average_iteration_performance\": " << average_iteration_performance << ",\n" + << " \"fastest_iteration_performance\": " << fastest_iteration_performance << ",\n" + << " \"slowest_iteration_performance\": " << slowest_iteration_performance << "\n" + << " }"; + + return os.str(); + } + + std::string to_md() const { + std::stringstream os; + os << std::fixed << std::setprecision(2) << "|" << name << "|" << num_warmup_runs << "|" + << num_runs * num_iterations << "|" << mean_execution_time << "|" << fastest_execution_time + << "|" << slowest_execution_time << "|" << lowest_rsd_mean << "|" << lowest_rsd << "|" + << lowest_rsd_index << "|" << average_iteration_performance << "|" + << fastest_iteration_performance << "|" << slowest_iteration_performance << "\n"; + return os.str(); + } +}; + +} // namespace criterion +// #include +#include +#include +#include + +namespace criterion { + +class console_writer { + static std::string duration_to_string(const long double &ns) { + const auto duration = std::abs(ns); + std::stringstream os; + if (duration < 1E3) { + os << std::fixed << std::setprecision(2) << duration << " ns"; + } else if (duration < 1E6) { + os << std::fixed << std::setprecision(2) << (duration / 1E3) << " us"; + } else if (duration < 1E9) { + os << std::fixed << std::setprecision(2) << (duration / 1E6) << " ms"; + } else { + os << std::fixed << std::setprecision(2) << (duration / 1E9) << " s"; + } + + std::string result{""}; + if (ns < 0) { + result += "-"; + } + result += os.str(); + return result; + } + + static std::string ordinal(std::size_t n) { + static const std::array ends{"th", "st", "nd", "rd", "th", + "th", "th", "th", "th", "th"}; + if (((n % 100) >= 11) && ((n % 100) <= 13)) { + return std::to_string(n) + "th"; + } else { + return std::to_string(n) + ends[n % 10]; + } + } + +public: + static void write_result(const benchmark_result &result) { + std::cout << termcolor::bold << termcolor::green << " ✓ " << result.name << termcolor::reset + << "\n"; + + std::cout << " " << termcolor::bold << termcolor::underline << "Configuration" + << termcolor::reset << "\n"; + + std::cout << " " << result.num_warmup_runs << " warmup runs, " << result.num_runs + << (result.num_runs > 1 ? " benchmark runs, " : " benchmark run, ") + << result.num_iterations << " iterations per run\n"; + + std::cout << " " << termcolor::bold << termcolor::underline << "Execution Time" + << termcolor::reset << "\n"; + + std::cout << termcolor::yellow << termcolor::bold << termcolor::italic << " Average " + << std::right << std::setw(10) << duration_to_string(result.mean_execution_time) + << termcolor::reset << "\n"; + + const auto best_mean_difference = result.fastest_execution_time - result.mean_execution_time; + const auto best_mean_percentage_difference = + (best_mean_difference / static_cast(result.mean_execution_time) * 100.0); + + std::cout << " Fastest " << std::right << std::setw(10) + << duration_to_string(result.fastest_execution_time); + std::cout << " (" << termcolor::green << duration_to_string(best_mean_difference) << ", " + << std::setprecision(2) << std::fixed << best_mean_percentage_difference << " %" + << termcolor::reset << ")" + << "\n"; + + const auto worst_mean_difference = result.slowest_execution_time - result.mean_execution_time; + const auto worst_mean_percentage_difference = + (worst_mean_difference / static_cast(result.mean_execution_time) * 100.0); + + std::cout << " Slowest " << std::right << std::setw(10) + << duration_to_string(result.slowest_execution_time); + std::cout << " (" << termcolor::red << duration_to_string(worst_mean_difference) << ", " + << std::setprecision(2) << std::fixed << worst_mean_percentage_difference << " %" + << termcolor::reset << ")" + << "\n"; + + std::cout << termcolor::bold << termcolor::white << " Lowest RSD " << std::right + << std::setw(10) << duration_to_string(result.lowest_rsd_mean) << " ± " + << std::setprecision(2) << result.lowest_rsd << "%" + << " (" << ordinal(result.lowest_rsd_index) << " run)" << termcolor::reset << "\n"; + + std::cout << " " << termcolor::bold << termcolor::underline << "Performance" + << termcolor::reset << "\n"; + + std::cout << " Average " << std::setprecision(2) << std::fixed << std::right + << std::setw(10) << result.average_iteration_performance << " iterations/s" + << termcolor::reset << "\n"; + + const auto best_mean_iterations_difference = + result.average_iteration_performance - result.fastest_iteration_performance; + const auto best_mean_iterations_percentage_difference = + (best_mean_iterations_difference / + static_cast(result.average_iteration_performance) * 100.0); + + std::cout << " Fastest " << std::setprecision(2) << std::fixed << std::right + << std::setw(10) << result.fastest_iteration_performance << " iterations/s" + << termcolor::reset; + std::cout << " (" << termcolor::green << std::setprecision(2) << std::fixed + << best_mean_iterations_difference << " iterations/s, " + << best_mean_iterations_percentage_difference << " %" << termcolor::reset << ")" + << "\n"; + + const auto worst_mean_iterations_difference = + result.average_iteration_performance - result.slowest_iteration_performance; + const auto worst_mean_iterations_percentage_difference = + (worst_mean_iterations_difference / + static_cast(result.average_iteration_performance) * 100.0); + + std::cout << " Slowest " << std::setprecision(2) << std::fixed << std::right + << std::setw(10) << result.slowest_iteration_performance << " iterations/s" + << termcolor::reset; + std::cout << " (" << termcolor::red << std::setprecision(2) << std::fixed + << worst_mean_iterations_difference << " iterations/s, " + << worst_mean_iterations_percentage_difference << " %" << termcolor::reset << ")" + << "\n"; + + std::cout << "\n"; + } +}; + +} // namespace criterion + +#pragma once +#include +#include + +namespace criterion { + +#if 0 +struct benchmark_result { + std::string name; + std::size_t num_warmup_runs; + std::size_t num_runs; + std::size_t num_iterations; + + long double lowest_rsd; // Lowest relative standard deviation (RSD) + long double lowest_rsd_mean; // mean @ lowest RSD + std::size_t lowest_rsd_index; // which run had the lowest RSD best estimate + + long double warmup_execution_time; // execution time during warmup + long double mean_execution_time; // global mean execution time + long double fastest_execution_time; // global best execution time + long double slowest_execution_time; // global worst execution time + + long double average_iteration_performance; // iterations per second in the average case + long double fastest_iteration_performance; // iterations per second in the fastest case + long double slowest_iteration_performance; // iterations per second in the slowest case + + std::string to_csv() const { + std::stringstream os; + + os << '"' << name << "\","; + os << std::fixed << std::setprecision(2) << num_warmup_runs << ',' << num_runs * num_iterations + << ',' << mean_execution_time << ',' << fastest_execution_time << ',' + << slowest_execution_time << ',' << lowest_rsd_mean << ',' << lowest_rsd << ',' + << lowest_rsd_index << ',' << average_iteration_performance << ',' + << fastest_iteration_performance << ',' << slowest_iteration_performance; + + return os.str(); + } + + std::string to_json() const { + std::stringstream os; + os << std::fixed << std::setprecision(2) << " {\n" + << " \"name\": \"" << name << "\",\n" + << " \"warmup_runs\": " << num_warmup_runs << ",\n" + << " \"iterations\": " << num_runs * num_iterations << ",\n" + << " \"mean_execution_time\": " << mean_execution_time << ",\n" + << " \"fastest_execution_time\": " << fastest_execution_time << ",\n" + << " \"slowest_execution_time\": " << slowest_execution_time << ",\n" + << " \"lowest_rsd_execution_time\": " << lowest_rsd_mean << ",\n" + << " \"lowest_rsd_percentage\": " << lowest_rsd << ",\n" + << " \"lowest_rsd_index\": " << lowest_rsd_index << ",\n" + << " \"average_iteration_performance\": " << average_iteration_performance << ",\n" + << " \"fastest_iteration_performance\": " << fastest_iteration_performance << ",\n" + << " \"slowest_iteration_performance\": " << slowest_iteration_performance << "\n" + << " }"; + + return os.str(); + } + + std::string to_md() const { + std::stringstream os; + os << std::fixed << std::setprecision(2) << "|" << name << "|" << num_warmup_runs << "|" + << num_runs * num_iterations << "|" << mean_execution_time << "|" << fastest_execution_time + << "|" << slowest_execution_time << "|" << lowest_rsd_mean << "|" << lowest_rsd << "|" + << lowest_rsd_index << "|" << average_iteration_performance << "|" + << fastest_iteration_performance << "|" << slowest_iteration_performance << "\n"; + return os.str(); + } +}; +#endif + +} // namespace criterion + +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// #include +// #include +// #include +// #include + +namespace criterion { + +class benchmark { + benchmark_config config_; + using Fn = benchmark_config::Fn; + + static inline constexpr std::size_t num_iterations_{20}; + std::size_t max_num_runs_{0}; + const long double ten_seconds_{1e+10}; + long double min_benchmark_time_{ten_seconds_}; + long double benchmark_time_; + long double early_estimate_execution_time_; + + long double estimate_minimum_measurement_cost() { + using namespace std::chrono; + std::vector durations; + + for (std::size_t i = 0; i < 10; i++) { + const auto start = steady_clock::now(); + // do nothing + const auto end = steady_clock::now(); + const auto execution_time = + static_cast(duration_cast(end - start).count()); + durations.push_back(execution_time); + } + return *std::min_element(durations.begin(), durations.end()); + } + + long double estimate_execution_time() { + using namespace std::chrono; + + long double result = 0; + bool first_run{true}; + for (std::size_t i = 0; i < warmup_runs; i++) { + std::chrono::steady_clock::time_point start_timestamp; + std::optional teardown_timestamp; + const auto start = steady_clock::now(); + config_.fn(start_timestamp, teardown_timestamp, config_.parameters); + const auto end = steady_clock::now(); + const auto execution_time = + static_cast(duration_cast(end - start).count()); + if (first_run) { + result = execution_time; + first_run = false; + } else { + result = std::min(execution_time, result); + } + } + return result; + } + + void update_iterations() { + early_estimate_execution_time_ = estimate_execution_time(); + + if (early_estimate_execution_time_ < 1) + early_estimate_execution_time_ = 1; + + auto min_runs = 2; + + if (early_estimate_execution_time_ <= 100) { // 100ns + benchmark_time_ = 5e+8; // 500 ms + } else if (early_estimate_execution_time_ <= 1000) { // 1us + benchmark_time_ = 1e+9; // 1s + } else if (early_estimate_execution_time_ <= 100000) { // 100us + benchmark_time_ = 2.5e+9; // 2.5s + } else if (early_estimate_execution_time_ <= 1000000) { // 1ms + benchmark_time_ = 5e+9; // 5s + } else if (early_estimate_execution_time_ <= 100000000) { // 100ms + benchmark_time_ = 7.5e+9; // 7.5s + } else { + benchmark_time_ = min_benchmark_time_; + } + + benchmark_time_ = + std::max(early_estimate_execution_time_ * min_runs * num_iterations_, benchmark_time_); + const auto total_iterations = size_t(benchmark_time_) / early_estimate_execution_time_; + max_num_runs_ = std::max(size_t(total_iterations / num_iterations_), size_t(min_runs)); + } + +public: + benchmark(const benchmark_config &config) : config_(config) {} + + static inline std::unordered_map results; + static inline std::vector benchmark_execution_order; + static inline bool show_console_output = true; + static inline std::size_t warmup_runs = 3; + + void run() { + std::chrono::steady_clock::time_point benchmark_start_timestamp; + + using namespace std::chrono; + + // run empty function to estimate minimum delay in scheduling and executing user function + const auto estimated_minimum_measurement_cost = estimate_minimum_measurement_cost(); + + const std::string benchmark_instance_name = config_.name + config_.parameterized_instance_name; + benchmark_execution_order.push_back(benchmark_instance_name); + + // Get an early estimate for execution time + // Update number of iterations to run for this benchmark based on estimate + update_iterations(); + + const auto total_number_of_iterations = max_num_runs_ * num_iterations_; + + long double lowest_rsd = 100; + long double lowest_rsd_mean = 0; + std::size_t lowest_rsd_index = 0; + bool first_run{true}; + + long double fastest_execution_time = 0; + long double slowest_execution_time = 0; + std::vector mean_in_each_run{}; + mean_in_each_run.reserve(max_num_runs_); + + std::size_t num_runs = 0; + std::array durations; + + using namespace indicators; + + // Hide cursor + if (show_console_output) { + show_console_cursor(false); + } + + BlockProgressBar bar{option::BarWidth{50}, + option::Start{"["}, + option::End{"]"}, + option::ShowElapsedTime{true}, + option::ShowRemainingTime{true}, + option::ShowPercentage{true}, + option::ForegroundColor{Color::white}, + option::FontStyles{std::vector{FontStyle::bold}}, + option::MaxProgress{total_number_of_iterations}, + option::PrefixText{" > " + benchmark_instance_name + " "}}; + + while (true) { + // Benchmark runs + if (first_run) { + benchmark_start_timestamp = std::chrono::steady_clock::now(); + } + for (std::size_t i = 0; i < num_iterations_; i++) { + std::optional teardown_timestamp; + auto start = steady_clock::now(); + config_.fn(start, teardown_timestamp, config_.parameters); + auto end = steady_clock::now(); + if (teardown_timestamp) + end = teardown_timestamp.value(); + const auto execution_time = duration_cast(end - start).count(); + durations[i] = std::abs(execution_time - estimated_minimum_measurement_cost); + if (show_console_output) { + bar.tick(); + } + } + auto size = num_iterations_; + const long double mean = std::accumulate(durations.begin(), durations.end(), 0.0) / size; + + long double E = 0; + for (std::size_t i = 0; i < size; i++) { + E += std::pow(durations[i] - mean, 2); + } + const long double variance = E / size; + const long double standard_deviation = std::sqrt(variance); + const long double relative_standard_deviation = standard_deviation * 100 / mean; + + const auto mean_in_this_run = + std::accumulate(durations.begin(), durations.end(), 0.0) / num_iterations_; + mean_in_each_run.push_back(mean_in_this_run); + + if (first_run) { + lowest_rsd = relative_standard_deviation; + lowest_rsd_mean = mean; + lowest_rsd_index = num_runs + 1; + fastest_execution_time = *std::min_element(durations.begin(), durations.end()); + slowest_execution_time = *std::max_element(durations.begin(), durations.end()); + first_run = false; + } else { + // Save record of lowest RSD + const auto current_lowest_rsd = lowest_rsd; + const auto current_lowest_rsd_index = lowest_rsd_index; + lowest_rsd = std::min(relative_standard_deviation, lowest_rsd); + if (lowest_rsd < current_lowest_rsd) { + // There's a new LOWEST relative standard deviation + + if (mean < lowest_rsd_mean) { + lowest_rsd_mean = mean; // new mean is lower + lowest_rsd_index = num_runs + 1; + } else { + lowest_rsd = current_lowest_rsd; // go back to old estimate + lowest_rsd_index = current_lowest_rsd_index; + } + } else { + lowest_rsd = current_lowest_rsd; // go back to old estimate + lowest_rsd_index = current_lowest_rsd_index; + } + + // Save best and worst duration + const auto current_best_execution_time = + *std::min_element(durations.begin(), durations.end()); + if (current_best_execution_time > 0) + fastest_execution_time = std::min(fastest_execution_time, current_best_execution_time); + + const auto current_worst_execution_time = + *std::max_element(durations.begin(), durations.end()); + slowest_execution_time = std::max(slowest_execution_time, current_worst_execution_time); + } + + num_runs += 1; + + if (num_runs >= max_num_runs_) { + break; + } + + const auto now = std::chrono::steady_clock::now(); + const auto elapsed_time = + std::chrono::duration_cast(now - benchmark_start_timestamp) + .count(); + if (elapsed_time > benchmark_time_) { + break; + } else { + if (show_console_output) { + const auto percentage_completed = elapsed_time / benchmark_time_; + const auto new_bar_progress = percentage_completed * total_number_of_iterations; + bar.set_progress(new_bar_progress); + } + } + } + + const auto mean_execution_time = + (std::accumulate(mean_in_each_run.begin(), mean_in_each_run.end(), 0.0) / num_runs); + + const auto benchmark_result = criterion::benchmark_result{ + .name = benchmark_instance_name, + .num_warmup_runs = warmup_runs, + .num_runs = max_num_runs_, + .num_iterations = num_iterations_, + .lowest_rsd = lowest_rsd, + .lowest_rsd_mean = lowest_rsd_mean, + .lowest_rsd_index = lowest_rsd_index, + .warmup_execution_time = early_estimate_execution_time_, + .mean_execution_time = mean_execution_time, + .fastest_execution_time = fastest_execution_time, + .slowest_execution_time = slowest_execution_time, + .average_iteration_performance = (1E9 / mean_execution_time), + .fastest_iteration_performance = (1E9 / fastest_execution_time), + .slowest_iteration_performance = (1E9 / slowest_execution_time)}; + + results.insert(std::make_pair(benchmark_instance_name, benchmark_result)); + + if (show_console_output) { + bar.set_progress(total_number_of_iterations); + bar.mark_as_completed(); + + // Show console cursor + show_console_cursor(true); + + console_writer::write_result(benchmark_result); + } + } +}; + +} // namespace criterion + +#pragma once +// #include +// #include +#include +#include +#include +#include +#include + +namespace criterion { + +class csv_writer { +public: + static bool write_results(const std::string &filename, + const std::unordered_map &results) { + bool result{false}; + std::ofstream os(filename); + if (os.is_open()) { + os << "name,warmup_runs,iterations,mean_execution_time,fastest_execution_time,slowest_" + "execution_time,lowest_rsd_execution_time,lowest_rsd_percentage,lowest_rsd_index," + "average_iteration_performance,fastest_iteration_performance,slowest_iteration_" + "performance\n"; + for (const auto &name : benchmark::benchmark_execution_order) { + const auto &this_result = results.at(name); + os << this_result.to_csv() << "\n"; + } + result = true; + } + os.close(); + return result; + } +}; + +} // namespace criterion + +#pragma once +// #include +// #include +#include +#include +#include +#include +#include + +namespace criterion { + +class json_writer { +public: + static bool write_results(const std::string &filename, + const std::unordered_map &results) { + bool result{false}; + std::ofstream os(filename); + if (os.is_open()) { + os << "{\n"; + os << " \"benchmarks\": [\n"; + + bool first{true}; + for (const auto &name : benchmark::benchmark_execution_order) { + const auto &this_result = results.at(name); + if (first) { + first = false; + } else { + os << ",\n"; + } + os << this_result.to_json(); + } + + os << "\n"; + os << " ]\n"; + os << "}"; + result = true; + } + os.close(); + return result; + } +}; + +} // namespace criterion + +#pragma once +// #include +// #include +#include +#include +#include +#include +#include + +namespace criterion { + +class md_writer { +public: + static bool write_results(const std::string &filename, + const std::unordered_map &results) { + bool result{false}; + std::ofstream os(filename); + if (os.is_open()) { + + os << "|Name |Warmup Runs|Iterations|Mean Execution Time (ns)|Fastest Execution " + "Time (ns)|Slowest Execution Time (ns)|Lowest RSD Execution Time (ns)|Lowest RSD " + "(%)|Lowest RSD Index|Average Iteration Performance (iterations/s)|Fastest Iteration " + "Performance (iterations/s)|Slowest Iteration Performance (iterations/s)|\n"; + os << "|:---------------|----------:|---------:|-----------------------:|--------------------" + "------:|--------------------------:|-----------------------------:|-------------:|----" + "-----------:|-------------------------------------------:|----------------------------" + "---------------:|-------------------------------------------:|\n"; + + for (const auto &name : benchmark::benchmark_execution_order) { + const auto &this_result = results.at(name); + os << this_result.to_md(); + } + result = true; + } + os.close(); + return result; + } +}; + +} // namespace criterion + +#pragma once +// #include +// #include +#include +#include +#include +#include +#include +#include + +namespace criterion { + +class asciidoc_writer { +public: + static bool write_results(const std::string &filename, + const std::unordered_map &results) { + bool result{false}; + std::ofstream os(filename); + if (os.is_open()) { + + std::time_t t = std::time(nullptr); + os << ".Criterion Benchmark Results (" << std::put_time(std::gmtime(&t), "%c %Z") << ")\n"; + os << "[cols=\"<,>,>,>,>,>,>,>,>,>,>,>\", options=\"header\"]\n"; + os << "|===\n"; + os << "|Name |Warmup Runs|Iterations|Mean Execution Time (ns)|Fastest Execution " + "Time (ns)|Slowest Execution Time (ns)|Lowest RSD Execution Time (ns)|Lowest RSD " + "(%)|Lowest RSD Index|Average Iteration Performance (iterations/s)|Fastest Iteration " + "Performance (iterations/s)|Slowest Iteration Performance (iterations/s)\n"; + + for (const auto &name : benchmark::benchmark_execution_order) { + const auto &this_result = results.at(name); + os << this_result.to_md(); + } + os << "|===\n"; + result = true; + } + os.close(); + return result; + } +}; + +} // namespace criterion + +#pragma once +#include +// #include +// #include +// #include +#include +#include +#include +#include +#include + +namespace criterion { + +struct benchmark_registration_helper_struct { + static std::vector ®istered_benchmarks() { + static std::vector v; + return v; + } + + static void register_benchmark(const benchmark_config &config) { + registered_benchmarks().push_back(config); + } + + static void execute_registered_benchmarks() { + for (const auto &config : registered_benchmarks()) { + benchmark{config}.run(); + } + } + + static void list_registered_benchmarks() { + for (const auto &config : registered_benchmarks()) { + std::cout << config.name << config.parameterized_instance_name << "\n"; + } + } + + static void list_filtered_registered_benchmarks(const std::string ®ex_string) { + std::regex regexp(regex_string); + std::smatch matches; + for (const auto &config : registered_benchmarks()) { + const auto benchmark_instance_name = config.name + config.parameterized_instance_name; + std::regex_search(benchmark_instance_name, matches, regexp); + if (!matches.empty()) { + std::cout << benchmark_instance_name << "\n"; + } + } + } + + static void execute_filtered_registered_benchmarks(const std::string ®ex_string) { + std::regex regexp(regex_string); + std::smatch matches; + for (const auto &config : registered_benchmarks()) { + const auto benchmark_instance_name = config.name + config.parameterized_instance_name; + std::regex_search(benchmark_instance_name, matches, regexp); + if (!matches.empty()) { + benchmark{config}.run(); + } + } + } +}; + +} // namespace criterion + +#define SETUP_BENCHMARK(...) \ + __VA_ARGS__ \ + __benchmark_start_timestamp = \ + std::chrono::steady_clock::now(); // updated benchmark start timestamp + +#define TEARDOWN_BENCHMARK(...) \ + __benchmark_teardown_timestamp = std::chrono::steady_clock::now(); \ + __VA_ARGS__ + +namespace criterion { + +struct benchmark_template_registration_helper_struct { + static std::unordered_multimap ®istered_benchmark_templates() { + static std::unordered_multimap m; + return m; + } + + static void register_benchmark_template(const benchmark_config &config) { + registered_benchmark_templates().insert({config.name, config}); + } + + template + static void execute_registered_benchmark_template(const std::string &template_name, + const std::string &instance_name, + ArgTuple &arg_tuple) { + for (auto &[k, v] : registered_benchmark_templates()) { + if (k == template_name) { + benchmark_registration_helper_struct::register_benchmark( + benchmark_config{.name = template_name, + .fn = v.fn, + .parameterized_instance_name = instance_name, + .parameters = (void *)(&arg_tuple)}); + } + } + } +}; + +} // namespace criterion + +#define CONCAT_IMPL(a, b) a##b +#define CONCAT(a, b) CONCAT_IMPL(a, b) + +#define BENCHMARK_WITHOUT_PARAMETERS(Name) \ + typedef std::tuple<> CONCAT(Name, BenchmarkParameters); \ + namespace detail { \ + /* forward declare the benchmark function that we define later */ \ + template \ + struct CONCAT(Name, CONCAT(__benchmark_function_wrapper__, __LINE__)) { \ + static inline void CONCAT(Name, CONCAT(_registered_fun_, __LINE__))( \ + std::chrono::steady_clock::time_point &, \ + std::optional &, void *); \ + }; \ + \ + namespace /* ensure internal linkage for struct */ \ + { \ + /* helper struct for static registration in ctor */ \ + struct CONCAT(Name, CONCAT(_register_struct_, __LINE__)) { \ + CONCAT(Name, CONCAT(_register_struct_, __LINE__))() { /* called once before main */ \ + criterion::benchmark_template_registration_helper_struct::register_benchmark_template( \ + criterion::benchmark_config{ \ + .name = #Name, \ + .fn = CONCAT(Name, CONCAT(__benchmark_function_wrapper__, __LINE__)) < \ + CONCAT(Name, BenchmarkParameters) > \ + ::CONCAT(Name, CONCAT(_registered_fun_, __LINE__))}); \ + } \ + } CONCAT(Name, CONCAT(_register_struct_instance_, __LINE__)); \ + } \ + \ + namespace /* ensure internal linkage for struct */ \ + { \ + static CONCAT(Name, BenchmarkParameters) \ + CONCAT(CONCAT(Name, _benchmark_template_parameters), __LINE__) = {}; \ + /* helper struct for static registration in ctor */ \ + struct CONCAT(Name, CONCAT(_instantiation_struct_, __LINE__)) { \ + CONCAT(Name, CONCAT(_instantiation_struct_, __LINE__))() { /* called once before main */ \ + criterion::benchmark_template_registration_helper_struct:: \ + execute_registered_benchmark_template( \ + #Name, "", CONCAT(CONCAT(Name, _benchmark_template_parameters), __LINE__)); \ + } \ + } CONCAT(Name, CONCAT(_instantiation_struct_instance_, __LINE__)); \ + } \ + } \ + \ + /* now actually defined to allow BENCHMARK("name") { ... } syntax */ \ + template \ + void detail::CONCAT(Name, CONCAT(__benchmark_function_wrapper__, __LINE__))::CONCAT( \ + Name, CONCAT(_registered_fun_, __LINE__))( \ + [[maybe_unused]] std::chrono::steady_clock::time_point & __benchmark_start_timestamp, \ + [[maybe_unused]] std::optional & \ + __benchmark_teardown_timestamp, \ + [[maybe_unused]] void *__benchmark_parameters) + +#define BENCHMARK_WITH_PARAMETERS(Name, ...) \ + typedef std::tuple<__VA_ARGS__> CONCAT(Name, BenchmarkParameters); \ + namespace detail { \ + /* forward declare the benchmark function that we define later */ \ + template \ + struct CONCAT(Name, CONCAT(__benchmark_function_wrapper__, __LINE__)) { \ + static inline void CONCAT(Name, CONCAT(_registered_fun_, __LINE__))( \ + std::chrono::steady_clock::time_point &, \ + std::optional &, void *); \ + }; \ + \ + namespace /* ensure internal linkage for struct */ \ + { \ + /* helper struct for static registration in ctor */ \ + struct CONCAT(Name, CONCAT(_register_struct_, __LINE__)) { \ + CONCAT(Name, CONCAT(_register_struct_, __LINE__))() { /* called once before main */ \ + criterion::benchmark_template_registration_helper_struct::register_benchmark_template( \ + criterion::benchmark_config{ \ + .name = #Name, \ + .fn = CONCAT(Name, CONCAT(__benchmark_function_wrapper__, __LINE__)) < \ + CONCAT(Name, BenchmarkParameters) > \ + ::CONCAT(Name, CONCAT(_registered_fun_, __LINE__))}); \ + } \ + } CONCAT(Name, CONCAT(_register_struct_instance_, __LINE__)); \ + } \ + } \ + \ + /* now actually defined to allow BENCHMARK("name") { ... } syntax */ \ + template \ + void detail::CONCAT(Name, CONCAT(__benchmark_function_wrapper__, __LINE__))::CONCAT( \ + Name, CONCAT(_registered_fun_, __LINE__))( \ + [[maybe_unused]] std::chrono::steady_clock::time_point & __benchmark_start_timestamp, \ + [[maybe_unused]] std::optional & \ + __benchmark_teardown_timestamp, \ + [[maybe_unused]] void *__benchmark_parameters) + +#define GET_ARGUMENT_TUPLE *((T *)__benchmark_parameters) + +#define GET_ARGUMENT(index) std::get(*((T *)__benchmark_parameters)); + +#define BENCHMARK_1(Name) BENCHMARK_WITHOUT_PARAMETERS(Name) + +#define BENCHMARK_2(Name, A) BENCHMARK_WITH_PARAMETERS(Name, A) +#define BENCHMARK_3(Name, A, B) BENCHMARK_WITH_PARAMETERS(Name, A, B) +#define BENCHMARK_4(Name, A, B, C) BENCHMARK_WITH_PARAMETERS(Name, A, B, C) +#define BENCHMARK_5(Name, A, B, C, D) BENCHMARK_WITH_PARAMETERS(Name, A, B, C, D) +#define BENCHMARK_6(Name, A, B, C, D, E) BENCHMARK_WITH_PARAMETERS(Name, A, B, C, D, E, F) +#define BENCHMARK_7(Name, A, B, C, D, E, F) BENCHMARK_WITH_PARAMETERS(Name, A, B, C, D, E, F) +#define BENCHMARK_8(Name, A, B, C, D, E, F, G) BENCHMARK_WITH_PARAMETERS(Name, A, B, C, D, E, F, G) +#define BENCHMARK_9(Name, A, B, C, D, E, F, G, H) \ + BENCHMARK_WITH_PARAMETERS(Name, A, B, C, D, E, F, G, H) +#define BENCHMARK_10(Name, A, B, C, D, E, F, G, H, I) \ + BENCHMARK_WITH_PARAMETERS(Name, A, B, C, D, E, F, G, H, I) +#define BENCHMARK_11(Name, A, B, C, D, E, F, G, H, I, J) \ + BENCHMARK_WITH_PARAMETERS(Name, A, B, C, D, E, F, G, H, I, J) +#define BENCHMARK_12(Name, A, B, C, D, E, F, G, H, I, J, K) \ + BENCHMARK_WITH_PARAMETERS(Name, A, B, C, D, E, F, G, H, I, J, K) +#define BENCHMARK_13(Name, A, B, C, D, E, F, G, H, I, J, K, L) \ + BENCHMARK_WITH_PARAMETERS(Name, A, B, C, D, E, F, G, H, I, J, K, L) +#define BENCHMARK_14(Name, A, B, C, D, E, F, G, H, I, J, K, L, M) \ + BENCHMARK_WITH_PARAMETERS(Name, A, B, C, D, E, F, G, H, I, J, K, L, M) +#define BENCHMARK_15(Name, A, B, C, D, E, F, G, H, I, J, K, L, M, N) \ + BENCHMARK_WITH_PARAMETERS(Name, A, B, C, D, E, F, G, H, I, J, K, L, M, N) +#define BENCHMARK_16(Name, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O) \ + BENCHMARK_WITH_PARAMETERS(Name, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O) +#define BENCHMARK_17(Name, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P) \ + BENCHMARK_WITH_PARAMETERS(Name, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P) +#define BENCHMARK_18(Name, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q) \ + BENCHMARK_WITH_PARAMETERS(Name, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q) +#define BENCHMARK_19(Name, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R) \ + BENCHMARK_WITH_PARAMETERS(Name, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R) +#define BENCHMARK_20(Name, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S) \ + BENCHMARK_WITH_PARAMETERS(Name, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S) +#define BENCHMARK_21(Name, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T) \ + BENCHMARK_WITH_PARAMETERS(Name, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T) +#define BENCHMARK_22(Name, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U) \ + BENCHMARK_WITH_PARAMETERS(Name, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U) +#define BENCHMARK_23(Name, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V) \ + BENCHMARK_WITH_PARAMETERS(Name, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V) +#define BENCHMARK_24(Name, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W) \ + BENCHMARK_WITH_PARAMETERS(Name, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, \ + V, W) +#define BENCHMARK_25(Name, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X) \ + BENCHMARK_WITH_PARAMETERS(Name, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, \ + V, W, X) +#define BENCHMARK_26(Name, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, \ + Y) \ + BENCHMARK_WITH_PARAMETERS(Name, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, \ + V, W, X, Y) +#define BENCHMARK_27(Name, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, \ + Y, Z) \ + BENCHMARK_WITH_PARAMETERS(Name, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, \ + V, W, X, Y, Z) + +// The interim macro that simply strips the excess and ends up with the required macro +#define BENCHMARK_X(Name, x, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, \ + X, Y, Z, FUNC, ...) \ + FUNC + +// BENCHMARK macro supports no more than 26 parameters +// The macro that the programmer uses +#define BENCHMARK(...) \ + BENCHMARK_X(, ##__VA_ARGS__, BENCHMARK_27(__VA_ARGS__), BENCHMARK_26(__VA_ARGS__), \ + BENCHMARK_25(__VA_ARGS__), BENCHMARK_24(__VA_ARGS__), BENCHMARK_23(__VA_ARGS__), \ + BENCHMARK_22(__VA_ARGS__), BENCHMARK_21(__VA_ARGS__), BENCHMARK_20(__VA_ARGS__), \ + BENCHMARK_19(__VA_ARGS__), BENCHMARK_18(__VA_ARGS__), BENCHMARK_17(__VA_ARGS__), \ + BENCHMARK_16(__VA_ARGS__), BENCHMARK_15(__VA_ARGS__), BENCHMARK_14(__VA_ARGS__), \ + BENCHMARK_13(__VA_ARGS__), BENCHMARK_12(__VA_ARGS__), BENCHMARK_11(__VA_ARGS__), \ + BENCHMARK_10(__VA_ARGS__), BENCHMARK_9(__VA_ARGS__), BENCHMARK_8(__VA_ARGS__), \ + BENCHMARK_7(__VA_ARGS__), BENCHMARK_6(__VA_ARGS__), BENCHMARK_5(__VA_ARGS__), \ + BENCHMARK_4(__VA_ARGS__), BENCHMARK_3(__VA_ARGS__), BENCHMARK_2(__VA_ARGS__), \ + BENCHMARK_1(__VA_ARGS__)) + +#define INVOKE_BENCHMARK(TemplateName, InstanceName, ...) \ + \ + namespace /* ensure internal linkage for struct */ \ + { \ + static CONCAT(TemplateName, BenchmarkParameters) \ + CONCAT(CONCAT(TemplateName, _benchmark_template_parameters), __LINE__) = {__VA_ARGS__}; \ + /* helper struct for static registration in ctor */ \ + struct CONCAT(TemplateName, CONCAT(_instantiation_struct_, __LINE__)) { \ + CONCAT(TemplateName, CONCAT(_instantiation_struct_, __LINE__)) \ + () { /* called once before main */ \ + criterion::benchmark_template_registration_helper_struct:: \ + execute_registered_benchmark_template( \ + #TemplateName, InstanceName, \ + CONCAT(CONCAT(TemplateName, _benchmark_template_parameters), __LINE__)); \ + } \ + } CONCAT(TemplateName, CONCAT(_instantiation_struct_instance_, __LINE__)); \ + } + +#define GET_FIRST(first, ...) first +#define GET_REST(first, ...) __VA_ARGS__ + +#define INVOKE_BENCHMARK_N(TemplateName, Index, PackedArgument) \ + \ + namespace /* ensure internal linkage for struct */ \ + { \ + static CONCAT(TemplateName, BenchmarkParameters) \ + CONCAT(CONCAT(CONCAT(TemplateName, _benchmark_template_parameters), __LINE__), \ + Index) = {GET_REST(PackedArgument)}; \ + /* helper struct for static registration in ctor */ \ + struct CONCAT(TemplateName, CONCAT(CONCAT(_instantiation_struct_, __LINE__), Index)) { \ + CONCAT(TemplateName, CONCAT(CONCAT(_instantiation_struct_, __LINE__), \ + Index)()) { /* called once before main */ \ + criterion::benchmark_template_registration_helper_struct:: \ + execute_registered_benchmark_template( \ + #TemplateName, GET_FIRST(PackedArgument), \ + CONCAT(CONCAT(CONCAT(TemplateName, _benchmark_template_parameters), __LINE__), \ + Index)); \ + } \ + } CONCAT(TemplateName, CONCAT(CONCAT(_instantiation_struct_instance_, __LINE__), Index)); \ + } + +#define _Args(...) __VA_ARGS__ +#define STRIP_PARENS(X) X +#define PASS_PARAMETERS(X) STRIP_PARENS(_Args X) + +#define STRINGIZE(arg) STRINGIZE1(arg) +#define STRINGIZE1(arg) STRINGIZE2(arg) +#define STRINGIZE2(arg) #arg + +#define CONCATENATE(arg1, arg2) CONCATENATE1(arg1, arg2) +#define CONCATENATE1(arg1, arg2) CONCATENATE2(arg1, arg2) +#define CONCATENATE2(arg1, arg2) arg1##arg2 + +#define FOR_EACH_1(what, first, x, ...) what(1, first, x); + +#define FOR_EACH_2(what, first, x, ...) \ + what(2, first, x); \ + FOR_EACH_1(what, first, __VA_ARGS__); + +#define FOR_EACH_3(what, first, x, ...) \ + what(3, first, x); \ + FOR_EACH_2(what, first, __VA_ARGS__); + +#define FOR_EACH_4(what, first, x, ...) \ + what(4, first, x); \ + FOR_EACH_3(what, first, __VA_ARGS__); + +#define FOR_EACH_5(what, first, x, ...) \ + what(5, first, x); \ + FOR_EACH_4(what, first, __VA_ARGS__); + +#define FOR_EACH_6(what, first, x, ...) \ + what(6, first, x); \ + FOR_EACH_5(what, first, __VA_ARGS__); + +#define FOR_EACH_7(what, first, x, ...) \ + what(7, first, x); \ + FOR_EACH_6(what, first, __VA_ARGS__); + +#define FOR_EACH_8(what, first, x, ...) \ + what(8, first, x); \ + FOR_EACH_7(what, first, __VA_ARGS__); + +#define FOR_EACH_9(what, first, x, ...) \ + what(9, first, x); \ + FOR_EACH_8(what, first, __VA_ARGS__); + +#define FOR_EACH_10(what, first, x, ...) \ + what(10, first, x); \ + FOR_EACH_9(what, first, __VA_ARGS__); + +#define FOR_EACH_11(what, first, x, ...) \ + what(11, first, x); \ + FOR_EACH_10(what, first, __VA_ARGS__); + +#define FOR_EACH_12(what, first, x, ...) \ + what(12, first, x); \ + FOR_EACH_11(what, first, __VA_ARGS__); + +#define FOR_EACH_13(what, first, x, ...) \ + what(13, first, x); \ + FOR_EACH_12(what, first, __VA_ARGS__); + +#define FOR_EACH_14(what, first, x, ...) \ + what(14, first, x); \ + FOR_EACH_13(what, first, __VA_ARGS__); + +#define FOR_EACH_15(what, first, x, ...) \ + what(15, first, x); \ + FOR_EACH_14(what, first, __VA_ARGS__); + +#define FOR_EACH_16(what, first, x, ...) \ + what(16, first, x); \ + FOR_EACH_15(what, first, __VA_ARGS__); + +#define FOR_EACH_17(what, first, x, ...) \ + what(17, first, x); \ + FOR_EACH_16(what, first, __VA_ARGS__); + +#define FOR_EACH_18(what, first, x, ...) \ + what(18, first, x); \ + FOR_EACH_17(what, first, __VA_ARGS__); + +#define FOR_EACH_19(what, first, x, ...) \ + what(19, first, x); \ + FOR_EACH_18(what, first, __VA_ARGS__); + +#define FOR_EACH_20(what, first, x, ...) \ + what(20, first, x); \ + FOR_EACH_19(what, first, __VA_ARGS__); + +#define FOR_EACH_21(what, first, x, ...) \ + what(21, first, x); \ + FOR_EACH_20(what, first, __VA_ARGS__); + +#define FOR_EACH_22(what, first, x, ...) \ + what(22, first, x); \ + FOR_EACH_21(what, first, __VA_ARGS__); + +#define FOR_EACH_23(what, first, x, ...) \ + what(23, first, x); \ + FOR_EACH_22(what, first, __VA_ARGS__); + +#define FOR_EACH_24(what, first, x, ...) \ + what(24, first, x); \ + FOR_EACH_23(what, first, __VA_ARGS__); + +#define FOR_EACH_25(what, first, x, ...) \ + what(25, first, x); \ + FOR_EACH_24(what, first, __VA_ARGS__); + +#define FOR_EACH_26(what, first, x, ...) \ + what(26, first, x); \ + FOR_EACH_25(what, first, __VA_ARGS__); + +#define FOR_EACH_27(what, first, x, ...) \ + what(27, first, x); \ + FOR_EACH_26(what, first, __VA_ARGS__); + +#define FOR_EACH_28(what, first, x, ...) \ + what(28, first, x); \ + FOR_EACH_27(what, first, __VA_ARGS__); + +#define FOR_EACH_29(what, first, x, ...) \ + what(29, first, x); \ + FOR_EACH_28(what, first, __VA_ARGS__); + +#define FOR_EACH_30(what, first, x, ...) \ + what(30, first, x); \ + FOR_EACH_29(what, first, __VA_ARGS__); + +#define FOR_EACH_31(what, first, x, ...) \ + what(31, first, x); \ + FOR_EACH_30(what, first, __VA_ARGS__); + +#define FOR_EACH_32(what, first, x, ...) \ + what(32, first, x); \ + FOR_EACH_31(what, first, __VA_ARGS__); + +#define FOR_EACH_33(what, first, x, ...) \ + what(33, first, x); \ + FOR_EACH_32(what, first, __VA_ARGS__); + +#define FOR_EACH_34(what, first, x, ...) \ + what(34, first, x); \ + FOR_EACH_33(what, first, __VA_ARGS__); + +#define FOR_EACH_35(what, first, x, ...) \ + what(35, first, x); \ + FOR_EACH_34(what, first, __VA_ARGS__); + +#define FOR_EACH_36(what, first, x, ...) \ + what(36, first, x); \ + FOR_EACH_35(what, first, __VA_ARGS__); + +#define FOR_EACH_37(what, first, x, ...) \ + what(37, first, x); \ + FOR_EACH_36(what, first, __VA_ARGS__); + +#define FOR_EACH_38(what, first, x, ...) \ + what(38, first, x); \ + FOR_EACH_37(what, first, __VA_ARGS__); + +#define FOR_EACH_39(what, first, x, ...) \ + what(39, first, x); \ + FOR_EACH_38(what, first, __VA_ARGS__); + +#define FOR_EACH_40(what, first, x, ...) \ + what(40, first, x); \ + FOR_EACH_39(what, first, __VA_ARGS__); + +#define FOR_EACH_41(what, first, x, ...) \ + what(41, first, x); \ + FOR_EACH_40(what, first, __VA_ARGS__); + +#define FOR_EACH_42(what, first, x, ...) \ + what(42, first, x); \ + FOR_EACH_41(what, first, __VA_ARGS__); + +#define FOR_EACH_43(what, first, x, ...) \ + what(43, first, x); \ + FOR_EACH_42(what, first, __VA_ARGS__); + +#define FOR_EACH_44(what, first, x, ...) \ + what(44, first, x); \ + FOR_EACH_43(what, first, __VA_ARGS__); + +#define FOR_EACH_45(what, first, x, ...) \ + what(45, first, x); \ + FOR_EACH_44(what, first, __VA_ARGS__); + +#define FOR_EACH_46(what, first, x, ...) \ + what(46, first, x); \ + FOR_EACH_45(what, first, __VA_ARGS__); + +#define FOR_EACH_47(what, first, x, ...) \ + what(47, first, x); \ + FOR_EACH_46(what, first, __VA_ARGS__); + +#define FOR_EACH_48(what, first, x, ...) \ + what(48, first, x); \ + FOR_EACH_47(what, first, __VA_ARGS__); + +#define FOR_EACH_49(what, first, x, ...) \ + what(49, first, x); \ + FOR_EACH_48(what, first, __VA_ARGS__); + +#define FOR_EACH_50(what, first, x, ...) \ + what(50, first, x); \ + FOR_EACH_49(what, first, __VA_ARGS__); + +#define FOR_EACH_51(what, first, x, ...) \ + what(51, first, x); \ + FOR_EACH_50(what, first, __VA_ARGS__); + +#define FOR_EACH_52(what, first, x, ...) \ + what(52, first, x); \ + FOR_EACH_51(what, first, __VA_ARGS__); + +#define FOR_EACH_53(what, first, x, ...) \ + what(53, first, x); \ + FOR_EACH_52(what, first, __VA_ARGS__); + +#define FOR_EACH_54(what, first, x, ...) \ + what(54, first, x); \ + FOR_EACH_53(what, first, __VA_ARGS__); + +#define FOR_EACH_55(what, first, x, ...) \ + what(55, first, x); \ + FOR_EACH_54(what, first, __VA_ARGS__); + +#define FOR_EACH_56(what, first, x, ...) \ + what(56, first, x); \ + FOR_EACH_55(what, first, __VA_ARGS__); + +#define FOR_EACH_57(what, first, x, ...) \ + what(57, first, x); \ + FOR_EACH_56(what, first, __VA_ARGS__); + +#define FOR_EACH_58(what, first, x, ...) \ + what(58, first, x); \ + FOR_EACH_57(what, first, __VA_ARGS__); + +#define FOR_EACH_59(what, first, x, ...) \ + what(59, first, x); \ + FOR_EACH_58(what, first, __VA_ARGS__); + +#define FOR_EACH_60(what, first, x, ...) \ + what(60, first, x); \ + FOR_EACH_59(what, first, __VA_ARGS__); + +#define FOR_EACH_61(what, first, x, ...) \ + what(61, first, x); \ + FOR_EACH_60(what, first, __VA_ARGS__); + +#define FOR_EACH_62(what, first, x, ...) \ + what(62, first, x); \ + FOR_EACH_61(what, first, __VA_ARGS__); + +#define FOR_EACH_63(what, first, x, ...) \ + what(63, first, x); \ + FOR_EACH_62(what, first, __VA_ARGS__); + +#define FOR_EACH_NARG(...) FOR_EACH_NARG_(__VA_ARGS__, FOR_EACH_RSEQ_N()) +#define FOR_EACH_NARG_(...) FOR_EACH_ARG_N(__VA_ARGS__) +#define FOR_EACH_ARG_N(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, \ + _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, \ + _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, \ + _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, \ + _63, N, ...) \ + N +#define FOR_EACH_RSEQ_N() \ + 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, \ + 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, \ + 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 + +#define FOR_EACH_(N, what, first, x, ...) CONCATENATE(FOR_EACH_, N)(what, first, x, __VA_ARGS__) +#define FOR_EACH(what, first, ...) FOR_EACH_(FOR_EACH_NARG(__VA_ARGS__), what, first, __VA_ARGS__) + +#define INVOKE_BENCHMARK_FOR_EACH_HELPER(Index, TemplateName, ...) \ + INVOKE_BENCHMARK_N(TemplateName, Index, PASS_PARAMETERS(__VA_ARGS__)) + +#define INVOKE_BENCHMARK_FOR_EACH(TemplateName, ...) \ + FOR_EACH(INVOKE_BENCHMARK_FOR_EACH_HELPER, TemplateName, __VA_ARGS__) + + + + +// (C) Copyright 2015 - 2018 Christopher Beck + +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef VISIT_STRUCT_HPP_INCLUDED +#define VISIT_STRUCT_HPP_INCLUDED + +/*** + * Provides a facility to declare a structure as "visitable" and apply a visitor + * to it. The list of members is a compile-time data structure, and there is no + * run-time overhead. + */ + +#include +#include + +// Library version + +#define VISIT_STRUCT_VERSION_MAJOR 1 +#define VISIT_STRUCT_VERSION_MINOR 0 +#define VISIT_STRUCT_VERSION_PATCH 0 + +#define VISIT_STRUCT_STRING_HELPER(X) #X +#define VISIT_STRUCT_STRING(X) VISIT_STRUCT_STRING_HELPER(X) + +#define VISIT_STRUCT_VERSION_STRING \ + VISIT_STRUCT_STRING(VISIT_STRUCT_VERSION_MAJOR) \ + "." VISIT_STRUCT_STRING(VISIT_STRUCT_VERSION_MINOR) "." VISIT_STRUCT_STRING( \ + VISIT_STRUCT_VERSION_PATCH) + +// For MSVC 2013 support, we put constexpr behind a define. + +#ifndef VISIT_STRUCT_CONSTEXPR +#if (defined _MSC_VER) && (_MSC_VER <= 1800) +#define VISIT_STRUCT_CONSTEXPR +#else +#define VISIT_STRUCT_CONSTEXPR constexpr +#endif +#endif + +// After C++14 the apply_visitor function can be constexpr. +// We target C++11, but such functions are tagged VISIT_STRUCT_CXX14_CONSTEXPR. + +#ifndef VISIT_STRUCT_CXX14_CONSTEXPR +#if ((defined _MSC_VER) && (_MSC_VER <= 1900)) || (!defined __cplusplus) || (__cplusplus == 201103L) +#define VISIT_STRUCT_CXX14_CONSTEXPR +#else +#define VISIT_STRUCT_CXX14_CONSTEXPR constexpr +#endif +#endif + +namespace visit_struct { + +namespace traits { + +// Primary template which is specialized to register a type +template struct visitable; + +// Helper template which checks if a type is registered +template struct is_visitable : std::false_type {}; + +template +struct is_visitable::value>::type> + : std::true_type {}; + +// Helper template which removes cv and reference from a type (saves some typing) +template struct clean { + typedef typename std::remove_cv::type>::type type; +}; + +template using clean_t = typename clean::type; + +// Mini-version of std::common_type (we only require C++11) +template struct common_type { + typedef decltype(true ? std::declval() : std::declval()) type; +}; + +} // end namespace traits + +// Tag for tag dispatch +template struct type_c { using type = T; }; + +// Accessor type: function object encapsulating a pointer-to-member +template struct accessor { + template + VISIT_STRUCT_CONSTEXPR auto operator()(T &&t) const -> decltype(std::forward(t).*ptr) { + return std::forward(t).*ptr; + } +}; + +// +// User-interface +// + +// Return number of fields in a visitable struct +template VISIT_STRUCT_CONSTEXPR std::size_t field_count() { + return traits::visitable>::field_count; +} + +template VISIT_STRUCT_CONSTEXPR std::size_t field_count(S &&) { + return field_count(); +} + +// apply_visitor (one struct instance) +template +VISIT_STRUCT_CXX14_CONSTEXPR auto apply_visitor(V &&v, S &&s) -> + typename std::enable_if>::value>::type { + traits::visitable>::apply(std::forward(v), std::forward(s)); +} + +// apply_visitor (two struct instances) +template +VISIT_STRUCT_CXX14_CONSTEXPR auto apply_visitor(V &&v, S1 &&s1, S2 &&s2) -> + typename std::enable_if::type>>::value>::type { + using common_S = typename traits::common_type::type; + traits::visitable>::apply(std::forward(v), std::forward(s1), + std::forward(s2)); +} + +// for_each (Alternate syntax for apply_visitor, reverses order of arguments) +template +VISIT_STRUCT_CXX14_CONSTEXPR auto for_each(S &&s, V &&v) -> + typename std::enable_if>::value>::type { + traits::visitable>::apply(std::forward(v), std::forward(s)); +} + +// for_each with two structure instances +template +VISIT_STRUCT_CXX14_CONSTEXPR auto for_each(S1 &&s1, S2 &&s2, V &&v) -> + typename std::enable_if::type>>::value>::type { + using common_S = typename traits::common_type::type; + traits::visitable>::apply(std::forward(v), std::forward(s1), + std::forward(s2)); +} + +// Visit the types (visit_struct::type_c<...>) of the registered members +template +VISIT_STRUCT_CXX14_CONSTEXPR auto visit_types(V &&v) -> + typename std::enable_if>::value>::type { + traits::visitable>::visit_types(std::forward(v)); +} + +// Visit the member pointers (&S::a) of the registered members +template +VISIT_STRUCT_CXX14_CONSTEXPR auto visit_pointers(V &&v) -> + typename std::enable_if>::value>::type { + traits::visitable>::visit_pointers(std::forward(v)); +} + +// Visit the accessors (function objects) of the registered members +template +VISIT_STRUCT_CXX14_CONSTEXPR auto visit_accessors(V &&v) -> + typename std::enable_if>::value>::type { + traits::visitable>::visit_accessors(std::forward(v)); +} + +// Apply visitor (with no instances) +// This calls visit_pointers, for backwards compat reasons +template +VISIT_STRUCT_CXX14_CONSTEXPR auto apply_visitor(V &&v) -> + typename std::enable_if>::value>::type { + visit_struct::visit_pointers(std::forward(v)); +} + +// Get value by index (like std::get for tuples) +template +VISIT_STRUCT_CONSTEXPR auto get(S &&s) -> + typename std::enable_if>::value, + decltype(traits::visitable>::get_value( + std::integral_constant{}, std::forward(s)))>::type { + return traits::visitable>::get_value(std::integral_constant{}, + std::forward(s)); +} + +// Get name of field, by index +template +VISIT_STRUCT_CONSTEXPR auto get_name() -> + typename std::enable_if>::value, + decltype(traits::visitable>::get_name( + std::integral_constant{}))>::type { + return traits::visitable>::get_name(std::integral_constant{}); +} + +template +VISIT_STRUCT_CONSTEXPR auto get_name(S &&) -> decltype(get_name()) { + return get_name(); +} + +// Get member pointer, by index +template +VISIT_STRUCT_CONSTEXPR auto get_pointer() -> + typename std::enable_if>::value, + decltype(traits::visitable>::get_pointer( + std::integral_constant{}))>::type { + return traits::visitable>::get_pointer(std::integral_constant{}); +} + +template +VISIT_STRUCT_CONSTEXPR auto get_pointer(S &&) -> decltype(get_pointer()) { + return get_pointer(); +} + +// Get member accessor, by index +template +VISIT_STRUCT_CONSTEXPR auto get_accessor() -> + typename std::enable_if>::value, + decltype(traits::visitable>::get_accessor( + std::integral_constant{}))>::type { + return traits::visitable>::get_accessor(std::integral_constant{}); +} + +template +VISIT_STRUCT_CONSTEXPR auto get_accessor(S &&) -> decltype(get_accessor()) { + return get_accessor(); +} + +// Get type, by index +template struct type_at_s { + using type_c = + decltype(traits::visitable>::type_at(std::integral_constant{})); + using type = typename type_c::type; +}; + +template using type_at = typename type_at_s::type; + +// Get name of structure +template +VISIT_STRUCT_CONSTEXPR auto get_name() -> + typename std::enable_if>::value, + decltype(traits::visitable>::get_name())>::type { + return traits::visitable>::get_name(); +} + +template VISIT_STRUCT_CONSTEXPR auto get_name(S &&) -> decltype(get_name()) { + return get_name(); +} + +/*** + * To implement the VISITABLE_STRUCT macro, we need a map-macro, which can take + * the name of a macro and some other arguments, and apply that macro to each other + * argument. + * + * There are some techniques you can use within C preprocessor to accomplish this + * succinctly, by settng up "recursive" macros. + * + * But this can also cause it to give worse error messages when something goes wrong. + * + * We are now doing it in a more "dumb", bulletproof way which has the advantage that it + * is more portable and gives better error messages. For discussion see + * IMPLEMENTATION_NOTES.md + * + * The code below is based on a patch from Jarod42, and is now generated by a python + * script. The purpose of the generated code is to define VISIT_STRUCT_PP_MAP as + * described. + */ + +/*** Generated code ***/ + +static VISIT_STRUCT_CONSTEXPR const int max_visitable_members = 69; + +#define VISIT_STRUCT_EXPAND(x) x +#define VISIT_STRUCT_PP_ARG_N( \ + _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, \ + _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, \ + _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, \ + _59, _60, _61, _62, _63, _64, _65, _66, _67, _68, _69, N, ...) \ + N +#define VISIT_STRUCT_PP_NARG(...) \ + VISIT_STRUCT_EXPAND(VISIT_STRUCT_PP_ARG_N( \ + __VA_ARGS__, 69, 68, 67, 66, 65, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, \ + 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, \ + 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, \ + 1, 0)) + +/* need extra level to force extra eval */ +#define VISIT_STRUCT_CONCAT_(a, b) a##b +#define VISIT_STRUCT_CONCAT(a, b) VISIT_STRUCT_CONCAT_(a, b) + +#define VISIT_STRUCT_APPLYF0(f) +#define VISIT_STRUCT_APPLYF1(f, _1) f(_1) +#define VISIT_STRUCT_APPLYF2(f, _1, _2) f(_1) f(_2) +#define VISIT_STRUCT_APPLYF3(f, _1, _2, _3) f(_1) f(_2) f(_3) +#define VISIT_STRUCT_APPLYF4(f, _1, _2, _3, _4) f(_1) f(_2) f(_3) f(_4) +#define VISIT_STRUCT_APPLYF5(f, _1, _2, _3, _4, _5) f(_1) f(_2) f(_3) f(_4) f(_5) +#define VISIT_STRUCT_APPLYF6(f, _1, _2, _3, _4, _5, _6) f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) +#define VISIT_STRUCT_APPLYF7(f, _1, _2, _3, _4, _5, _6, _7) \ + f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) +#define VISIT_STRUCT_APPLYF8(f, _1, _2, _3, _4, _5, _6, _7, _8) \ + f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) +#define VISIT_STRUCT_APPLYF9(f, _1, _2, _3, _4, _5, _6, _7, _8, _9) \ + f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) +#define VISIT_STRUCT_APPLYF10(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) \ + f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) +#define VISIT_STRUCT_APPLYF11(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) \ + f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) +#define VISIT_STRUCT_APPLYF12(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) \ + f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) +#define VISIT_STRUCT_APPLYF13(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) \ + f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) +#define VISIT_STRUCT_APPLYF14(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) \ + f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) +#define VISIT_STRUCT_APPLYF15(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) \ + f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) +#define VISIT_STRUCT_APPLYF16(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, \ + _16) \ + f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) \ + f(_16) +#define VISIT_STRUCT_APPLYF17(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, \ + _16, _17) \ + f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) \ + f(_16) f(_17) +#define VISIT_STRUCT_APPLYF18(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, \ + _16, _17, _18) \ + f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) \ + f(_16) f(_17) f(_18) +#define VISIT_STRUCT_APPLYF19(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, \ + _16, _17, _18, _19) \ + f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) \ + f(_16) f(_17) f(_18) f(_19) +#define VISIT_STRUCT_APPLYF20(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, \ + _16, _17, _18, _19, _20) \ + f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) \ + f(_16) f(_17) f(_18) f(_19) f(_20) +#define VISIT_STRUCT_APPLYF21(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, \ + _16, _17, _18, _19, _20, _21) \ + f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) \ + f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) +#define VISIT_STRUCT_APPLYF22(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, \ + _16, _17, _18, _19, _20, _21, _22) \ + f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) \ + f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) +#define VISIT_STRUCT_APPLYF23(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, \ + _16, _17, _18, _19, _20, _21, _22, _23) \ + f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) \ + f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) +#define VISIT_STRUCT_APPLYF24(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, \ + _16, _17, _18, _19, _20, _21, _22, _23, _24) \ + f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) \ + f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) +#define VISIT_STRUCT_APPLYF25(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, \ + _16, _17, _18, _19, _20, _21, _22, _23, _24, _25) \ + f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) \ + f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) +#define VISIT_STRUCT_APPLYF26(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, \ + _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26) \ + f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) \ + f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) +#define VISIT_STRUCT_APPLYF27(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, \ + _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27) \ + f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) \ + f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) +#define VISIT_STRUCT_APPLYF28(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, \ + _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28) \ + f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) \ + f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) f(_28) +#define VISIT_STRUCT_APPLYF29(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, \ + _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29) \ + f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) \ + f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) f(_28) \ + f(_29) +#define VISIT_STRUCT_APPLYF30(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, \ + _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30) \ + f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) \ + f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) f(_28) \ + f(_29) f(_30) +#define VISIT_STRUCT_APPLYF31(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, \ + _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31) \ + f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) \ + f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) f(_28) \ + f(_29) f(_30) f(_31) +#define VISIT_STRUCT_APPLYF32(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, \ + _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32) \ + f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) \ + f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) f(_28) \ + f(_29) f(_30) f(_31) f(_32) +#define VISIT_STRUCT_APPLYF33(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, \ + _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33) \ + f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) \ + f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) f(_28) \ + f(_29) f(_30) f(_31) f(_32) f(_33) +#define VISIT_STRUCT_APPLYF34(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, \ + _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34) \ + f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) \ + f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) f(_28) \ + f(_29) f(_30) f(_31) f(_32) f(_33) f(_34) +#define VISIT_STRUCT_APPLYF35(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, \ + _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35) \ + f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) \ + f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) f(_28) \ + f(_29) f(_30) f(_31) f(_32) f(_33) f(_34) f(_35) +#define VISIT_STRUCT_APPLYF36(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, \ + _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36) \ + f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) \ + f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) f(_28) \ + f(_29) f(_30) f(_31) f(_32) f(_33) f(_34) f(_35) f(_36) +#define VISIT_STRUCT_APPLYF37(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, \ + _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37) \ + f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) \ + f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) f(_28) \ + f(_29) f(_30) f(_31) f(_32) f(_33) f(_34) f(_35) f(_36) f(_37) +#define VISIT_STRUCT_APPLYF38(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, \ + _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, _38) \ + f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) \ + f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) f(_28) \ + f(_29) f(_30) f(_31) f(_32) f(_33) f(_34) f(_35) f(_36) f(_37) f(_38) +#define VISIT_STRUCT_APPLYF39(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, \ + _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39) \ + f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) \ + f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) f(_28) \ + f(_29) f(_30) f(_31) f(_32) f(_33) f(_34) f(_35) f(_36) f(_37) f(_38) f(_39) +#define VISIT_STRUCT_APPLYF40(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, \ + _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40) \ + f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) \ + f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) f(_28) \ + f(_29) f(_30) f(_31) f(_32) f(_33) f(_34) f(_35) f(_36) f(_37) f(_38) f(_39) f(_40) +#define VISIT_STRUCT_APPLYF41(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, \ + _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41) \ + f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) \ + f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) f(_28) \ + f(_29) f(_30) f(_31) f(_32) f(_33) f(_34) f(_35) f(_36) f(_37) f(_38) f(_39) f(_40) \ + f(_41) +#define VISIT_STRUCT_APPLYF42(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, \ + _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, \ + _42) \ + f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) \ + f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) f(_28) \ + f(_29) f(_30) f(_31) f(_32) f(_33) f(_34) f(_35) f(_36) f(_37) f(_38) f(_39) f(_40) \ + f(_41) f(_42) +#define VISIT_STRUCT_APPLYF43(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, \ + _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, \ + _42, _43) \ + f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) \ + f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) f(_28) \ + f(_29) f(_30) f(_31) f(_32) f(_33) f(_34) f(_35) f(_36) f(_37) f(_38) f(_39) f(_40) \ + f(_41) f(_42) f(_43) +#define VISIT_STRUCT_APPLYF44(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, \ + _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, \ + _42, _43, _44) \ + f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) \ + f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) f(_28) \ + f(_29) f(_30) f(_31) f(_32) f(_33) f(_34) f(_35) f(_36) f(_37) f(_38) f(_39) f(_40) \ + f(_41) f(_42) f(_43) f(_44) +#define VISIT_STRUCT_APPLYF45(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, \ + _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, \ + _42, _43, _44, _45) \ + f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) \ + f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) f(_28) \ + f(_29) f(_30) f(_31) f(_32) f(_33) f(_34) f(_35) f(_36) f(_37) f(_38) f(_39) f(_40) \ + f(_41) f(_42) f(_43) f(_44) f(_45) +#define VISIT_STRUCT_APPLYF46(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, \ + _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, \ + _42, _43, _44, _45, _46) \ + f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) \ + f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) f(_28) \ + f(_29) f(_30) f(_31) f(_32) f(_33) f(_34) f(_35) f(_36) f(_37) f(_38) f(_39) f(_40) \ + f(_41) f(_42) f(_43) f(_44) f(_45) f(_46) +#define VISIT_STRUCT_APPLYF47(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, \ + _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, \ + _42, _43, _44, _45, _46, _47) \ + f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) \ + f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) f(_28) \ + f(_29) f(_30) f(_31) f(_32) f(_33) f(_34) f(_35) f(_36) f(_37) f(_38) f(_39) f(_40) \ + f(_41) f(_42) f(_43) f(_44) f(_45) f(_46) f(_47) +#define VISIT_STRUCT_APPLYF48(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, \ + _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, \ + _42, _43, _44, _45, _46, _47, _48) \ + f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) \ + f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) f(_28) \ + f(_29) f(_30) f(_31) f(_32) f(_33) f(_34) f(_35) f(_36) f(_37) f(_38) f(_39) f(_40) \ + f(_41) f(_42) f(_43) f(_44) f(_45) f(_46) f(_47) f(_48) +#define VISIT_STRUCT_APPLYF49(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, \ + _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, \ + _42, _43, _44, _45, _46, _47, _48, _49) \ + f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) \ + f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) f(_28) \ + f(_29) f(_30) f(_31) f(_32) f(_33) f(_34) f(_35) f(_36) f(_37) f(_38) f(_39) f(_40) \ + f(_41) f(_42) f(_43) f(_44) f(_45) f(_46) f(_47) f(_48) f(_49) +#define VISIT_STRUCT_APPLYF50(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, \ + _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, \ + _42, _43, _44, _45, _46, _47, _48, _49, _50) \ + f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) \ + f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) f(_28) \ + f(_29) f(_30) f(_31) f(_32) f(_33) f(_34) f(_35) f(_36) f(_37) f(_38) f(_39) f(_40) \ + f(_41) f(_42) f(_43) f(_44) f(_45) f(_46) f(_47) f(_48) f(_49) f(_50) +#define VISIT_STRUCT_APPLYF51(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, \ + _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, \ + _42, _43, _44, _45, _46, _47, _48, _49, _50, _51) \ + f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) \ + f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) f(_28) \ + f(_29) f(_30) f(_31) f(_32) f(_33) f(_34) f(_35) f(_36) f(_37) f(_38) f(_39) f(_40) \ + f(_41) f(_42) f(_43) f(_44) f(_45) f(_46) f(_47) f(_48) f(_49) f(_50) f(_51) +#define VISIT_STRUCT_APPLYF52(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, \ + _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, \ + _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52) \ + f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) \ + f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) f(_28) \ + f(_29) f(_30) f(_31) f(_32) f(_33) f(_34) f(_35) f(_36) f(_37) f(_38) f(_39) f(_40) \ + f(_41) f(_42) f(_43) f(_44) f(_45) f(_46) f(_47) f(_48) f(_49) f(_50) f(_51) f(_52) +#define VISIT_STRUCT_APPLYF53(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, \ + _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, \ + _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53) \ + f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) \ + f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) f(_28) \ + f(_29) f(_30) f(_31) f(_32) f(_33) f(_34) f(_35) f(_36) f(_37) f(_38) f(_39) f(_40) \ + f(_41) f(_42) f(_43) f(_44) f(_45) f(_46) f(_47) f(_48) f(_49) f(_50) f(_51) f(_52) \ + f(_53) +#define VISIT_STRUCT_APPLYF54(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, \ + _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, \ + _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54) \ + f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) \ + f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) f(_28) \ + f(_29) f(_30) f(_31) f(_32) f(_33) f(_34) f(_35) f(_36) f(_37) f(_38) f(_39) f(_40) \ + f(_41) f(_42) f(_43) f(_44) f(_45) f(_46) f(_47) f(_48) f(_49) f(_50) f(_51) f(_52) \ + f(_53) f(_54) +#define VISIT_STRUCT_APPLYF55( \ + f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, \ + _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, \ + _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55) \ + f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) \ + f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) f(_28) \ + f(_29) f(_30) f(_31) f(_32) f(_33) f(_34) f(_35) f(_36) f(_37) f(_38) f(_39) f(_40) \ + f(_41) f(_42) f(_43) f(_44) f(_45) f(_46) f(_47) f(_48) f(_49) f(_50) f(_51) f(_52) \ + f(_53) f(_54) f(_55) +#define VISIT_STRUCT_APPLYF56( \ + f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, \ + _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, \ + _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56) \ + f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) \ + f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) f(_28) \ + f(_29) f(_30) f(_31) f(_32) f(_33) f(_34) f(_35) f(_36) f(_37) f(_38) f(_39) f(_40) \ + f(_41) f(_42) f(_43) f(_44) f(_45) f(_46) f(_47) f(_48) f(_49) f(_50) f(_51) f(_52) \ + f(_53) f(_54) f(_55) f(_56) +#define VISIT_STRUCT_APPLYF57( \ + f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, \ + _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, \ + _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57) \ + f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) \ + f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) f(_28) \ + f(_29) f(_30) f(_31) f(_32) f(_33) f(_34) f(_35) f(_36) f(_37) f(_38) f(_39) f(_40) \ + f(_41) f(_42) f(_43) f(_44) f(_45) f(_46) f(_47) f(_48) f(_49) f(_50) f(_51) f(_52) \ + f(_53) f(_54) f(_55) f(_56) f(_57) +#define VISIT_STRUCT_APPLYF58( \ + f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, \ + _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, \ + _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58) \ + f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) \ + f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) f(_28) \ + f(_29) f(_30) f(_31) f(_32) f(_33) f(_34) f(_35) f(_36) f(_37) f(_38) f(_39) f(_40) \ + f(_41) f(_42) f(_43) f(_44) f(_45) f(_46) f(_47) f(_48) f(_49) f(_50) f(_51) f(_52) \ + f(_53) f(_54) f(_55) f(_56) f(_57) f(_58) +#define VISIT_STRUCT_APPLYF59(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, \ + _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, \ + _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, \ + _55, _56, _57, _58, _59) \ + f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) \ + f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) f(_28) \ + f(_29) f(_30) f(_31) f(_32) f(_33) f(_34) f(_35) f(_36) f(_37) f(_38) f(_39) f(_40) \ + f(_41) f(_42) f(_43) f(_44) f(_45) f(_46) f(_47) f(_48) f(_49) f(_50) f(_51) f(_52) \ + f(_53) f(_54) f(_55) f(_56) f(_57) f(_58) f(_59) +#define VISIT_STRUCT_APPLYF60(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, \ + _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, \ + _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, \ + _55, _56, _57, _58, _59, _60) \ + f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) \ + f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) f(_28) \ + f(_29) f(_30) f(_31) f(_32) f(_33) f(_34) f(_35) f(_36) f(_37) f(_38) f(_39) f(_40) \ + f(_41) f(_42) f(_43) f(_44) f(_45) f(_46) f(_47) f(_48) f(_49) f(_50) f(_51) f(_52) \ + f(_53) f(_54) f(_55) f(_56) f(_57) f(_58) f(_59) f(_60) +#define VISIT_STRUCT_APPLYF61(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, \ + _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, \ + _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, \ + _55, _56, _57, _58, _59, _60, _61) \ + f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) \ + f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) f(_28) \ + f(_29) f(_30) f(_31) f(_32) f(_33) f(_34) f(_35) f(_36) f(_37) f(_38) f(_39) f(_40) \ + f(_41) f(_42) f(_43) f(_44) f(_45) f(_46) f(_47) f(_48) f(_49) f(_50) f(_51) f(_52) \ + f(_53) f(_54) f(_55) f(_56) f(_57) f(_58) f(_59) f(_60) f(_61) +#define VISIT_STRUCT_APPLYF62(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, \ + _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, \ + _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, \ + _55, _56, _57, _58, _59, _60, _61, _62) \ + f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) \ + f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) f(_28) \ + f(_29) f(_30) f(_31) f(_32) f(_33) f(_34) f(_35) f(_36) f(_37) f(_38) f(_39) f(_40) \ + f(_41) f(_42) f(_43) f(_44) f(_45) f(_46) f(_47) f(_48) f(_49) f(_50) f(_51) f(_52) \ + f(_53) f(_54) f(_55) f(_56) f(_57) f(_58) f(_59) f(_60) f(_61) f(_62) +#define VISIT_STRUCT_APPLYF63(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, \ + _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, \ + _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, \ + _55, _56, _57, _58, _59, _60, _61, _62, _63) \ + f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) \ + f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) f(_28) \ + f(_29) f(_30) f(_31) f(_32) f(_33) f(_34) f(_35) f(_36) f(_37) f(_38) f(_39) f(_40) \ + f(_41) f(_42) f(_43) f(_44) f(_45) f(_46) f(_47) f(_48) f(_49) f(_50) f(_51) f(_52) \ + f(_53) f(_54) f(_55) f(_56) f(_57) f(_58) f(_59) f(_60) f(_61) f(_62) f(_63) +#define VISIT_STRUCT_APPLYF64(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, \ + _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, \ + _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, \ + _55, _56, _57, _58, _59, _60, _61, _62, _63, _64) \ + f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) \ + f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) f(_28) \ + f(_29) f(_30) f(_31) f(_32) f(_33) f(_34) f(_35) f(_36) f(_37) f(_38) f(_39) f(_40) \ + f(_41) f(_42) f(_43) f(_44) f(_45) f(_46) f(_47) f(_48) f(_49) f(_50) f(_51) f(_52) \ + f(_53) f(_54) f(_55) f(_56) f(_57) f(_58) f(_59) f(_60) f(_61) f(_62) f(_63) \ + f(_64) +#define VISIT_STRUCT_APPLYF65(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, \ + _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, \ + _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, \ + _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, _65) \ + f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) \ + f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) f(_28) \ + f(_29) f(_30) f(_31) f(_32) f(_33) f(_34) f(_35) f(_36) f(_37) f(_38) f(_39) f(_40) \ + f(_41) f(_42) f(_43) f(_44) f(_45) f(_46) f(_47) f(_48) f(_49) f(_50) f(_51) f(_52) \ + f(_53) f(_54) f(_55) f(_56) f(_57) f(_58) f(_59) f(_60) f(_61) f(_62) f(_63) \ + f(_64) f(_65) +#define VISIT_STRUCT_APPLYF66(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, \ + _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, \ + _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, \ + _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, _65, _66) \ + f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) \ + f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) f(_28) \ + f(_29) f(_30) f(_31) f(_32) f(_33) f(_34) f(_35) f(_36) f(_37) f(_38) f(_39) f(_40) \ + f(_41) f(_42) f(_43) f(_44) f(_45) f(_46) f(_47) f(_48) f(_49) f(_50) f(_51) f(_52) \ + f(_53) f(_54) f(_55) f(_56) f(_57) f(_58) f(_59) f(_60) f(_61) f(_62) f(_63) \ + f(_64) f(_65) f(_66) +#define VISIT_STRUCT_APPLYF67(f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, \ + _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, \ + _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, \ + _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, _65, _66, _67) \ + f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) \ + f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) f(_28) \ + f(_29) f(_30) f(_31) f(_32) f(_33) f(_34) f(_35) f(_36) f(_37) f(_38) f(_39) f(_40) \ + f(_41) f(_42) f(_43) f(_44) f(_45) f(_46) f(_47) f(_48) f(_49) f(_50) f(_51) f(_52) \ + f(_53) f(_54) f(_55) f(_56) f(_57) f(_58) f(_59) f(_60) f(_61) f(_62) f(_63) \ + f(_64) f(_65) f(_66) f(_67) +#define VISIT_STRUCT_APPLYF68( \ + f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, \ + _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, \ + _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, \ + _59, _60, _61, _62, _63, _64, _65, _66, _67, _68) \ + f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) \ + f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) f(_28) \ + f(_29) f(_30) f(_31) f(_32) f(_33) f(_34) f(_35) f(_36) f(_37) f(_38) f(_39) f(_40) \ + f(_41) f(_42) f(_43) f(_44) f(_45) f(_46) f(_47) f(_48) f(_49) f(_50) f(_51) f(_52) \ + f(_53) f(_54) f(_55) f(_56) f(_57) f(_58) f(_59) f(_60) f(_61) f(_62) f(_63) \ + f(_64) f(_65) f(_66) f(_67) f(_68) +#define VISIT_STRUCT_APPLYF69( \ + f, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, \ + _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, \ + _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, \ + _59, _60, _61, _62, _63, _64, _65, _66, _67, _68, _69) \ + f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) f(_10) f(_11) f(_12) f(_13) f(_14) f(_15) \ + f(_16) f(_17) f(_18) f(_19) f(_20) f(_21) f(_22) f(_23) f(_24) f(_25) f(_26) f(_27) f(_28) \ + f(_29) f(_30) f(_31) f(_32) f(_33) f(_34) f(_35) f(_36) f(_37) f(_38) f(_39) f(_40) \ + f(_41) f(_42) f(_43) f(_44) f(_45) f(_46) f(_47) f(_48) f(_49) f(_50) f(_51) f(_52) \ + f(_53) f(_54) f(_55) f(_56) f(_57) f(_58) f(_59) f(_60) f(_61) f(_62) f(_63) \ + f(_64) f(_65) f(_66) f(_67) f(_68) f(_69) + +#define VISIT_STRUCT_APPLY_F_(M, ...) VISIT_STRUCT_EXPAND(M(__VA_ARGS__)) +#define VISIT_STRUCT_PP_MAP(f, ...) \ + VISIT_STRUCT_EXPAND(VISIT_STRUCT_APPLY_F_( \ + VISIT_STRUCT_CONCAT(VISIT_STRUCT_APPLYF, VISIT_STRUCT_PP_NARG(__VA_ARGS__)), f, \ + __VA_ARGS__)) + +/*** End generated code ***/ + +/*** + * These macros are used with VISIT_STRUCT_PP_MAP + */ + +#define VISIT_STRUCT_FIELD_COUNT(MEMBER_NAME) +1 + +#define VISIT_STRUCT_MEMBER_HELPER(MEMBER_NAME) \ + std::forward(visitor)(#MEMBER_NAME, std::forward(struct_instance).MEMBER_NAME); + +#define VISIT_STRUCT_MEMBER_HELPER_PTR(MEMBER_NAME) \ + std::forward(visitor)(#MEMBER_NAME, &this_type::MEMBER_NAME); + +#define VISIT_STRUCT_MEMBER_HELPER_TYPE(MEMBER_NAME) \ + std::forward(visitor)(#MEMBER_NAME, visit_struct::type_c{}); + +#define VISIT_STRUCT_MEMBER_HELPER_ACC(MEMBER_NAME) \ + std::forward(visitor)( \ + #MEMBER_NAME, \ + visit_struct::accessor{}); + +#define VISIT_STRUCT_MEMBER_HELPER_PAIR(MEMBER_NAME) \ + std::forward(visitor)(#MEMBER_NAME, std::forward(s1).MEMBER_NAME, \ + std::forward(s2).MEMBER_NAME); + +#define VISIT_STRUCT_MAKE_GETTERS(MEMBER_NAME) \ + template \ + static VISIT_STRUCT_CONSTEXPR auto get_value( \ + std::integral_constant, S &&s) \ + ->decltype((std::forward(s).MEMBER_NAME)) { \ + return std::forward(s).MEMBER_NAME; \ + } \ + \ + static VISIT_STRUCT_CONSTEXPR auto get_name( \ + std::integral_constant) \ + ->decltype(#MEMBER_NAME) { \ + return #MEMBER_NAME; \ + } \ + \ + static VISIT_STRUCT_CONSTEXPR auto get_pointer( \ + std::integral_constant) \ + ->decltype(&this_type::MEMBER_NAME) { \ + return &this_type::MEMBER_NAME; \ + } \ + \ + static VISIT_STRUCT_CONSTEXPR auto get_accessor( \ + std::integral_constant) \ + ->visit_struct::accessor { \ + return {}; \ + } \ + \ + static auto type_at(std::integral_constant) \ + ->visit_struct::type_c; + +// This macro specializes the trait, provides "apply" method which does the work. +// Below, template parameter S should always be the same as STRUCT_NAME modulo const and +// reference. The interface defined above ensures that STRUCT_NAME is clean_t +// basically. +// +// Note: The code to make the indexed getters work is more convoluted than I'd like. +// PP_MAP doesn't give you the index of each member. And rather than hack it so that +// it will do that, what we do instead is: 1: Declare an enum `field_enum` in the +// scope of visitable, which maps names to indices. +// This gives an easy way for the macro to get the index from the name token. +// 2: Intuitively we'd like to use template partial specialization to make indices +// map to +// values, and have a new specialization for each member. But, specializations +// can only be made at namespace scope. So to keep things tidy and contained +// within this trait, we use tag dispatch with std::integral_constant +// instead. + +#define VISITABLE_STRUCT(STRUCT_NAME, ...) \ + namespace visit_struct { \ + namespace traits { \ + \ + template <> struct visitable { \ + \ + using this_type = STRUCT_NAME; \ + \ + static VISIT_STRUCT_CONSTEXPR auto get_name() -> decltype(#STRUCT_NAME) { \ + return #STRUCT_NAME; \ + } \ + \ + static VISIT_STRUCT_CONSTEXPR const std::size_t field_count = \ + 0 VISIT_STRUCT_PP_MAP(VISIT_STRUCT_FIELD_COUNT, __VA_ARGS__); \ + \ + template \ + VISIT_STRUCT_CXX14_CONSTEXPR static void apply(V &&visitor, S &&struct_instance) { \ + VISIT_STRUCT_PP_MAP(VISIT_STRUCT_MEMBER_HELPER, __VA_ARGS__) \ + } \ + \ + template \ + VISIT_STRUCT_CXX14_CONSTEXPR static void apply(V &&visitor, S1 &&s1, S2 &&s2) { \ + VISIT_STRUCT_PP_MAP(VISIT_STRUCT_MEMBER_HELPER_PAIR, __VA_ARGS__) \ + } \ + \ + template VISIT_STRUCT_CXX14_CONSTEXPR static void visit_pointers(V &&visitor) { \ + VISIT_STRUCT_PP_MAP(VISIT_STRUCT_MEMBER_HELPER_PTR, __VA_ARGS__) \ + } \ + \ + template VISIT_STRUCT_CXX14_CONSTEXPR static void visit_types(V &&visitor) { \ + VISIT_STRUCT_PP_MAP(VISIT_STRUCT_MEMBER_HELPER_TYPE, __VA_ARGS__) \ + } \ + \ + template VISIT_STRUCT_CXX14_CONSTEXPR static void visit_accessors(V &&visitor) { \ + VISIT_STRUCT_PP_MAP(VISIT_STRUCT_MEMBER_HELPER_ACC, __VA_ARGS__) \ + } \ + \ + struct fields_enum { \ + enum index { __VA_ARGS__ }; \ + }; \ + \ + VISIT_STRUCT_PP_MAP(VISIT_STRUCT_MAKE_GETTERS, __VA_ARGS__) \ + \ + static VISIT_STRUCT_CONSTEXPR const bool value = true; \ + }; \ + } \ + } \ + static_assert(true, "") + +} // end namespace visit_struct + +#endif // VISIT_STRUCT_HPP_INCLUDED + +// __ __ _ ______ _____ +// | \/ | (_) | ____| / ____|_ _ +// | \ / | __ _ __ _ _ ___ | |__ _ __ _ _ _ __ ___ | | _| |_ _| |_ +// | |\/| |/ _` |/ _` | |/ __| | __| | '_ \| | | | '_ ` _ \ | | |_ _|_ _| +// | | | | (_| | (_| | | (__ | |____| | | | |_| | | | | | | | |____|_| |_| +// |_| |_|\__,_|\__, |_|\___| |______|_| |_|\__,_|_| |_| |_| \_____| +// __/ | https://github.com/Neargye/magic_enum +// |___/ version 0.6.6 +// +// Licensed under the MIT License . +// SPDX-License-Identifier: MIT +// Copyright (c) 2019 - 2020 Daniil Goncharov . +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#ifndef NEARGYE_MAGIC_ENUM_HPP +#define NEARGYE_MAGIC_ENUM_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored \ + "-Wsign-conversion" // Implicit conversion changes signedness: 'int' to 'size_t'. +#elif defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored \ + "-Wsign-conversion" // Implicit conversion changes signedness: 'int' to 'size_t'. +#elif defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable : 26495) // Variable 'static_string::chars' is uninitialized. +#endif + +// Checks magic_enum compiler compatibility. +#if defined(__clang__) && __clang_major__ >= 5 || defined(__GNUC__) && __GNUC__ >= 9 || \ + defined(_MSC_VER) && _MSC_VER >= 1910 +#undef MAGIC_ENUM_SUPPORTED +#define MAGIC_ENUM_SUPPORTED 1 +#endif + +// Enum value must be greater or equals than MAGIC_ENUM_RANGE_MIN. By default +// MAGIC_ENUM_RANGE_MIN = -128. If need another min range for all enum types by default, +// redefine the macro MAGIC_ENUM_RANGE_MIN. +#if !defined(MAGIC_ENUM_RANGE_MIN) +#define MAGIC_ENUM_RANGE_MIN -128 +#endif + +// Enum value must be less or equals than MAGIC_ENUM_RANGE_MAX. By default +// MAGIC_ENUM_RANGE_MAX = 128. If need another max range for all enum types by default, +// redefine the macro MAGIC_ENUM_RANGE_MAX. +#if !defined(MAGIC_ENUM_RANGE_MAX) +#define MAGIC_ENUM_RANGE_MAX 128 +#endif + +namespace magic_enum { + +// Enum value must be in range [MAGIC_ENUM_RANGE_MIN, MAGIC_ENUM_RANGE_MAX]. By default +// MAGIC_ENUM_RANGE_MIN = -128, MAGIC_ENUM_RANGE_MAX = 128. If need another range for all +// enum types by default, redefine the macro MAGIC_ENUM_RANGE_MIN and +// MAGIC_ENUM_RANGE_MAX. If need another range for specific enum type, add specialization +// enum_range for necessary enum type. +template struct enum_range { + static_assert(std::is_enum_v, "magic_enum::enum_range requires enum type."); + inline static constexpr int min = MAGIC_ENUM_RANGE_MIN; + inline static constexpr int max = MAGIC_ENUM_RANGE_MAX; + static_assert(max > min, "magic_enum::enum_range requires max > min."); +}; + +static_assert(MAGIC_ENUM_RANGE_MIN <= 0, "MAGIC_ENUM_RANGE_MIN must be less or equals than 0."); +static_assert(MAGIC_ENUM_RANGE_MIN > (std::numeric_limits::min)(), + "MAGIC_ENUM_RANGE_MIN must be greater than INT16_MIN."); + +static_assert(MAGIC_ENUM_RANGE_MAX > 0, "MAGIC_ENUM_RANGE_MAX must be greater than 0."); +static_assert(MAGIC_ENUM_RANGE_MAX < (std::numeric_limits::max)(), + "MAGIC_ENUM_RANGE_MAX must be less than INT16_MAX."); + +static_assert(MAGIC_ENUM_RANGE_MAX > MAGIC_ENUM_RANGE_MIN, + "MAGIC_ENUM_RANGE_MAX must be greater than MAGIC_ENUM_RANGE_MIN."); + +namespace detail { + +template +struct supported +#if defined(MAGIC_ENUM_SUPPORTED) && MAGIC_ENUM_SUPPORTED || defined(MAGIC_ENUM_NO_CHECK_SUPPORT) + : std::true_type { +}; +#else + : std::false_type { +}; +#endif + +template +inline constexpr bool is_enum_v = std::is_enum_v &&std::is_same_v>; + +template struct static_string { + constexpr explicit static_string(std::string_view str) noexcept + : static_string{str, std::make_index_sequence{}} { + assert(str.size() == N); + } + + constexpr const char *data() const noexcept { return chars.data(); } + + constexpr std::size_t size() const noexcept { return N; } + + constexpr operator std::string_view() const noexcept { return {data(), size()}; } + +private: + template + constexpr static_string(std::string_view str, std::index_sequence) noexcept + : chars{{str[I]..., '\0'}} {} + + const std::array chars; +}; + +template <> struct static_string<0> { + constexpr explicit static_string(std::string_view) noexcept {} + + constexpr const char *data() const noexcept { return nullptr; } + + constexpr std::size_t size() const noexcept { return 0; } + + constexpr operator std::string_view() const noexcept { return {}; } +}; + +constexpr std::string_view pretty_name(std::string_view name) noexcept { + for (std::size_t i = name.size(); i > 0; --i) { + if (!((name[i - 1] >= '0' && name[i - 1] <= '9') || + (name[i - 1] >= 'a' && name[i - 1] <= 'z') || + (name[i - 1] >= 'A' && name[i - 1] <= 'Z') || (name[i - 1] == '_'))) { + name.remove_prefix(i); + break; + } + } + + if (name.size() > 0 && ((name.front() >= 'a' && name.front() <= 'z') || + (name.front() >= 'A' && name.front() <= 'Z') || (name.front() == '_'))) { + return name; + } + + return {}; // Invalid name. +} + +template +constexpr bool cmp_equal(std::string_view lhs, std::string_view rhs, BinaryPredicate &&p) noexcept( + std::is_nothrow_invocable_r_v) { + if (lhs.size() != rhs.size()) { + return false; + } + + const auto size = lhs.size(); + for (std::size_t i = 0; i < size; ++i) { + if (!p(lhs[i], rhs[i])) { + return false; + } + } + + return true; +} + +template constexpr bool cmp_less(L lhs, R rhs) noexcept { + static_assert(std::is_integral_v && std::is_integral_v, + "magic_enum::detail::cmp_less requires integral type."); + + if constexpr (std::is_signed_v == std::is_signed_v) { + // If same signedness (both signed or both unsigned). + return lhs < rhs; + } else if constexpr (std::is_signed_v) { + // If 'right' is negative, then result is 'false', otherwise cast & compare. + return rhs > 0 && lhs < static_cast>(rhs); + } else { + // If 'left' is negative, then result is 'true', otherwise cast & compare. + return lhs < 0 || static_cast>(lhs) < rhs; + } +} + +template constexpr auto n() noexcept { + static_assert(is_enum_v, "magic_enum::detail::n requires enum type."); +#if defined(MAGIC_ENUM_SUPPORTED) && MAGIC_ENUM_SUPPORTED +#if defined(__clang__) + constexpr std::string_view name{__PRETTY_FUNCTION__ + 34, sizeof(__PRETTY_FUNCTION__) - 36}; +#elif defined(__GNUC__) + constexpr std::string_view name{__PRETTY_FUNCTION__ + 49, sizeof(__PRETTY_FUNCTION__) - 51}; +#elif defined(_MSC_VER) + constexpr std::string_view name{__FUNCSIG__ + 40, sizeof(__FUNCSIG__) - 57}; +#endif + return static_string{name}; +#else + return std::string_view{}; // Unsupported compiler. +#endif +} + +template inline constexpr auto type_name_v = n(); + +template constexpr auto n() noexcept { + static_assert(is_enum_v, "magic_enum::detail::n requires enum type."); +#if defined(MAGIC_ENUM_SUPPORTED) && MAGIC_ENUM_SUPPORTED +#if defined(__clang__) || defined(__GNUC__) + constexpr auto name = pretty_name({__PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 2}); +#elif defined(_MSC_VER) + constexpr auto name = pretty_name({__FUNCSIG__, sizeof(__FUNCSIG__) - 17}); +#endif + return static_string{name}; +#else + return std::string_view{}; // Unsupported compiler. +#endif +} + +template inline constexpr auto name_v = n(); + +template constexpr bool is_valid() noexcept { + static_assert(is_enum_v, "magic_enum::detail::is_valid requires enum type."); + + return n(V)>().size() != 0; +} + +template constexpr std::size_t range_size() noexcept { + static_assert(is_enum_v, "magic_enum::detail::range_size requires enum type."); + constexpr auto size = Max - Min + 1; + static_assert(size > 0, "magic_enum::enum_range requires valid size."); + static_assert(size < (std::numeric_limits::max)(), + "magic_enum::enum_range requires valid size."); + + return static_cast(size); +} + +template constexpr int reflected_min() noexcept { + static_assert(is_enum_v, "magic_enum::detail::reflected_min requires enum type."); + constexpr auto lhs = enum_range::min; + static_assert(lhs > (std::numeric_limits::min)(), + "magic_enum::enum_range requires min must be greater than INT16_MIN."); + constexpr auto rhs = (std::numeric_limits>::min)(); + + return cmp_less(lhs, rhs) ? rhs : lhs; +} + +template constexpr int reflected_max() noexcept { + static_assert(is_enum_v, "magic_enum::detail::reflected_max requires enum type."); + constexpr auto lhs = enum_range::max; + static_assert(lhs < (std::numeric_limits::max)(), + "magic_enum::enum_range requires max must be less than INT16_MAX."); + constexpr auto rhs = (std::numeric_limits>::max)(); + + return cmp_less(lhs, rhs) ? lhs : rhs; +} + +template inline constexpr int reflected_min_v = reflected_min(); + +template inline constexpr int reflected_max_v = reflected_max(); + +template +constexpr std::size_t values_count(const std::array& valid) noexcept { + auto count = std::size_t{0}; + for (std::size_t i = 0; i < valid.size(); ++i) { + if (valid[i]) { + ++count; + } + } + + return count; +} + +// https://github.com/Neargye/magic_enum/commit/9ed98e3463568124588a0e76d980c141f222cb40 + +template constexpr auto values(std::integer_sequence) noexcept { + static_assert(is_enum_v, "magic_enum::detail::values requires enum type."); + constexpr std::array valid{{is_valid>()...}}; + //constexpr int count = ((valid[I] ? 1 : 0) + ...); + constexpr int count = values_count(valid); + + std::array values{}; + for (int i = 0, v = 0; v < count; ++i) { + if (valid[i]) { + values[v++] = static_cast(i + reflected_min_v); + } + } + + return values; +} + +template +inline constexpr auto values_v = values( + std::make_integer_sequence, reflected_max_v>()>{}); + +template inline constexpr std::size_t count_v = values_v.size(); + +template inline constexpr int min_v = static_cast(values_v.front()); + +template inline constexpr int max_v = static_cast(values_v.back()); + +template +inline constexpr std::size_t range_size_v = range_size, max_v>(); + +template +using index_t = std::conditional_t < range_size_v + <(std::numeric_limits::max)(), std::uint8_t, std::uint16_t>; + +template +inline constexpr auto invalid_index_v = (std::numeric_limits>::max)(); + +template constexpr auto indexes(std::integer_sequence) noexcept { + static_assert(is_enum_v, "magic_enum::detail::indexes requires enum type."); + [[maybe_unused]] index_t i = 0; + + return std::array, sizeof...(I)>{ + {(is_valid>() ? i++ : invalid_index_v)...}}; +} + +template +inline constexpr auto indexes_v = indexes(std::make_integer_sequence>{}); + +template constexpr auto names(std::index_sequence) noexcept { + static_assert(is_enum_v, "magic_enum::detail::names requires enum type."); + + return std::array{{name_v[I]>...}}; +} + +template +inline constexpr auto names_v = names(std::make_index_sequence>{}); + +template constexpr auto entries(std::index_sequence) noexcept { + static_assert(is_enum_v, "magic_enum::detail::entries requires enum type."); + + return std::array, sizeof...(I)>{ + {{values_v[I], name_v[I]>}...}}; +} + +template +inline constexpr auto entries_v = entries(std::make_index_sequence>{}); + +template inline constexpr bool is_sparse_v = range_size_v != count_v; + +template > +constexpr int undex(U value) noexcept { + static_assert(is_enum_v, "magic_enum::detail::undex requires enum type."); + + if (const auto i = static_cast(value) - min_v; + value >= static_cast(min_v) && value <= static_cast(max_v)) { + if constexpr (is_sparse_v) { + if (const auto idx = indexes_v[i]; idx != invalid_index_v) { + return idx; + } + } else { + return i; + } + } + + return -1; // Value out of range. +} + +template constexpr int endex(E value) noexcept { + static_assert(is_enum_v, "magic_enum::detail::endex requires enum type."); + + return undex(static_cast>(value)); +} + +template +using enable_if_enum_t = std::enable_if_t>, R>; + +template >>> +using enum_concept = T; + +template > struct is_scoped_enum : std::false_type {}; + +template +struct is_scoped_enum + : std::bool_constant>> {}; + +template > struct is_unscoped_enum : std::false_type {}; + +template +struct is_unscoped_enum + : std::bool_constant>> {}; + +template >> struct underlying_type {}; + +template struct underlying_type : std::underlying_type> {}; + +} // namespace detail + +// Checks is magic_enum supported compiler. +inline constexpr bool is_magic_enum_supported = detail::supported::value; + +template using Enum = detail::enum_concept; + +// Checks whether T is an Unscoped enumeration type. +// Provides the member constant value which is equal to true, if T is an [Unscoped +// enumeration](https://en.cppreference.com/w/cpp/language/enum#Unscoped_enumeration) +// type. Otherwise, value is equal to false. +template struct is_unscoped_enum : detail::is_unscoped_enum {}; + +template inline constexpr bool is_unscoped_enum_v = is_unscoped_enum::value; + +// Checks whether T is an Scoped enumeration type. +// Provides the member constant value which is equal to true, if T is an [Scoped +// enumeration](https://en.cppreference.com/w/cpp/language/enum#Scoped_enumerations) type. +// Otherwise, value is equal to false. +template struct is_scoped_enum : detail::is_scoped_enum {}; + +template inline constexpr bool is_scoped_enum_v = is_scoped_enum::value; + +// If T is a complete enumeration type, provides a member typedef type that names the +// underlying type of T. Otherwise, if T is not an enumeration type, there is no member +// type. Otherwise (T is an incomplete enumeration type), the program is ill-formed. +template struct underlying_type : detail::underlying_type {}; + +template using underlying_type_t = typename underlying_type::type; + +// Returns string name of enum type. +template +[[nodiscard]] constexpr auto enum_type_name() noexcept + -> detail::enable_if_enum_t { + using D = std::decay_t; + constexpr std::string_view name = detail::type_name_v; + static_assert(name.size() > 0, "Enum type does not have a name."); + + return name; +} + +// Returns number of enum values. +template +[[nodiscard]] constexpr auto enum_count() noexcept -> detail::enable_if_enum_t { + using D = std::decay_t; + static_assert(detail::supported::value, + "magic_enum unsupported compiler " + "(https://github.com/Neargye/magic_enum#compiler-compatibility)."); + static_assert(detail::count_v> 0, + "magic_enum requires enum implementation and valid max and min."); + + return detail::count_v; +} + +// Returns enum value at specified index. +// No bounds checking is performed: the behavior is undefined if index >= number of enum +// values. +template +[[nodiscard]] constexpr auto enum_value(std::size_t index) noexcept + -> detail::enable_if_enum_t> { + using D = std::decay_t; + static_assert(detail::supported::value, + "magic_enum unsupported compiler " + "(https://github.com/Neargye/magic_enum#compiler-compatibility)."); + static_assert(detail::count_v> 0, + "magic_enum requires enum implementation and valid max and min."); + + if constexpr (detail::is_sparse_v) { + return assert(index < detail::count_v), detail::values_v[index]; + } else { + return assert(index < detail::count_v), static_cast(index + detail::min_v); + } +} + +// Obtains value enum sequence. +// Returns std::array with enum values, sorted by enum value. +template +[[nodiscard]] constexpr auto enum_values() noexcept + -> detail::enable_if_enum_t>) &> { + using D = std::decay_t; + static_assert(detail::supported::value, + "magic_enum unsupported compiler " + "(https://github.com/Neargye/magic_enum#compiler-compatibility)."); + static_assert(detail::count_v> 0, + "magic_enum requires enum implementation and valid max and min."); + + return detail::values_v; +} + +// Returns string enum name from static storage enum variable. +// This version is much lighter on the compile times and is not restricted to the +// enum_range limitation. +template +[[nodiscard]] constexpr auto enum_name() noexcept + -> detail::enable_if_enum_t { + using D = std::decay_t; + static_assert(detail::supported::value, + "magic_enum unsupported compiler " + "(https://github.com/Neargye/magic_enum#compiler-compatibility)."); + constexpr std::string_view name = detail::name_v, V>; + static_assert(name.size() > 0, "Enum value does not have a name."); + + return name; +} + +// Returns string enum name from enum value. +// If enum value does not have name or value out of range, returns empty string. +template +[[nodiscard]] constexpr auto enum_name(E value) noexcept + -> detail::enable_if_enum_t { + using D = std::decay_t; + static_assert(detail::supported::value, + "magic_enum unsupported compiler " + "(https://github.com/Neargye/magic_enum#compiler-compatibility)."); + static_assert(detail::count_v> 0, + "magic_enum requires enum implementation and valid max and min."); + + if (const auto i = detail::endex(value); i != -1) { + return detail::names_v[i]; + } + + return {}; // Value out of range. +} + +// Obtains string enum name sequence. +// Returns std::array with string enum names, sorted by enum value. +template +[[nodiscard]] constexpr auto enum_names() noexcept + -> detail::enable_if_enum_t>) &> { + using D = std::decay_t; + static_assert(detail::supported::value, + "magic_enum unsupported compiler " + "(https://github.com/Neargye/magic_enum#compiler-compatibility)."); + static_assert(detail::count_v> 0, + "magic_enum requires enum implementation and valid max and min."); + + return detail::names_v; +} + +// Obtains pair (value enum, string enum name) sequence. +// Returns std::array with std::pair (value enum, string enum name), sorted by enum value. +template +[[nodiscard]] constexpr auto enum_entries() noexcept + -> detail::enable_if_enum_t>) &> { + using D = std::decay_t; + static_assert(detail::supported::value, + "magic_enum unsupported compiler " + "(https://github.com/Neargye/magic_enum#compiler-compatibility)."); + static_assert(detail::count_v> 0, + "magic_enum requires enum implementation and valid max and min."); + + return detail::entries_v; +} + +// Obtains enum value from enum string name. +// Returns std::optional with enum value. +template +[[nodiscard]] constexpr auto enum_cast(std::string_view value, BinaryPredicate p) noexcept( + std::is_nothrow_invocable_r_v) + -> detail::enable_if_enum_t>> { + using D = std::decay_t; + static_assert(detail::supported::value, + "magic_enum unsupported compiler " + "(https://github.com/Neargye/magic_enum#compiler-compatibility)."); + static_assert(detail::count_v> 0, + "magic_enum requires enum implementation and valid max and min."); + static_assert(std::is_invocable_r_v, + "magic_enum::enum_cast requires bool(char, char) invocable predicate."); + + for (std::size_t i = 0; i < detail::count_v; ++i) { + if (detail::cmp_equal(value, detail::names_v[i], p)) { + return enum_value(i); + } + } + + return std::nullopt; // Invalid value or out of range. +} + +template +[[nodiscard]] constexpr auto enum_cast(std::string_view value) noexcept + -> detail::enable_if_enum_t>> { + using D = std::decay_t; + static_assert(detail::supported::value, + "magic_enum unsupported compiler " + "(https://github.com/Neargye/magic_enum#compiler-compatibility)."); + static_assert(detail::count_v> 0, + "magic_enum requires enum implementation and valid max and min."); + + for (std::size_t i = 0; i < detail::count_v; ++i) { + if (value == detail::names_v[i]) { + return enum_value(i); + } + } + + return std::nullopt; // Invalid value or out of range. +} + +// Obtains enum value from integer value. +// Returns std::optional with enum value. +template +[[nodiscard]] constexpr auto enum_cast(underlying_type_t value) noexcept + -> detail::enable_if_enum_t>> { + using D = std::decay_t; + static_assert(detail::supported::value, + "magic_enum unsupported compiler " + "(https://github.com/Neargye/magic_enum#compiler-compatibility)."); + static_assert(detail::count_v> 0, + "magic_enum requires enum implementation and valid max and min."); + + if (detail::undex(value) != -1) { + return static_cast(value); + } + + return std::nullopt; // Invalid value or out of range. +} + +// Returns integer value from enum value. +template +[[nodiscard]] constexpr auto enum_integer(E value) noexcept + -> detail::enable_if_enum_t> { + return static_cast>(value); +} + +// Obtains index in enum value sequence from enum value. +// Returns std::optional with index. +template +[[nodiscard]] constexpr auto enum_index(E value) noexcept + -> detail::enable_if_enum_t> { + using D = std::decay_t; + static_assert(detail::supported::value, + "magic_enum unsupported compiler " + "(https://github.com/Neargye/magic_enum#compiler-compatibility)."); + static_assert(detail::count_v> 0, + "magic_enum requires enum implementation and valid max and min."); + + if (const auto i = detail::endex(value); i != -1) { + return i; + } + + return std::nullopt; // Value out of range. +} + +// Checks whether enum contains enumerator with such value. +template +[[nodiscard]] constexpr auto enum_contains(E value) noexcept -> detail::enable_if_enum_t { + using D = std::decay_t; + static_assert(detail::supported::value, + "magic_enum unsupported compiler " + "(https://github.com/Neargye/magic_enum#compiler-compatibility)."); + static_assert(detail::count_v> 0, + "magic_enum requires enum implementation and valid max and min."); + + return detail::endex(value) != -1; +} + +// Checks whether enum contains enumerator with such integer value. +template +[[nodiscard]] constexpr auto enum_contains(underlying_type_t value) noexcept + -> detail::enable_if_enum_t { + using D = std::decay_t; + static_assert(detail::supported::value, + "magic_enum unsupported compiler " + "(https://github.com/Neargye/magic_enum#compiler-compatibility)."); + static_assert(detail::count_v> 0, + "magic_enum requires enum implementation and valid max and min."); + + return detail::undex(value) != -1; +} + +// Checks whether enum contains enumerator with such string enum name. +template +[[nodiscard]] constexpr auto enum_contains(std::string_view value) noexcept + -> detail::enable_if_enum_t { + using D = std::decay_t; + static_assert(detail::supported::value, + "magic_enum unsupported compiler " + "(https://github.com/Neargye/magic_enum#compiler-compatibility)."); + static_assert(detail::count_v> 0, + "magic_enum requires enum implementation and valid max and min."); + + return enum_cast(value).has_value(); +} + +namespace ostream_operators { + +template +auto operator<<(std::basic_ostream &os, E value) + -> detail::enable_if_enum_t &> { + if (const auto name = enum_name(value); !name.empty()) { + for (const auto c : name) { + os.put(c); + } + } else { + os << enum_integer(value); + } + + return os; +} + +template +auto operator<<(std::basic_ostream &os, std::optional value) + -> detail::enable_if_enum_t &> { + if (value.has_value()) { + os << value.value(); + } + + return os; +} + +} // namespace ostream_operators + +namespace bitwise_operators { + +template constexpr auto operator~(E rhs) noexcept -> detail::enable_if_enum_t { + return static_cast(~static_cast>(rhs)); +} + +template +constexpr auto operator|(E lhs, E rhs) noexcept -> detail::enable_if_enum_t { + return static_cast(static_cast>(lhs) | + static_cast>(rhs)); +} + +template +constexpr auto operator&(E lhs, E rhs) noexcept -> detail::enable_if_enum_t { + return static_cast(static_cast>(lhs) & + static_cast>(rhs)); +} + +template +constexpr auto operator^(E lhs, E rhs) noexcept -> detail::enable_if_enum_t { + return static_cast(static_cast>(lhs) ^ + static_cast>(rhs)); +} + +template +constexpr auto operator|=(E &lhs, E rhs) noexcept -> detail::enable_if_enum_t { + return lhs = lhs | rhs; +} + +template +constexpr auto operator&=(E &lhs, E rhs) noexcept -> detail::enable_if_enum_t { + return lhs = lhs & rhs; +} + +template +constexpr auto operator^=(E &lhs, E rhs) noexcept -> detail::enable_if_enum_t { + return lhs = lhs ^ rhs; +} + +} // namespace bitwise_operators + +} // namespace magic_enum + +#if defined(__clang__) +#pragma clang diagnostic pop +#elif defined(__GNUC__) +#pragma GCC diagnostic pop +#elif defined(_MSC_VER) +#pragma warning(pop) +#endif + +#endif // NEARGYE_MAGIC_ENUM_HPP + +#pragma once +#include +using std::array; + +namespace structopt { + +template struct array_size; +template struct array_size> { static size_t const size = N; }; + +} // namespace structopt +#pragma once + +namespace structopt { + +template class Ref> +struct is_specialization : std::false_type {}; + +template