Call a Lua function from C++ - c++

I have google high and low and found examples, but none of them seems to work (Lua 5.2).
I have a simple function in Lua
function onData ( data )
print ( data )
end
I want to call onData from C++ and tried this:
// Create new Lua state
L = luaL_newstate();
// Load all Lua libraries
luaL_openlibs(L);
// Create co-routine
CO = lua_newthread(L);
// Load and compile script
AnsiString script(Frame->Script_Edit->Text);
if (luaL_loadbuffer(CO,script.c_str(),script.Length(),AnsiString(Name).c_str()) == LUA_OK) {
Compiled = true;
} else {
cs_error(CO, "Compiler error: "); // Print compiler error
Compiled = false;
}
// Script compiled and ready?
if (Compiled == true) {
lua_getglobal(CO, "onData"); // <-------- Doesn't find the function
if( !lua_isfunction(CO,-1)) {
lua_pop(CO,1);
return;
}
lua_pushlstring(CO,data,len);
lua_resume(CO,NULL,0)
}
As you can see I'm starting my script as a co-routine so I can use the lua_yield() function on it. I have tried to look for the function in both the L and CO states.

luaL_loadbuffer loads the script but does not run it. onData will only be defined when the script is run.
Try calling luaL_dostring instead of luaL_loadbuffer.
Or add lua_pcall(CO,0,0,0) before lua_getglobal.
Moreover, you need lua_resume(CO,NULL,1) to pass data to onData.

Related

Saving the output from Lua in C++ with SOL3 to an std::string

I'm trying to implement a lua interpreter to my C++ code. I have implemented a small editor for my project using ImGui and I'm saving the output from the editor to an std::vector.
My attempted implementation of my lua interpeter looks like so;
// header
std::string ExecuteLua();
std::vector<char> m_luaEditorData;
// cpp
std::string Proxy::ExecuteLua()
{
// Load the Lua code from the string
std::string luaCode(m_luaEditorData.data());
// Create a Lua state
sol::state lua;
// Load standard Lua libraries
lua.open_libraries(sol::lib::base, sol::lib::package, sol::lib::string, sol::lib::table);
// Execute the Lua code and store the result
sol::protected_function_result result = lua.script(luaCode);
// Check for errors
if (!result.valid())
{
sol::error error = result;
std::string errorMsg = error.what();
return "Lua error: " + errorMsg;
}
// Get the result as a string
std::string output = lua["tostring"](result.get<sol::object>());
// Return the output
return output;
}
...
if (m_luaEditorData.empty())
m_luaEditorData.push_back('\0');
auto luaEditorFlags = ImGuiInputTextFlags_AllowTabInput | ImGuiInputTextFlags_CallbackResize;
ImGui::InputTextMultiline("##LuaEditor", m_luaEditorData.data(), m_luaEditorData.size(), ImVec2(ImGui::GetWindowContentRegionWidth(), ImGui::GetWindowHeight() - (ImGui::GetTextLineHeight() * 16)), luaEditorFlags, ResizeInputTextCallback, &m_luaEditorData);
...
When I run this code, I only get nil in my output, the correct output to stdout (don't really want it to output here, but to my std::string and when I put in bad code, it throws an exception in sol.hpp. I didn't really find any examples on how I can do this and I'm therefore am trying to figure this out on my own.

How to get flow output from non flow fuction

I want flow output (return type Flow<T>) from a non-flow function (return typeT).
fun getTotalFiles(): Int
// Say, This is a library function it'll return the number of files (Int) in that folder at that specific moment.
//And,
fun getAllFiles(): List<File>
// Say, This is a library function it'll return all the files (List<File>) in that folder.
The files in that folder can and will change in the future.
Now, I want to constantly observe the output, so how do I implement it?
fun getFlowOfTotalFiles(): Flow<Int> =
// A wrapper function that converts the library function return type to an observable flow, Flow<Int>
//And,
fun getFlowOfAllFiles(): Flow<List<File>> =
// A wrapper function that converts the library function return type to an observable flow, Flow<List<File>>
For specifically monitoring a directory for files, you can use WatchService and convert it to a flow with the flow builder. Something like this:
fun getDirectoryMonitorFlow(directory: String) = flow {
FileSystems.getDefault().newWatchService().use { watchService ->
while (true) {
val watchKey = Path.of(directory).register(watchService, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY)
if (watchKey.pollEvents().isNotEmpty()) {
emit(Unit)
}
yield() // give flow opportunity to be cancelled.
if (!watchKey.reset()) {
println("Directory became unreadable. Finishing flow.")
break
}
}
}
}
.catch { println("Exception while monitoring directory.") }
.flowOn(Dispatchers.IO)
And then your class might look like:
fun getFlowOfTotalFiles(): Flow<Int> = getFlowOfAllFiles()
.map { it.size }
.distinctUntilChanged()
fun getFlowOfAllFiles(): Flow<List<File>> = flow {
emit(Unit) // so current state is always emitted
emitAll(getDirectoryMonitorFlow(directory))
}
.map {
File(directory).listFiles()?.toList().orEmpty()
}
.flowOn(Dispatchers.IO)
.distinctUntilChanged()
Although you might consider making the first flow a private SharedFlow so you aren't running multiple WatchServices to monitor the same directory concurrently.
I believe you need an infinite loop inside a flow builder, something like the following:
fun getFlowOfTotalFiles(): Flow<Int> = flow {
while (true) {
emit(getTotalFiles())
// delays for 5 sec before next request and
// terminates the infinite cycle when a coroutine,
// that collects this Flow, is canceled
delay(5000)
}
}
fun getAllFilesFlow(): Flow<List<File>> = flow {
while (true) {
emit(getAllFiles())
delay(5000)
}
}

Adobe Air - native process communication with exe fails

I'm struggling with Adobe Air native process communication. I'd like to pass an c++ exe shellcommands stored at processArgs when started. The c++ program uses this arguments to choose device channel and symbolrate of an CAN-Bus-Adapter. The c program itself creates an json database for an html page. While the c program is processing i'd like to get some feedback of the program and if it exits adobe air should create a link for the html page with the function onExit. The c program uses standart output of iostream lib ("cout", "cerr") to send messages to adobe air.
Adobe Air script:
var process;
function launchProcess()
{
if(air.NativeProcess.isSupported)
{
setupAndLaunch();
}
else
{
air.Introspector.Console.log("NativeProcess not supported.");
}
}
function setupAndLaunch()
{
var cpp_device = $( "#device option:selected" ).val();
var cpp_channel= $("#channel option:selected").text();
var cpp_symbolRate= $("#symbolRate option:selected").val();
air.Introspector.Console.log("CHANNEL",cpp_channel);
air.Introspector.Console.log("Device",cpp_device);
air.Introspector.Console.log("Symbol Rate",cpp_symbolRate);
var nativeProcessStartupInfo = new air.NativeProcessStartupInfo();
var file = air.File.applicationDirectory.resolvePath("InteractiveDocumentation.exe");
nativeProcessStartupInfo.executable = file;
var processArgs = new air.Vector["<String>"]();
processArgs.push(cpp_device);
processArgs.push(cpp_channel);
processArgs.push(cpp_symbolRate);
nativeProcessStartupInfo.arguments = processArgs;
process = new air.NativeProcess();
process.start(nativeProcessStartupInfo);
process.addEventListener(air.ProgressEvent.STANDARD_OUTPUT_DATA, onOutputData);
process.addEventListener(air.ProgressEvent.STANDARD_ERROR_DATA, onErrorData);
process.addEventListener(air.IOErrorEvent.STANDARD_OUTPUT_IO_ERROR, onIOError);
process.addEventListener(air.IOErrorEvent.STANDARD_ERROR_IO_ERROR, onIOError);
process.addEventListener(air.NativeProcessExitEvent.EXIT, onExit);
}
function onOutputData(event)
{
air.Introspector.Console.log("Got: ", process.standardOutput.readUTFBytes(process.standardOutput.bytesAvailable));
}
function onErrorData(event)
{
air.Introspector.Console.log("ERROR -", process.standardError.readUTFBytes(process.standardError.bytesAvailable));
}
function onExit(event)
{
air.Introspector.Console.log("Process exited with ",event.exitCode);
$("#output").html("Start");
}
function onIOError(event)
{
air.Introspector.Console.log(event.toString());
}
$(function()
{
$("#start").click(function()
{
air.Introspector.Console.log("START");
launchProcess();
});
});
the c program is quite long, and here i post only the main part
int main( int argc, char *argv[])
{
//! get shell commands for init
// echo shell commands
for( int count = 0; count < argc; count++ )
{
cout << " argv[" << count << "] "
<< argv[count];
}
// handle data
intDevice = (int)(argv[1][0] - '0');
intChannel = (int)(argv[2][0] - '0');
intChannel -= 1;
intSymbolRate = atoi(argv[3]);
//! class function calls
useDll CanFunction;
try
{
CanFunction.initProg();
CanFunction.ecuScan();
CanFunction.openSession();
CanFunction.readECU();
CanFunction.stopProg();
return 0;
}
//! exception handling
catch(int faultNumber)
{
....
}
}
First I used only the onExit listener and everything works fine. After I used start conditions adobe air only responses if i call no class functions except the CanFunction.initProg() and the onExit function was called but skipped the jQuery command to create the link. If I add the class function calls, the c program is called and the json database created but Adobe Air doesnt received any responses. The json database is still created. This confuses me.
My c program uses some *.dll files to communicate with the bus so i could imagine that it is a windows problem. But it is still weird.
Has anybody an idea why adobe air communication doesnt work with my c program if i call my class functions or why the jQuery command is skipped?
Or is there a better solution to call a c++ exe from adobe air?

lua function argument expected near <eof>

I try to use lua in a C++ project. For lua executing I write this:
#include <lua.hpp>
...
luaEngine = luaL_newstate();
luaL_openlibs(luaEngine);
register_results(luaEngine); // For register c++ object in the LUA script as metatable
lua_pushstring(luaEngine, resultsId.c_str());
lua_setglobal(luaEngine, "resultsId");
lua_pushboolean(luaEngine, needReloadModel);
lua_setglobal(luaEngine, "needReload");
...
e = luaL_loadbuffer(luaEngine, script.c_str(), script.size(), NULL);
if(e != 0)
// error message
e = lua_pcall(luaEngine, 0, 1, 0);
if(e != 0)
// error message
...
lua_close(luaEngine);
And the lua script:
local Res = ResUpdateLUA(resultsId)
if current_result == "Normal" or current_result=='-' then
status = 'E'
else
status = 'O'
end
needReload = Res:setShowAnalyte('2320', status)
That didn't work and I've got error message:
[string "?"]:7: function arguments expected near <eof>
But when I add
print(needReload)
at the end of the lua script it works nice. What am I doing wrong?
The error message means that Lua reached the end of the source after seeing Res:s but before seeing (.
I suspect that script.size() is wrong. But I can't explain why adding that line works.
Thank you all for your answers. Yes, that was trouble with script.size() coz when it was replaced to e = luaL_loadbuffer(luaEngine, script.c_str(), strlen(script.c_str()), NULL); that started work fine. Sorry for my stupid question.

LLVM API: correct way to create/dispose

I'm attempting to implement a simple JIT compiler using the LLVM C API. So far, I have no problems generating IR code and executing it, that is: until I start disposing objects and recreating them.
What I basically would like to do is to clean up the JIT'ted resources the moment they're no longer used by the engine. What I'm basically attempting to do is something like this:
while (true)
{
// Initialize module & builder
InitializeCore(GetGlobalPassRegistry());
module = ModuleCreateWithName(some_unique_name);
builder = CreateBuilder();
// Initialize target & execution engine
InitializeNativeTarget();
engine = CreateExecutionEngineForModule(...);
passmgr = CreateFunctionPassManagerForModule(module);
AddTargetData(GetExecutionEngineTargetData(engine), passmgr);
InitializeFunctionPassManager(passmgr);
// [... my fancy JIT code ...] --** Will give a serious error the second iteration
// Destroy
DisposePassManager(passmgr);
DisposeExecutionEngine(engine);
DisposeBuilder(builder);
// DisposeModule(module); //--> Commented out: Deleted by execution engine
Shutdown();
}
However, this doesn't seem to be working correctly: the second iteration of the loop I get a pretty bad error...
So to summarize: what's the correct way to destroy and re-create the LLVM API?
Posting this as Answer because the code's too long. If possible and no other constraints, try to use LLVM like this. I am pretty sure the Shutdown() inside the loop is the culprit here. And I dont think it would hurt to keep the Builder outside, too. This reflects well the way I use LLVM in my JIT.
InitializeCore(GetGlobalPassRegistry());
InitializeNativeTarget();
builder = CreateBuilder();
while (true)
{
// Initialize module & builder
module = ModuleCreateWithName(some_unique_name);
// Initialize target & execution engine
engine = CreateExecutionEngineForModule(...);
passmgr = CreateFunctionPassManagerForModule(module);
AddTargetData(GetExecutionEngineTargetData(engine), passmgr);
InitializeFunctionPassManager(passmgr);
// [... my fancy JIT code ...] --** Will give a serious error the second iteration
// Destroy
DisposePassManager(passmgr);
DisposeExecutionEngine(engine);
}
DisposeBuilder(builder);
Shutdown();
/* program init */
LLVMInitializeNativeTarget();
LLVMInitializeNativeAsmPrinter();
LLVMInitializeNativeAsmParser();
LLVMLinkInMCJIT();
ctx->context = LLVMContextCreate();
ctx->builder = LLVMCreateBuilderInContext(ctx->context);
LLVMParseBitcodeInContext2(ctx->context, module_template_buf, &module) // create module
do IR code creation
{
function = LLVMAddFunction(ctx->module, "my_func")
LLVMAppendBasicBlockInContext(ctx->context, ...
LLVMBuild...
...
}
optional optimization
{
LLVMPassManagerBuilderRef pass_builder = LLVMPassManagerBuilderCreate();
LLVMPassManagerBuilderSetOptLevel(pass_builder, 3);
LLVMPassManagerBuilderSetSizeLevel(pass_builder, 0);
LLVMPassManagerBuilderUseInlinerWithThreshold(pass_builder, 1000);
LLVMPassManagerRef function_passes = LLVMCreateFunctionPassManagerForModule(ctx->module);
LLVMPassManagerRef module_passes = LLVMCreatePassManager();
LLVMPassManagerBuilderPopulateFunctionPassManager(pass_builder, function_passes);
LLVMPassManagerBuilderPopulateModulePassManager(pass_builder, module_passes);
LLVMPassManagerBuilderDispose(pass_builder);
LLVMInitializeFunctionPassManager(function_passes);
for (LLVMValueRef value = LLVMGetFirstFunction(ctx->module); value;
value = LLVMGetNextFunction(value))
{
LLVMRunFunctionPassManager(function_passes, value);
}
LLVMFinalizeFunctionPassManager(function_passes);
LLVMRunPassManager(module_passes, ctx->module);
LLVMDisposePassManager(function_passes);
LLVMDisposePassManager(module_passes);
}
optional for debug
{
LLVMVerifyModule(ctx->module, LLVMAbortProcessAction, &error);
LLVMPrintModule
}
if (LLVMCreateJITCompilerForModule(&ctx->engine, ctx->module, 0, &error) != 0)
my_func = (exec_func_t)(uintptr_t)LLVMGetFunctionAddress(ctx->engine, "my_func");
LLVMRemoveModule(ctx->engine, ctx->module, &ctx->module, &error);
LLVMDisposeModule(ctx->module);
LLVMDisposeBuilder(ctx->builder);
do
{
my_func(...);
}
LLVMDisposeExecutionEngine(ctx->engine);
LLVMContextDispose(ctx->context);
/* program finit */
LLVMShutdown();