Storing reference to lua function in C - c++

I have a basic event handler implemented in C++. I also have an embedded Lua interpreter in my application that I need to interact with the Event Manager. The ultimate goal is to be able to have one event handler that will execute both c++ and Lua functions when an event is fired.
My problem is that I can't come up with a simple way to store references to the lua functions in my C++ code. I know how to execute Lua functions from c (using lua_getglobal and lua_pcall), but I would prefer to store a reference to the function itself, so that I can pass a Lua function directly to registerListener
Note It is acceptable to assume that userdata will be NULL for all Lua Listeners.
Here's my code:
EventManager.h
#include <string>
#include <map>
#include <vector>
using namespace std;
typedef void (*fptr)(const void* userdata, va_list args);
typedef pair<fptr, void*> Listener;
typedef map<string, vector<Listener> > CallbackMap;
class EventManager {
private:
friend ostream& operator<<(ostream& out, const EventManager& r);
CallbackMap callbacks;
static EventManager* emInstance;
EventManager() {
callbacks = CallbackMap();
}
~EventManager() {
}
public:
static EventManager* Instance();
bool RegisterEvent(string const& name);
void RegisterListener(string const &event_name, fptr callback,
void* userdata);
bool FireEvent(string name, ...);
};
inline ostream& operator<<(ostream& out, const EventManager& em) {
return out << "EventManager: " << em.callbacks.size() << " registered event"
<< (em.callbacks.size() == 1 ? "" : "s");
}
EventManager.cpp
#include <cstdarg>
#include <iostream>
#include <string>
#include "EventManager.h"
using namespace std;
EventManager* EventManager::emInstance = NULL;
EventManager* EventManager::Instance() {
if (!emInstance) {
emInstance = new EventManager;
}
return emInstance;
}
bool EventManager::RegisterEvent(string const& name) {
if (!callbacks.count(name)) {
callbacks[name] = vector<Listener>();
return true;
}
return false;
}
void EventManager::RegisterListener(string const &event_name, fptr callback,
void* userdata) {
RegisterEvent(event_name);
callbacks[event_name].push_back(Listener(callback, userdata));
}
bool EventManager::FireEvent(string name, ...) {
map<string, vector<Listener> >::iterator event_callbacks =
callbacks.find(name);
if (event_callbacks == callbacks.end()) {
return false;
}
for (vector<Listener>::iterator cb =
event_callbacks->second.begin();
cb != event_callbacks->second.end(); ++cb) {
va_list args;
va_start(args, NULL);
(*cb->first)(cb->second, args);
va_end(args);
}
return true;
}
luaw_eventmanager.h
#pragma once
#ifndef LUAW_EVENT_H
#define LUAW_EVENT_H
#include "EventManager.h"
extern "C" {
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
void luaw_eventmanager_push(lua_State* L, EventManager* em);
int luaopen_weventmanager(lua_State* L);
}
#endif
luaw_eventmanager.cpp
#include <assert.h>
#include <stdio.h>
#include <sstream>
#include <iostream>
#include "luaw_eventmanager.h"
using namespace std;
static int
luaw_eventmanager_registerevent(lua_State* L)
{
int nargs = lua_gettop(L);
if (nargs != 2) {
return 0;
}
stringstream ss;
ss << luaL_checkstring(L, 2);
EventManager::Instance()->RegisterEvent(ss.str());
return 1;
}
static int
luaw_eventmanager_registerlistener(lua_State* L)
{
return 1;
}
static int
luaw_eventmanager_fireevent(lua_State* L)
{
return 1;
}
static int
luaw_eventmanager_tostring(lua_State* L)
{
stringstream ss;
ss << *EventManager::Instance();
lua_pushstring(L, &ss.str()[0]);
return 1;
}
static const struct luaL_Reg luaw_eventmanager_m [] = {
{"registerEvent", luaw_eventmanager_registerevent},
{"registerListener", luaw_eventmanager_registerlistener},
{"fireEvent", luaw_eventmanager_fireevent},
{"__tostring", luaw_eventmanager_tostring},
{NULL, NULL}
};
void
luaw_eventmanager_push(lua_State* L, EventManager* em)
{
EventManager** emUserdata = (EventManager**)lua_newuserdata(L, sizeof(EventManager*));
*emUserdata = em;
luaL_getmetatable(L, "WEAVE.mEventManager");
lua_setmetatable(L, -2);
}
int
luaopen_weventmanager(lua_State* L)
{
luaL_newmetatable(L, "WEAVE.mEventManager");
lua_pushvalue(L, -1);
lua_setfield(L, -2, "__index");
luaL_register(L, NULL, luaw_eventmanager_m);
assert(!lua_isnil(L, -1));
return 1;
}

All Lua-owned objects are garbage-collected. This includes functions. Therefore, even if you could get a reference to a Lua function, Lua would still own it and thus it would be subject to the GC whenever Lua detects that it isn't being referenced anymore.
External code cannot own a Lua reference. But external code can store that reference in a place that Lua code cannot reach (and thus cannot break): the Lua registry.
The Lua registry is a Lua table (which is at stack pseudo-index LUA_REGISTRYINDEX, so it's accessible from the stack) which Lua code cannot (directly) access. As such, it is a safe place for you to store whatever you need. Since it is a Lua table, you can manipulate it like any other Lua table (adding values, keys, etc).
However, the registry is global, and if you use other C modules, it is entirely possible that they could start stomping on each other's stuff. So it's a good idea to pick a specific registry key for each of your modules and build a table within that registry key.
Step one: when initializing your C interface code, create a table and stick it in a known key in the registry table. Just an empty table.
When the Lua code passes you a Lua function to use as a callback, load that table from the special key and stick the Lua function there. Of course, to do that, you need to give each registered function a unique key (which you store as the Lua function's void* data), which you can later use to retrieve that function.
Lua has a simple mechanism for doing this: luaL_ref. This function will register the object on the top of the stack with the table it is given. This registration process is guaranteed to return unique integer keys for each registered object (as long as you don't manually modify the table behind the system's back). luaL_unref releases a reference, allo
Since the references are integer keys, you could just do a cast from int to void* and have that be the data. I would probably use an explicit object (mallocing an int), but you can store it however you like.
Step two: when a Lua function is registered, use luaL_ref to add it to the registry table created in step 1. Store the key returned by this function in the void* parameter for the registered function.
Step three: when that function needs to be called, use the integer key you stored in the void* parameter to access the registry table created in step 1. That will give you the function, which you can then call with the usual Lua methods.
Step four: when you are unregistering the Lua function, use luaL_unref to release the function (thus you avoid leaking Lua's memory). If you malloced memory to store the integer key, free it here.

I would suggest to store your functions into the registry and use the reference mechanism provided by the functions luaL_ref and luaL_unref.
These functions use an C int value to access the values. It is easy to store such an integer value in a C++ class member for example.

#Nicolas Bolas has provided nice instructions, but are too vague for newbies (including myself).
Through trial and error I have come up with working example:
Storage
lua_newtable(L); // create table for functions
int tab_idx = luaL_ref(L,LUA_REGISTRYINDEX); // store said table in pseudo-registry
lua_rawgeti(L,LUA_REGISTRYINDEX,tab_idx); // retrieve table for functions
lua_getglobal(L, "f"); // retrieve function named "f" to store
int t = luaL_ref(L,-2); // store a function in the function table
// table is two places up the current stack counter
lua_pop(L,1); // we are done with the function table, so pop it
Retrieval
lua_rawgeti(L,LUA_REGISTRYINDEX,tab_idx); // retrieve function table
lua_rawgeti(L,-1,t); // retreive function
//use function
//don't forget to pop returned value and function table from the stack
lua_pop(L,2);

Related

Dynamically create a function and get a pointer

I am using Arduino and motor encoders to track the rotations of a motor. To do this, I am using interrupts on the Arduino. I can create a function, an ISR, that will be executed by the processor whenever the signal changes on a pin. That Interrupt/ISR combinations works like this:
void setup() {
attachInterrupt(1,ISR_function,FALLING);
}
void ISR_function() {
// do something
}
Seeing as I have multiple motors with encoders, I decided I would make a class to handle this. However, the attachInterrupt method requires a function pointer, and I am aware that in C++ you cannot have a pointer to a method function of an instance of an object. So something like this will not work:
class Encoder {
public:
Encoder(void);
void ISR_function(void);
private:
// Various private members
}
Encoder::Encoder() {
attachInterrupt(1,ISR_function,FALLING);
}
Encoder::ISR_function() {
// Do some interrupt things with private members
}
Because ISR_function is not static. The ISR_function however executes code that is dependent on the the private data members of each specific instance.
Is it possible to create a function dynamically? And then retrieve a pointer to that function? Almost like in javascript:
class Encoder {
public:
Encoder(void);
void* ISR_function(void);
private:
// Various private members
}
Encoder::Encoder() {
attachInterrupt(1,ISR_function(),FALLING);
}
Encoder::ISR_function() {
return dynamicFunctionPointer;
}
Is this possible? If not, how can accomplish what I am trying to do without manually creating separate static ISR_functions.
// type of an interrupt service routine pointer
using ISR = void(*)();
// a fake version of the environment we are working with
// for testing purposes
namespace fake_environment {
enum bob{FALLING};
ISR isrs[100] = {0};
void attachInterrupt(int i, void(*f)(), bob) {
isrs[i] = f;
}
void runInterrupt(int i) {
isrs[i]();
}
}
// type storing a pointer to member function
// as a compile-time constant
template<class T, void(T::*m)()>
struct pmf {};
// stores a pointer to a class instance
// and a member function. Invokes it
// when called with operator(). Type erases
// stuff down to void pointers.
struct funcoid {
using pfunc = void(*)(void*);
pfunc pf = 0;
void* pv = 0;
void operator()()const { pf(pv); }
template<class T, void(T::*m)()>
funcoid(T* t, pmf<T,m>):
pv(t)
{
// create a lambda, then decay it into a function pointer
// this stateless lambda takes a void* which it casts to a T*
// then invokes the member function m on it.
pf = +[](void* pt) {
(static_cast<T*>(pt)->*m)();
};
}
funcoid()=default;
};
// a global array of interrupts, which have a this pointer
// and a member function pointer type erased:
namespace client {
enum {interrupt_count = 20};
std::array<funcoid, interrupt_count> interrupt_table = {{}};
// with a bit of work, could replace this with a std::vector
}
// some metaprogramming utility code
// this lets me iterate over a set of size_t at compile time
// without writing extra helper functions at point of use.
namespace utility {
template<std::size_t...Is>
auto index_over( std::index_sequence<Is...> ) {
return [](auto&& f)->decltype(auto) {
return f(std::integral_constant<std::size_t, Is>{}...);
};
}
template<std::size_t N>
auto index_upto( std::integral_constant<std::size_t, N> ={} ) {
return index_over( std::make_index_sequence<N>{} );
}
}
// builds an array of interrupt service routines
// that invoke the same-index interrupt_table above.
namespace client {
// in g++, you'd write a helper function taking an `index_sequence`
// and take the code out of that lambda and build the array there:
std::array<ISR, interrupt_count> make_isrs() {
// creates an array of ISRs that invoke the corresponding element in interrupt_table.
// have to do it at compile time, because we are generating 20 different functions
// each one "knows" its index, then storing pointers to them.
// Could be done with a lot of copy-pasta or a macro
return ::utility::index_upto< interrupt_count >()(
[](auto...Is)->std::array<ISR, interrupt_count>{
return {{ []{ interrupt_table[decltype(Is)::value](); }... }};
}
);
}
// isr is a table of `void(*)()`, suitable for use
// by your interrupt API. Each function pointer "knows" its
// index, which it uses to invoke the appropraite `interrupt_table`
// above.
auto isr = make_isrs();
// with a bit of work, could replace this with a std::vector
}
// interrupt is the interrupt number
// index is the index in our private table (0 to 19 inclusive)
// t is the object we want to use
// mf is the member function we call
// kind is FALLING or RISING or the like
// index must be unique, that is your job.
template<class T, void(T::*m)()>
void add_interrupt( int interrupt, int index, T* t, pmf<T, m> mf, fake_environment::bob kind ) {
client::interrupt_table[index] = {t, mf};
fake_environment::attachInterrupt(interrupt,client::isr[index],kind);
}
class Encoder {
public:
Encoder():Encoder(1, 7) {};
Encoder(int interrupt, int index);
void ISR_function(void);
// my choice for some state:
std::string my_name;
};
Encoder::Encoder(int interrupt, int index) {
add_interrupt( interrupt, index, this, pmf<Encoder, &Encoder::ISR_function>{}, fake_environment::FALLING );
}
void Encoder::ISR_function() {
// display state:
std::cout << my_name << "\n";
}
int main() {
Encoder e0;
e0.my_name = "Hello World";
fake_environment::runInterrupt(1);
Encoder e1(0, 10);
e1.my_name = "Goodbye World";
fake_environment::runInterrupt(0);
}
Does not compile in g++ and uses C++14.
Does solve your problem. g++ problem is in make_isrs, which can be replaced by verbose copy-paste initialization. C++14 is from index_upto and index_over, which can similarly be reworked for C++11.
Live example.
However, ISRs are supposed to be minmal; I suspect you should just record the message and handle it elsewhere instead of interacting with object state.
To call a member function you need an instance to invoke it on, so it doesn't seem like a good choice to use for interrupts.
From pointers-to-members:
A member function is meaningless without an object to invoke it on.
Non-static member functions have a hidden parameter that corresponds to the this pointer. The this pointer points to the instance data for the object. The interrupt hardware/firmware in the system is not capable of providing the this pointer argument. You must use “normal” functions (non class members) or static member functions as interrupt service routines.
One possible solution is to use a static member as the interrupt service routine and have that function look somewhere to find the instance/member pair that should be called on interrupt. Thus the effect is that a member function is invoked on an interrupt, but for technical reasons you need to call an intermediate function first.
First of all, you can extract pointer to a class method and call it:
auto my_method_ptr = &MyClass::my_method;
....
(myClassInstance->*my_method_ptr)(); // calling via class ptr
(myclassInstance.*my_method_ptr)(); // calling via class ref
This basically passes myClassInstance pointer to MyClass::my_method as an implicit argument, accessible via this.
Unfortunately, AVR interrupt controller can't call class method, as the hardware operate on simple pointers only and can't call that method with implicit argument. You'll need a wrapper function for this.
MotorEncoderClass g_motor; // g_ for global
void my_isr() {
g_motor.do_something();
}
int main() {
// init g_motor with relevant data
// install my_isr handler
// enable interrupts
// ... do rest of stuff
return 0;
}
Create your class instance as a global variable.
Create ordinary function that calls that method
Initialize your motor class with relevant data
Install my_isr as IRQ handler.
Press start to begin :)

How to use lambda functions or std::function with an API that takes function pointers?

Suppose I want to implement a simple abstraction over pthreads.
(or any C API that takes function pointers for callbacks or threads).
Like std::thread, I want the interface to be able to take function objects in general.
How do I bridge the gap in a way that works for all cases?
(That includes binds, lambda functions, etc.)
I know about the std::function::target but afaik, it does not do what I need.
If the API takes functions with a void* for user data as, e.g., pthread_create() does, you'd pass a pointer to the function as user data, call a trampoline which casts the user data to your function type, and calls the function. For example:
#include <functional>
#include <pthread.h>
extern "C" void* trampoline(void* userData) {
return (*static_cast<std::function<void*()>*>(userData))();
}
void* start() {
// ...
return 0;
}
int main() {
pthread_t thread;
std::function<void*()> entry(start);
pthread_create(&thread, 0, &trampoline, &entry);
// ...
}
The immediate implication is, however, that the function object life-time isn't easily controlled. In the example above the std::function<void*()> object happens to live long enough but it isn't always as easy.
If the function you try to call doesn't have a user data argument, you are pretty much out of luck. You might get away with using global objects but it is almost certainly a rather fragile approach.
A lambda function can be used anywhere that takes regular function pointers. In other words, it can be used wherever you would use regular functions/pointers to functions..
Example: https://ideone.com/4CJjlL
#include <iostream>
void voidfunc(void (*func_ptr)(void))
{
func_ptr();
}
void funcwithargs(void (*func_ptr)(int, char, std::string), int a, char b, std::string c)
{
func_ptr(a, b, c);
}
int main()
{
auto vf = []{std::cout<<"Called void func..\n";};
auto vfwa = [](int a, char b, std::string c) {std::cout<<"Called func with args with: "<<a<<b<<" "<<c<<"\n";};
voidfunc(vf);
funcwithargs(vfwa, 10, 'x', " + 3");
return 0;
}
Likewise, you can use std::function instead of the function pointer..

Using straight Lua, how do I expose an existing C++ class objec for use in a Lua script?

I've been furthering my experience in embedding Lua scripting in C++,
and I could use a hand, here.
Consider the following two classes:
// Person.hpp
#pragma once
#include <string>
class Person {
private:
std::string p_Name;
int p_Age;
public:
Person(const std::string & strName, const int & intAge)
: p_Name(strName), p_Age(intAge) { }
Person() : p_Name(""), p_Age(0) { }
std::string getName() const { return p_Name; }
int getAge() const { return p_Age; }
void setName(const std::string & strName) { p_Name = strName; }
void setAge(const int & intAge) { p_Age = intAge; }
};
... and ...
// PersonManager.hpp
#pragma once
#include "Person.hpp"
#include <vector>
class PersonManager {
// Assume that this class is a singleton, and therefore
// has no public constructor, but a static function that returns the
// singleton instance.
private:
std::vector<Person *> pm_People;
public:
bool personExists(const std::string & strName) { /* ... */ }
bool addPerson(const std::string & strName, const int & intAge) { /* ... */ }
Person * getPerson(const std::string & strName) { /* ... */ }
void removePerson(const std::string & strName) { /* ... */ }
void removeAllPeople() { /* ... */ }
};
... where getPerson checks the pm_People vector to see if the person with the specified name exists, using personExists.
Now, consider the following function that gets a Person object from Lua and returns its age.
// Lua_Person.cpp
#include "Lua_Person.hpp" // "Lua_Person.hpp" declares the function called to expose the "Person" functions to Lua.
#include "PersonManager.hpp"
#include "Person.hpp"
int lua_GetPersonAge(lua_State * LS) {
// Validate the userdata.
luaL_checktype(LS, 1, LUA_TUSERDATA);
// Get the "Person" userdata.
Person * luaPerson = reinterpret_cast<Person *>(lua_touserdata(LS, 1));
// Check to see if the Person pointer is not null.
if(luaPerson == nullptr)
luaL_error(LS, "lua_GetPersonAge: You gave me a null pointer!");
// Push the person's age onto the Lua stack.
lua_pushnumber(LS, luaPerson->getAge());
// Return that age integer.
return 1;
}
What I want to do is to get an already-instantiated and existing Person object from the PersonManager singleton, using getPerson, and expose that object to Lua,
so I can do something like this:
local testPerson = People.get("Stack Overflower")
print(testPerson:getAge())
I tried something like the code block below, to no avail:
int lua_GetPerson(lua_State * LS) {
// Validate the argument passed in.
luaL_checktype(LS, 1, LUA_TSTRING);
// Get the string.
std::string personName = lua_tostring(LS, 1);
// Verify that the person exists.
if(PersonManager::getInstance().personExists(personName) == false)
luaL_error(LS, "lua_GetPerson: No one exists with this ID: %s", personName.c_str());
// Put a new userdata into a double pointer, and assign it to the already existing "Person" object requested.
Person ** p = static_cast<Person **>(lua_newuserdata(LS, sizeof(Person *))); // <Userdata>
*p = PersonManager::getInstance().getPerson(personName);
// Put that person object into the "Meta_Person" metatable.
// Assume that metatable is created during the registration of the Person/Person Manager functions with Lua.
luaL_getmetatable(LS, "Meta_Person"); // <Metatable>, <Userdata>
lua_setmetatable(LS, -2); // <Metatable>
// Return that metatable.
return 1;
}
Can anybody lend a helping hand here, or at least point me in the right direction?
I am not using any lua wrapper libraries, just straight Lua.
Thank you.
EDIT: The functions that I use to expose my Person and PersonManager functions are as follows:
void exposePerson(lua_State * LS) {
static const luaL_reg person_functions[] = {
{ "getAge", lua_getPersonAge },
{ nullptr, nullptr }
};
luaL_newmetatable(LS, "Meta_Person");
lua_pushstring(LS, "__index");
lua_pushvalue(LS, -2);
lua_settable(LS, -3);
luaL_openlib(LS, nullptr, person_functions, 0);
}
void exposePersonManager(lua_State * LS) {
static const luaL_reg pman_functions[] = {
{ "get", lua_getPerson },
{ nullptr, nullptr }
};
luaL_openlib(LS, "People", pman_functions, 0);
lua_pop(LS, 1);
}
Let's start off the top, that is by registering PersonManager in Lua. Since it's a singleton, we'll register it as a global.
void registerPersonManager(lua_State *lua)
{
//First, we create a userdata instance, that will hold pointer to our singleton
PersonManager **pmPtr = (PersonManager**)lua_newuserdata(
lua, sizeof(PersonManager*));
*pmPtr = PersonManager::getInstance(); //Assuming that's the function that
//returns our singleton instance
//Now we create metatable for that object
luaL_newmetatable(lua, "PersonManagerMetaTable");
//You should normally check, if the table is newly created or not, but
//since it's a singleton, I won't bother.
//The table is now on the top of the stack.
//Since we want Lua to look for methods of PersonManager within the metatable,
//we must pass reference to it as "__index" metamethod
lua_pushvalue(lua, -1);
lua_setfield(lua, -2, "__index");
//lua_setfield pops the value off the top of the stack and assigns it to our
//field. Hence lua_pushvalue, which simply copies our table again on top of the stack.
//When we invoke lua_setfield, Lua pops our first reference to the table and
//stores it as "__index" field in our table, which is also on the second
//topmost position of the stack.
//This part is crucial, as without the "__index" field, Lua won't know where
//to look for methods of PersonManager
luaL_Reg personManagerFunctions[] = {
'get', lua_PersonManager_getPerson,
nullptr, nullptr
};
luaL_register(lua, 0, personManagerFunctions);
lua_setmetatable(lua, -2);
lua_setglobal(lua, "PersonManager");
}
Now we handle PersonManager's get method:
int lua_PersonManager_getPerson(lua_State *lua)
{
//Remember that first arbument should be userdata with your PersonManager
//instance, as in Lua you would call PersonManager:getPerson("Stack Overflower");
//Normally I would first check, if first parameter is userdata with metatable
//called PersonManagerMetaTable, for safety reasons
PersonManager **pmPtr = (PersonManager**)luaL_checkudata(
lua, 1, "PersonManagerMetaTable");
std::string personName = luaL_checkstring(lua, 2);
Person *person = (*pmPtr)->getPerson(personName);
if (person)
registerPerson(lua, person); //Function that registers person. After
//the function is called, the newly created instance of Person
//object is on top of the stack
else
lua_pushnil(lua);
return 1;
}
void registerPerson(lua_State *lua, Person *person)
{
//We assume that the person is a valid pointer
Person **pptr = (Person**)lua_newuserdata(lua, sizeof(Person*));
*pptr = person; //Store the pointer in userdata. You must take care to ensure
//the pointer is valid entire time Lua has access to it.
if (luaL_newmetatable(lua, "PersonMetaTable")) //This is important. Since you
//may invoke it many times, you should check, whether the table is newly
//created or it already exists
{
//The table is newly created, so we register its functions
lua_pushvalue(lua, -1);
lua_setfield(lua, -2, "__index");
luaL_Reg personFunctions[] = {
"getAge", lua_Person_getAge,
nullptr, nullptr
};
luaL_register(lua, 0, personFunctions);
}
lua_setmetatable(lua, -2);
}
And finally handling Person's getAge.
int lua_Person_getAge(lua_State *lua)
{
Person **pptr = (Person**)lua_checkudata(lua, 1, "PersonMetaTable");
lua_pushnumber(lua, (*pptr)->getAge());
return 1;
}
You should now call registerPersonManager before executing your Lua code, best just after you create new Lua state and open needed libraries.
Now within Lua, you should be able to do that:
local person = PersonManager:getPerson("Stack Overflower");
print(person:getAge());
I don't have access to either Lua or C++ at the moment to test it, but that should get you started. Please be careful with lifetime of the Person pointer you give Lua access to.
You use a full userdata that contains an entry that is pointer to a light userdata. Light userdata are values that can only be created from C/C++, they are like a number in Lua in that they don't have methods, metatable, etc. Then whenever your C++ functions get the full userdata, they get the pointer from it, which can then be used to access C++ methods of the underlying C++ object.
See Accessing Light userdata in Lua and the links there and see if you can work it out. There are also many posts on the Lua newsgroup archive that you can find via google.
Note that with SWIG to generate wrapper code for you, this task would be trivial and you could focus on your app rather than on binding C++ and Lua.

Lua C API: Handling and storing additional arguments

CreateEntity is a C function I bound to Lua in my project. It takes an entity class name string as first argument, and any number of additional arguments which should get passed to the constructor of the chosen entity.
For example, if CreateEntity was a normal Lua function I could do it this way:
function CreateEntity( class, ... )
-- (choose a constructor function based on class)
args = {...}
-- (store args somewhere for whatever reason)
TheConstructor( ... )
end
But how can I do this with a C Lua function?
The C function lua_gettop will return how many parameters were passed to your C function. You must either read these all from the stack and store them in a C data structure, or place them in the Lua registry (see Registry and luaL_ref) and store references to them for later use. The example program below uses the registry approach.
#include <lauxlib.h>
#include <lua.h>
#include <lualib.h>
#include <stdio.h>
#include <stdlib.h>
/* this function prints the name and extra variables as a demonstration */
static void
TheConstructor(lua_State *L, const char *name, int *registry, int n)
{
int i;
puts(name);
for (i = 0; i < n; ++i) {
lua_rawgeti(L, LUA_REGISTRYINDEX, registry[i]);
puts(lua_tostring(L, -1));
}
free(registry);
}
static int
CreateEntity(lua_State *L)
{
const char *NAME = luaL_checkstring(L, 1);
int *registry;
int i, n;
/* remove the name parameter from the stack */
lua_remove(L, 1);
/* check how many arguments are left */
n = lua_gettop(L);
/* create an array of registry entries */
registry = calloc(n, sizeof (int));
for (i = n; i > 0; --i)
registry[i-1] = luaL_ref(L, LUA_REGISTRYINDEX);
TheContructor(L, NAME, registry, n);
return 0;
}
int
main(int argc, char **argv[])
{
const char TEST_CHUNK[] =
"CreateEntity('foo', 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)";
lua_State *L;
L = luaL_newstate();
lua_register(L, "CreateEntity", CreateEntity);
luaL_dostring(L, TEST_CHUNK);
lua_close(L);
return EXIT_SUCCESS;
}
args = {...}
-- (store args somewhere for whatever reason)
The arguments of the call are on the Lua stack and you can do with them as you please: put them in a structure of your own (std::vector<boost::any> or something like that) or store individual arguments in the Lua registry or create a Lua table with the arguments and store that in the registry instead. What suits you better?
TheConstructor( ... )
I'm fairly convinced this part is not possible in C++ the way it is in Lua. C++ requires the number of parameters you pass to a function to be known at compile time.
It's a major inconvenience to try and do those things in C++. Maybe be betters solution will come if you tell us why do you want your CreateEntity function to be on the C++ side instead of Lua.

Callback to non-static method

Think of your basic GLUT programs. They simply run from a main method and contain callbacks like `glutMouseFunc(MouseButton) where MouseButton is the name of a method.
What I have done is I have encapsulated the main file into a class, so that MouseButton is no longer a static function but has an instance. But doing this gives me a compilation error :
Error 2 error C3867: 'StartHand::MouseButton': function call missing argument list; use '&StartHand::MouseButton' to create a pointer to member c:\users\angeleyes\documents\visual studio 2008\projects\capstone ver 4\starthand.cpp 388 IK Engine
It is not possible to provide a code sample as the class is quite huge.
I have tried using this->MouseButton but that gives the same error. Can't a pointer to an instance function be given for callback?
As the error message says, you must use &StartHand::MouseButton syntax to get a pointer to a member function (ptmf); this is simply mandated as part of the language.
When using a ptmf, the function you are calling, glutMouseFunc in this case, must also expect to get a ptmf as a callback, otherwise using your non-static MouseButton won't work. Instead, a common technique is for callbacks to work with a user-supplied void* context, which can be the instance pointer—but the library doing the callbacks must explicitly allow this parameter. It's also important to make sure you match the ABI expected by the external library (the handle_mouse function below).
Since glut doesn't allow user-supplied context, you have to use another mechanism: associate your objects with glut's current window. It does provide a way to get the "current window", however, and I've used this to associate a void* with the window. Then you simply need to create a trampoline to do the type conversion and call the method.
Machinery:
#include <map>
int glutGetWindow() { return 0; } // make this example compile and run ##E##
typedef std::pair<void*, void (*)(void*,int,int,int,int)> MouseCallback;
typedef std::map<int, MouseCallback> MouseCallbacks;
MouseCallbacks mouse_callbacks;
extern "C" void handle_mouse(int button, int state, int x, int y) {
MouseCallbacks::iterator i = mouse_callbacks.find(glutGetWindow());
if (i != mouse_callbacks.end()) { // should always be true, but possibly not
// if deregistering and events arrive
i->second.second(i->second.first, button, state, x, y);
}
}
void set_mousefunc(
MouseCallback::first_type obj,
MouseCallback::second_type f
) {
assert(obj); // preconditions
assert(f);
mouse_callbacks[glutGetWindow()] = MouseCallback(obj, f);
//glutMouseFunc(handle_mouse); // uncomment in non-example ##E##
handle_mouse(0, 0, 0, 0); // pretend it's triggered immediately ##E##
}
void unset_mousefunc() {
MouseCallbacks::iterator i = mouse_callbacks.find(glutGetWindow());
if (i != mouse_callbacks.end()) {
mouse_callbacks.erase(i);
//glutMouseFunc(0); // uncomment in non-example ##E##
}
}
Example:
#include <iostream>
struct Example {
void MouseButton(int button, int state, int x, int y) {
std::cout << "callback\n";
}
static void MouseButtonCallback(
void* self, int button, int state, int x, int y
) {
static_cast<Example*>(self)->MouseButton(button, state, x, y);
}
};
int main() {
Example obj;
set_mousefunc(&obj, &Example::MouseButtonCallback);
return 0;
}
Notice that you don't call glutMouseFunc directly anymore; it is managed as part of [un]set_mousefunc.
Just in case it isn't clear: I've rewritten this answer so it should work for you and so that it avoids the C/C++ linkage issue being debated. It will compile and run as-is (without glut), and it should work with glut with only minor modification: comment or uncomment the 4 lines marked ##E##.
No, a pointer to an instance function can not be given to a callback function expecting a function pointer of a certain signature. Their signatures are different. It won't compile.
Generally such APIs allow you to pass in a void* as a "context" parameter. You pass in your object there, and write a wrapper function which takes the context as the callback. The wrapper casts it back to whatever class you were using, and calls the appropriate member function.
You can't replace a static callback with an instance one. When the caller calls your callback, on what instance whoul it call? In other words, how does the caller pass in the formal 'this' argument?
The solution is to have a static callback stub and pass the instance as argument, which implies the callee must accept an arbitrary pvoid that will pass back when invoking the callback. In the stub, you can then call the non-static method:
class C {
void f() {...}
static void F(void* p) {
C* pC = (C*)p;
pC->f();
}
}
C* pC = ...;
someComponent.setCallback(&C::F, pC);
Contrary to what everyone seems to be saying, you most definitely CAN use a non-static member function as a callback method. It requires special syntax designed specifically for getting pointers to non-static members, and special syntax to call that function on a specific instance of a class. See here for a discussion of the needed syntax.
Here is sample code that illustrates how this works:
#include <cstdlib>
#include <string>
#include <iostream>
#include <vector>
#include <sstream>
#include <algorithm>
using namespace std;
class Operational
{
public:
Operational(int value) : value_(value) {};
string FormatValue() const ;
private:
int value_;
};
string Operational::FormatValue() const
{
stringstream ss;
ss << "My value is " << value_;
return ss.str();
}
typedef string(Operational::*FormatFn)() const; // note the funky syntax
Operational make_oper(int val)
{
return Operational(val);
}
int main()
{
// build the list of objects with the instance callbacks we want to call
Operational ops[] = {1, 2, 3, 5, 8, 13};
size_t numOps = sizeof(ops)/sizeof(ops[0]);
// now call the instance callbacks
for( size_t i = 0; i < numOps; ++i )
{
// get the function pointer
FormatFn fn = &Operational::FormatValue;
// get a pointer to the instance
Operational* op = &ops[i];
// call the callback on the instance
string retval = (op->*fn)();
// display the output
cout << "The object # " << hex << (void*)op << " said: '" << retval << "'" << endl;
}
return 0;
}
The output of this program when I ran it on my machine was:
The object # 0017F938 said: 'My value is 1'
The object # 0017F93C said: 'My value is 2'
The object # 0017F940 said: 'My value is 3'
The object # 0017F944 said: 'My value is 5'
The object # 0017F948 said: 'My value is 8'
The object # 0017F94C said: 'My value is 13'
You cannot use a non-static member function in this case.
Basically the type of the argument expected by glutMouseFunc is
void (*)(int, int, int, int)
while the type of your non-static member function is
void (StartHand::*)(int, int, int, int)
First problem is that types don't really match.
Second, in order to be able to call that method, the callback would have to know which object ( i.e. "this" pointer ) your method belongs to ( that's pretty much why the types are different in the first place ).
And third, I think you're using the wrong syntax to retrieve the method's pointer. The right syntax should be: &StartHand::MouseButton.
So, you have to either make that method static or use some other static method that would know which StartHand pointer to use to call MouseButton.
The following works in c++ to define a c callback function, useful for example when using glut (glutDisplayFunc, glutKeyboardFunc, glutMouseFunc ...) when you only need a single instance of this class :
MyClass * ptr_global_instance = NULL;
extern "C" void mouse_buttons_callback(int button, int state, int x, int y) {
// c function call which calls your c++ class method
ptr_global_instance->mouse_buttons_cb(button, state, x, y);
}
void MyClass::mouse_buttons_cb(int button, int state, int x, int y) {
// this is actual body of callback - ie. if (button == GLUT_LEFT_BUTTON) ...
// implemented as a c++ method
}
void MyClass::setup_glut(int argc, char** argv) { // largely boilerplate glut setup
glutInit(&argc, argv);
// ... the usual suspects go here like glutInitWindowSize(900, 800); ...
setupMouseButtonCallback(); // <-- custom linkage of c++ to cb
// ... other glut setup calls here
}
void MyClass::setupMouseButtonCallback() {
// c++ method which registers c function callback
::ptr_global_instance = this;
::glutMouseFunc(::mouse_buttons_callback);
}
In your MyClass header we add :
void mouse_buttons_cb(int button, int state, int x, int y);
void setupMouseButtonCallback();
This also works using identical logic flows to setup your glut
call to glutDisplayFunc(display)