Can't compile Lua as C++

Viewed 242

I'm trying to build Lua 5.4.3 (the latest release as of writing this) using the following CMakeLists.txt (and CMake 3.21) (ignore the fact that the target is called lua_static despite it being a shared lib):

cmake_minimum_required(VERSION 3.16)

project(Lua LANGUAGES CXX)

# make cache variables for install destinations
include(GNUInstallDirs)

set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)

set(LUA_LIB_SRCS 
    "src/ldblib.c"
    "src/ldebug.c"
    "src/ldo.c"
    "src/ldump.c"
    "src/lfunc.c"
    "src/lgc.c"
    "src/linit.c"
    "src/liolib.c"
    "src/lopcodes.c"
    "src/llex.c"
    "src/lmathlib.c"
    "src/lmem.c"
    "src/loadlib.c"
    "src/lobject.c"
    "src/loslib.c"
    "src/lparser.c"
    "src/lstate.c"
    "src/lstring.c"
    "src/lstrlib.c"
    "src/ltable.c"
    "src/ltablib.c"
    "src/ltm.c"
    "src/lundump.c"
    "src/lutf8lib.c"
    "src/lvm.c"
    "src/lzio.c"
    "src/lapi.c"
    "src/lauxlib.c"
    "src/lbaselib.c"
    "src/lcode.c"
    "src/lcorolib.c"
    "src/lctype.c"
)

set(LUA_HEADERS_INSTALL
    "src/lua.h"
    "src/luaconf.h"
    "src/lualib.h"
    "src/lauxlib.h"
    "src/lua.hpp"
)

# Compile directly as C++.
set_source_files_properties(${LUA_LIB_SRCS} PROPERTIES LANGUAGE CXX )

add_library(lua_static SHARED ${LUA_LIB_SRCS})
set_target_properties(lua_static PROPERTIES LINKER_LANGUAGE CXX)
set_target_properties(lua_static PROPERTIES DEBUG_POSTFIX d)

# make sure __cplusplus is defined when using msvc
if (MSVC)
    target_compile_options(lua_static PRIVATE /TP       # /TP doesn't is somehow stripped or ignored?
                                       /Zc:__cplusplus)
endif ()

target_include_directories(lua_static
                           PUBLIC
                           "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
)

install(TARGETS lua_static
        EXPORT lua_static_targets
        LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
        ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
        RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
        INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)

install(FILES ${LUA_HEADERS_INSTALL} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})

install(EXPORT lua_static_targets
        FILE LuaStaticTargets.cmake
        NAMESPACE lua::
        DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/lua
)

include(CMakePackageConfigHelpers)

configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in
  "${CMAKE_CURRENT_BINARY_DIR}/LuaConfig.cmake"
  INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/lua
)

install(FILES
          "${CMAKE_CURRENT_BINARY_DIR}/LuaConfig.cmake"
        DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/lua
)

When building with MSVC, __cplusplus is defined and therefore I presume everything up until this point is working as intended. Then after installing, I link against the library like so

cmake_minimum_required(VERSION 3.16)
project(TestProject)

add_executable(Test Main.cpp)

# Lua
find_package(lua REQUIRED PATHS ${CMAKE_CURRENT_LIST_DIR}/lua)
set_target_properties(lua::lua_static PROPERTIES LINKER_LANGUAGE CXX)
target_link_libraries(Test lua::lua_static)

The reason why I want to compile Lua as C++ is to enable exceptions (rather than longjmp). So to test if exceptions would be thrown, I tried invoking one in this test program

#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>

#include <iostream>

constexpr char* Test_Lua = R"(
function f (x, y)
      unknown()
      return 1/0
en
)";

lua_State* state;

int main(int argc, char* argv[])
{
    state = luaL_newstate();
    luaL_openlibs(state);

    try
    {
        int error = luaL_loadbuffer(state, Test_Lua, strlen(Test_Lua), "Test");
        lua_call(state, 0, LUA_MULTRET); // Should thow, because of the syntax error
    }
    catch (...)
    {
        std::cout << "Exception!" << std::endl;
        goto end;
    }

end:
    lua_close(state);
    return 0;
}

When stepping into lua_call with the debugger, I at some point end up in ldo.c where Visual Studio indicates that LUAI_THROW is not defined via C++ exeption handling. This is because __cplusplus is not defined. Jumping to that source file in my Lua project files created earlier, LUAI_THROW was defined as desired. The fact that I can include the Lua headers suggests to me that the library has been compiled as a C++ library. In fact if I try to add extern "C" { ... }, I get linker errors.

Can someone shed some light on the cause of the problem?


Edit:

After looking more into the issue, I realized all compilation as C++ does, is that Lua internally handles functions that return error codes (such as luaL_loadbuffer) via exceptions. That doesn't mean I get to catch and handle them (which I originally thought). Functions like lua_call run in unprotected mode and thus call the panic function (see @bremen_matt's post).

1 Answers

I know nothing about Lua. With that out of the way...

I think you are missing something with regards to how Lua is working internally. If I modify the macros in ldo.c at about line 57 to read:

#include <iostream>
#define LUAI_THROW(L,c) (fprintf(stderr, "THROWING\n"), throw(c))
#define LUAI_TRY(L,c,a) \
    try { a } catch(...) { fprintf(stderr, "CAUGHT\n"); if ((c)->status == 0) (c)->status = -1; }

Then when I run this, I see the output:

THROWING
CAUGHT
PANIC: unprotected error in call to Lua API (attempt to call a string value)

Which suggests that internally Lua is trying to run a snippet of code, catches the exception, and then exits without throwing another exception. Therefore, you as the user cannot catch this exception. It is being consumed inside of Lua.

Just a real quick look at the code suggests that maybe you want to use lua_pcall. It seems that the maybe the p means protected? and you can do some error handling with that? Either way, that function also does not seem to throw the error higher. It seems like you have to pass a function for error handling.

Related