OOLua  2.0.1
C++ and Lua binding
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
Proxy

DSL

The domain specific language(DSL) used for generating C++ bindings to Lua.

OOLua provides a DSL for defining C++ types which are to be made available to a Lua script. The intention of this DSL is to hide the implementation details whilst providing a simple and rememberable interface to perform the required actions. For the generation of function proxies, the DSL contains two sub categories named Minimalist and Expressive.

Note
"Optional" here means that extra macro parameters are optional, up to the configuration max for a specific operation.

Class Proxy

For a class type, the library uses a proxy as an intermediary between the two languages of Lua and C++. A proxy contains information about an exposed type, for example its hierarchical structure and functions of interest.

Generating a proxy, using the DSL, takes place between the two DSL procedures OOLUA_PROXY and OOLUA_PROXY_END. However, alone this does not enable the generation and usage of the type within a Lua state, to do this requires a three part process. The tasks of the process are:

Minimal Class Proxy

The following shows the usage of the DSL to proxy a very simple class, Stub1, and to use this proxy in Lua.

struct Stub1 {};

Proxy Block

Firstly you create a proxy block. The block starts with a OOLUA_PROXY call to which you pass the name of the C++ class to be proxied and the block ends at the next OOLUA_PROXY_END. Soon we will see how to proxy other aspects of a class in this block.

Exporting

Secondly you export the member functions which are to be made available for the type in Lua. Exporting defines which member functions will be registered with Lua when the class type is registered. Even when there are no member functions to be exported you still need to inform OOLua about this. Calling an OOLUA_EXPORT* procedure in a header file is an error that will fail to compile.

See Also
OOLUA_EXPORT_FUNCTIONS
OOLUA_EXPORT_FUNCTIONS_CONST
OOLUA_EXPORT_NO_FUNCTIONS

As the simple class that is being proxied does not have any member functions, the code here uses the specific DSL procedure for this.

Registering

Lastly we register the type with a lua_State after which the type can be created and used in the virtual machine.

void setUp()
{
m_lua = new OOLUA::Script;
m_lua->register_class<Stub1>();
}
void new_luaCreatesInstance_noException()
{
CPPUNIT_ASSERT_NO_THROW(m_lua->run_chunk("Stub1.new()"));
}

Tags

Tags provide a method to inform the library that the type :

  • has relationship and/or mathematical operators
  • is an abstract class
  • doesn't have a default constructor or any public constructors
  • has enumerations

For an exhaustive list of the possible tags see Tags.

OOLUA_TAGS(TagList)

Parameters
TagListComma separated list of Tags
Note
An OOLUA_TAGS list without any Tags entries is invalid.

Constructors

Default Constructor

The default constructor of a proxy type is a special member function, much like C++, and it will be implicitly defined for the type unless otherwise specified. When available for a type "foo" it can be called in Lua using the following syntax.

foo.new()
See Also
Abstract, No_default_constructor and No_public_constructors

Non-default Constructors

OOLUA_CTORS(ConstructorEntriesList)

Parameters
ConstructorEntriesListList of OOLUA_CTOR

To enable the construction of an instance without using the default constructor, there must be a constructor block specified for the proxy type. The constructor block, OOLUA_CTORS, is where non-default constructor entries can be specified using an OOLUA_CTOR per entry.

Constructors are the only real type of overloading which is permitted by OOLua and there is an important point which should be noted. This being that OOLua will attempt to match the number and type of parameters on the stack with the amount and types specified for each OOLUA_CTOR entry. The order in which it will attempt the matching is the same order in which they were defined. When interacting with the Lua stack certain types can not be differentiated between, these include some integral types such as float, int, double etc and types which are of a proxy class type or derived from that type. OOLua implicitly converts between classes in a hierarchy even if a reference is required. This means for example that if there are constructors such as Foo::Foo(int) and Foo::Foo(float) it will depend on which was defined first in the OOLUA_CTORS block as to which will be invoked for a call such as Foo.new(1).

See Also
No_default_constructor
Note
An OOLUA_CTORS block without any OOLUA_CTOR entries is invalid.
OOLUA_PROXY(ParamConstructor)
No_default_constructor
)
OOLUA_CTOR(bool) /*NOLINT(readability/casting)*/
OOLUA_CTOR(int) /*NOLINT(readability/casting)*/
OOLUA_CTOR(char const*)
OOLUA_CTOR(int, bool)
OOLUA_CTOR(Stub1 *) /*NOLINT(readability/casting)*/
OOLUA_CTOR(Stub1 *, Stub2*)
OOLUA_CTOR(Stub3*) /*NOLINT(readability/casting)*/
OOLUA_CTOR(Stub3 const *)
OOLUA_CTOR(OOLUA::Lua_func_ref)
OOLUA_CTOR(OOLUA::Table)
)

Enumerations

Class enumerations, whether weak or scoped, are specified inside the OOLUA_ENUMS block. To register the enumeration values when the class type is, the Register_class_enums tag must be present in the tags block.

OOLUA_ENUMS(EnumEntriesList)

Parameters
EnumEntriesListList which contains OOLUA_ENUM and/or OOLUA_SCOPED_ENUM entries.
Note
An OOLUA_ENUMS block without any OOLUA_ENUM or OOLUA_SCOPED_ENUM entries is invalid.

Weak Enumerations

OOLUA_ENUM(EnumName)

Parameters
EnumNameThe class enumeration name
class Enums
{
public:
enum COLOUR{GREEN = 0, INVALID};
Enums()
:m_enum(INVALID)
{}
Enums(COLOUR e)
:m_enum(e)
{}
COLOUR m_enum;
void set_enum(COLOUR e)
{
m_enum = e;
}
COLOUR get_enum()
{
return m_enum;
}
};
Register_class_enums
)
OOLUA_CTOR(Enums::COLOUR)
)
OOLUA_ENUM(GREEN)
OOLUA_ENUM(INVALID)
)
OOLUA_MFUNC(set_enum)
OOLUA_MFUNC(get_enum)
, set_enum
, get_enum)
void constructWithEnum_passedValueGreen_functionReturnsGreen()
{
m_lua->register_class<Enums>();
m_lua->run_chunk("foo = function() "
"local obj = Enums.new(Enums.GREEN) "
"return obj:get_enum() "
"end");
Enums::COLOUR result(Enums::INVALID);
m_lua->call("foo");
OOLUA::pull(*m_lua, result);
CPPUNIT_ASSERT_EQUAL(Enums::GREEN, result);
}

Scoped Enumerations

OOLUA_SCOPED_ENUM(EnumName, Entry)

Parameters
NameThe class enumeration name which will be used to access it from Lua
EntryThe class enumeration scoped qualified name (minus the class type)
See Also
OOLUA_ENUM
struct Has_scoped_enum
{
enum class scoped_enum{INVALID, VALID};
Has_scoped_enum()
:e(scoped_enum::VALID)
{}
Has_scoped_enum(scoped_enum input)
:e(input)
{}
scoped_enum e;
void param(scoped_enum /*e*/){}
scoped_enum return_enum(){return scoped_enum::VALID;}
};
OOLUA_PROXY(Has_scoped_enum)
OOLUA_TAGS(Register_class_enums)
OOLUA_CTOR(Has_scoped_enum::scoped_enum)
)
OOLUA_SCOPED_ENUM(INVALID, scoped_enum::INVALID)
OOLUA_SCOPED_ENUM(VALID, scoped_enum::VALID)
)
OOLUA_MFUNC(param)
OOLUA_MFUNC(return_enum)
OOLUA_EXPORT_FUNCTIONS(Has_scoped_enum
, param
, return_enum
, set_e)
, get_e)
void publicMember_inCppSetMemberToInvalidInLuaSetToValid_resultEqualsValid()
{
Has_scoped_enum instance;
instance.e = Has_scoped_enum::scoped_enum::INVALID;
m_lua->register_class<Has_scoped_enum>();
m_lua->run_chunk("return function(obj) obj:set_e(Has_scoped_enum.VALID) end");
m_lua->call(-1, &instance);
CPPUNIT_ASSERT_EQUAL(static_cast<int>(Has_scoped_enum::scoped_enum::VALID), static_cast<int>(instance.e));
}

Exposing Member Functions

Minimalist
Generates a proxy function using the only the minimal amount of information which is generally the name of the thing being proxied and possibly a new name for the proxy. If a new name is supplied then the function will be made available to Lua using it and this name must be used when Exporting the function.

This part of the DSL attempts to automatically determine the parameter types and return type for the function in question. However, if the function is overloaded then the compiler will be unable to resolve the function, due to the ambiguity, and will produce a compile time error. To help the compiler resolve this ambiguity, the user should specify more information using the corresponding, yet longer named, Expressive DSL entry.
The longer DSL name requires more information.

Note
No Traits can be expressed with this DSL group.

Expressive
Generates a function for which the user has expressed all the parameters and the return type for a function. These types may also have Traits applied to them which the Minimalist section of the DSL does not allow.

OOLUA_MFUNC(FunctionName, Optional)

Parameters
FunctionNameName of the member function to be proxied
OptionalProxyFunctionName. Defaults to FunctionName
See Also
cpp_params
OOLUA_MEM_FUNC
OOLUA_MEM_FUNC_RENAME

OOLUA_MFUNC_CONST(FunctionName, Optional)

Parameters
FunctionNameName of the constant function to be proxied
OptionalProxyFunctionName. Defaults to FunctionName
See Also
cpp_params
OOLUA_MEM_FUNC_CONST
OOLUA_MEM_FUNC_CONST_RENAME
//typedef the type of vector into the global namespace
//This is required as a vector has more than one template type
//and the commas in the template confuse a macro.
typedef std::vector<int> vector_int;
OOLUA_PROXY(vector_int)
//C++11 adds an overload
//OOLUA_MFUNC(push_back)
OOLUA_MEM_FUNC(void, push_back, class_::const_reference)
OOLUA_MFUNC(pop_back)
OOLUA_EXPORT_FUNCTIONS(vector_int, push_back, pop_back)

Abstract Class

Generating an abstract proxy requires that you specify the Abstract tag in the OOLUA_TAGS block. When OOLua encounters the Abstract tag it will not look for any constructors for the type and the type will not be constructable from Lua. Specifying an OOLUA_CTORS block will have no effect and such a block will be ignored.

class Abstract1
{
public:
virtual ~Abstract1(){}
virtual void func1()=0;
virtual void virtualVoidParam3Int(int, int, int) = 0;
};
OOLUA_PROXY(Abstract1)
OOLUA_TAGS(Abstract)
OOLUA_MFUNC(virtualVoidParam3Int)
OOLUA_MFUNC(func1)
OOLUA_EXPORT_FUNCTIONS(Abstract1, func1, virtualVoidParam3Int)

Base Classes

Using OOLUA_PROXY's optional parameter(s) enables the specifying of base class(es) for a proxy. OOLUA_PROXY(ClassName, Optional)

Parameters
ClassNameClass to be proxied
OptionalComma seperated list of real base classes
Precondition
Each class specified in Optional must be a real base class of ClassName
class Derived1Abstract1 : public Abstract1
{
public:
virtual ~Derived1Abstract1(){}
MOCK_METHOD0(func1, void());
MOCK_METHOD3(virtualVoidParam3Int, void(int, int, int));
};

The following snippets do not proxy or expose any of the functions from the base class as they are automatically made available for the derived class. This is true for all derived proxies which have a base proxy.

OOLUA_PROXY(Derived1Abstract1, Abstract1)
OOLUA_EXPORT_FUNCTIONS(Derived1Abstract1)
OOLUA_EXPORT_FUNCTIONS_CONST(Derived1Abstract1)

Operators

Operator Tags inform OOLua that a class exposes one or more of the operators supported:

class Class_ops
{
public:
Class_ops(int const & i):m_i(i){}
Class_ops():m_i(0){}
Class_ops(Class_ops const& rhs)
:m_i(rhs.m_i)
{}
int const& geti()const
{
return m_i;
}
bool operator == (Class_ops const& rhs)const
{
return m_i == rhs.m_i;
}
bool operator < (Class_ops const& rhs)const
{
return m_i < rhs.m_i;
}
bool operator <= (Class_ops const& rhs)const
{
return m_i <= rhs.m_i;
}
Class_ops operator + (Class_ops const& rhs)const
{
return Class_ops( m_i + rhs.m_i );
}
Class_ops operator * (Class_ops const& rhs)const
{
return Class_ops(m_i * rhs.m_i);
}
Class_ops operator - (Class_ops const& rhs)const
{
return Class_ops(m_i - rhs.m_i);
}
Class_ops operator / (Class_ops const& rhs)const
{
return Class_ops(m_i / rhs.m_i);
}
private:
int m_i;
};
OOLUA_PROXY(Class_ops)
Equal_op
, Less_op
, Less_equal_op
, Add_op
, Sub_op
, Mul_op
, Div_op
)

Public Members

Getting or setting a public member is achieved by a function which completes the operation. These functions must be exported like all other proxy functions, so that they are available to a Lua script.

OOLUA_MGET(PublicName, Optional)

Parameters
PublicNameName of the public variable to be proxied.
OptionalGetterName. Defaults to get_PublicName
Note
A generated getter for a pointer, or shared pointer, with a proxied pointee type, has an implicit OOLUA::maybe_null trait applied.

OOLUA_MSET(PublicName, Optional)

Parameters
PublicNameName of the public variable to be proxied.
OptionalSetterName. Defaults to set_PublicName

OOLUA_MGET_MSET(PublicName, Optional1, Optional2)

Parameters
PublicNameName of the public variable to be proxied.
Optional1GetterName. Defaults to get_PublicName
Optional2SetterName. Defaults to set_PublicName
See Also
OOLUA_MGET and OOLUA_MSET
Note
If one optional parameter is supplied then both must be given and they must use different names.
class Public_variables
{
public:
Public_variables();
~Public_variables();
int an_int;
int m_int;
int* int_ptr;
Stub1* dummy_instance;
Stub1 dummy_instance_none_ptr;
Stub1& dummy_ref;
Enums enum_instance_none_ptr;
static const int set_value = 1;
static const int initial_value = 0;
Public_variables(Public_variables const&);
Public_variables& operator = (Public_variables const&);
};
OOLUA_PROXY(Public_variables)
OOLUA_MGET_MSET(int_ptr, get_int_ptr, set_int_ptr)
OOLUA_MGET_MSET(dummy_instance)
OOLUA_MGET_MSET(dummy_ref)
OOLUA_MGET_MSET(dummy_instance_none_ptr)
OOLUA_MGET(m_int, get_int)
OOLUA_MGET(m_int)
OOLUA_MSET(m_int, set_int)
OOLUA_MSET(m_int)
OOLUA_MSET(enum_instance_none_ptr)
OOLUA_EXPORT_FUNCTIONS(Public_variables
, set_an_int
, set_int_ptr
, set_dummy_instance
, set_dummy_ref
, set_m_int
, set_int
, set_dummy_instance_none_ptr
, set_enum_instance_none_ptr)
, get_an_int
, get_int_ptr
, get_dummy_instance
, get_dummy_ref
, get_dummy_instance_none_ptr
, get_int
, get_m_int)

Public member access in Lua is via a member function

void getAnInt_publicVariablesClassPassedToLua_returnsSetValue()
{
m_class_with_public_vars->an_int = Public_variables::set_value;
m_lua->run_chunk("func = function(obj) return obj:get_an_int() end");
m_lua->call("func", m_class_with_public_vars);
int result;
OOLUA::pull(*m_lua, result);
CPPUNIT_ASSERT_EQUAL(Public_variables::set_value, result);
}

Static Functions

OOLUA_SFUNC(FunctionName, Optional)

Parameters
FunctionNameName of the static function to be proxied
OptionalProxyFunctionName. Defaults to FunctionName
Note
This function will not be exported and needs to be registered with OOLua see OOLUA::register_class_static
See Also
cpp_params
class ClassHasStaticFunction
{
public:
static void static_function(){}
static void static_function(int /*DontCare*/){}
static int returns_input(int t){return t;}
};
OOLUA_PROXY(ClassHasStaticFunction)
OOLUA_TAGS(No_public_constructors)
OOLUA_SFUNC(returns_input)
OOLUA_EXPORT_NO_FUNCTIONS(ClassHasStaticFunction)

When registering a static function that was exposed with OOLUA_SFUNC, the second parameter to the OOLUA::register_class_static function is the address of the proxy function. The parameter therefore needs to be a fully qualified static function for the specialised Proxy_class.

m_lua->register_class_static<ClassHasStaticFunction>("returns_input",
m_lua->run_chunk("foo = function(obj, input) "
"return obj.returns_input(input) "
"end ");

C Functions

Minimalist

We have already seen the Minimalist version in the Hello Moon example.
Deduce and generate a proxy for a C function.
OOLUA_CFUNC(FunctionName, ProxyFunctionName)

Parameters
FunctionNameName of the C function to be proxied
ProxyFunctionNameName of the function to generate which will proxy FunctionName
See Also
cpp_params
OOLUA_C_FUNCTION
void say(char const* input)
{
printf("%s from a standalone function\n", input);
}
OOLUA_CFUNC(say, l_say)
void hello_minimalist_function()
{
using namespace OOLUA; //NOLINT(build/namespaces)
Script vm;
set_global(vm, "say", l_say);
run_chunk(vm, "say('Hello Lua')");
}

Expressive

Generates a block which will call the C function FunctionName.
OOLUA_C_FUNCTION(FunctionReturnType,FunctionName, Optional)

Parameters
FunctionReturnType
FunctionName
OptionalComma separated list of function parameter types
See Also
cpp_params
Precondition
The function in which this macro is contained must declare a lua_State pointer which can be identified by the name "vm"
extern void foo(int);
int l_foo(lua_State* vm)
{
OOLUA_C_FUNCTION(void,foo,int)
}
Note
This macro should ideally be used as the last operation of a function body as control will return to the caller. Notice there is no return statement in l_foo

In the following example we have a C function which is overloaded, we can use the Expressive DSL here in which we supply the return and parameter types. The function will then be resolved to the correct overload.

void expressive_say(char const* input)
{
printf("%s from a expressive function\n", input);
}
void expressive_say(int input)
{
printf("Huh %d\n", input);
CPPUNIT_ASSERT(0);
}
int expressive_lsay(lua_State* vm)
{
OOLUA_C_FUNCTION(void, expressive_say, char const*)
}
void hello_expressive_function()
{
using namespace OOLUA; //NOLINT(build/namespaces)
Script vm;
set_global(vm, "say", expressive_lsay);
vm.run_chunk("say('Hello Lua')");
}

Overloaded Minimalist

You may have noticed that we did not apply any Traits for the Expressive C version, so maybe it would be nice if we could do it another way; well that all depends on what you consider nice! The function can not be resolved unless we give the compiler more information, but in this case it does not mean we have to use the Expressive DSL. We can instead cast the function pointer, note that a stand alone function name is a function pointer, to the wanted type and therefore resolve to the correct function overload whilst still using the Minimalist DSL

void expressive_say(char const* input)
{
printf("%s from a expressive function\n", input);
}
void expressive_say(int input)
{
printf("Huh %d\n", input);
CPPUNIT_ASSERT(0);
}
OOLUA_CFUNC( (( void(*)(char const*))expressive_say), cast_expressive_say)
void hello_cast_minimalist_function()
{
using namespace OOLUA; //NOLINT(build/namespaces)
Script vm;
set_global(vm, "say", cast_expressive_say);
vm.run_chunk("say('Hello Lua, we are a cast function not')");
}

Traits

Provides direction and/or ownership information.

The general naming conventions for traits are :

Parameter Traits

DSL Traits for function parameter types.

Traits which allow control of ownership include in their name either "lua" or "cpp"; directional traits contain "in", "out" or a combination.

in_p

The calling Lua procedure supplies the parameter to the proxied function. No change of ownership occurs.

Note
This is the default trait used for function parameters when no trait is supplied.

Member Function:

virtual void refPtrConst(ParamType const* & instance) = 0;

Proxy Function:

OOLUA_MFUNC(refPtrConst)

Usage:

void inTraitConst_refPtrConst_calledOnceWithCorrectValue()
{
InHelper helper(m_lua);
EXPECT_CALL(helper.mock, refPtrConst(::testing::Eq(helper.inputParam_ptrConst))).Times(1);
helper.run_method();
m_lua->call(1, helper.object, "refPtrConst", helper.inputParam_ptrConst);
}

out_p

The calling Lua procedure does not pass the parameter to the proxied function, instead one is created using the default constructor and passed to the proxied function. The result after the proxied call with be returned to the calling procedure. If this is a type which has a proxy then it will cause a heap allocation of the type, which Lua will own.

Member Function:

virtual void refPtr(ParamType*& instance) = 0;

Proxy Function:

OOLUA_MEM_FUNC_RENAME(outTraitRefPtr, void, refPtr, out_p<HasIntMember*&>)

Usage:

void OutTraitRefPtr_luaPassesNoParam_topOfStackIsOwnedByLua()
{
::testing::NiceMock<OutParamUserDataMock> stub;
m_lua->run_chunk("return function(obj) return obj:outTraitRefPtr() end");
m_lua->call(1, static_cast<OutParamUserData*>(&stub));
OOLUA::INTERNAL::Lua_ud * ud = static_cast<OOLUA::INTERNAL::Lua_ud *>(lua_touserdata(*m_lua, -1));
CPPUNIT_ASSERT_EQUAL(true, OOLUA::INTERNAL::userdata_is_to_be_gced(ud));
}

in_out_p

The calling Lua procedure supplies the parameter to the proxied function, the value of the parameter after the proxied call will be passed back to the calling procedure as a return value. No change of ownership occurs.

Member Function:

virtual void ref(ParamType& instance)=0;

Proxy Function:

OOLUA_MEM_FUNC(void, ref, in_out_p<int&>)

Usage:

void inOutTraitRef_luaPassesIntCppAssignsNewValue_returnIsNewlyAssignedValue()
{
InOutParamHelper helper(m_lua);
EXPECT_CALL(helper.mock, ref(::testing::_)).Times(1).WillOnce(::testing::SetArgReferee<0>(helper.expected));
m_lua->run_chunk("return function(object) return object:ref(1) end");
m_lua->call(1, helper.object);
assert_top_of_stack_is_expected_value(helper.expected);
}

lua_out_p

Lua code does not pass an instance to the C++ function, yet the pushed back value after the function call will be owned by Lua. This is meaningful only if called with a type which has a proxy and it is by reference, otherwise undefined.

Member Function:

virtual void refPtr(ParamType*& instance) = 0;

Proxy Function:

OOLUA_MEM_FUNC_RENAME(lua_takes_ownership_of_ref_2_ptr
, void, refPtr, lua_out_p<Stub1*&>)

Usage:

m_lua->register_class<OwnershipParamUserData>();
m_lua->run_chunk("return function(object) return object:lua_takes_ownership_of_ref_2_ptr() end");
m_lua->call(1, object);
//there is now a proxy type on top of the stack which Lua owns

cpp_in_p

Parameter supplied via Lua changes ownership to C++.

Member Function:

virtual void ptr(ParamType* instance) = 0;

Proxy Function:

OOLUA_MEM_FUNC_RENAME(cpp_takes_ownership_of_ptr_param
, void, ptr, cpp_in_p<Stub1*>)

Usage:

void cppInP_ptr2UserDataType_passingPtrThatLuaOwns_topOfStackGcIsFalse()
{
bool result = returnGarbageCollectValueAfterCppTakingOwnership(
"cpp_takes_ownership_of_ptr_param");
CPPUNIT_ASSERT_EQUAL(false, result);
}

light_p

The calling Lua procedure supplies a LUA_TLIGHTUSERDATA which will be cast to the requested T type. If T is not the correct type for the light userdata then the casting is undefined. A light userdata is never owned by Lua

Member Function:

void value(void* void_ptr);

Proxy Function:

OOLUA_MEM_FUNC(void, value, light_p<void*>)

Usage:

void functionParam_functionWhichTakesVoidPointer_functionIsCalledWithTheCorrectValue()
{
LightParamUserDataMock mock;
LightParamUserData* object = &mock;
m_lua->register_class<LightParamUserData>();
int i(0);
void* input_ud = &i;
EXPECT_CALL(mock, value(::testing::Eq(input_ud))).Times(1);
m_lua->run_chunk("return function(object,param) return object:value(param) end");
m_lua->call(1, object, input_ud);
}

or

Member Function:

void ptr(InvalidStub* data);

Proxy Function:

OOLUA_MEM_FUNC(void, ptr, light_p<InvalidStub*>)

Usage:

void functionParam_functionWhichTakesNoneVoidPointer_functionIsCalledWithTheCorrectValue()
{
LightNoneVoidParamUserDataMock mock;
LightNoneVoidParamUserData* object = &mock;
m_lua->register_class<LightNoneVoidParamUserData>();
InvalidStub lightud;
void* lightud_ptr = &lightud;
EXPECT_CALL(mock, ptr(::testing::Eq(lightud_ptr))).Times(1);
m_lua->run_chunk("return function(object,param) return object:ptr(param) end");
m_lua->call(1, object, lightud_ptr);
}

calling_lua_state

This is different from all other traits as it does not take a type, yet is a type. It informs OOLua that the calling state is a parameter for a function

Member Function:

virtual void value(ParamType instance) = 0;

Proxy Function:

OOLUA_MEM_FUNC(void, value, calling_lua_state)

Usage:

void callingLuaState_luaPassesNoParameterYetFunctionWantsALuaInstance_calledOnceWithCorrectInstance()
{
LuaStateParamMock mock;
lua_State* vm = *m_lua;
EXPECT_CALL(mock, value(::testing::Eq(vm))).Times(1);
m_lua->register_class<LuaStateParam>();
m_lua->run_chunk("return function(object) object:value() end");
m_lua->call(1, static_cast<LuaStateParam*>(&mock));
}

Function Return Traits

DSL traits for function return types.

Some of the these traits allow for NULL pointers to be returned from functions, which was something commonly requested for the library. When such a trait is used and the runtime value is NULL, Lua's value of nil will be pushed to the stack.

lua_return

The type returned from the function is a heap allocated instance whose ownership will be controlled by Lua. This is only valid for function return types.

Member Function:

virtual ReturnType* ptr() = 0;

Proxy Function:

OOLUA_MEM_FUNC(lua_return<Stub1*>, ptr)

Usage:

void luaReturnTrait_callsMethodPtr_returnValueIsToBeGarbageCollected()
{
ReturnTraitHelper helper(m_lua);
EXPECT_CALL(helper.mock, ptr()).Times(1).WillOnce(::testing::Return(&helper.return_stub));
helper.call_object_method("ptr");
assert_that_tops_gc_flag_is(true);
set_tops_gc_flag_to(false);
}

maybe_null

The type returned from the function is a pointer instance whose runtime value maybe NULL. If it is NULL then lua_pushnil will be called else the pointer will be pushed as normal. No change of ownership will occur for the type. This is only valid for function return types.

Note
To be consistent in naming this should really be called maybe_null_return, however I feel this would be too long a name for the trait so "return" has been dropped.

Member Function:

virtual ReturnType * const constPtr() = 0;

Proxy Function:

OOLUA_MEM_FUNC(maybe_null<Stub1*>, ptr)

Usage:

void maybeNullTrait_callsMethodConstPtrWhichReturnsNull_stackTopIsNil()
{
MaybeNullTraitHelper helper(m_lua);
EXPECT_CALL(helper.mock, constPtr()).Times(1).WillOnce(::testing::Return(static_cast<Stub1 *const>(NULL)));
helper.call_object_method("constPtr");
CPPUNIT_ASSERT_EQUAL(LUA_TNIL, lua_type(*m_lua, -1));
}

maybe_null and lua_return

The maybe_null and lua_return traits can be combined for a function return type. If the instance is non NULL then this combination provides the behaviour of the lua_return trait. On the other hand, when the instance is NULL it will provide the behaviour of the maybe_null trait.

Member Function:

virtual ReturnType* ptr() = 0;

Proxy Function:

OOLUA_MEM_FUNC(maybe_null<lua_return<Stub1*> >, ptr)

Usage:

void luaMaybeNullTrait_callsMethodPtrWhichReturnsValidPtr_stackTopGcValueIsTrue()
{
LuaMaybeNullTraitHelper helper(m_lua);
EXPECT_CALL(helper.mock, ptr()).Times(1).WillOnce(::testing::Return(&helper.return_stub));
helper.call_object_method("ptr");
assert_that_tops_gc_flag_is(true);
set_tops_gc_flag_to(false);
}

light_return

The type returned from the function is either a void pointer or a pointer to another type. When the function returns, it will push a LUA_TLIGHTUSERDATA to the stack even when the pointer is NULL; therefore a NULL pointer using this traits is never converted to a Lua nil value. A light userdata is also never owned by Lua and OOLua does not store any type information for the it; light_return is a black box which when used incorrectly will invoke undefined behaviour.
This is only valid for function return types.

Void pointer:

OOLUA_MEM_FUNC(light_return<void*>, value)

Non void pointer:

OOLUA_MEM_FUNC(light_return<InvalidStub*>, ptr)

Stack Traits

Public API traits which control a change of ownership.

Valid to usage for the Public API which interact with the Lua stack.

cpp_acquire_ptr

Informs the library that C++ will take control of the pointer being used and call delete on it when appropriate. This is only valid for public API functions which OOLUA::pull from the stack.

m_lua->run_chunk("return Stub1.new()");
OOLUA::pull(*m_lua, res);
CPPUNIT_ASSERT_EQUAL(true, res.m_ptr != 0);
delete res.m_ptr;

lua_acquire_ptr

Informs the library that Lua will take control of the pointer being used and call delete on it when appropriate. This is only valid for public API functions which OOLUA::push to the stack.

void callFunction_passingPointerUsingLuaAcquirePtr_topOfStackGcIsTrue()
{
Stub1 stub;
m_lua->run_chunk("foo = function(param) return param end");
m_lua->call("foo", OOLUA::lua_acquire_ptr<Stub1*>(&stub));
OOLUA::INTERNAL::Lua_ud * ud = get_ud_helper();
bool gc_value = OOLUA::INTERNAL::userdata_is_to_be_gced(ud);
OOLUA::INTERNAL::userdata_gc_value(ud, false);
CPPUNIT_ASSERT_EQUAL(true, gc_value);
}
Note
Here we use the public API function OOLUA::Script::call which uses OOLUA::push

Return Order

Lua supports multiple return values for functions ( return = [explist] ). The order of returns in the stack is shown in the following example, simply the first will be pushed to the top of the stack, then the second to the top. This continues until all returns have been pushed on to the stack and the final return is located at the top.

void luaReturnOrder_luaFunctionWhichReturnsMultipleValuesToCpp_orderFromTopOfStackIsInput2Input1()
{
m_lua->run_chunk("return function(input1, input2) return input1, input2 end ");
int input1(1);
int input2(2);
m_lua->call(1, input1, input2);
/*
========
| input2 | <-- stack top
========
| input1 |
========
| ... |
*/
int topOfStack, nextSlot;
OOLUA::pull(*m_lua, topOfStack);
OOLUA::pull(*m_lua, nextSlot);
CPPUNIT_ASSERT_EQUAL(input2, topOfStack);
CPPUNIT_ASSERT_EQUAL(input1, nextSlot);
}

C++ in a way also supports multiple returns via references. Here we have a C++ member function which returns an int, the function also assigns a new value to the parameter which is taken by reference.

{
enum {returnValue=-1, paramValue};
int foo(int& bar)
{
bar = paramValue;
return returnValue;
}
};

In effect this function has two return values so one way we could proxy the function and detail that information would be using the Expressive DSL macro OOLUA_MEM_FUNC and applying an in_out_p trait to the parameter.

After calling this function there will be two returned values; the return of the C++ function and the value of the parameter after the call. The top of stack will contain the furthest right handside parameter which had an out trait, which in this case there was only one, below this will be proceeding parameters which had out traits and then the return value in that order.

void ordering_functionWhichReturnsValueAndTwoInOutParams_orderFromTopOfStackIsParam2Param1Return()
{
int input1(OutParamsTest::Dummy);
int input2(OutParamsTest::Dummy);
run_chunk_function_push_two_ints("return_int_and_2_int_refs", input1, input2, true);
::testing::NiceMock<MockOutParamsTest> stub;
m_lua->call("func", static_cast<OutParamsTest*>(&stub));
int r1, r2, r3;
OOLUA::pull(*m_lua, r1);//top of stack
OOLUA::pull(*m_lua, r2);
OOLUA::pull(*m_lua, r3);
CPPUNIT_ASSERT_EQUAL(static_cast<int>(OutParamsTest::Param2), r1);
CPPUNIT_ASSERT_EQUAL(static_cast<int>(OutParamsTest::Param1), r2);
CPPUNIT_ASSERT_EQUAL(static_cast<int>(OutParamsTest::Return), r3);
}






















Are you a bottom up kind of person?
The return value is on the bottom of the stack (Lua stack index 1) with parameter one at index 2.