PropXX-Tutorial



Inhaltsverzeichnis

PropXX-Tutorial 1

1 About this tutorial 1

2 Specifying a property set definition 1

2.1 Basic property set definitions 1

2.2 Using features of the Lua language 2

2.3 Supported property types 2

2.4 Advanced options for property definitions 3

2.5 Importing and including files 4

3 Editing property files 4

3.1 Basic structure of property files 4

3.2 Lua code in property files 5

4 Using C++ properties 5

4.1 Generating C++ property classes 5

4.2 Access to properties in C++ code 6

4.3 Imported property set definitions 6

4.4 Including PropXX in your C++ project 7

4.5 Patterns for using PropXX in your application 8

5 Using the property editor UI 8

6 Running Lua scripts 8

7 Extending PropXX 9

7.1 Defining new property types 9





1About this tutorial

This tutorial gives you a quick start with PropXX. We start an overview about the definition of property sets (Section 2) that define the structure of property sets and the property files (Section 3) that set the actual values of properties. Then you will see how to generate C++ code from a given property set definition and how to integrate this step into your build process (Section 4). Section 5 contains a summary of the features of the property editor which can be used if end-users shall be able to modify properties in a more restricted way than with a standard text editor. Section 6 explains how to use the Lua scripts that come with PropXX. Section 7 shows how you can extend PropXX and define your own types of properties.

The examples in this tutorial are included in the distribution of PropXX. You will find them in the test-directory (detailed references to individual files are given below).

2Specifying a property set definition

2.1Basic property set definitions

A property set definition defines the structure of a property set, i.e., the hierarchy of nested property subsets and types, defaults, etc. of the elementary properties.

You can specify a property set definition in a simple text file, typically with the suffix .propdef.

The examples in this section can be found in the PropXX-distribution in the file tst/tutorial*.propdef.

Example:

aPropertySubset = set {
  aNestedPropertySubset = set {
    elementaryIntegerProperty = int(2),
    elementaryStringProperty = string('Hello World'),
  },
  elementaryFloatProperty = bool(false)
}
anotherElementaryProperty = int(10)

This example defines a property set with the following properties:

aPropertySubset.aNestedPropertySubset.elementaryIntegerProperty
aPropertySubset.aNestedPropertySubset.elementaryStringProperty
aPropertySubSet.elementaryFloatProperty
anotherElementaryProperty

2.2Using features of the Lua language

Actually, a property set definition is nothing else than a couple of nested tables defined in Lua. The names of the the properties are given as key and the corresponding value specifies the type and the default value of the property.

Since the .propdef-file is interpreted as Lua-code, the full expressiveness of the Lua language is available:

Example:

-- a single-line Lua comment
--[[
  A
  multi-line
  Lua-comment
--]]

halfPi = math.pi / 2.0
tenTimesX = str.rep("X", 10)
multilineMessage = [[
A string that occupies
multiple lines
]]

2.3Supported property types

The example above shows that the type of the property can be left unspecified. In that case, the type is inferred from the Lua type of the value which is then interpreted as the default of the property. In the example above, tenTimesX is initialized with a Lua string and is hence defined as string property. Similarly, halfPi is initialized as float property. Please note that the standard string Lua-module can be accessed with 'str' instead of 'string'. This is necessary because string() is already used as identifier for string properties.

Currently, the following property types are supported:

Initializer in .propdef

Generated C++ type

Inferred from Lua type

Int

Int

Number (if the given number is integer)

Long

Long

Not automatically inferred

Float

Float

Number (if the given number is non-integer)

Double

Double

Not automatically inferred

String

Std::string

String

Bool

Bool

Boolean

2.4Advanced options for property definitions

So far, we have just seen the short and simple variant for defining properties. Actually, the property definition functions like 'string' or 'int' provide many more options. These options can be given as named parameters (or, technically speaking, in a Lua hash). Consider the following example:

dnaSequence = string{
  default = "GATA",
  validate = function(val)
               local ok = val:find("^[ACGT]+$")
               local msg = ok and "" or
                           "DNA sequence must only contain letters A, C, G, T"
               return ok, msg
             end,
  }

Here, a special validation function is given which ensures that the property 'dnaSequence' will only contain strings consisting of the four letters A, C, G and T. So far, the following named parameters are supported

Parameter

Type

Purpose

default

Lua type of the property

The default value of the property which is used if the property is not explicitly defined.

validate

Function:
validate(value)

Function which is called when a new value shall be assigned to the property. Must take the Lua representation of a property value as single argument and return a boolean value (false signals and invalid assignment). In case of an error, an optional error message may be returned as second result.

Convert

Function:

convert(value)

Function which takes a Lua representation of the property value and converts it before the value is validated and (possibly) assigned to the property.


This feature can be used, e.g., to normalize the representation of a property, for example by converting all letters in a string to lower-case if the property shall be non-case sensitive.

convertStr

Function:

convertStr(stringValue)

Function which takes a string representation of the property value and converts it before the value is validated and (possibly) assigned to the property. In this case, also the convert function is applied.


This feature is used if a property is edited not by reading a property file directly but, e.g., in a GUI where only strings can be entered by the user.



2.5Importing and including files

Property set definitions can be split into multiple files and combined through include- and import statements.

Command

Effect

include(<file name>)

Interprets the content of the given file in the context of the current file, i.e., as if the content of the given file were part of the current file.

import(<file name>)

Construct a property set definition from the content of the given file and return it. This property set definition can then be assigned to an element of the current property set definition.



This is best explained by an example. Consider the following property set definition:

include("tutorial2")
superSet = import("tutorial2")

and a 'main' property set definition with the following include and import statements:

includedSet = set {
  a = int(10),
  b = "Hello!"
}

This property set definition contains the following elementary properties:

includedSet.a
includedSet.b
superSet.includedSet.a
superSet.includedSet.b

The property definitions below 'superSet' are generated by the import statement. The other property definitions stem from the include statement.

It is important to note that during code generation an imported property set definition is handled differently than a direct definition in the same file (cf. Section 4).

The file name argument for include and import can be given with or without the suffix '.propdef' and is interpreted relative to the current working directory.

3Editing property files

3.1Basic structure of property files

Based on a property set definition, one or more property files can be created. Property files contain values settings that override the default values of some (or usually most) properties.

Property files basically follow the syntax of Java properties where elements of the full name of a property are separated by dots. Hence, a.b.c indicates the property c inside the set b which is in turn inside the set a.

Property values are assigned after an assignment operator '='. Hence, we get assignments like

someFloatProperty = 2.5e-10
a.b.someIntProperty = 10
a.someStringProperty = 'Hello, world!'

etc.

PropXX comes with a Lua script that reads a property set definition and one or more property files that are supposed to match this property set definition. Both the property set definition and the property files are parsed and interpreted, and errors are reported. This script can be used like that:

lua/checkprop.lua someDefinition.propdef propfile1.properties, propfile2.properties

For details on running Lua scripts, please refer to Section 6.

Property file names should end with the suffix '.properties'.

3.2Lua code in property files

From a technical perspective, a property file is again simply Lua code that is executed in a special environment. Consequently, Lua expressions can be used for more complex properties. Some examples:

two = 1+1
lineSeparator = string.rep(“-”, 70)
now = os.date()
three = two+1
pi = math.pi

Some care has to be taken when one property references another property (as 'three' references 'two' in the example above) as the property settings are processed sequentially. If the setting for property 'three' would be processed before property 'two', the default value for property 'two' would be used in the expression, which will typically not be the expected effect.

Property files can included other property files with the command include(<filename>). The given filename is resolved relative to the current working directory and can be given with or without the suffix '.properties'.

4Using C++ properties

4.1Generating C++ property classes

PropXX comes with the Lua script prop2cpp.lua (see Section 6 for further explanations how to run such scripts). It is used like this:

lua $PATH_TO_LUASCRIPTS/prop2cpp.lua <.propdef-files...>

For a given propdef file 'demo.propdef', prop2cpp.lua generates a header file Prop_demo.hpp and a cpp-file Prop_demo.cpp. These files contain the source code for a C++ class Prop_demo that holds a set of properties with the structure specified in the .propdef-file.

4.2Access to properties in C++ code

The nested structure of the property set is mapped to a nested C++ classes. Elementary properties are accessed through accessor functions with the name of the property. Again, this is best explained by an example. Consider an integer property a.b.c. Within the header file, you will find the following structure:

class Prop_a {
public:
...
	class Prop_b {
	public:
	...
		const int& c() const;
	...
	};
...
	const Prop_b& b() const;
...
};

Hence, if you have a reference to a property set of type Prop_a, the C++ syntax for accessing a property within this set is rather intuitive. For the given example we get

const Prop_a& a = getThePropsFromSomewhere();
cout << a.b().c()

4.3Imported property set definitions

If property set definitions are split into multiple files, the generated C++ code differs for included and imported definitions. Included files behave as if the definitions were part of the including main file. Hence, a single C++ class will be generated that contains both the code for the properties that are directly defined in the main property set and for properties that are contained in an included property set definitions.

Imported property set definitions, however, are mapped to separate C++ classes. The main C++ property classes only contains a pointer to the C++ property class for the imported file. The following example illustrates this:

Let's assume that the main property set is mapped to the class Prop_main and the included set to Prop_incl. Furthermore, this set shall be included twice into the main property set as subset b and c. Hence, if the included set contains a property x, the main set contains two properties main.b.x and main.c.x.

The generated C++ code for class Prop_main looks like this:

class Prop_incl;

class Prop_main {
public:
...
	const Prop_incl& b() const;
	const Prop_incl& c() const;
…
private:
	Prop_incl* _b;
	Prop_incl* _c;
};

During the build process it must be ensured that both the C++ code for the main set and for the included set are generated. Otherwise, the generated code will not compile because the C++ files for the referenced class Prop_incl do not exist.

Please note that the header for class Prop_main only contains a forward declaration to Prop_incl. Thus, there are no compile dependencies between the two classes. Changes to the content of Prop_incl will not trigger a recompile of Prop_main. Code that uses properties in Prop_main but in in Prop_incl can include only Prop_main.hpp and will not be recompiled if Prop_incl changes.

This design is crucial if PropXX shall be used for large projects where a main property set includes a couple of subsets, e.g., one subject for each major component of the project. The separation of the property set definitions ensures that no compile time dependencies are created between the property subsets.

Compare this to the code that would have been generated when the subset would have been included instead of imported in the example above:

class Prop_main {
public:
...
	class Prop_b {
...
	};
	class Prop_c {
...
	};

...
	const Prop_b& b() const;
	const Prop_c& c() const;
...
private:
	Prop_b _b;
	Prop_c _c;
};

Obviously, the better separation of imported subsets comes at a price. For included subsets the compiler has complete access to all definitions and can thus efficiently inline access to properties in subsets. For imported subsets, the accessor methods in a chain like a.b().c() have to be called explicitly and we incur, thus, a (small) runtime overhead.

4.4Including PropXX in your C++ project

PropXX contains a small C++ static library libpropxx.a. This library is included in the binary distribution or can easily be built and installed with the classic './configure; make; make install'. This library and liblua.a, which comes with Lua, need to be included in the build process of your C++ application.

If you want to create a property class, say, Prop_main (as in the example above), you simply need to include the generated header file Prop_main.hpp. Declaring a variable

Prop_main propMain;

will create a class that holds default values for all properties. In order to fill it with property values from a property file, you have to use a property loader. This can be done as follows:

LuaPropLoader loader("test.propdef", "test.properties", "propread.lua");
propMain.loadFrom(loader);

Obviously, the first and second argument to the constructor of the property loader set the property set definition and the property file. Both are given as file names and are interpreted in relation to the current working directory.

The third argument points to a Lua script that is executed during the construction of the property loader. It gets two arguments, the file names of the property set definition and of the property file. As final statement it must return a property table that is filled with the property values that have been read from the given property file. A minimal implementation of propread.lua comes with PropXX (see lua-directory). It looks like that:

require "PropDef"
require "PropTab"

local deffile, propfile, extra = ...

assert(deffile and propfile and not extra,
       "wrong arguments. Expected: <.propdef-file> <.properties-file> ")

local pdef = PropDef.parseDefFile(deffile)

local proptab = PropTab.readPropFile(pdef, propfile)

return proptab

Obviously, this script is just a simple wrapper around the functionality for parsing property set definitions and property files. But, of course, you can replace this script with your own variant that does more complicated file name resolution or property validation etc. if you need that for your project.

4.5Patterns for using PropXX in your application

PropXX does not require a specific way how property classes are made available within your application. A simple but typical and for many cases sufficient approach would be to provide access through a static accessor function (e.g. prop()) that returns the main property class.

For all but rather small projects it will make sense to split the property set into subsets, either through the import mechanism or even (if the subsets are always used by different client code) into completely separate property classes. This reduces compile time dependencies that could otherwise render a simple addition or renaming of a property into a compile time nightmare.

5Using the property editor UI

In most cases property files will be edited by programmers or system administrators that simply use a text editor or their choice for that job. However, there are also use cases where end-users shall get access to some properties and they shell get a tool for this that is more focused than a general-purpose text editor, and hence easier to use for end-users and better protected against wrong input.

Such an editor can of course be generated from a property set definition. PropXX demonstrates that with an editor for property files that is included in the distribution. This editor uses a GUI that is based on TekUI, a small, light-weight GUI toolkit for Lua (cf. http://tekui.teklib.org/).


In order to get a feel for this tool, you can start it from the test-directory of the distribution with

lua propxxedit.lua test.propdef .

The first argument indicates the property set definition from which the graphical editor shall be derived. The second argument specifies a directory which contains the property files that shall be accessible in the editor.

The usage of the editor should be rather self-explanatory. Just play around with it a little if you would like to use this tool.

For simple use cases this editor will provide enough functionality, but please note that it is not as feature-rich as the PropXX core itself. For example, property values can only be edited but it is so far impossible to create a new property file. Such an extension would not be complicated but the focus of the current release is on the core functionality and it is also not clear whether PropXX will be extended in the future or whether it will be replaced by a different editor that is based on a more 'standard' GUI toolkit like Qt or WxWidgets. This will strongly depend on the requirements of the PropXX user community.

6Running Lua scripts

This section is intended for readers who are not familiar with Lua, its environment and its mechanism for including other Lua files.

PropXX comes with a set of Lua modules that are located in the lua-directory of the distribution. When you use PropXX, these scripts must be 'visible' to the main Lua scripts are executed either directly by the user or through C++ code. The standard resolution mechanism for this uses the environment variable LUA_PATH. Please refer to the standard Lua documentation for a detailed description of the syntax. However, for typical uses of PropXX, the following setting will just be enough:

export LUA_PATH=”/the/path/to/the/propxx/scripts/in/your/installation/lua/?.lua;;”

If you want to run sample scripts from the test-directory of the distribution, you can source the luaenv.sh shell script. This shell script assumes a bash syntax but it should be no problem to adapt it to your particular shell or also to a Windows batch file (it's a one-liner...)

7Extending PropXX

7.1Defining new property types

Planned for full release.