From 7cbf40e3d683a003b66ade25cf803f5af6cccff3 Mon Sep 17 00:00:00 2001 From: Josh Yelon Date: Wed, 3 Nov 2021 12:31:13 -0400 Subject: [PATCH] Add 'ostream' method to class StreamBuffer, and use it. --- luprex/core/cpp/drivenengine.hpp | 4 ++++ luprex/core/cpp/lpxclient.cpp | 13 +++++------ luprex/core/cpp/streambuffer.cpp | 38 ++++++++++++++++++++++++++++++++ luprex/core/cpp/streambuffer.hpp | 8 ++++++- luprex/core/cpp/textgame.cpp | 33 ++++++++++++++------------- 5 files changed, 71 insertions(+), 25 deletions(-) diff --git a/luprex/core/cpp/drivenengine.hpp b/luprex/core/cpp/drivenengine.hpp index 848198ee..e110e7b1 100644 --- a/luprex/core/cpp/drivenengine.hpp +++ b/luprex/core/cpp/drivenengine.hpp @@ -246,6 +246,10 @@ public: // Channel *get_stdio_channel(); + // Obtain the output buffer of the stdio channel as an ostream. + // + std::ostream &stdostream() { return get_stdio_channel()->out()->ostream(); } + // Fetches the lua source, and takes ownership of it. The DrivenEngine // no longer contains the source after calling this. // diff --git a/luprex/core/cpp/lpxclient.cpp b/luprex/core/cpp/lpxclient.cpp index 64dd41de..8565cbc7 100644 --- a/luprex/core/cpp/lpxclient.cpp +++ b/luprex/core/cpp/lpxclient.cpp @@ -6,7 +6,6 @@ #include "invocation.hpp" #include "util.hpp" #include -#include class LpxClient : public DrivenEngine { public: @@ -36,15 +35,15 @@ public: } void do_lua(const std::string &lua) { - std::cerr << "Lua: " << lua << std::endl; + stdostream() << "Lua: " << lua << std::endl; } void do_command(const util::StringVec &words) { - std::cerr << "Command: "; + stdostream() << "Command: "; for (const std::string &word : words) { - std::cerr << word << " "; + stdostream() << word << " "; } - std::cerr << std::endl; + stdostream() << std::endl; } void console_process(const std::string &line) { @@ -57,7 +56,7 @@ public: do_command(console_.words()); console_.clear(); } else if (action == LuaConsole::DO_SYNTAX) { - std::cerr << console_.syntax() << std::endl; + stdostream() << console_.syntax() << std::endl; console_.clear(); } } @@ -74,7 +73,7 @@ public: // Check for communication from server.. if (channel_ != nullptr) { if (channel_->closed()) { - std::cerr << "Server closed connection " << channel_->error() << std::endl; + stdostream() << "Server closed connection " << channel_->error() << std::endl; channel_.reset(); // stop_driver(); } else { diff --git a/luprex/core/cpp/streambuffer.cpp b/luprex/core/cpp/streambuffer.cpp index 3427040c..2bd1fd21 100644 --- a/luprex/core/cpp/streambuffer.cpp +++ b/luprex/core/cpp/streambuffer.cpp @@ -477,6 +477,38 @@ void *StreamBuffer::lua_reader_ud(int64_t size) { return this; } +class StreamBufferWriter : public std::streambuf { +private: + StreamBuffer *target_; +public: + StreamBufferWriter(StreamBuffer *t) : target_(t) {} + + virtual int_type overflow(int_type c) { + if (c != EOF) { + target_->write_uint8(c); + } + return c; + } +}; + +class StreamBufferOStream : public std::ostream { +private: + StreamBufferWriter writer_; +public: + StreamBufferOStream(StreamBuffer *t) : std::ostream(nullptr), writer_(t) { + rdbuf(&writer_); + } + virtual ~StreamBufferOStream() { + } +}; + +std::ostream &StreamBuffer::ostream() { + if (ostream_ == nullptr) { + ostream_.reset(new StreamBufferOStream(this)); + } + return *ostream_; +} + static bool streq(const char *str, const char *data) { int len = strlen(str); return memcmp(str, data, len) == 0; @@ -611,5 +643,11 @@ LuaDefine(unittests_streambuffer, "c") { eqsb1.write_int32(34); LuaAssert(L, !eqsb1.contents_equal(&eqsb2)); + // Check the OStream functionality. + StreamBuffer ossb; + ossb.ostream() << "Testing."; + ossb.ostream() << "Foo."; + LuaAssertStrEq(L, ossb.read_entire_contents(), "Testing.Foo."); + return 0; } diff --git a/luprex/core/cpp/streambuffer.hpp b/luprex/core/cpp/streambuffer.hpp index cdd0644a..c92d13fc 100644 --- a/luprex/core/cpp/streambuffer.hpp +++ b/luprex/core/cpp/streambuffer.hpp @@ -239,7 +239,7 @@ public: }; class StreamBuffer { -public: +public: // Construct an empty buffer. StreamBuffer(); @@ -387,6 +387,9 @@ public: static const char *lua_reader(lua_State *L, void *data, size_t *size); void *lua_reader_ud(int64_t bytes); + // Get an ostream that writes into the StreamBuffer. + std::ostream &ostream(); + private: // Start and end of the allocated block. char *buf_lo_; @@ -409,6 +412,9 @@ private: const char *lua_reader_data_; int64_t lua_reader_size_; + // The ostream. Only allocated on demand. + std::unique_ptr ostream_; + // Initialize with a new buffer. void init(bool fixed, bool owned, char *buf, int64_t size); diff --git a/luprex/core/cpp/textgame.cpp b/luprex/core/cpp/textgame.cpp index e2488d51..670c2cc7 100644 --- a/luprex/core/cpp/textgame.cpp +++ b/luprex/core/cpp/textgame.cpp @@ -5,7 +5,6 @@ #include #include #include -#include #include "luastack.hpp" #include "util.hpp" #include "gui.hpp" @@ -52,13 +51,13 @@ public: void TextGame::do_view_command(const StringVec &cmd) { if (cmd.size() != 1) { - std::cerr << "v command (view) takes no arguments" << std::endl; + stdostream() << "v command (view) takes no arguments" << std::endl; return; } for (int64_t id : world_->get_near(actor_id_, 100, true)) { const Tangible *tan = world_->tangible_get(id); const AnimStep &aqback = tan->anim_queue_.back(); - std::cerr << id << ": " << aqback.graphic() << " " << aqback.plane() << " " << aqback.xyz().debug_string() << std::endl; + stdostream() << id << ": " << aqback.graphic() << " " << aqback.plane() << " " << aqback.xyz().debug_string() << std::endl; } } @@ -69,14 +68,14 @@ void TextGame::do_menu_command(const StringVec &cmd) { } else if (cmd.size() == 2) { id = util::strtoint(cmd[1], -1); } else { - std::cerr << "m command (menu) expects a tangible ID or defaults to actor_id" << std::endl; + stdostream() << "m command (menu) expects a tangible ID or defaults to actor_id" << std::endl; return; } gui_place_ = id; world_->update_gui(actor_id_, id, &gui_); int index = 0; for (const GuiElt &elt : gui_.elts()) { - std::cerr << index << " " << elt.label() << std::endl; + stdostream() << index << " " << elt.label() << std::endl; index += 1; } } @@ -86,16 +85,16 @@ void TextGame::do_choose_command(const StringVec &cmd) { if (cmd.size() == 1) { index = util::strtoint(cmd[0], -1); } else { - std::cerr << "c command (choose) expects a menu line number" << std::endl; + stdostream() << "c command (choose) expects a menu line number" << std::endl; return; } const Gui::EltVec &elts = gui_.elts(); if ((index < 0) || (index >= int(elts.size()))) { - std::cerr << "No menu item #" << index << std::endl; + stdostream() << "No menu item #" << index << std::endl; return; } std::string action = elts[index].action(); - std::cerr << "Invoking plan: " << action << std::endl; + stdostream() << "Invoking plan: " << action << std::endl; InvocationData dummyresult; dummyresult["flavor"] = "chocolate"; dummyresult["color"] = "blue"; @@ -112,7 +111,7 @@ void TextGame::do_lua(const std::string &exp) { void TextGame::do_snapshot_command(const StringVec &cmd) { if (cmd.size() != 1) { - std::cerr << "s command (snapshot) takes no arguments" << std::endl; + stdostream() << "s command (snapshot) takes no arguments" << std::endl; return; } world_->snapshot(); @@ -120,7 +119,7 @@ void TextGame::do_snapshot_command(const StringVec &cmd) { void TextGame::do_rollback_command(const StringVec &cmd) { if (cmd.size() != 1) { - std::cerr << "r command (rollback) takes no arguments" << std::endl; + stdostream() << "r command (rollback) takes no arguments" << std::endl; return; } world_->rollback(); @@ -131,7 +130,7 @@ void TextGame::do_tick_command(const StringVec &cmd) { if (cmd.size() == 2) { clock = util::strtoint(cmd[1], -1); } else { - std::cerr << "t command (tick) expects a time value" << std::endl; + stdostream() << "t command (tick) expects a time value" << std::endl; return; } world_->run_scheduled_threads(clock); @@ -139,7 +138,7 @@ void TextGame::do_tick_command(const StringVec &cmd) { void TextGame::do_quit_command(const StringVec &cmd) { if (cmd.size() != 1) { - std::cerr << "q command (quit) takes no arguments" << std::endl; + stdostream() << "q command (quit) takes no arguments" << std::endl; return; } actor_id_ = 0; @@ -155,7 +154,7 @@ void TextGame::do_command(const StringVec &words) { else if (words[0] == "t") do_tick_command(words); else if (util::validinteger(words[0])) do_choose_command(words); else { - std::cerr << "Unknown command: " << words[0] << std::endl; + stdostream() << "Unknown command: " << words[0] << std::endl; } } @@ -164,7 +163,7 @@ void TextGame::check_redirects() { for (const auto &p : redir) { if (p.first == actor_id_) { actor_id_ = p.second; - std::cerr << "Login actor ID: " << actor_id_ << std::endl; + stdostream() << "Login actor ID: " << actor_id_ << std::endl; gui_.clear(); } } @@ -177,7 +176,7 @@ void TextGame::channel_printbuffer() { printbuffer_line_ = printbuffer->first_line(); } while (printbuffer_line_ < printbuffer->first_unchecked()) { - std::cerr << "* " << printbuffer->nth(printbuffer_line_) << std::endl; + stdostream() << "* " << printbuffer->nth(printbuffer_line_) << std::endl; printbuffer_line_ += 1; } if (printbuffer_line_ > printbuffer->first_line()) { @@ -195,7 +194,7 @@ void TextGame::event_init(int argc, char *argv[]) world_->run_unittests(); actor_id_ = world_->create_login_actor(); printbuffer_line_ = 0; - std::cerr << "Login actor ID: " << actor_id_ << std::endl; + stdostream() << "Login actor ID: " << actor_id_ << std::endl; get_stdio_channel()->out()->write_bytes(console_.get_prompt()); } @@ -216,7 +215,7 @@ void TextGame::event_update() console_.clear(); channel_printbuffer(); } else if (action == LuaConsole::DO_SYNTAX) { - std::cerr << console_.syntax() << std::endl; + stdostream() << console_.syntax() << std::endl; console_.clear(); } check_redirects();