In C++, member functions of components are exposed using the plugin system.
(see Plugin Walkthrough)
To bind to scripting languages there are the following options:
- Direct support in the script binding module
- ParserI
- DispatchI
Direct support in the script binding module
The preferred method of direct binding is to bind interfaces, not implementations.
Pros:
- high level of control over the scripting exposure
Cons:
- binding code must be written
- binding code must be written per scripting language supported
- bound interfaces must be 'above' the binding in the dependency hierarchy of modules
Direct Binding in Lua
In ext/lua/LuaComponent.cc there is the function populateMethods() which is where directly bound interfaces have their implementations bound. Note that this binding is between a string (the function name) and C-like functions.
void LuaComponentObject::populateMethods(void)
{
if(is<SignalerI>())
{
setMethod("insert", signal_insert);
setMethod("signal", signal_signal);
}
if(is<HandlerI>())
{
setMethod("handle", handler_handle);
setMethod("slot", handler_slot);
}
...
}
The C-like binding functions usually go through this sequence:
- convert arguments to useful C++ objects
- call the member function
- construct the return value(s)
(preferred)
See fe::ConfigI
ParserI (deprecated)
see fe::ParserI
ParserI is simply an interface which itself is directly bound as above, but allows components to expose arbitrary functionality that can be called via a sequence of string tokens.
Pros:
- very simple to use for simple purposes
- does not require support in the binding modules
Cons:
- only supports string arguments
- no return values
Example Usage:
class MyParser : virtual public ParserI
{
public:
virtual void parse(std::vector<String> &tokens)
{
for(int i = 0; i < tokens.size(); i++)
{
feLog("%s\n", tokens[i].c_str());
}
}
};
ParserI in Lua
In lua a call to a ParserI component might look like:
c_parser = create("ArbitraryI.SomeObjectThatIsAParserI")
c_parser:parse("string one", 2) -- note that 2 is converted into a string
DispatchI (only when ConfigI falls short)
see fe::DispatchI
DispatchI is simply an interface which itself is directly bound as above. DispatchI supports named calls with typed arguments.
With DispatchI all arguments are two-way/by-reference/read-write.
Pros:
- does not require support in the binding modules
Cons:
- Signatures must me built up and exposed
Example Usage:
class MyDispatch : virtual public DispatchI, virtual Initialize<MyDispatch>
{
public:
virtual void initialize(void)
{
dispatch<Record>("my_function");
dispatch<int>("my_function");
dispatch<float>("sqr");
}
virtual bool call(const String &a_name, std::vector<Instance> a_argv)
{
if(a_name == "my_function")
{
Record r = a_argv[0].cast<Record>();
int &i = a_argv[1].cast<int>();
feLog("myfunction(record, %d)\n", i);
i = 42;
}
if(a_name == "sqr")
{
float &f = a_argv[0].cast<float>();
f = f*f;
}
return true;
}
{
return m_signatures;
}
private:
};
DispatchI in Lua
Due to the way lua works, to support DispatchI's two-way arguments, the return values are the written version of the call arguments.
In lua a call to a DispatchI component might look like:
c_dispatch = create("ArbitraryI.SomeObjectThatIsADispatchI")
return_rec, return_int = c_dispatch:my_function(rec, 17)