mirror of
https://github.com/ChaiScript/ChaiScript.git
synced 2026-07-30 16:26:28 +08:00
Address review: define println in terms of print, expose set_print_handler to ChaiScript
Remove separate println_handler — println_string now dispatches through the single print handler with a newline appended. Only set_print_handler is needed to redirect all output. The set_print_handler function is also registered in the ChaiScript engine, so scripts can capture and redirect their own output. Requested by @lefticus in PR #657 review. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
973f9c554c
commit
e2aa3e929e
@ -818,18 +818,14 @@ m.is_type("MyClass") // true (checks the ChaiScript class name)
|
||||
## IO Redirection
|
||||
|
||||
By default, ChaiScript's `print()` and `puts()` functions write to stdout. You can redirect
|
||||
output on a per-instance basis using custom handlers. Each ChaiScript instance can have its
|
||||
own independent handlers.
|
||||
output on a per-instance basis by setting a single print handler. Both `println_string`
|
||||
(used by `print()`) and `print_string` (used by `puts()`) dispatch through the same handler —
|
||||
`println_string` simply appends a newline before calling it.
|
||||
|
||||
```cpp
|
||||
chaiscript::ChaiScript chai;
|
||||
|
||||
// Redirect print() / println_string() output (includes newline)
|
||||
chai.set_println_handler([](const std::string &s) {
|
||||
my_log_window.append(s + "\n");
|
||||
});
|
||||
|
||||
// Redirect puts() / print_string() output (no newline)
|
||||
// Redirect all output (print_string and println_string both use this handler)
|
||||
chai.set_print_handler([](const std::string &s) {
|
||||
my_log_window.append(s);
|
||||
});
|
||||
@ -841,9 +837,6 @@ context where stdout is not the desired output destination.
|
||||
```cpp
|
||||
// Example: capture all output to a string
|
||||
std::string captured;
|
||||
chai.set_println_handler([&captured](const std::string &s) {
|
||||
captured += s + "\n";
|
||||
});
|
||||
chai.set_print_handler([&captured](const std::string &s) {
|
||||
captured += s;
|
||||
});
|
||||
@ -852,5 +845,12 @@ chai.eval("print(42)"); // captured == "42\n"
|
||||
chai.eval("puts(\"hi\")"); // captured == "42\nhi"
|
||||
```
|
||||
|
||||
The print handler can also be set from within ChaiScript itself via `set_print_handler`:
|
||||
|
||||
```chaiscript
|
||||
// Redirect output from within a script
|
||||
set_print_handler(fun(s) { my_custom_log(s) })
|
||||
```
|
||||
|
||||
## Extras
|
||||
ChaiScript itself does not provide a link to the math functions defined in `<cmath>`. You can either add them yourself, or use the [ChaiScript_Extras](https://github.com/ChaiScript/ChaiScript_Extras) helper library. (Which also provides some additional string functions.)
|
||||
|
||||
@ -438,7 +438,7 @@ namespace chaiscript::bootstrap {
|
||||
m.add(fun(&Build_Info::debug_build), "debug_build");
|
||||
|
||||
// print_string and println_string are registered in ChaiScript_Basic::build_eval_system()
|
||||
// to support per-instance IO redirection via set_print_handler/set_println_handler
|
||||
// to support per-instance IO redirection via set_print_handler
|
||||
|
||||
m.add(dispatch::make_dynamic_proxy_function(&bind_function), "bind");
|
||||
|
||||
|
||||
@ -83,10 +83,6 @@ namespace chaiscript {
|
||||
fwrite(s.c_str(), 1, s.size(), stdout);
|
||||
};
|
||||
|
||||
std::function<void(const std::string &)> m_println_handler = [](const std::string &s) noexcept {
|
||||
puts(s.c_str());
|
||||
};
|
||||
|
||||
/// 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) {
|
||||
try {
|
||||
@ -133,7 +129,11 @@ namespace chaiscript {
|
||||
}
|
||||
|
||||
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_println_handler(s); }), "println_string");
|
||||
m_engine.add(fun([this](const std::string &s) { m_print_handler(s + "\n"); }), "println_string");
|
||||
|
||||
m_engine.add(fun([this](const std::function<void(const std::string &)> &t_handler) {
|
||||
m_print_handler = t_handler;
|
||||
}), "set_print_handler");
|
||||
|
||||
m_engine.add(fun([this]() { m_engine.dump_system(); }), "dump_system");
|
||||
m_engine.add(fun([this](const Boxed_Value &t_bv) { m_engine.dump_object(t_bv); }), "dump_object");
|
||||
@ -268,18 +268,12 @@ namespace chaiscript {
|
||||
|
||||
public:
|
||||
|
||||
/// \brief Set a custom handler for print_string (no newline), used by ChaiScript's puts()
|
||||
/// \brief Set a custom handler for print output, used by both print_string and println_string
|
||||
/// \param[in] t_handler Function to call with the string to print
|
||||
void set_print_handler(std::function<void(const std::string &)> t_handler) {
|
||||
m_print_handler = std::move(t_handler);
|
||||
}
|
||||
|
||||
/// \brief Set a custom handler for println_string (with newline), used by ChaiScript's print()
|
||||
/// \param[in] t_handler Function to call with the string to print (handler should append newline if desired)
|
||||
void set_println_handler(std::function<void(const std::string &)> t_handler) {
|
||||
m_println_handler = std::move(t_handler);
|
||||
}
|
||||
|
||||
/// \brief Virtual destructor for ChaiScript
|
||||
virtual ~ChaiScript_Basic() = default;
|
||||
|
||||
|
||||
@ -1442,22 +1442,17 @@ TEST_CASE("IO redirection with set_print_handler") {
|
||||
|
||||
std::string captured_output;
|
||||
|
||||
// Set custom print handler (for print_string, called by puts)
|
||||
// Set custom print handler — both print_string and println_string dispatch through it
|
||||
chai.set_print_handler([&captured_output](const std::string &s) {
|
||||
captured_output += s;
|
||||
});
|
||||
|
||||
// Set custom println handler (for println_string, called by print)
|
||||
chai.set_println_handler([&captured_output](const std::string &s) {
|
||||
captured_output += s + "\n";
|
||||
});
|
||||
|
||||
// Test that puts() uses the custom handler
|
||||
captured_output.clear();
|
||||
chai.eval("puts(\"hello\")");
|
||||
CHECK(captured_output == "hello");
|
||||
|
||||
// Test that print() uses the custom handler
|
||||
// Test that print() uses the custom handler (println_string appends newline before calling handler)
|
||||
captured_output.clear();
|
||||
chai.eval("print(\"world\")");
|
||||
CHECK(captured_output == "world\n");
|
||||
@ -1467,7 +1462,7 @@ TEST_CASE("IO redirection with set_print_handler") {
|
||||
chai.eval("print_string(\"direct\")");
|
||||
CHECK(captured_output == "direct");
|
||||
|
||||
// Test that println_string() directly uses the custom handler
|
||||
// Test that println_string() directly uses the custom handler with newline
|
||||
captured_output.clear();
|
||||
chai.eval("println_string(\"direct_ln\")");
|
||||
CHECK(captured_output == "direct_ln\n");
|
||||
@ -1477,8 +1472,8 @@ TEST_CASE("IO redirection captures numeric output") {
|
||||
chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser());
|
||||
|
||||
std::string captured_output;
|
||||
chai.set_println_handler([&captured_output](const std::string &s) {
|
||||
captured_output += s + "\n";
|
||||
chai.set_print_handler([&captured_output](const std::string &s) {
|
||||
captured_output += s;
|
||||
});
|
||||
|
||||
chai.eval("print(42)");
|
||||
@ -1492,14 +1487,31 @@ TEST_CASE("IO redirection different instances are independent") {
|
||||
std::string output1;
|
||||
std::string output2;
|
||||
|
||||
chai1.set_println_handler([&output1](const std::string &s) { output1 += s; });
|
||||
chai2.set_println_handler([&output2](const std::string &s) { output2 += s; });
|
||||
chai1.set_print_handler([&output1](const std::string &s) { output1 += s; });
|
||||
chai2.set_print_handler([&output2](const std::string &s) { output2 += s; });
|
||||
|
||||
chai1.eval("print(\"from1\")");
|
||||
chai2.eval("print(\"from2\")");
|
||||
|
||||
CHECK(output1 == "from1");
|
||||
CHECK(output2 == "from2");
|
||||
CHECK(output1 == "from1\n");
|
||||
CHECK(output2 == "from2\n");
|
||||
}
|
||||
|
||||
TEST_CASE("set_print_handler accessible from ChaiScript") {
|
||||
chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser());
|
||||
|
||||
auto captured = std::make_shared<std::string>();
|
||||
chai.add(chaiscript::fun([captured](const std::string &s) { *captured += s; }), "test_output_sink");
|
||||
|
||||
// Set the print handler from within ChaiScript
|
||||
chai.eval("set_print_handler(fun(s) { test_output_sink(s) })");
|
||||
|
||||
chai.eval("print(\"from_script\")");
|
||||
CHECK(*captured == "from_script\n");
|
||||
|
||||
captured->clear();
|
||||
chai.eval("puts(\"no_newline\")");
|
||||
CHECK(*captured == "no_newline");
|
||||
}
|
||||
|
||||
// Regression test: push_back() on script-created vector has no effect when
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user