diff --git a/src/xinterpreter.cpp b/src/xinterpreter.cpp index 14972324..15d6861b 100644 --- a/src/xinterpreter.cpp +++ b/src/xinterpreter.cpp @@ -56,16 +56,22 @@ using namespace std::placeholders; namespace xcpp { struct StreamRedirectRAII { - std::string &err; - StreamRedirectRAII(std::string &e) : err(e) { - Cpp::BeginStdStreamCapture(Cpp::kStdErr); - Cpp::BeginStdStreamCapture(Cpp::kStdOut); - } - ~StreamRedirectRAII() { - std::string out = Cpp::EndStdStreamCapture(); - err = Cpp::EndStdStreamCapture(); - std::cout << out; - } + std::string& out; + std::string& err; + + StreamRedirectRAII(std::string& o, std::string& e) + : out(o) + , err(e) + { + Cpp::BeginStdStreamCapture(Cpp::kStdOut); + Cpp::BeginStdStreamCapture(Cpp::kStdErr); + } + + ~StreamRedirectRAII() + { + err = Cpp::EndStdStreamCapture(); + out = Cpp::EndStdStreamCapture(); + } }; void interpreter::configure_impl() @@ -163,11 +169,12 @@ __get_cxx_version () } std::string err; + std::string out; // Attempt normal evaluation try { - StreamRedirectRAII R(err); + StreamRedirectRAII R(out, err); compilation_result = Cpp::Process(code.c_str()); } catch (std::exception& e) @@ -182,11 +189,12 @@ __get_cxx_version () ename = "Error: "; } - if (compilation_result) + if (!out.empty()) + { + std::cout << out; + } + if (!err.empty()) { - errorlevel = 1; - ename = "Error: "; - evalue = "Compilation error! " + err; std::cerr << err; } @@ -201,6 +209,13 @@ __get_cxx_version () std::cerr.rdbuf(cerr_strbuf); } + if (compilation_result) + { + errorlevel = 1; + ename = "Error: "; + evalue = "Compilation error! " + err; + } + // Depending of error level, publish execution result or execution // error, and compose execute_reply message. if (errorlevel) diff --git a/test/test_interpreter.cpp b/test/test_interpreter.cpp index cfb0d7d5..5ce9d540 100644 --- a/test/test_interpreter.cpp +++ b/test/test_interpreter.cpp @@ -1094,3 +1094,45 @@ TEST_SUITE("mime_bundle_repr") REQUIRE(res == expected); } } + +TEST_CASE("C and C++ stdout/stderr capture") +{ + std::vector Args = {}; + xcpp::interpreter interpreter((int)Args.size(), Args.data()); + + xeus::execute_request_config config; + config.silent = false; + config.store_history = false; + config.allow_stdin = false; + + nl::json header = nl::json::object(); + xeus::xrequest_context::guid_list id = {}; + xeus::xrequest_context context(header, id); + + std::promise promise; + auto callback = [&promise](nl::json result) { promise.set_value(result); }; + + // Redirect std::cout and std::cerr + StreamRedirectRAII cout_redirect(std::cout); + StreamRedirectRAII cerr_redirect(std::cerr); + + std::string code = R"( + #include + #include + printf("C stdout\n"); + fprintf(stderr, "C stderr\n"); + std::cout << "C++ stdout\n"; + std::cerr << "C++ stderr\n"; + )"; + + interpreter.execute_request(context, callback, code, config, nl::json::object()); + (void)promise.get_future().get(); // wait for result + + std::string captured_out = cout_redirect.getCaptured(); + std::string captured_err = cerr_redirect.getCaptured(); + + REQUIRE(captured_out.find("C stdout") != std::string::npos); + REQUIRE(captured_out.find("C++ stdout") != std::string::npos); + REQUIRE(captured_err.find("C stderr") != std::string::npos); + REQUIRE(captured_err.find("C++ stderr") != std::string::npos); +}