lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


Replying to everyone here...



On Mon, Mar 30, 2020 at 4:26 AM v <v19930312@gmail.com> wrote:
There is a bunch of C++ modules in luarocks that work just fine with
standard Lua that was built as C.

Can you point me to one or two such examples? I don't know how to effectively search LuaRocks for such a thing.
 


On Mon, Mar 30, 2020 at 6:22 AM Viacheslav Usov <via.usov@gmail.com> wrote:
On Mon, Mar 30, 2020 at 6:09 AM Jonathan Goble <jcgoble3@gmail.com> wrote:

> Can a Lua executable compiled as one (C or C++) successfully load and use an extension module (*.dll or *.so) written in the other?

If you mean that your main application executable has Lua compiled and statically linked into it, then you will not be able to load a shared/dynamic library that needs to use the Lua API - because it expects the Lua API to be exposed by a shared/dynamic library. That is, unless you develop some private interface between your application and "extension modules", or patch the low-level library loader code, either way going quite beyond the C/C++ question.

I'm not worried about custom application executables. I will be working with the stock stand-alone interpreter.
 
Normally, if you need to load libraries that use Lua, the Lua engine must itself be a shared/dynamic library, in which case the C/C++ question applies to this library first and foremost.

Linking Lua either statically or dynamically, there are two major things to consider:

1. C++ name decoration/mangling. If the Lua library is compiled in the C++ mode, by default, the Lua API functions will be decorated as such, making them unavailable to C-mode code. This can be remedied by making the Lua API defined as export "C". Unfortunately, this is not available out of the box, and you will have to patch luaconf.h, for example, as follows: 

#define LUA_API extern "C"

The patched luacnf.h must be used when compiling both the Lua library and any other code that uses it. If the Lua library is compiled in the C mode, and it needs to be used by C++ code, the same solution works, in principle, but you could use the stock headers, too:

extern "C"
{
#include "lua.h"
}

Noted. (I was aware of the extern "C" wrapper for the headers, but not the #define trick.)
 
2. If the Lua library is compiled in the C++ mode, long jumps vs C++ exceptions for the Lua error mechanism. Unfortunately, there is no one size fits all solution. The only truly portable solution is the long jump mode, but then the C++ code that is invoked by Lua and itself uses the Lua API, must essentially become pure-C code and not have any automatic storage objects with non-trivial destructors. Such code must also not leak any C++ exceptions (in fact, this restriction stays in the other case, too). Depending on your situation, this may be easy or pretty much impossible. For example, consider a function callable from Lua.

int foo(lua_State *L)
{
    std::string ret = "some lengthy string that needs memory allocation";
    auto inp = luaL_checklstring(L, 1, std::nullptr);
    lua_pushstring(L, ret.c_str());    
    return 1;
}

The ret variable has a non-trivial destructor that won't be called if luaL_checklstring() raises an error. And its constructor could, in principle, throw an exception. While in this toy functions both problems can be easily mitigated, in a more complex situation that may get quite a bit more involved.

See, this is the type of problem I was looking for. I will make sure to pay attention to destructors and exceptions. Exceptions seem easy to handle by just wrapping the function body in a try-catch and calling lua_error() from the catch block. Destructors are trickier; is there a way to "catch" a longjmp, perform operations (like calling destructors), and "re-throw"? I admit that setjmp/longjmp are a bit beyond my comprehension. Or is there a better way to take care of destructors?

Another question: What happens if I create a Lua userdata object to hold a C++ class object? Is such a thing possible? I assume C-compiled Lua will not call the destructor on garbage collection, so I would need a __gc metamethod to call it; is that sufficient, or does that not work?
 
Using C++ exceptions for the error mechanism, and using some C-mode code that is invoked by Lua, you would have to ensure that such exceptions can propagate correctly through C/C++/Lua boundaries, which would be highly platform dependent, where the platform includes such details as static vs dynamic linking and compiler options.

Static vs. dynamic linking and compiler options are standardized here since I'm working with the stock Lua Makefile on most platforms (modulo minimal modifications for C++ compiling, which I'll have to investigate). Only Windows will require custom compilation, likely using Visual Studio.
 
> I'm most interested in a C-compiled Lua and C++ extension module

In this case, the two major decisions are automatically settled, with the above mentioned implications on the C++ code.

Fair enough. Thank you for the detailed information!




On Mon, Mar 30, 2020 at 6:40 AM Luiz Henrique de Figueiredo <lhf@tecgraf.puc-rio.br> wrote:
> If you mean that your main application executable has Lua compiled and statically linked into it, then you will not be able to load a shared/dynamic library that needs to use the Lua API - because it expects the Lua API to be exposed by a shared/dynamic library.

That may be true in Windows. In macOS you don't need to do anything.
In Linux you need to link your application with -Wl,-E as the lua.org
Makefile does for the standalone interpreter.

Interesting. Though as I said above, the standalone interpreter is all I need. I'm not writing custom applications that embed Lua.