James (Jim) Pascoe
http://www.james-pascoe.com
james@james-pascoe.com
http://jamespascoe.github.io/cpponsea2020
https://github.com/jamespascoe/LuaChat.git
-- Create a global table 't'
t = { x=1, y=2 }
function f (str, val, int)
print(
string.format(
"Lua: f called with args: %s %d %d", str, val, int
)
)
-- Call a C++ function
local rc = cppFunc(str, t.y, int)
return rc
end
#include <iostream>
#include "lua5.3/lua.hpp"
int cppFunc(lua_State *L) {
std::cout << "cppFunc called with args:" << std::endl;
for (int n=1; n <= lua_gettop(L); ++n)
std::cout << lua_tostring(L, n) << std::endl;
return 0;
}
int main([[maybe_unused]] int argc, char ** argv)
{
// Create a new lua state
lua_State *L = luaL_newstate();
// Open all libraries
luaL_openlibs(L);
// export a C++ function to Lua
lua_register(L, "cppFunc", cppFunc);
// Load and run the Lua file
luaL_loadfile(L, argv[1]);
lua_pcall(L, 0, 0, 0);
// Call 'f' with the arguments "how", t.x, 14
lua_getglobal(L, "f"); /* function to be called */
lua_pushliteral(L, "how"); /* 1st argument */
lua_getglobal(L, "t"); /* table to be indexed */
lua_getfield(L, -1, "x"); /* push t.x (2nd arg) */
lua_remove(L, -2); /* remove 't' from the stack */
lua_pushinteger(L, 14); /* 3rd argument */
lua_call(L, 3, 1); /* call 'f' (3 args, 1 res) */
lua_close(L);
}
> brew install lua # sudo apt-get -y install lua5.3
> clang++ -std=c++17 -llua -o lua-cpp lua-cpp.cpp
> ./lua-cpp lua-cpp.lua
Lua: f called with args: how 1 14
cppFunc called with args:
how
2
14
Ubuntu 18.04 (Linux Mint 19):
git clone https://github.com/jamespascoe/LuaChat.git
sudo apt-get -y install lua5.3 lua5.3-dev luarocks swig
sudo luarocks install luaposix
mkdir build; cd build; cmake ../LuaChat; make
./src/lua_chat
MacOS (Catalina):
git clone https://github.com/jamespascoe/LuaChat.git
brew install lua luarocks swig
luarocks install luaposix
mkdir build; cd build; cmake ../LuaChat; make
./src/lua_chat
set(LUA_CHAT_SWIG_SRCS
lua_chat_actions.i lua_chat_action_log.cpp)
set_source_files_properties(${LUA_CHAT_SWIG_SRCS}
PROPERTIES CPLUSPLUS ON)
swig_add_library(actions TYPE USE_BUILD_SHARED_LIBS
LANGUAGE lua
SOURCES ${LUA_CHAT_SWIG_SRCS})
target_include_directories(actions PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
${LUA_CHAT_SOURCE_DIR}/src
${LUA_CHAT_SOURCE_DIR}/third_party
${LUA_INCLUDE_DIR})
target_compile_definitions(actions PRIVATE ASIO_STANDALONE)
target_link_libraries(actions PRIVATE std::filesystem)
%module Actions
%include <std_string.i>
// Definitions required by the SWIG wrapper to compile
%{
#include "lua_chat_log_manager.hpp"
#include "lua_chat_action_log.hpp"
#include "lua_chat_action_talk.hpp"
#include "lua_chat_action_timer.hpp"
%}
// Files to be wrapped by SWIG
%include "lua_chat_action_log.hpp"
%define CTOR_ERROR
{
try {
$function
}
catch (std::exception const& e) {
log_fatal(e.what());
}
}
%enddef
// Include the actions and define exception handlers
%exception Talk::Talk CTOR_ERROR;
%include "lua_chat_action_talk.hpp"
%exception Timer::Timer CTOR_ERROR;
%include "lua_chat_action_timer.hpp"
%typemap(typecheck) Example::Callback & {
$1 = lua_isfunction(L, $input);
}
%typemap(in) Example::Callback & (Example::Callback temp) {
// Create a reference to the Lua callback
SWIGLUA_REF fn;
swiglua_ref_set(&fn, L, $input);
temp = [&fn]() {
swiglua_ref_get(&fn);
lua_pcall(fn.L, 0, 0, 0);
};
$1 = &temp;
}
// %include source files AFTER typemap declarations
class tcp_connection
{
public:
using pointer = std::shared_ptr<tcp_connection>
static pointer create(asio::io_context& io_context) {
return pointer(new tcp_connection(io_context));
}
asio::ip::tcp::socket& socket() { return m_socket; }
std::string& data() { return m_data; }
private:
tcp_connection(asio::io_context& io_context)
: m_socket(io_context) {}
asio::ip::tcp::socket m_socket;
std::string m_data;
};
Talk::Talk(unsigned short port)
: m_acceptor(m_io_context,
tcp::endpoint(tcp::v4(), port)) {
start_accept();
m_thread = std::thread([this](){ m_io_context.run(); });
log_trace("Talk action starting");
}
void Talk::start_accept() {
tcp_connection::pointer connection =
tcp_connection::create(
m_acceptor.get_executor().context()
);
m_acceptor.async_accept(connection->socket(),
[this, connection](const asio::error_code& error) {
handle_accept(connection, error);
}
);
}
void Talk::handle_accept(tcp_connection::pointer connection,
asio::error_code const& error) {
if (!error) {
log_debug("Accepted message connection");
asio::async_read(
connection->socket(),
asio::dynamic_buffer(connection->data()),
[this, connection](
const asio::error_code& error,
std::size_t bytes_transferred)
{
handle_read(error, bytes_transferred, connection);
}
);
} else
log_error("Talk accept failed: {}", error.message());
start_accept();
}
void Talk::handle_read(asio::error_code const& error,
std::size_t bytes_transferred,
tcp_connection::pointer connection) {
// Check error - 'eof' means that the remote connection has closed
if (!error || error == asio::error::eof) {
// Limit the message array
if (m_messages.size() > max_messages)
m_messages.erase(m_messages.begin());
// Store the message for Lua retrieval
m_messages.emplace_back(connection->data());
log_info("Received message ({} bytes): {}",
bytes_transferred,
connection->data());
} else
log_error("Talk read failed: {}", error.message());
}
std::string Talk::GetNextMessage(void) {
if (!IsMessageAvailable())
return "";
std::string ret = m_messages.front();
m_messages.erase(m_messages.begin());
return ret;
}
function sender (talk, host, port)
while true do
local ret = require 'posix'.rpoll(0, 1000)
if (ret == 1) then
local message = io.read()
if (message ~= "") then
local ret = talk:Send(
tostring(host), tostring(port), tostring(message)
)
if (ret == Actions.Talk.ErrorType_SUCCESS) then
Actions.Log.info(
string.format(
"Message sent to %s:%s %s", host, port, message
)
)
end
end
end
coroutine.yield()
end
end
function receiver (talk, host, port)
while true do
-- Yield until a message arrives, at which point, print it
repeat
coroutine.yield()
until talk:IsMessageAvailable()
local message = talk:GetNextMessage()
Actions.Log.info(
string.format(
"Received from %s:%s %s", host, port, message
)
)
print(host .. ":" .. tostring(port) .. "> " .. message)
end
end
function dispatcher (coroutines)
local timer = Actions.Timer()
while true do
if next(coroutines) == nil then break end -- no coroutines
for name, co in pairs(coroutines) do
local status, result = coroutine.resume(co)
if result then -- coroutine has exited
if type(result) == "string" then -- runtime error
Actions.Log.critical(
"Coroutine '" .. tostring(name) .. "' error " .. result
)
else
Actions.Log.warn(
"Coroutine '" .. tostring(name) .. "' exited"
)
end
coroutines[name] = nil
end
end
timer(Actions.Timer.WaitType_BLOCK, 1, "ms", 0xffffffff)
end
end
http://www.james-pascoe.com
james@james-pascoe.com
http://jamespascoe.github.io/cpponsea2020
https://github.com/jamespascoe/LuaChat.git