ug4
ug4-Registry

In order to make functionality (such as C++ classes, methods and functions) available in the Lua-Script or the VRL, this functionality must be registered by the C++ programmer. By this registration, the programmer indicates, that the functionality is exported. Once exported the functionality is accessible in Script and VRL simultaneously.

This page gives an introduction to the registration process.


How to register Functionality

Registering is done in the *.cpp files in the folder ugbase/ug_bridge/bridges. Here are found several so called bridges that map (bridge) the functionality of different parts of the software project to the script (or VRL).

To understand the usage of the ug::Registry, lets start with the simple and exemplary file test_bridges.cpp. Here, you find several simple test classes and functions, written in normal C++. This functionality is now exported.

The function, where the registration takes place is RegisterTestInterface.

bool RegisterTestInterface(Registry& reg, const char* parentGroup)
// get group string
std::stringstream groupString; groupString << parentGroup << "/Test";
std::string grp = groupString.str();
// now groupString is simply "ug4/Test"
// ...
}

This function gets passed the Registry to which all functionality is added. In addition a Group string is added, that can be used to group functionality together. Usually all separated parts (e.g. Grid, Algebra, ...) are grouped, but this is not essential for the registration. (Therefore, lets not stress this point here)


How to register Global Functions

Now, we come to the point to register a global C++-Function. We start with a very simple example, the famous Hello World. You will find in the file a global Function PrintHelloWorldToScreen:

// prints "Hello World"
{
UG_LOG("Hello World !\n");
}
void PrintHelloWorldToScreen()
Definition: test_bridge.cpp:60
#define UG_LOG(msg)
Definition: log.h:367

In order to export this global function, you must use the syntax

reg.add_function( "NameAppearingInScript", Pointer to Function, ... )

For the Hello World function this reads:

// registering hello world
reg.add_function("PrintHelloWorld", &PrintHelloWorldToScreen);

Now, you can call this function in the Script. So, start the ug shell in your terminal:

user$ ./ugshell 
********************************************************************************
* ugshell - v4.0.1                                                             *
*                                                                              *
* arguments:                                                                   *
*   -outproc id:         Sets the output-proc to id. Default is 0.             *
*   -ex scriptname:      Executes the specified script.                        *
*   -noquit:             Runs the interactive shell after specified script.    *
*   -noterm:             Terminal logging will be disabled.                    *
*   -logtofile filename: Output will be written to the specified file.         *
* Additional parameters are passed to the script through ugargc and ugargv.    *
********************************************************************************
ug:> 

Now, just type the function:

ug:> PrintHelloWorld()
Hello World !
ug:> 

How to register Classes and Methods

C++-Classes and their methods are registered similarly. Lets focus on the example class "Test":

class Test
{
public:
int add(int a, int b)
{
return a+b;
}
int print_name()
{
UG_LOG("Name is Test\n");
return 1;
}
int print() {UG_LOG("Test::print()\n"); return 0;}
int print() const {UG_LOG("Test::print() const\n"); return 1;}
};
function table print(data, style)

This class is registered by

// register class "Test"
reg.add_class_<Test>("Test", grp)
.add_constructor()
.add_method("add", &Test::add, "c", "a#b")
.add_method("print_name", &Test::print_name);

The following is done here:

  • In add_class_< Class > the exported class is specified
  • By Test is specified the name of the class used in the script
  • By .add_constructor() is specified, that the constructor is available in the script.
  • By .add_method("MethodNameInSkript", Pointer to method) a method is exported.

Now, you can create an object of this class from script using the ()-Operator:

ug:> myVariable = Test()

Calling a method of the class is done by using the :-Operator

ug:> myVariable:print_name()
Name is Test

How to specify additional information

As shown in the last example, the .add_method() method allows to specify additional parameter related information. This is also true for add_function and add_class:

reg.add_function( "NameAppearingInScript", PointerToFunction, "group", "returnInformation", "paramInformation1#paramInformation2#...", "tooltip", "help" )
reg.add_class_<ClassName>("ClassName", grp, "tooltip")
.add_method("method_name", &ClassName::method_name, "returnInformation", "paramInformation1#paramInformation2#...", "tooltip", "help")

so the above test class registration should be extended to

reg.add_class_<Test>("Test", grp, "Test Class for demonstration")
.add_constructor()
.add_method("add", &Test::add, "a+b", "a#b", "adds two numbers")
.add_method("print_name", &Test::print_name, "", "", "prints the name");

notice how we use "" for empty strings like when there's no return value or parameters.

The syntax for the parameter information for N parameters is paramName1#paramName2#paramName3#...#paramNameN", so you have to seperate the parameters with the hash sign # (see below for additional parameter information).

The additional information you specify can be accessed in various ways:

  • The ug4 registry docu. See here for a nice example.
  • Autocompletion and hover information in the UGIDE.
  • In the VRL
  • In the ugshell with e.g. CG? or TypeInfo("CG") (also in LUA scripts)

Supported and not supported types

Supported Basic Types BT are :

bool, int, size_t, float, double, const char *, std::string

UT = User Defined Types.
pUT = Set consisting of T*, const T*, SmartPtr<T>, ConstSmartPtr<T> of User Defined Types T in UT.

Then the supported types for parameters and return values are

  • all types T in BT or pUT,
  • std::vector<T>, const std::vector<T> of all T in BT or pUT,
Note
char is not a supported type. use int instead.
std::vector<T>& is not supported

How to specify VRL information for parameters

VRL uses a so called style identifier to choose between several possible type representations. As noted above, the parameters are seperated by the hash sign #. Additional options allow to specify range conditions, default values etc.


Syntax

Parameter properties can be specified as follows:

name | style | options
location name
Definition: checkpoint_util.lua:128

The short form name is also valid if no additional option shall be specified.

An example for return value properties: c|default|min=1;max=10.

For the parameter string an additional separator is used to split the individual parameter properties. Parameters can be separated as follows (seperating two parameters):

name1 | style1 | options1 # name2 | style2 | options2

Available styles and options

Name

For the name it is safe to specify ASCII strings. Roughly speaking, it is safe to specify strings consisting of letters from the latin alphabet, numbers and characters from the following list: ^!"ยง$%&/()=?*,.-;:_<>@ (list may be not complete).

VRL supports simple HTML syntax (most tags from HTML 3.x are supported). Thus, it is possible to request bold or underlined names. Using colors and other enhanced stylings should not be used as this will most likely conflict with the VRL interface styles because in this case VRL cannot automatically change the text color accordingly.

Note
Special characters and control characters, such as \n are not supported. The characters | and # must not be used either.

Style

VRL uses a so called style identifier to choose between type representations. The most relevant type representations for UG are listed below (more details can be found in the VRL API documentation):


Type representations for the number type:

  • style identifier: default
  • possible options: min, max, value
  • example string: Double|default|min=1D;max=100D;value=50D

Type representations for the integer type:

  • style identifier: default
  • possible options: min, max, value
  • example string: Integer|default|min=1;max=100;value=50

  • style identifier: slider
  • possible options: min, max
  • example string: Integer|slider|min=1;max=255

  • style identifier: selection
  • possible options: min, max
  • example string: Integer|selection|value=[1,2,3]

Type representations for the string type:

  • style identifier: default
  • possible options: - none -
  • example string: String|default

  • style identifier: load-dialog or save-dialog
  • possible options: endings, description
  • example string: "File Name|load-dialog|endings=[\"png\",\"jpg\"];description=\"Image-Files\"



  • style identifier: selection
  • possible options: value
  • example string: String|selection|value=["string1\",\"string2\"]

How to register Base Classes

Class hierarchies can also be exported to the Script. Please note, that currently only one base class is supported (i.e. multiply derivation is not possible in the registry). In order to register a base class, use the usual C++-syntax:

class Base
class Derived : public Base
{
public:
virtual ~Derived() {}
virtual void print()
{
UG_LOG("Derived::print() called\n");
}
};

Now, in registration, add the base class without constructor:

// registering base class (without constructor)
reg.add_class_<Base>("Base", grp)
.add_method("print", &Base::print);

When registering the Derived class, add also the Base class as a second template argument.

// registering derived class
reg.add_class_<Derived, Base>("Derived", grp)
.add_constructor();

How to register Overloaded Functions

When function is overloaded, the compiler can not find the function you want to register by only passing the method pointer. You have to explicitly specify the method signature to choose between the possibilities:

int MyGlobalFunction(int parameter1, int parameter2);
bool MyGlobalFunction();
// register MyGlobalFunction overloads
reg.add_function("MyGlobalFunction", static_cast<int (*)(int parameter1, int parameter2)>(&MyGlobalFunction));
reg.add_function("MyGlobalFunction", static_cast<bool (*)()>(&MyGlobalFunction));

How to register Overloaded Methods

class Test
{
public:
// ... something ...
// overloaded functions
int print() {UG_LOG("Test::print()\n"); return 0;}
int print() const {UG_LOG("Test::print() const\n"); return 1;}
};
// register class "Test"
reg.add_class_<Test>("Test", grp)
.add_method("print", static_cast<int(Test::*)()>(&Test::print))
.add_method("print", static_cast<int(Test::*)() const>(&Test::print));

How to register Overloaded Constructors

Constructors with parameters and several (overloaded) Constructors are also possible. Since it is impossible to take the function pointer of a constructor the special method add_constructor<SignatureFunction>(...) must be used.

class Test
{
public:
Test() { UG_LOG("Test::Test() constructor used.\n")}
Test(const char* msg)
{
UG_LOG("Test::Test(const char*) constructor used.\n")
UG_LOG("Message is: '"<<msg<<"'.\n");
}
};
// register class "Test"
reg.add_class_<Test>("Test", grp)
.add_constructor()
.template add_constructor<void (*)(const char*)>("Message");

The template argument of the add_constructor method must be a global function pointer with void return value. The signature of the function pointer type is used to get the arguments of the constructor. In total these informations can be passed when registering a constructor:

template <typename TFunc>
ExportedClass<TClass>& add_constructor(std::string paramInfos = "",
std::string tooltip = "", std::string help = "",
std::string options = "")
  • paramInfos — a list of parameter names (and options) separated by '#' (see above)
  • tooltip — some information about the usage of the constructor
  • help — help informations
  • options — style option of the constructor itself (for visualisation)