Redirecting/Redefining Print() for Embedded Lua

Lua 5.2 redirecting the print function

From Lua 5.2 manual:

Lua keeps a distinguished environment called the global environment.
This value is kept at a special index in the C registry (see §4.5).
In Lua, the variable _G is initialized with this same value.

So this answer to the question you linked will work if you replace the calls to luaL_register to calls to luaL_setfuncs.

How to print Lua table using the redefined print function?

Just use luaL_tolstring to get the string representation of anything. This also respects the __tostring metamethod. The example below uses std::string_view from C++17 for zero-copy read-only string arguments.

#include <iostream>
#include <string_view>

#include <lua.hpp>

void poststring(std::string_view sv) { std::cout << sv << '\n'; }

void endpost() { std::cout << "---\n"; }

int l_my_print(lua_State *L) {
int nargs = lua_gettop(L);
for (int i = 1; i <= nargs; ++i) {
poststring(luaL_tolstring(L, i, nullptr));
lua_pop(L, 1); // remove the string
}
endpost();
return 0;
}

int main() {
lua_State *L = luaL_newstate();
luaL_openlibs(L);

lua_pushcfunction(L, l_my_print);
lua_setglobal(L, "my_print");

int i = 0;
lua_pushlightuserdata(L, &i);
lua_setglobal(L, "udata");

luaL_dostring(L, "my_print(1, 3.14, \"Hello World\")\n"
"my_print(false, udata, {})\n");

lua_close(L);
}

Example invocation:

$ clang++ -Wall -Wextra -Wpedantic -std=c++17 -I/usr/include/lua5.3 test.cpp -llua5.3
$ ./a.out
1
3.14
Hello World
---
false
userdata: 0x7fff4685993c
table: 0x883300
---

send lua output to non stdout

This answer can get critics from some readers, but first please have a look at my blog post which I've prepared specially for this answer, and read the foreword why I choose this solution.

As promised, I've upstreamed my old Lua 5.1 output redirection patch to the latest version.

Patches are available here: 5.1.5 and 5.2.1.

Patch:

patch -p1 < ~/download/lua-5.2.1-output-redirect.patch

Build:

cd src  
make a LUA_A="liblua-5.2.1-redirect.a" SYSCFLAGS="-DLUA_USE_LINUX -DLUA_REDIRECT" SYSLIBS="-Wl,-E -ldl -lreadline -lncurses"

Check:

nm liblua-5.x.y-redirect.a | grep printf
nm liblua-5.x.y-redirect.a | grep fputs
nm liblua-5.x.y-redirect.a | grep fwrite

Test:

Obtain test program here (C/C++ mixed, sorry). Build it via:

g++ -DLUA_REDIRECT -I/path/to/lua-5.2.1/src/ -L. lua-redirect-test.cpp -llua-5.2.1-redirect -ldl -o lua-redirect-test  

Output:

===== Test 1 output =====
Lua stdout buffer:
---
hello world!

---
Lua stderr buffer:
---

---
Lua error message:
---
(null)
---
===== Test 2 output =====
Lua stdout buffer:
---

---
Lua stderr buffer:
---

---
Lua error message:
---
[string "bad_function()"]:1: attempt to call global 'bad_function' (a nil value)
---

trouble redefining print function in lua

First error: It's luaL_Reg, not luaL_reg.

Second error:
luaL_register is deprecated (in Lua 5.2), and is only available if LUA_COMPAT_MODULE is defined before you include the Lua headers. You should use luaL_setfuncs instead.

Rerouting Lua print in C++ with default print behavior

You can try on default this:

lua_pushfstring(L, "%s: %p", luaL_typename(L, i), lua_topointer(L, i));

or

 luaout << luaL_typename(L, i) << ": " << lua_topointer(L, i);

This will add the name of the function and the pointer.

You can call lua function from C++ (don't guarantee to work as I have not tested it, but it should aside from grammatical errors)

lua_getglobal(L, "tostring");
lua_pushvalue (L, i);

if (lua_pcall(L, 1, 1, 0) != 0) {
printf("error running function `%s': %s\n", "tostring", lua_tostring(L, -1));
return -1;
}
// get result
char *result = luaL_checkstring (L, -1);

Embedded Lua print not working in debug mode from Visual Studio

Unfortunately, you are probably up against a known deficiency in the print() function, which is really intended for quick and dirty debugging at a console prompt, and is missing some necessary flexibility.

The base library function print() implemented by luaB_print() in lbaselib.c explicitly uses the C runtime's stdout stream as its destination. Since it refers to the global variable stdout explicitly in its implementation, the only way to redirect it is to cause that file handle to be redirected. In a C program that can be done by calling freopen(stdout,...). Unfortunately, there isn't a stock library function in Lua that can do that.

The io library is implemented in liolib.c. It uses the function environment to hold a table of open file descriptors, and during its initialization it creates file objects named io.stdin, io.stdout and io.stderr for the three standard descriptors. It also provides functions named io.output and io.input to allow those two descriptors to be modified to point to any open file object (or a newly opened file if a file name is passed). However, those functions only change the function environment table and do not call freopen() to modify the C runtime's table of FILE values.

I have no idea how LuaInterface attempts to treat the standard C runtime's idea of stdout. It may very well be that stdout is not connected to anything useful in the VS debugger because it probably takes advantage of some .NET feature to capture output from the module being debugged that may not be all that compatible with C in any case.

That said, it is easy to replace the standard print function. Just use existing features of LuaInterface to write a global function named print that calls tostring() on each argument and passes it to the whatever .NET things is the standard output device.

Lua vs Embedded Lisp and potential other candidates. for set based data processing

I strongly agree with @jpjacobs's points. Lua is an excellent choice for embedding, unless there's something very specific about lisp that you need (for instance, if your data maps particularly well to cons-cells).

I've used lisp for many many years, BTW, and I quite like lisp syntax, but these days I'd generally pick Lua. While I like the lisp language, I've yet to find a lisp implementation that captures the wonderful balance of features/smallness/usability for embedded use the way Lua does.

Lua:

  1. Is very small, both source and binary, an order of magnitude or more smaller than many more popular languages (Python etc). Because the Lua source code is so small and simple, it's perfectly reasonable to just include the entire Lua implementation in your source tree, if you want to avoid adding an external dependency.

  2. Is very fast. The Lua interpreter is much faster than most scripting languages (again, an order of magnitude is not uncommon), and LuaJIT2 is a very good JIT compiler for some popular CPU architectures (x86, arm, mips, ppc). Using LuaJIT can often speed things up by another order of magnitude, and in many cases, the result approaches the speed of C. LuaJIT is also a "drop-in" replacement for standard Lua 5.1: no application or user code changes are required to use it.

  3. Has LPEG. LPEG is a "Parsing Expression Grammar" library for Lua, which allows very easy, powerful, and fast parsing, suitable for both large and small tasks; it's a great replacement for yacc/lex/hairy-regexps. [I wrote a parser using LPEG and LuaJIT, which is much faster than the yacc/lex parser I was trying emulate, and was very easy and straight-forward to create.] LPEG is an add-on package for Lua, but is well-worth getting (it's one source file).

  4. Has a great C-interface, which makes it a pleasure to call Lua from C, or call C from Lua. For interfacing large/complex C++ libraries, one can use SWIG, or any one of a number of interface generators (one can also just use Lua's simple C interface with C++ of course).

  5. Has liberal licensing ("BSD-like"), which means Lua can be embedded in proprietary projects if you wish, and is GPL-compatible for FOSS projects.

  6. Is very, very elegant. It's not lisp, in that it's not based around cons-cells, but it shows clear influences from languages like scheme, with a straight-forward and attractive syntax. Like scheme (at least in it's earlier incarnations), it tends towards "minimal" but does a good job of balancing that with usability. For somebody with a lisp background (like me!), a lot about Lua will seem familiar, and "make sense", despite the differences.

  7. Is very flexible, and such features as metatables allow easily integrating domain-specific types and operations.

  8. Has a simple, attractive, and approachable syntax. This might not be such an advantage over lisp for existing lisp users, but might be relevant if you intend to have end-users write scripts.

  9. Is designed for embedding, and besides its small size and fast speed, has various features such as an incremental GC that make using a scripting language more viable in such contexts.

  10. Has a long history, and responsible and professional developers, who have shown good judgment in how they've evolved the language over the last 2 decades.

  11. Has a vibrant and friendly user-community.



Related Topics



Leave a reply



Submit