PrevUpHomeNext

API Print Manager

When using the "sandboxed" libraries, any functions that allow lua to communicate with some global state outside of the lua VM are removed. This includes dofile and loadfile, it includes accessing the C standard rand function, and it includes print even. Usually, when you use the sandboxed base library, you'll want to install a new print right away that directs input to where you would like it.

The api::print_manager class is a relatively powerful API feature that can handle that and some related tasks.

Overview

The print_manager feature adds two global functions to the lua environment:

These functions format their arguments in slightly different ways. print uses the same formatting as the built-in lua print. _pretty_print tries to display small tables in a readable format.

They both send their output to the current interpreter_context. interpreter_context is a C++ concept which we'll define shortly. An interpreter_context is set by installing it in the print_manager using the set_interpreter_context method.

If no interpreter context is set, then formatted output is piped to std::cout.

The print_manager actually maintains a stack of pointers to interpreter_context's. The method set_interpreter_context pushes a pointer onto this stack, and pop_interpreter_context pops from this stack, unless it is empty.

Besdies redirecting output, the print_manager can also act to provide debug-console functionality. It can take a user input string and execute it in the lua environment, through the handle_interpreter_input method. The string passed to it should be the text typed by the user. The results of this are relayed to the interpreter context, by calling its methods.

Interpreter Context

The interpreter_context concept consists of three methods:

void new_text(const std::string &);
void error_text(const std::string &);
void clear_input();
Synopsis

Besides redirecting output and providing a special method for handling user text input, the print_manager behavior can be further customized by providing custom implementations of print and _pretty_print. Any function which takes a lua_State * and produces a std::string could be used to replace the default formatting.

namespace api {

class print_manager {
  std::vector<detail::interpreter_context_ptr> stack_;

  using format_func_t = std::string (*)(lua_State *);

  // Pointers for custom print / pretty print formatting functions
  format_func_t print_format_ = nullptr;
  format_func_t pretty_print_format_ = nullptr;

public:
  // Add or remove interpreter context pointers from the stack
  template <typename T>
  void set_interpreter_context(T * t) {
    stack_.emplace_back(t);
  }

  void pop_interpreter_context() {
    if (stack_.size()) { stack_.pop_back(); }
  }

  // Set a custom print or pretty-print formatting
  // Function should take a `lua_State *` and return `std::string`.
  // Do whatever you like with the stack. Don't raise errors.
  void set_custom_print_format_func(format_func_t f) { print_format_ = f; }

  void set_custom_pretty_print_format_func(format_func_t f) {
    pretty_print_format_ = f;
  }

  // Interact directly with context on top of stack, or,
  // with stdout / stderr if stack is empty
  void new_text(const std::string & str) const {
    if (stack_.size()) {
      stack_.back().new_text(str);
    } else {
      std::cout << str << std::endl;
    }
  }

  void error_text(const std::string & str) const {
    if (stack_.size()) {
      stack_.back().error_text(str);
    } else {
      std::cerr << str << std::endl;
    }
  }

  void clear_input() const {
    if (stack_.size()) { stack_.back().clear_input(); }
  }

  // Handle interpreter input
  // Note: Clears the entire stack when it runs.
  inline void handle_interpreter_input(lua_State * L,
                                       const std::string & user_input);

  // Convenience function over the above.
  // Recover the print_manager from a lua_State * pointer, then setup an
  // arbitrary interpreter context, run a command, and pop the context.
  //
  // Note: There must be a print manager attached to the lua_State * or an
  // assertion will fail.
  template <typename T>
  static void interpreter_input(lua_State * L, T & t, const std::string & s) {
    print_manager * man = recover_self(L);
    man->set_interpreter_context(&t);
    man->handle_interpreter_input(L, s);
    man->pop_interpreter_context();
  }

  //
  // API Feature
  //

  void on_init(lua_State * L) {
    PRIMER_ASSERT_STACK_NEUTRAL(L);

    registry_helper<print_manager>::store(L, this);

    lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS);
    primer::set_funcs(L, get_funcs());
    lua_pop(L, 1);
  }

  void on_persist_table(lua_State * L) {
    primer::set_funcs_prefix_reverse(L, "print_manager__", get_funcs());
  }

  void on_unpersist_table(lua_State * L) {
    primer::set_funcs_prefix(L, "print_manager__", get_funcs());
  }
};

} // end namespace api

PrevUpHomeNext