diff --git a/fuzz_test/fuzz_tester.cpp b/fuzz_test/fuzz_tester.cpp index ed0fc4c..29d698b 100644 --- a/fuzz_test/fuzz_tester.cpp +++ b/fuzz_test/fuzz_tester.cpp @@ -2,21 +2,19 @@ #include #include -[[nodiscard]] auto sum_values(const uint8_t *Data, size_t Size) -{ - constexpr auto scale = 1000; +[[nodiscard]] auto sum_values(const uint8_t* Data, size_t Size) { + constexpr auto scale = 1000; - int value = 0; - for (std::size_t offset = 0; offset < Size; ++offset) { - value += static_cast(*std::next(Data, static_cast(offset))) * scale; - } - return value; + int value = 0; + for (std::size_t offset = 0; offset < Size; ++offset) { + value += static_cast(*std::next(Data, static_cast(offset))) * scale; + } + return value; } // Fuzzer that attempts to invoke undefined behavior for signed integer overflow // cppcheck-suppress unusedFunction symbolName=LLVMFuzzerTestOneInput -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) -{ - fmt::print("Value sum: {}, len{}\n", sum_values(Data, Size), Size); - return 0; +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) { + fmt::print("Value sum: {}, len{}\n", sum_values(Data, Size), Size); + return 0; } diff --git a/include/chains/sample_library.hpp b/include/chains/sample_library.hpp index 0299a7b..22afe40 100644 --- a/include/chains/sample_library.hpp +++ b/include/chains/sample_library.hpp @@ -5,11 +5,12 @@ [[nodiscard]] SAMPLE_LIBRARY_EXPORT int factorial(int) noexcept; -[[nodiscard]] constexpr int factorial_constexpr(int input) noexcept -{ - if (input == 0) { return 1; } +[[nodiscard]] constexpr int factorial_constexpr(int input) noexcept { + if (input == 0) { + return 1; + } - return input * factorial_constexpr(input - 1); + return input * factorial_constexpr(input - 1); } #endif diff --git a/include/chains/tuple.hpp b/include/chains/tuple.hpp index 2475a25..2f54238 100644 --- a/include/chains/tuple.hpp +++ b/include/chains/tuple.hpp @@ -6,7 +6,7 @@ #ifndef CHAIN_TUPLE_HPP #define CHAIN_TUPLE_HPP -namespace chain::inline v0 { +namespace chains::inline v0 { namespace detail { @@ -58,7 +58,7 @@ auto tuple_compose(std::tuple&& sequence) { //-------------------------------------------------------------------------------------------------- -} // namespace chain::inline v0 +} // namespace chains::inline v0 //-------------------------------------------------------------------------------------------------- diff --git a/src/ftxui_sample/main.cpp b/src/ftxui_sample/main.cpp index ead6410..dc6d786 100644 --- a/src/ftxui_sample/main.cpp +++ b/src/ftxui_sample/main.cpp @@ -6,9 +6,9 @@ #include #include -#include // for ftxui -#include // for Slider -#include // for ScreenInteractive +#include // for ftxui +#include // for Slider +#include // for ScreenInteractive #include #include @@ -18,332 +18,358 @@ // the source template at `configured_files/config.hpp.in`. #include -template struct GameBoard -{ - static constexpr std::size_t width = Width; - static constexpr std::size_t height = Height; +template +struct GameBoard { + static constexpr std::size_t width = Width; + static constexpr std::size_t height = Height; - std::array, width> strings; - std::array, width> values{}; + std::array, width> strings; + std::array, width> values{}; - std::size_t move_count{ 0 }; - - std::string &get_string(std::size_t cur_x, std::size_t cur_y) { return strings.at(cur_x).at(cur_y); } + std::size_t move_count{0}; + std::string& get_string(std::size_t cur_x, std::size_t cur_y) { + return strings.at(cur_x).at(cur_y); + } - void set(std::size_t cur_x, std::size_t cur_y, bool new_value) - { - get(cur_x, cur_y) = new_value; + void set(std::size_t cur_x, std::size_t cur_y, bool new_value) { + get(cur_x, cur_y) = new_value; - if (new_value) { - get_string(cur_x, cur_y) = " ON"; - } else { - get_string(cur_x, cur_y) = "OFF"; + if (new_value) { + get_string(cur_x, cur_y) = " ON"; + } else { + get_string(cur_x, cur_y) = "OFF"; + } } - } - void visit(auto visitor) - { - for (std::size_t cur_x = 0; cur_x < width; ++cur_x) { - for (std::size_t cur_y = 0; cur_y < height; ++cur_y) { visitor(cur_x, cur_y, *this); } + void visit(auto visitor) { + for (std::size_t cur_x = 0; cur_x < width; ++cur_x) { + for (std::size_t cur_y = 0; cur_y < height; ++cur_y) { + visitor(cur_x, cur_y, *this); + } + } } - } - - [[nodiscard]] bool get(std::size_t cur_x, std::size_t cur_y) const { return values.at(cur_x).at(cur_y); } - - [[nodiscard]] bool &get(std::size_t cur_x, std::size_t cur_y) { return values.at(cur_x).at(cur_y); } - - GameBoard() - { - visit([](const auto cur_x, const auto cur_y, auto &gameboard) { gameboard.set(cur_x, cur_y, true); }); - } - void update_strings() - { - for (std::size_t cur_x = 0; cur_x < width; ++cur_x) { - for (std::size_t cur_y = 0; cur_y < height; ++cur_y) { set(cur_x, cur_y, get(cur_x, cur_y)); } + [[nodiscard]] bool get(std::size_t cur_x, std::size_t cur_y) const { + return values.at(cur_x).at(cur_y); } - } - - void toggle(std::size_t cur_x, std::size_t cur_y) { set(cur_x, cur_y, !get(cur_x, cur_y)); } - - void press(std::size_t cur_x, std::size_t cur_y) - { - ++move_count; - toggle(cur_x, cur_y); - if (cur_x > 0) { toggle(cur_x - 1, cur_y); } - if (cur_y > 0) { toggle(cur_x, cur_y - 1); } - if (cur_x < width - 1) { toggle(cur_x + 1, cur_y); } - if (cur_y < height - 1) { toggle(cur_x, cur_y + 1); } - } - - [[nodiscard]] bool solved() const - { - for (std::size_t cur_x = 0; cur_x < width; ++cur_x) { - for (std::size_t cur_y = 0; cur_y < height; ++cur_y) { - if (!get(cur_x, cur_y)) { return false; } - } - } - - return true; - } -}; - - -void consequence_game() -{ - auto screen = ftxui::ScreenInteractive::TerminalOutput(); - GameBoard<3, 3> game_board; - - std::string quit_text; - - const auto update_quit_text = [&quit_text](const auto &game_board_param) { - quit_text = fmt::format("Quit ({} moves)", game_board_param.move_count); - if (game_board_param.solved()) { quit_text += " Solved!"; } - }; - - const auto make_buttons = [&] { - std::vector buttons; - for (std::size_t cur_x = 0; cur_x < game_board.width; ++cur_x) { - for (std::size_t cur_y = 0; cur_y < game_board.height; ++cur_y) { - buttons.push_back(ftxui::Button(&game_board.get_string(cur_x, cur_y), [=, &game_board] { - if (!game_board.solved()) { game_board.press(cur_x, cur_y); } - update_quit_text(game_board); - })); - } + [[nodiscard]] bool& get(std::size_t cur_x, std::size_t cur_y) { + return values.at(cur_x).at(cur_y); } - return buttons; - }; - auto buttons = make_buttons(); + GameBoard() { + visit([](const auto cur_x, const auto cur_y, auto& gameboard) { + gameboard.set(cur_x, cur_y, true); + }); + } - auto quit_button = ftxui::Button(&quit_text, screen.ExitLoopClosure()); + void update_strings() { + for (std::size_t cur_x = 0; cur_x < width; ++cur_x) { + for (std::size_t cur_y = 0; cur_y < height; ++cur_y) { + set(cur_x, cur_y, get(cur_x, cur_y)); + } + } + } - auto make_layout = [&] { - std::vector rows; + void toggle(std::size_t cur_x, std::size_t cur_y) { set(cur_x, cur_y, !get(cur_x, cur_y)); } + + void press(std::size_t cur_x, std::size_t cur_y) { + ++move_count; + toggle(cur_x, cur_y); + if (cur_x > 0) { + toggle(cur_x - 1, cur_y); + } + if (cur_y > 0) { + toggle(cur_x, cur_y - 1); + } + if (cur_x < width - 1) { + toggle(cur_x + 1, cur_y); + } + if (cur_y < height - 1) { + toggle(cur_x, cur_y + 1); + } + } - std::size_t idx = 0; + [[nodiscard]] bool solved() const { + for (std::size_t cur_x = 0; cur_x < width; ++cur_x) { + for (std::size_t cur_y = 0; cur_y < height; ++cur_y) { + if (!get(cur_x, cur_y)) { + return false; + } + } + } - for (std::size_t cur_x = 0; cur_x < game_board.width; ++cur_x) { - std::vector row; - for (std::size_t cur_y = 0; cur_y < game_board.height; ++cur_y) { - row.push_back(buttons[idx]->Render()); - ++idx; - } - rows.push_back(ftxui::hbox(std::move(row))); + return true; } +}; - rows.push_back(ftxui::hbox({ quit_button->Render() })); +void consequence_game() { + auto screen = ftxui::ScreenInteractive::TerminalOutput(); - return ftxui::vbox(std::move(rows)); - }; + GameBoard<3, 3> game_board; + std::string quit_text; - static constexpr int randomization_iterations = 100; - static constexpr int random_seed = 42; + const auto update_quit_text = [&quit_text](const auto& game_board_param) { + quit_text = fmt::format("Quit ({} moves)", game_board_param.move_count); + if (game_board_param.solved()) { + quit_text += " Solved!"; + } + }; - std::mt19937 gen32{ random_seed };// NOLINT fixed seed + const auto make_buttons = [&] { + std::vector buttons; + for (std::size_t cur_x = 0; cur_x < game_board.width; ++cur_x) { + for (std::size_t cur_y = 0; cur_y < game_board.height; ++cur_y) { + buttons.push_back( + ftxui::Button(&game_board.get_string(cur_x, cur_y), [=, &game_board] { + if (!game_board.solved()) { + game_board.press(cur_x, cur_y); + } + update_quit_text(game_board); + })); + } + } + return buttons; + }; - // NOLINTNEXTLINE This cannot be const - std::uniform_int_distribution cur_x(static_cast(0), game_board.width - 1); - // NOLINTNEXTLINE This cannot be const - std::uniform_int_distribution cur_y(static_cast(0), game_board.height - 1); + auto buttons = make_buttons(); - for (int i = 0; i < randomization_iterations; ++i) { game_board.press(cur_x(gen32), cur_y(gen32)); } - game_board.move_count = 0; - update_quit_text(game_board); + auto quit_button = ftxui::Button(&quit_text, screen.ExitLoopClosure()); - auto all_buttons = buttons; - all_buttons.push_back(quit_button); - auto container = ftxui::Container::Horizontal(all_buttons); + auto make_layout = [&] { + std::vector rows; - auto renderer = ftxui::Renderer(container, make_layout); + std::size_t idx = 0; - screen.Loop(renderer); -} + for (std::size_t cur_x = 0; cur_x < game_board.width; ++cur_x) { + std::vector row; + for (std::size_t cur_y = 0; cur_y < game_board.height; ++cur_y) { + row.push_back(buttons[idx]->Render()); + ++idx; + } + rows.push_back(ftxui::hbox(std::move(row))); + } -struct Color -{ - lefticus::tools::uint_np8_t R{ static_cast(0) }; - lefticus::tools::uint_np8_t G{ static_cast(0) }; - lefticus::tools::uint_np8_t B{ static_cast(0) }; -}; + rows.push_back(ftxui::hbox({quit_button->Render()})); -// A simple way of representing a bitmap on screen using only characters -struct Bitmap : ftxui::Node -{ - Bitmap(std::size_t width, std::size_t height)// NOLINT same typed parameters adjacent to each other - : width_(width), height_(height) - {} - - Color &at(std::size_t cur_x, std::size_t cur_y) { return pixels.at(width_ * cur_y + cur_x); } - - void ComputeRequirement() override - { - requirement_ = ftxui::Requirement{ - .min_x = static_cast(width_), .min_y = static_cast(height_ / 2), .selected_box{ 0, 0, 0, 0 } + return ftxui::vbox(std::move(rows)); }; - } - - void Render(ftxui::Screen &screen) override - { - for (std::size_t cur_x = 0; cur_x < width_; ++cur_x) { - for (std::size_t cur_y = 0; cur_y < height_ / 2; ++cur_y) { - auto &pixel = screen.PixelAt(box_.x_min + static_cast(cur_x), box_.y_min + static_cast(cur_y)); - pixel.character = "▄"; - const auto &top_color = at(cur_x, cur_y * 2); - const auto &bottom_color = at(cur_x, cur_y * 2 + 1); - pixel.background_color = ftxui::Color{ top_color.R.get(), top_color.G.get(), top_color.B.get() }; - pixel.foreground_color = ftxui::Color{ bottom_color.R.get(), bottom_color.G.get(), bottom_color.B.get() }; - } - } - } - [[nodiscard]] auto width() const noexcept { return width_; } + static constexpr int randomization_iterations = 100; + static constexpr int random_seed = 42; - [[nodiscard]] auto height() const noexcept { return height_; } + std::mt19937 gen32{random_seed}; // NOLINT fixed seed - [[nodiscard]] auto &data() noexcept { return pixels; } + // NOLINTNEXTLINE This cannot be const + std::uniform_int_distribution cur_x(static_cast(0), + game_board.width - 1); + // NOLINTNEXTLINE This cannot be const + std::uniform_int_distribution cur_y(static_cast(0), + game_board.height - 1); -private: - std::size_t width_; - std::size_t height_; + for (int i = 0; i < randomization_iterations; ++i) { + game_board.press(cur_x(gen32), cur_y(gen32)); + } + game_board.move_count = 0; + update_quit_text(game_board); - std::vector pixels = std::vector(width_ * height_, Color{}); -}; + auto all_buttons = buttons; + all_buttons.push_back(quit_button); + auto container = ftxui::Container::Horizontal(all_buttons); -void game_iteration_canvas() -{ - // this should probably have a `bitmap` helper function that does what cur_you expect - // similar to the other parts of FTXUI - auto bm = std::make_shared(50, 50);// NOLINT magic numbers - auto small_bm = std::make_shared(6, 6);// NOLINT magic numbers + auto renderer = ftxui::Renderer(container, make_layout); - double fps = 0; + screen.Loop(renderer); +} - std::size_t max_row = 0; - std::size_t max_col = 0; +struct Color { + lefticus::tools::uint_np8_t R{static_cast(0)}; + lefticus::tools::uint_np8_t G{static_cast(0)}; + lefticus::tools::uint_np8_t B{static_cast(0)}; +}; - // to do, add total game time clock also, not just current elapsed time - auto game_iteration = [&](const std::chrono::steady_clock::duration elapsed_time) { - // in here we simulate however much game time has elapsed. Update animations, - // run character AI, whatever, update stats, etc +// A simple way of representing a bitmap on screen using only characters +struct Bitmap : ftxui::Node { + Bitmap(std::size_t width, + std::size_t height) // NOLINT same typed parameters adjacent to each other + : width_(width), height_(height) {} - // this isn't actually timing based for now, it's just updating the display however fast it can - fps = 1.0 - / (static_cast(std::chrono::duration_cast(elapsed_time).count()) - / 1'000'000.0);// NOLINT magic numbers + Color& at(std::size_t cur_x, std::size_t cur_y) { return pixels.at(width_ * cur_y + cur_x); } - for (std::size_t row = 0; row < max_row; ++row) { - for (std::size_t col = 0; col < bm->width(); ++col) { ++(bm->at(col, row).R); } + void ComputeRequirement() override { + requirement_ = ftxui::Requirement{.min_x = static_cast(width_), + .min_y = static_cast(height_ / 2), + .selected_box{0, 0, 0, 0}}; } - for (std::size_t row = 0; row < bm->height(); ++row) { - for (std::size_t col = 0; col < max_col; ++col) { ++(bm->at(col, row).G); } + void Render(ftxui::Screen& screen) override { + for (std::size_t cur_x = 0; cur_x < width_; ++cur_x) { + for (std::size_t cur_y = 0; cur_y < height_ / 2; ++cur_y) { + auto& pixel = screen.PixelAt(box_.x_min + static_cast(cur_x), + box_.y_min + static_cast(cur_y)); + pixel.character = "▄"; + const auto& top_color = at(cur_x, cur_y * 2); + const auto& bottom_color = at(cur_x, cur_y * 2 + 1); + pixel.background_color = + ftxui::Color{top_color.R.get(), top_color.G.get(), top_color.B.get()}; + pixel.foreground_color = + ftxui::Color{bottom_color.R.get(), bottom_color.G.get(), bottom_color.B.get()}; + } + } } - // for the fun of it, let's have a second window doing interesting things - auto &small_bm_pixel = - small_bm->data().at(static_cast(elapsed_time.count()) % small_bm->data().size()); - - switch (elapsed_time.count() % 3) { - case 0: - small_bm_pixel.R += 11;// NOLINT Magic Number - break; - case 1: - small_bm_pixel.G += 11;// NOLINT Magic Number - break; - case 2: - small_bm_pixel.B += 11;// NOLINT Magic Number - break; - } + [[nodiscard]] auto width() const noexcept { return width_; } + [[nodiscard]] auto height() const noexcept { return height_; } - ++max_row; - if (max_row >= bm->height()) { max_row = 0; } - ++max_col; - if (max_col >= bm->width()) { max_col = 0; } - }; + [[nodiscard]] auto& data() noexcept { return pixels; } - auto screen = ftxui::ScreenInteractive::TerminalOutput(); +private: + std::size_t width_; + std::size_t height_; - int counter = 0; + std::vector pixels = std::vector(width_ * height_, Color{}); +}; - auto last_time = std::chrono::steady_clock::now(); +void game_iteration_canvas() { + // this should probably have a `bitmap` helper function that does what cur_you expect + // similar to the other parts of FTXUI + auto bm = std::make_shared(50, 50); // NOLINT magic numbers + auto small_bm = std::make_shared(6, 6); // NOLINT magic numbers + + double fps = 0; + + std::size_t max_row = 0; + std::size_t max_col = 0; + + // to do, add total game time clock also, not just current elapsed time + auto game_iteration = [&](const std::chrono::steady_clock::duration elapsed_time) { + // in here we simulate however much game time has elapsed. Update animations, + // run character AI, whatever, update stats, etc + + // this isn't actually timing based for now, it's just updating the display however fast it + // can + fps = 1.0 / + (static_cast( + std::chrono::duration_cast(elapsed_time).count()) / + 1'000'000.0); // NOLINT magic numbers + + for (std::size_t row = 0; row < max_row; ++row) { + for (std::size_t col = 0; col < bm->width(); ++col) { + ++(bm->at(col, row).R); + } + } + + for (std::size_t row = 0; row < bm->height(); ++row) { + for (std::size_t col = 0; col < max_col; ++col) { + ++(bm->at(col, row).G); + } + } + + // for the fun of it, let's have a second window doing interesting things + auto& small_bm_pixel = small_bm->data().at(static_cast(elapsed_time.count()) % + small_bm->data().size()); + + switch (elapsed_time.count() % 3) { + case 0: + small_bm_pixel.R += 11; // NOLINT Magic Number + break; + case 1: + small_bm_pixel.G += 11; // NOLINT Magic Number + break; + case 2: + small_bm_pixel.B += 11; // NOLINT Magic Number + break; + } + + ++max_row; + if (max_row >= bm->height()) { + max_row = 0; + } + ++max_col; + if (max_col >= bm->width()) { + max_col = 0; + } + }; - auto make_layout = [&] { - // This code actually processes the draw event - const auto new_time = std::chrono::steady_clock::now(); + auto screen = ftxui::ScreenInteractive::TerminalOutput(); - ++counter; - // we will dispatch to the game_iteration function, where the work happens - game_iteration(new_time - last_time); - last_time = new_time; + int counter = 0; - // now actually draw the game elements - return ftxui::hbox({ bm | ftxui::border, - ftxui::vbox({ ftxui::text("Frame: " + std::to_string(counter)), - ftxui::text("FPS: " + std::to_string(fps)), - small_bm | ftxui::border }) }); - }; + auto last_time = std::chrono::steady_clock::now(); - auto renderer = ftxui::Renderer(make_layout); + auto make_layout = [&] { + // This code actually processes the draw event + const auto new_time = std::chrono::steady_clock::now(); + ++counter; + // we will dispatch to the game_iteration function, where the work happens + game_iteration(new_time - last_time); + last_time = new_time; - std::atomic refresh_ui_continue = true; + // now actually draw the game elements + return ftxui::hbox( + {bm | ftxui::border, + ftxui::vbox({ftxui::text("Frame: " + std::to_string(counter)), + ftxui::text("FPS: " + std::to_string(fps)), small_bm | ftxui::border})}); + }; - // This thread exists to make sure that the event queue has an event to - // process at approximately a rate of 30 FPS - std::thread refresh_ui([&] { - while (refresh_ui_continue) { - using namespace std::chrono_literals; - std::this_thread::sleep_for(1.0s / 30.0);// NOLINT magic numbers - screen.PostEvent(ftxui::Event::Custom); - } - }); + auto renderer = ftxui::Renderer(make_layout); - screen.Loop(renderer); + std::atomic refresh_ui_continue = true; - refresh_ui_continue = false; - refresh_ui.join(); + // This thread exists to make sure that the event queue has an event to + // process at approximately a rate of 30 FPS + std::thread refresh_ui([&] { + while (refresh_ui_continue) { + using namespace std::chrono_literals; + std::this_thread::sleep_for(1.0s / 30.0); // NOLINT magic numbers + screen.PostEvent(ftxui::Event::Custom); + } + }); + + screen.Loop(renderer); + + refresh_ui_continue = false; + refresh_ui.join(); } // NOLINTNEXTLINE(bugprone-exception-escape) -int main(int argc, const char **argv) -{ - try { - CLI::App app{ fmt::format("{} version {}", chains::cmake::project_name, chains::cmake::project_version) }; +int main(int argc, const char** argv) { + try { + CLI::App app{fmt::format("{} version {}", chains::cmake::project_name, + chains::cmake::project_version)}; - std::optional message; - app.add_option("-m,--message", message, "A message to print back out"); - bool show_version = false; - app.add_flag("--version", show_version, "Show version information"); + std::optional message; + app.add_option("-m,--message", message, "A message to print back out"); + bool show_version = false; + app.add_flag("--version", show_version, "Show version information"); - bool is_turn_based = false; - auto *turn_based = app.add_flag("--turn_based", is_turn_based); + bool is_turn_based = false; + auto* turn_based = app.add_flag("--turn_based", is_turn_based); - bool is_loop_based = false; - auto *loop_based = app.add_flag("--loop_based", is_loop_based); + bool is_loop_based = false; + auto* loop_based = app.add_flag("--loop_based", is_loop_based); - turn_based->excludes(loop_based); - loop_based->excludes(turn_based); + turn_based->excludes(loop_based); + loop_based->excludes(turn_based); + CLI11_PARSE(app, argc, argv); - CLI11_PARSE(app, argc, argv); + if (show_version) { + fmt::print("{}\n", chains::cmake::project_version); + return EXIT_SUCCESS; + } - if (show_version) { - fmt::print("{}\n", chains::cmake::project_version); - return EXIT_SUCCESS; - } + if (is_turn_based) { + consequence_game(); + } else { + game_iteration_canvas(); + } - if (is_turn_based) { - consequence_game(); - } else { - game_iteration_canvas(); + } catch (const std::exception& e) { + spdlog::error("Unhandled exception in main: {}", e.what()); } - - } catch (const std::exception &e) { - spdlog::error("Unhandled exception in main: {}", e.what()); - } } diff --git a/src/sample_library/sample_library.cpp b/src/sample_library/sample_library.cpp index afad964..aaeb999 100644 --- a/src/sample_library/sample_library.cpp +++ b/src/sample_library/sample_library.cpp @@ -1,13 +1,12 @@ #include -int factorial(int input) noexcept -{ - int result = 1; +int factorial(int input) noexcept { + int result = 1; - while (input > 0) { - result *= input; - --input; - } + while (input > 0) { + result *= input; + --input; + } - return result; + return result; } diff --git a/test/initial_draft.cpp b/test/initial_draft.cpp index f239ecf..c7eb283 100644 --- a/test/initial_draft.cpp +++ b/test/initial_draft.cpp @@ -20,7 +20,7 @@ set on the receiver. */ -namespace chain::inline v1 { +namespace chains::inline v1 { /* segment is invoked with a receiver - @@ -55,10 +55,9 @@ class segment { segment& operator=(segment&&) noexcept = default; template - auto append(F&& f) && -> segment> { - return segment>{ - std::move(_apply), - std::tuple_cat(std::move(_functions), std::tuple{std::forward(f)})}; + auto append(F&& f) && { + return chains::segment{std::move(_apply), std::tuple_cat(std::move(_functions), + std::tuple{std::forward(f)})}; } #if 0 @@ -101,53 +100,66 @@ using segment_result_type = simplify this code by handing the multi-argument case earlier (somehow). */ +#define STLAB_FWD(x) std::forward(x) + template class chain { Tail _tail; segment _head; - template - auto result_type_helper() && { - if constexpr (Index::value == std::tuple_size_v) { - return [_segment = std::move(_head)](auto&&... args) mutable { - return std::move(_segment).result_type_helper( - std::forward(args)...); - }; - } else { - return - [_segment = - std::move(std::get(_tail)) - .append(std::move(*this) - .template result_type_helper< - std::integral_constant>())]( - auto&&... args) mutable { - return std::move(_segment).result_type_helper( - std::forward(args)...); + /// Apply a recursive lambda to each element in _tail, followed by _head. + template + auto fold_over(F fold) && { + return std::apply([fold](auto&&... links) { return fold(fold, STLAB_FWD(links)...); }, + std::tuple_cat(std::move(_tail), std::tuple(std::move(_head)))); + } + + template + static consteval auto static_fold_over(F fold) { + return std::apply([fold](auto&&... links) { return fold(fold, STLAB_FWD(links)...); }, + std::tuple_cat(std::declval(), + std::tuple(std::declval>()))); + } + + /// Return a lambda with the signature of + /// head( tail( tail<1>( tail<0>( auto&& args... ) ) ) ) + /// for computing the result type of this chain. + static consteval auto result_type_helper() { + return static_fold_over([](auto fold, auto&& first, auto&&... rest) { + if constexpr (sizeof...(rest) == 0) { + return [_segment = STLAB_FWD(first)](auto&&... args) mutable { + return std::move(_segment).result_type_helper(STLAB_FWD(args)...); }; - } + } else { + return [_segment = STLAB_FWD(first).append(fold(fold, STLAB_FWD(rest)...))]( + auto&&... args) mutable { + return std::move(_segment).result_type_helper(STLAB_FWD(args)...); + }; + } + }); } - template + template auto expand(const R& receiver) && { - if constexpr (Index::value == std::tuple_size_v) { - return [_segment = std::move(_head).append(receiver), - _receiver = receiver](auto&&... args) mutable { - return std::move(_segment).invoke(_receiver, std::forward(args)...); - }; - } else { - return [_segment = - std::move(std::get(_tail)) - .append(std::move(*this) - .template expand< - std::integral_constant>( - receiver)), - _receiver = receiver](auto&&... args) mutable { - return std::move(_segment).invoke(_receiver, std::forward(args)...); - }; - } + return std::move(*this).fold_over([receiver](auto fold, auto&& first, auto&&... rest) { + if constexpr (sizeof...(rest) == 0) { + return [receiver, + _segment = STLAB_FWD(first).append(receiver)](auto&&... args) mutable { + return std::move(_segment).invoke(receiver, STLAB_FWD(args)...); + }; + } else { + return [receiver, _segment = STLAB_FWD(first).append( + fold(fold, STLAB_FWD(rest)...))](auto&&... args) mutable { + return std::move(_segment).invoke(receiver, STLAB_FWD(args)...); + }; + } + }); } public: + template + using result_type = decltype(result_type_helper()(std::declval()...)); + explicit chain(Tail&& tail, segment&& head) : _tail{std::move(tail)}, _head{std::move(head)} {} @@ -164,28 +176,21 @@ class chain { // append function to the last sequence template auto append(F&& f) && { - return chain{std::move(_tail), - std::move(_head).append(std::forward(f))}; + return chains::chain{std::move(_tail), std::move(_head).append(std::forward(f))}; } template auto append(segment&& head) && { - using tail_type = - decltype(std::tuple_cat(std::move(_tail), std::make_tuple(std::move(_head)))); - return chain{ - std::tuple_cat(std::move(_tail), std::make_tuple(std::move(_head))), std::move(head)}; + return chains::chain{std::tuple_cat(std::move(_tail), std::make_tuple(std::move(_head))), + std::move(head)}; } template auto operator()(Args&&... args) && { - using result_type = - decltype(std::move(*this) - .template result_type_helper>()( - std::forward(args)...)); + using result_t = result_type; auto [receiver, future] = - stlab::package(stlab::immediate_executor, std::identity{}); - (void)std::move(*this).template expand>(receiver)( - std::forward(args)...); + stlab::package(stlab::immediate_executor, std::identity{}); + (void)std::move(*this).expand(receiver)(std::forward(args)...); return std::move(future); } @@ -200,19 +205,22 @@ class chain { } }; +template +chain(Tail&& tail, segment&& head) -> chain; + template inline auto operator|(segment&& head, F&& f) { return chain{std::tuple<>{}, std::move(head).append(std::forward(f))}; } -} // namespace chain::inline v1 +} // namespace chains::inline v1 //-------------------------------------------------------------------------------------------------- #include #include -namespace chain::inline v1 { +namespace chains::inline v1 { #if 0 template @@ -284,7 +292,7 @@ inline auto then(F&& future) { #endif -} // namespace chain::inline v1 +} // namespace chains::inline v1 //-------------------------------------------------------------------------------------------------- @@ -294,7 +302,7 @@ inline auto then(F&& future) { #include using namespace std; -using namespace chain; +using namespace chains; using namespace stlab; TEST_CASE("Initial draft", "[initial_draft]") { diff --git a/test/tuple_tests.cpp b/test/tuple_tests.cpp index 1dfbb23..db7751c 100644 --- a/test/tuple_tests.cpp +++ b/test/tuple_tests.cpp @@ -11,5 +11,5 @@ TEST_CASE("Test tuple compose", "[tuple_compose]") { std::tuple t{[](int x) { return x + 1.0; }, [](double x) { return x * 2.0; }, [](double x) { return std::to_string(x / 2.0); }}; - REQUIRE(chain::tuple_compose(std::move(t))(1) == "2.000000"); + REQUIRE(chains::tuple_compose(std::move(t))(1) == "2.000000"); }