scriptingAuto.md 13 KB

Exposing code to script API (automated) {#scriptingAuto}

[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.

Exporting classes {#scriptingAuto_a}

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:

  • It CAN be used on non-templated classes or structs, as well as Resource%s and Component%s
  • It CANNOT be used on templated classes or structs
  • It CAN be used on public constructors and non-templated methods
  • It CANNOT be used on destructors, operators, templated methods or private/protected methods of any kind

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:

  • Resource%s and Component%s must be passed as handles (e.g. HMesh, HRenderable)
  • Other classes must be passed as shared pointers (e.g. SPtr)
  • Plain types such as int, float or types we'll describe below can be passed directly (e.g. int)
  • If a parameter is used as input it is beneficial to decorate it as a constant reference or a pointer (e.g. const HMesh&)
  • If a parameter is used as output it is beneficial to decorate it as a non-constant reference or a pointer (e.g. HMesh&)
  • 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
    };
    

    Renaming {#scriptingAuto_a_a}

    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() { ... }
    }
    

    Visibility {#scriptingAuto_a_b}

    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() { ... }
    }
    

    Exporting as properties {#scriptingAuto_a_c}

    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:

    • Getter method must return a non-void value, and have no parameters
    • Setter method must not have a return value and a single parameter
    • Getter return value and setter parameter types must match

    Extending script interface further {#scriptingAuto_a_d}

    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.

    Exporting structures {#scriptingAuto_b}

    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.

    Exporting enums {#scriptingAuto_c}

    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.

    Excluding enum entries {#scriptingAuto_c_a}

    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,
    }
    

    Renaming enum entries {#scriptingAuto_c_b}

    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
    }
    

    Exporting comments {#scriptingAuto_d}

    All Javadoc-type comments on exported types and methods will automatically be parsed, converted to XML documentation format and exported to script code.

    External methods {#scriptingAuto_e}

    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:

    • They must be part of a class that is also exported and marked with the "e" parameter
    • They must be static
    • External constructors must return a value of the type they're external to
    • External methods must accept the type they're external to as the first parameter

    Running the code generator {#scriptingAuto_f}

    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.