From 66e945782eec89fa45da31013145d1de85269f6d Mon Sep 17 00:00:00 2001 From: leftibot Date: Sat, 11 Apr 2026 17:21:29 -0600 Subject: [PATCH] Address review: populate null print handler when No_IO is set When No_IO is active, the default m_print_handler is now a no-op instead of writing to stdout. The stdout handler is only installed when No_IO is not set. Users can still override the handler via set_print_handler() even with No_IO enabled. Requested by @lefticus in PR #657 review. Co-Authored-By: Claude Opus 4.6 (1M context) --- include/chaiscript/chaiscript.hpp | 3 ++- include/chaiscript/dispatchkit/bootstrap.hpp | 4 ++-- .../chaiscript/language/chaiscript_engine.hpp | 17 +++++++++++------ unittests/compiled_tests.cpp | 13 +++++++++++-- 4 files changed, 26 insertions(+), 11 deletions(-) diff --git a/include/chaiscript/chaiscript.hpp b/include/chaiscript/chaiscript.hpp index 1be2f0b4..de8c3288 100644 --- a/include/chaiscript/chaiscript.hpp +++ b/include/chaiscript/chaiscript.hpp @@ -830,7 +830,8 @@ namespace chaiscript { std::make_unique>(), std::move(t_modulepaths), std::move(t_usepaths), - std::move(t_opts)) { + std::move(t_opts), + std::find(t_lib_opts.begin(), t_lib_opts.end(), Library_Options::No_IO) != t_lib_opts.end()) { } }; } // namespace chaiscript diff --git a/include/chaiscript/dispatchkit/bootstrap.hpp b/include/chaiscript/dispatchkit/bootstrap.hpp index e5cc873c..041af80e 100644 --- a/include/chaiscript/dispatchkit/bootstrap.hpp +++ b/include/chaiscript/dispatchkit/bootstrap.hpp @@ -439,8 +439,8 @@ namespace chaiscript::bootstrap { // print_string and println_string are registered in ChaiScript_Basic::build_eval_system() // to support per-instance IO redirection via set_print_handler. - // When No_IO is set, the handler-based functions are still registered there, - // so users can provide their own print handlers even without built-in IO. + // When No_IO is set, the functions are still registered but the default handler + // is a no-op, so users can provide their own print handlers without any stdout output. m.add(dispatch::make_dynamic_proxy_function(&bind_function), "bind"); diff --git a/include/chaiscript/language/chaiscript_engine.hpp b/include/chaiscript/language/chaiscript_engine.hpp index 13403f86..4afd0449 100644 --- a/include/chaiscript/language/chaiscript_engine.hpp +++ b/include/chaiscript/language/chaiscript_engine.hpp @@ -79,9 +79,7 @@ namespace chaiscript { std::map> m_namespace_generators; - std::function m_print_handler = [](const std::string &s) noexcept { - fwrite(s.c_str(), 1, s.size(), stdout); - }; + std::function m_print_handler = [](const std::string &) noexcept {}; /// Evaluates the given string in by parsing it and running the results through the evaluator Boxed_Value do_eval(const std::string &t_input, const std::string &t_filename = "__EVAL__", bool /* t_internal*/ = false) { @@ -123,11 +121,17 @@ namespace chaiscript { chaiscript::detail::Dispatch_Engine &get_eval_engine() noexcept { return m_engine; } /// Builds all the requirements for ChaiScript, including its evaluator and a run of its prelude. - void build_eval_system(const ModulePtr &t_lib, const std::vector &t_opts) { + void build_eval_system(const ModulePtr &t_lib, const std::vector &t_opts, const bool t_no_io = false) { if (t_lib) { add(t_lib); } + if (!t_no_io) { + m_print_handler = [](const std::string &s) noexcept { + fwrite(s.c_str(), 1, s.size(), stdout); + }; + } + m_engine.add(fun([this](const std::string &s) { m_print_handler(s); }), "print_string"); m_engine.add(fun([this](const std::string &s) { m_print_handler(s + "\n"); }), "println_string"); @@ -285,7 +289,8 @@ namespace chaiscript { std::unique_ptr &&parser, std::vector t_module_paths = {}, std::vector t_use_paths = {}, - const std::vector &t_opts = chaiscript::default_options()) + const std::vector &t_opts = chaiscript::default_options(), + const bool t_no_io = false) : m_module_paths(ensure_minimum_path_vec(std::move(t_module_paths))) , m_use_paths(ensure_minimum_path_vec(std::move(t_use_paths))) , m_parser(std::move(parser)) @@ -320,7 +325,7 @@ namespace chaiscript { m_module_paths.insert(m_module_paths.begin(), dllpath + "/"); } #endif - build_eval_system(t_lib, t_opts); + build_eval_system(t_lib, t_opts, t_no_io); } #ifndef CHAISCRIPT_NO_DYNLOAD diff --git a/unittests/compiled_tests.cpp b/unittests/compiled_tests.cpp index cf13b0f2..7b601b15 100644 --- a/unittests/compiled_tests.cpp +++ b/unittests/compiled_tests.cpp @@ -1360,14 +1360,16 @@ TEST_CASE("ChaiScript_Basic No_Stdlib option disables all standard library funct CHECK_THROWS(chai.eval("from_json(\"[1,2,3]\")")); } -TEST_CASE("ChaiScript_Basic No_IO option still allows print_handler registration") { +TEST_CASE("ChaiScript_Basic No_IO option uses null handler by default") { chaiscript::ChaiScript_Basic chai(chaiscript::Std_Lib::library({chaiscript::Library_Options::No_IO}), create_chaiscript_parser(), {}, {}, - {chaiscript::Options::No_Load_Modules, chaiscript::Options::No_External_Scripts}); + {chaiscript::Options::No_Load_Modules, chaiscript::Options::No_External_Scripts}, + true); // print_string and println_string should still be available via the handler mechanism + // but the default handler is a no-op (no stdout output) CHECK_NOTHROW(chai.eval("print_string(\"hello\")")); CHECK_NOTHROW(chai.eval("println_string(\"hello\")")); CHECK(chai.eval("5 + 3") == 8); @@ -1439,10 +1441,17 @@ TEST_CASE("ChaiScript No_IO option via library options parameter") { {chaiscript::Library_Options::No_IO}); // print_string and println_string remain available via the handler mechanism + // but the default handler is a no-op (no stdout output) CHECK_NOTHROW(chai.eval("print_string(\"hello\")")); CHECK_NOTHROW(chai.eval("println_string(\"hello\")")); CHECK(chai.eval("5 + 3") == 8); CHECK_NOTHROW(chai.eval("var v = Vector()")); + + // Users can override the null handler with their own + std::string captured; + chai.set_print_handler([&captured](const std::string &s) { captured += s; }); + chai.eval("print_string(\"redirected\")"); + CHECK(captured == "redirected"); } TEST_CASE("ChaiScript No_Prelude option via library options parameter") {