[TOC]
When you've added a new feature, system or just extended existing ones you might want to expose that functionality to the scripting API. Banshee makes this process easier through its automated script binding generator tool. All you need to do is to decorate the C++ types and methods you wish to export and run the tool. The tool is ran automatically whenever you build through CMake.
In order to decorate C++ you use the BS_SCRIPT_EXPORT macro, which supports a variety of parameters used for customizing how will the type/method be exported.
In order to export a class to script code you need to decorate the class and one or multiple methods with BS_SCRIPT_EXPORT macro.
// Decorate the class and the methods with BS_SCRIPT_EXPORT modifier
class BS_SCRIPT_EXPORT() MyClass
{
public:
BS_SCRIPT_EXPORT()
MyClass(); // Constructor
BS_SCRIPT_EXPORT()
UINT32 getSomeData();
};
The example above results in the following script interface (C#):
public partial class MyClass : ScriptObject
{
public MyClass() { ... }
public int getSomeData() { ... }
}
There are a few important rules when using BS_SCRIPT_EXPORT:
If you do not respect these rules the code generation will either fail, or the generated code will be invalid and will fail to compile.
There are also limitations on the types of parameters and return values the exported methods are allowed to have:
If a parameter is an array, it must be of type Vector (e.g. Vector or Vector>)
class BS_SCRIPT_EXPORT() MyOtherClass
{
...
};
// Examples of some valid exported methods
class BS_SCRIPT_EXPORT() MyClass
{
public:
BS_SCRIPT_EXPORT()
MyClass(); // Constructor
BS_SCRIPT_EXPORT()
UINT32 getPlainType();
BS_SCRIPT_EXPORT()
void getPlainTypeAsParam(UINT32& output);
BS_SCRIPT_EXPORT()
void setPlainType(UINT32 value);
BS_SCRIPT_EXPORT()
std::string getString(); // or std::wstring
BS_SCRIPT_EXPORT()
void setString(const std::string& value); // or std::wstring
BS_SCRIPT_EXPORT()
HMesh getResource();
BS_SCRIPT_EXPORT()
void setResource(const HMesh& mesh);
BS_SCRIPT_EXPORT()
HRenderable getComponent();
BS_SCRIPT_EXPORT()
void setComponent(const HRenderable& renderable);
BS_SCRIPT_EXPORT()
SPtr<MyOtherClass> getNormalObject();
BS_SCRIPT_EXPORT()
void setNormalObject(const SPtr<MyOtherClass>& value);
BS_SCRIPT_EXPORT()
Vector<HMesh> getArray(); // HMesh could have also been a normal class or a component
BS_SCRIPT_EXPORT()
void setArray(const Vector<HMesh>& value); // HMesh could have also been a normal class or a component
};
BS_SCRIPT_EXPORT accepts a variety of comma separated parameters in the format "param1:value1,param2:value2". Parameter named "n" allows you to specify a different name for a type or a method, so when exported it uses the specified name rather than the same name as in C++.
class BS_SCRIPT_EXPORT(n:MyRenamedClass) MyClass
{
public:
BS_SCRIPT_EXPORT(n:GetSomeData)
UINT32 getSomeData();
};
The example above results in the following script interface (C#):
public partial class MyRenamedClass : ScriptObject
{
public int GetSomeData() { ... }
}
You can make a type or a method public, internal or private by specifying the "v" parameter. Accepted values are "public", "internal" and "private. By default all types and methods are public.
class BS_SCRIPT_EXPORT() MyClass
{
public:
// Exported as a private method
BS_SCRIPT_EXPORT(n:GetSomeData,v:private)
UINT32 getSomeData();
};
The example above results in the following script interface (C#):
public partial class MyRenamedClass : ScriptObject
{
private int GetSomeData() { ... }
}
Parameter named "pr" allows you to specify that a method should be exported as a property. The supported values for the parameter are "getter" or "setter". When exposing a method as a property the name ("n") parameter is required and ii should be the name of the property.
// Decorate the class and the methods with BS_SCRIPT_EXPORT modifier
class BS_SCRIPT_EXPORT() MyClass
{
public:
BS_SCRIPT_EXPORT(pr:getter,n:SomeData)
UINT32 getSomeData();
BS_SCRIPT_EXPORT(pr:setter,n:SomeData)
void setSomeData(UINT32 value);
};
The example above results in the following script interface (C#):
public partial class MyClass : ScriptObject
{
public int SomeData
{
get { ... }
set { ... }
}
}
You are allowed to provide only getter, only setter, or both. Providing multiple getters or setters for the propery with the same name results in undefined behaviour.
Getter/setter methods must follow a specific template otherwise they will be ignored during generation:
Sometimes automatic code generation just isn't good enough. For that reason all exported C# classes are marked with the partial keyword, meaning you can extend their interface with manually written code in a separate file, as required.
A data type can be exported as a C# struct by using the "pl" parameter, accepting values "true" or "false" (default being false). When exported all of the fields of the data type will be exported as a C# struct. Any constructors will also be exported, but no other methods. This is meant to be used on simple types that will be used for passing data around. Such types are passed by value and will be copied when crossing the C++/C# boundary.
struct BS_SCRIPT_EXPORT(pl:true) Volume
{
Volume()
: left(0), top(0), right(1), bottom(1), front(0), back(1)
{ }
Volume(UINT32 left, UINT32 top, UINT32 right, UINT32 bottom):
left(left), top(top), right(right), bottom(bottom), front(0), back(1)
{ }
Volume(UINT32 left, UINT32 top, UINT32 front, UINT32 right, UINT32 bottom, UINT32 back):
left(left), top(top), right(right), bottom(bottom), front(front), back(back)
{ }
UINT32 left, top, right, bottom, front, back;
};
The example above results in the following script code (C#):
public partial struct Volume
{
// C# doesn't support parameterless struct constructors, and therefore a static Default() method is generated instead
public static Volume Default()
{
Volume value = new Volume();
value.left = 0;
value.top = 0;
value.right = 1;
value.bottom = 1;
value.front = 0;
value.back = 1;
return value;
}
public Volume(int left, int top, int right, int bottom)
{
this.left = left;
this.top = top;
this.right = right;
this.bottom = bottom;
this.front = 0;
this.back = 1;
}
public Volume(int left, int top, int front, int right, int bottom, int back)
{
this.left = left;
this.top = top;
this.right = right;
this.bottom = bottom;
this.front = front;
this.back = back;
}
public int left;
public int top;
public int right;
public int bottom;
public int front;
public int back;
}
Note when generating constructors the system is only able to parse class member initializers and constructor initializers defined in the header file and will ignore any in the .cpp file.
Structs support rename & visibility parameters same as normal class export.
Enums can be exported with no additional parameters, just by specifying BS_SCRIPT_EXPORT.
enum BS_SCRIPT_EXPORT() class MyEnum
{
Value1 = 1,
Value2 = 10,
Value3 = 100
};
The example above results in the following script code (C#):
public enum MyEnum
{
Value1 = 1,
Value2 = 10,
Value3 = 100
}
Enums support rename & visibility parameters same as normal class and struct export.
By default when exporting enums all of their entries will be exported. You can ignore a certain enum entry by using the "ex" parameter. Supported values are "true" or "false".
// Exclude the third enum entry from script code
enum BS_SCRIPT_EXPORT() class MyEnum
{
Value1 = 1,
Value2 = 10,
Value3 BS_SCRIPT_EXPORT(ex:true) = 100
};
The example above results in the following script code (C#):
public enum MyEnum
{
Value1 = 1,
Value2 = 10,
}
Individual enum entries can also be renamed using the "n" parameter.
enum BS_SCRIPT_EXPORT() MyEnum
{
ME_VAL1 BS_SCRIPT_EXPORT(n:Value1) = 1,
ME_VAL2 BS_SCRIPT_EXPORT(n:Value2) = 10,
ME_VAL3 BS_SCRIPT_EXPORT(n:Value3) = 100
};
The example above results in the following script code (C#):
public enum MyEnum
{
Value1 = 1,
Value2 = 10,
Value3 = 100
}
All Javadoc-type comments on exported types and methods will automatically be parsed, converted to XML documentation format and exported to script code.
Sometimes the C++ interface just isn't suitable for export to script code as-is. Sometimes you want to make the script code more streamlined and higher level, without modifying the existing C++ interface. Other times the method parameters or return values don't fit the requirements we stated above.
External methods allow you to extend functionality of some class, Resource or a Component by defining static methods which are then exported as if they were part of the original class. Note these types of methods are not relevant for struct or enum export.
Use the "e" parameter to mark a method as external. Value of the parameter should be the name of the class it is extending. Use the "ec" parameter to mark a constructor as external. Value of the parameter should be the name of the class it is extending.
struct MY_CLASS_DESC
{
int val1;
float val2;
}
// Some class we're exporting normally
class BS_SCRIPT_EXPORT() MyClass
{
public:
MyClass(const MY_CLASS_DESC& desc);
UINT32* getArrayData(UINT32& count) const;
};
// Extension class for MyClass
class BS_SCRIPT_EXPORT(e:MyClass) MyClassEx
{
public:
// External constructor because we don't want to expose MY_CLASS_DESC to script code
BS_SCRIPT_EXPORT(ec:MyClass)
static SPtr<MyClass> create(int val1, float val2)
{
MY_CLASS_DESC desc;
desc.val1 = val1;
desc.val2 = val2;
return bs_shared_ptr_new<MyClass>(desc);
}
// External method because MyClass returns an array in raw form, but we need it in a Vector
BS_SCRIPT_EXPORT(e:MyClass)
static UINT32 getArrayData(const SPtr<MyClass>& thisPtr)
{
UINT32 numEntries;
UINT32* entries = thisPtr->getArrayData(numEntries);
Vector<UINT32> output;
for(UINT32 i = 0; i < numEntries; i++)
output.push_back(entries[i]);
return output;
}
};
The example above results in the following script interface (C#):
public partial class MyClass : ScriptObject
{
public MyClass(int val1, float val2) { ... }
public int[] getArrayData() { ... }
}
External methods must follow these rules:
Once you have decorated the C++ classes with necessary export parameters you can run the code generator simply by regenerating the CMake build. The generator will run automatically over all Banshee code and generate relevant script code.