Table of Contents
Last update: 29.01.2025.
Some of the material has been collected from the Wafl Project source files. These files document some features that have not yet been prepared in regular tutorial form. Please forgive us for some technical inconsistencies in these files.
Wafl Evaluator provides a basic support for Just In Time compilation. Wafl JIT optimization is based on using the extern binary libraries. The interpreter first analyzes the code and selects the functions that can translate to C++. Then it generates the C++ source file and builds the binary library. The built library is dynamically loaded and linked. The linking process effectively replaces the usual Wafl evaluator nodes with dynamically loaded library nodes.
JIT optimization can be used it two different phases and with some significant differences in the algorithm and the capabilities: on abstract syntax tree (AST) and on abstract evaluation graph (AEG). It cannot be used in both ways. The default behavior is to have the JIT optimization on the AEG.
To use JIT optimization, the user/programmer needs to:
JIT optimization can be used it two different phases and with some significant differences in the algorithm and the capabilities: on abstract syntax tree (AST) and on abstract evaluation graph (AEG). It cannot be used in both ways. The default behavior is to have the JIT optimization on the AEG.
To enable JIT one of the JIT command line options has to be specified. By default the JIT optimization is disabled.
-jit-ast
- Enable JIT optimization on
AST;-jit-aeg
- Enable JIT optimization on
AEG;-jit
- Enable a default JIT optimization (AEG
in this version);-jit
- Enable a default JIT optimization and
force a module rebuild.The library building process is based on the extern libraries and
Wafl Core libraries. To have all the tools available, it is
required to have the development environment set properly, or to provide
the wafl.ini
configuration file in the working directory.
Configuration file uses the traditional ini file format, with
sections, names and values. The line comments begin with #
or //
.
For Linux operating system it is assumed that g++ tools are used. JIT
configuration options are located in [jit-g++]
section. It
is usual to have all the general options already set in the command
shell environment, so for Linux it is usually enough to se just the
Wafl-specific options, like include path and some compiler and linker
options.
All the paths are specified without quotes.
Include Search Path contains a list of directories where C++ header
files are located. It must include all compiler libraries, OS system
libraries and Wafl Core library, but the environment is usually already
prepared in general. The include files search path is specified by using
multiple lines with variables named like include-path-...
,
or by using a single variable include-path
with directories
separated by ;
. For example:
include-path-1 = ~/dev/WaflLocal/bld/debug/_install/waflcore/GNU_12.4.0_uxmkfl_x86_64_8_1_CPP20_Debug/include
Compiler options are specified by using multiple lines with variables
named like compiler-options-...
, or by using a single
variable compiler-options
. Options are separated by blank
spaces. There are no different configurations for debug and release
builds. The same built JIT modules can be used by both debug and release
mode Wafl interpreters.
For example:
compiler-options-1 = -std=c++20 -fpic -ldl -shared
compiler-options-release = -O3 -DNDEBUG
Linker options are specified by using multiple lines with variables
named like link-options-...
, or by using a single variable
link-options
. Options are separated by blank spaces. There
are no different configurations for debug and release builds. The same
built JIT modules can be used by both debug and release mode Wafl
interpreters.
Link options should include all the libraries required to build the JIT module, including the Wafl Core library.
For example:
link-options-1 = ~/dev/WaflLocal/bld/debug/_install/waflcore/GNU_12.4.0_uxmkfl_x86_64_8_1_CPP20_Debug/lib/libWaflCore.a;
When JIT build process is completed, the generated C++ source files
and command scripts are deleted. To keep the sources in user’s
TEMP
directory, use configuration option
keep-source=1
.
Here is a complete example, for Ubuntu Linux 24.10, g++ and appropriate Wafl Core:
[jit-g++]
include-path-1 = /home/smalkov/dev/WaflLocal/bld/debug/_install/waflcore/GNU_12.4.0_uxmkfl_x86_64_8_1_CPP20_Debug/include
compiler-options-1 = -std=c++20 -fpic -ldl -shared
compiler-options-release = -O3 -DNDEBUG
link-options-1 = ~/dev/WaflLocal/bld/debug/_install/waflcore/GNU_12.4.0_uxmkfl_x86_64_8_1_CPP20_Debug/lib/libWaflCore.a;
For Windows operating system it is assumed the MSVC tools are used.
JIT configuration options are located in [jit-msvc]
section. The options include tools path, include path, library path,
compiler options and linker options.
All the paths are specified without quotes.
Tools Search Path is a list of directories where compiler and linker
are located. It is specified as a value of tools-path
parameter. It is usually a single directory, but it can be a list of
directories separated by ;
. For example:
tools-path = C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.42.34433\bin\Hostx64\x64
Include Search Path contains a list of directories where C++ header
files are located. It must include all compiler libraries, OS system
libraries and Wafl Core library. The include files search path is
specified by using multiple lines with variables named like
include-path-...
, or by using a single variable
include-path
with directories separated by ;
.
For example:
include-path-1 = C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.42.34433\include
include-path-2 = C:\Program Files (x86)\Windows Kits\10\include\10.0.22621.0\ucrt
include-path-3 = E:\Wafl\bld\vs17_release\_install\waflcore\MSVC_143_win64_AMD64_8_1_CPP20_Release\include
Library Search Path contains a list of directories where C++
libraries are located. It must include all the libraries used to build
the JIT extern library, except the ones that are specified with their
full absolute path in linker options. The library files search path is
specified by using multiple lines with variables named like
lib-path-...
, or by using a single variable
lib-path
with directories separated by ;
. For
example:
lib-path-1 = C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.42.34433\lib\x64
lib-path-2 = C:\Program Files (x86)\Windows Kits\10\lib\10.0.22621.0\ucrt\x64
lib-path-3 = C:\Program Files (x86)\Windows Kits\10\lib\10.0.22621.0\um\x64
lib-path-4 = E:\Wafl\bld\vs17_release\_install\waflcore\MSVC_143_win64_AMD64_8_1_CPP20_Release\lib
Compiler options are specified by using multiple lines with variables
named like compiler-options-...
, or by using a single
variable compiler-options
. Options are separated by blank
spaces. There are no different configurations for debug and release
builds. The same built JIT modules can be used by both debug and release
mode Wafl interpreters.
For example:
compiler-options-1 = /nologo /std:c++20 /EHsc /Gd /MP
compiler-options-2 = /Fo: "%TEMP%"
compiler-options-3 = /D _WINDLL /D _ITERATOR_DEBUG_LEVEL=0 /D WIN32 /D _WINDOWS /D _WIN32_WINNT=0x0A00
# compiler-options-debug = /MTd /Od /Ob0
compiler-options-release = /MT /O2
Linker options are specified by using multiple lines with variables
named like link-options-...
, or by using a single variable
link-options
. Options are separated by blank spaces. There
are no different configurations for debug and release builds. The same
built JIT modules can be used by both debug and release mode Wafl
interpreters.
Link options should include all the libraries required to build the JIT module.
For example:
link-options-1 = /NOLOGO /DLL /DYNAMICBASE /MACHINE:X64 /LTCG /NOIMPLIB /NOEXP
link-options-2 = libWaflCore.lib
link-options-3 = kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib
When JIT build process is completed, the generated C++ source files
and command scripts are deleted. To keep the sources in user’s
TEMP
directory, use configuration option
keep-source=1
.
Here is a complete example, for VS 2022, Windows 10 and appropriate Wafl Core:
[jit-msvc]
tools-path = C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.42.34433\bin\Hostx64\x64
include-path-1 = C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.42.34433\include
include-path-2 = C:\Program Files (x86)\Windows Kits\10\include\10.0.22621.0\ucrt
include-path-3 = E:\Wafl\bld\vs17_release\_install\waflcore\MSVC_143_win64_AMD64_8_1_CPP20_Release\include
lib-path-1 = C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.42.34433\lib\x64
lib-path-2 = C:\Program Files (x86)\Windows Kits\10\lib\10.0.22621.0\ucrt\x64
lib-path-3 = C:\Program Files (x86)\Windows Kits\10\lib\10.0.22621.0\um\x64
lib-path-4 = E:\Wafl\bld\vs17_release\_install\waflcore\MSVC_143_win64_AMD64_8_1_CPP20_Release\lib
compiler-options-1 = /nologo /std:c++20 /EHsc /Gd /MP
compiler-options-2 = /Fo: "%TEMP%"
compiler-options-3 = /D _WINDLL /D _ITERATOR_DEBUG_LEVEL=0 /D WIN32 /D _WINDOWS /D _WIN32_WINNT=0x0A00
# compiler-options-debug = /MTd /Od /Ob0
compiler-options-release = /MT /O2
link-options-1 = /NOLOGO /DLL /DYNAMICBASE /MACHINE:X64 /LTCG /NOIMPLIB /NOEXP
link-options-2 = libWaflCore.lib
link-options-3 = kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib
Wafl Binary Libraries are function libraries
developed in C++. The main support for implementation of Wafl Binary
Libraries is the libExtern
library.
This document describes the libExtern
library and the
Wafl Binary Library development process. libExtern
is pure
header library. It consists of two header files:
ExternLibInterface.h
, which defines a function
arguments marshalling interface, andExternLibBase.h
, which defines the library catalog
interface and the catalog object itself.Each C++ source file implementing a binary library should include the first header, but only one source file should and must include the second.
The library loading is implemented in
BinaryLibraryDefinition
class and method
ImportLibrary
.
Before version 0.6.5 another marshalling mechanism was used. It was based on a transfer buffer, which was used from both sides. Argument were filled-in by Wafl interpreter an used by a library, and result was filled-in by a library and used by interpreter.
For more details please see the version of this file from version 0.6.4.
TODO: marshalling of other types... like List, Wafl functions,...
TODO: in the future, some other compiled programming languages will be supported...
TODO: ClassPrinter returns fString. Which destructor and deallocator are called?
Based on the libExtern
library, the
WaflCore
project is exported as waflcore
library. It includes all elements of the Wafl project that are required
to build binary libraries out of the main Wafl project.
The usage of the waflcore
exported library is similar to
the usage of the libExtern
library. It consists of:
include/waflcore/libExtern.h
is a header file,
equivalent to ExternLibInterface.h
. Ito should be included
by each module that implements some parts of the binary library
interface;include/waflcore/libExternBase.h
is a header file
equivalent to ExternLibBase.h
. It must be included by one
and only one module, because it defines some interface objects;lib/libWaflCore.lib
or lib/libWaflCore.a
(depending on the platform) is a static library that contains all
required elements of the Wafl project.Binary defined libraries are used in a similar way like the common Wafl defined libraries. The main difference is that library is references using a special declaration:
= library extern '<library file name>'; myLib
The library filename is usually specified without any extension. Each
implementation will automatically add the appropriate extension
(.dll
for Windows and .so
for Linux) when
loading the library. Moreover, if the library is not found, the
interpreter will try to add a prefix libw
to the library
name.
For example, the library defined in a file libwRegex.so
can be declared as:
= library extern 'Regex'; regex
The library functions are accessed as usual, using ::
domain operator:
::fn1 ...
... myLib::fn2 ... ... myLib
To list the content of a binary library, the usual command line interface can be used. Because a large number of extern libraries may exist, the extern library content is listed only if a library name is used as a filter.
For example, to list the contents of a binary library
libwRegex.so
, one of the following commands can be
used:
-listlib:Regex
clwafl -listlib:libwRegex
clwafl -listlib:libwRegex.so clwafl
Each exported binary function have to have a so called canonical form:
void aFunction( EvCell* reserved, const EvCell* args )
The first argument reserved
is pointer to the
preallocated (non-constructed) space where a result will be stored. The
second argument args
points to an array of function
arguments. All arguments and result are objects of EvCell
type.
It is, of course, usual to define (or to already have) a function in a regular way, like:
int fn( int, float, bool )
or like methods in some of the defined classes, like:
int Type::fn( ... )
The corresponding canonical function can be defined explicitly or automatically. If the canonical function is expected to be built automatically, then some of the following sections are not important. However, if the canonical function is defined explicitly, then it must follow some rules. The rules are explained in the following sections.
This section is not important if canonical functions are built automatically.
Each canonical function implementation has to do three jobs:
EvCell
objects;EvCell
object.The conversion of arguments and result is covered in the following
sections. It is not too complex, but for the most of the cases it
follows the same rules. This is why a wrapper function
wrappedFn
is provided (as well as a wrapper class
FunctionWrapper
), to make the wrapping of a
regular function easier.
For example, the following expression defines a canonical version of
library function for a given regular function fn
:
< decltype(fn), fn > wrappedFn
The wrapper function has the appropriate canonical type. It marshalls
all the arguments to the regular function, invokes the function and
finally marshalls the result back from a regular form to the
EvCell
form.
The wrapper function (and the wrapper class) works for functions with
argument types supported by getArg
template function and
result type supported by setResult
template function. For
other types these template functions have to be specialized, first. See
the following sections for details.
This section is not important if canonical functions are built automatically.
Please consult the section on the supported types.
Each argument is read from the appropriate EvCell
object
and marshalled to the appropriate expected type. It can be done manually
or using function template:
T getArg<T>( const EvCell* arg )
The template is defined for the common types as follows:
T
(including bool
):
(T) arg->AsInteger()
int
:
(int) arg->AsInteger()
T
:
(T) arg->AsFloat()
double
:
(double) arg->AsFloat()
char*
and const char*
:
arg->AsString().c_str()
std::string
and std::string&
:
arg->AsString().asBaseString()
sml::String
and sml::String&
:
arg->AsString()
T*
, where T
is a library defined
class, which is exported from the library:
(T*) TheLibrary.ParentContext() .GetObjectFromExternalObjectCell( arg )
T
, where T
is an internal library
defined class, which is not exported from the library, the marshalling
uses Wafl records.To support another type, it is required to define the appropriate
specialization of the template function getArg
. For
example, to support the type A
, a specialization like this
is required:
template<>
<A>( const EvCell* arg )
A getArg{
...
return (A) ...;
}
The new specializations will be used by the wrappers, too.
This section is not important if canonical functions are built automatically.
Function result has to be converted to Wafl EvCell
objects. It can be done manually or using function template:
void setResult<T>( EvCell* reserved, T value )
The setResult
is defined for the common types based on
the CellFactory
functions (and wrappers), which are
available from the callers context. The supported types include integral
types, bool
, floating point types, string types
(char*
, std::string
, sml::String
)
and object pointers.
For example, integer results are marshalled like this:
.ParentContext()
TheLibrary.CreateConstIntegerCellAt( reserved, (fInteger) value );
To support another type, it is required to define the appropriate
specialization of the template function setResult
. For
example, to support a type A
, a specialization like this is
required:
template<>
void setResult<A>( EvCell* reserved, A value )
{
... create a cell at reserved ...
... with given value of type A ...
}
The new specializations will be used by the wrappers, too.
This section is not important if canonical functions are built automatically.
When registering a function, its Wafl type is specified using a type
string representation. For example, a function that maps an integer to a
float has a type ( Int -> Float )
. For more details on
the types, see Wafl documents.
A function template is provided to automatically generate the type strings:
const fString& waflFnTypeName<FN>()
It creates a function type string using another template function to get individual argument or result type name:
const char* waflSimpleTypeName<T>()
Template function waflSimpleTypeName
supports the common
types (just like getArg
and setResult
) and
requires further specializations for advanced cases.
All primitive types are supported:
Int
type size corresponds to build settings and
ISA. For 64-bit processors it is signed 64 bit integer. However, any
integer type is allowed, but take care of the conversions;Float
type is defined as C++ double
type. However, any float type may be used, but take care of the
conversions;Bool
type corresponds to C++ bool
type.NOTE: If the canonical versions of the functions and/or the function types are automatically created, then the following additional constraints hold:
fBool
is used as a logical type, almost equivalent
to the bool
type. However, this type is defined as a 64-bit
unsigned integer. If integers are used, please take care not to use
64-bit unsigned integers (including size_t
and similar
types) for arguments and results, because they may be miss-detected as a
logical type.
fInteger
type represents 64-bit signed
integer.Wafl String
type is internally defined as
fString
, which is a synonym for sml::String
.
They are both defined in the specified header files. Of course,
std::string
is supported, also.
Because of the different handling of memory allocation in dynamic
modules in some environments (in Windows, for example, each DLL may have
its own memory heap), the String
arguments and results have
to be freed in the same module where they are created.
String
as argumentSo, the String
arguments must not be freed in binary
function. Moreover, they must not be copied. Please copy only the binary
char*
representation. This is why String&
is better to be used than simple String
.
TO DO! The solution is modified and the following sentence is false. Check and improve!
However, if there is a specific case when a String
argument has to be destructed, then first the
void FreeString() const
method must be invoked on the
string.
String
as resultUsing String
as result is similar to the argument case.
The string itself should be copied. This is why it is better to use
char*
as result than String
.
The following array types are supported:
Array[Int]
corresponds to C++ type
fArrayInt
;Array[Float]
corresponds to C++ type
fArrayFloat
;Array[Bool]
corresponds to C++ type
fArrayInt
;Array[Int]
corresponds to C++ type
fArrayInt
.Types fArrayInt
, fArrayFloat
,
fArrayBool
and fArrayString
are interfaces to
Wafl arrays.
Arguments of array types must be specified as const
pointers, like:
size_t getArrayLen( const fArrayInt* arr )
Such arguments must not be deleted by library functions.
Array must be returned by pointer. It will be deleted by Wafl evaluator after usage.
* getRandomArray( int range, int size ) fArrayInt
See examples for more details.
Array[...]
corresponds to C++ type
fArrayOfCells
. It is not recognized automatically and has
to be specified manually.Records are mapped to C++ classes defined in the library. Such classes are not exported from the library.
As C++ has no reflection features, it is not possible to implement
complete marshalling of record/class types automatically. The developers
have to provide explicitly the mapping of the types. The mapping is
specified by implementing a specialization of the template
RecordMarshalling
.
To map a class MyClass
to a Wafl record type, there are
a few requirements to fulfill:
template<> class RecordMarshalling<MyClass>
with four static methods has to be implementedMyClass WaflToCpp( const WAFL_ExternLib::Record* record )
for conversion of arguments of a Wafl record type to objects of the
MyClass
class.
MyClass
.MyClass
should support a move
construction.static MyClass WaflToCpp( const WAFL_ExternLib::Record* record )
{
{
MyClass obj <int>( record->ElementByName( "intAttr1" )),
getArg_safe<int>( record->ElementByName( "intAttr2" ))
getArg_safe};
return obj;
}
void CppToWafl( WAFL_ExternLib::Record* record, MyClass&& obj )
for conversion of function results of the MyClass
type to a
Wafl record type.
record
and with the appropriate attribute names.MyClass
argument is
destroyed.static void CppToWafl( WAFL_ExternLib::Record* record, MyClass&& obj )
{
.ParentContext().CreateDynamicRecordAt( record, getRecordAttributeNames() );
TheLibrary.ParentContext().CreateConstIntegerCellAt( record->Elements(), obj.intAttr1 );
TheLibrary.ParentContext().CreateConstIntegerCellAt( record->Elements() + 1, obj.intAttr2 );
TheLibrary}
const char* WaflTypeName()
returns a textual
specification of the corresponding Wafl record type. The usual form
is:static const char* WaflTypeName() {
return "Record[ intAttr2: Int, intAttr1: Int ]";
}
const WAFL_ExternLib::RecordAttributes& getRecordAttributeNames()
returns an attribute names handler object. The name specified should be
ordered lexically. The usual form is:static const WAFL_ExternLib::RecordAttributes& getRecordAttributeNames()
{
static WAFL_ExternLib::RecordAttributes names {
"intAttr1",
"intAttr2",
};
return names;
}
MyClass
arguments
must be passed by value or by r-value reference
MyClass&&
. The l-value references are not
allowed.MyClass
results
must be passed by value.If all the requirements are fulfilled, the functions using
MyClass
type as an argument or as a result are defined and
registered in the same way as any other function.
See examples in testLibExtern
project for an
example.
Assume that the arguments will be deleted by caller. If the argument is to be returned, it must be copied.
Assume that the returned object will be deleted by caller after the usage. If an argument is to be returned, or some permanent object, then it must be copied.
Any class defined in the binary library may be exported as a
binary type. The only requirement is that it has to be
described by a corresponding descriptor object in the library object.
The descriptor object is created using
LibraryClassDescription
template class in a form:
< aType > aTypeDesc( "aTypeName" ); LibraryClassDescription
When a function result is a binary type, the result is prepared using
SetResult
method in the following form:
.SetResult( new aType(...), aTypeDesc.GetData() ); data
Please note:
new
operator. It will be deleted by Wafl evaluator using
appropriate destructors and deallocators specified by the provided
descriptor.The only required method of the binary type class is
fString print() const
method, which is used to create a
display-ready string representation of the object.
Each binary library must define a catalog of its content. The catalog informs the Wafl evaluator, which uses the library, on the function names, types and implementations.
The catalog has to be implemented by
LibraryImplementation::InitLibrary
method of a predefined
class LibraryImplementation
. The class and its singleton
object are defined in ExternLibBase.h
header file, and the
initialization method is implemented by the library.
The InitLibrary
method is invoked on an already created
LibraryImplementation
object and it should populate it with
the functions data. The concept is simple and based on idea that each
chunk of information is inserted into the catalog using insertion
operator <<
:
const char*
:*this << "A Binary Library"
VersionNumber
object:*this << VersionNumber( 0, 6, 5 )
LibraryClassDescription<...>
object:*this << aBinaryTypeDesc
tLibFnData
object. It can be exported in its
canonical form:*this << tLibFnData {
"maxInt", // exported fn. name
, // canonical fn. implementation
canonicalMaxIntFn"( Integer * Integer -> Integer )", // fn. type
"Returns a greater of two numbers." // fn. description
}
It is easier to use macro `LIB_FN_DATA`, which creates the canonical version of the function on the fly, and prepares its registration data:
*this << LIB_FN_DATA(
"maxInt", // exported fn. name
, // regular fn. implementation
regularMaxIntFn"( Integer * Integer -> Integer )", // fn. type, using Wafl notation
"Returns a greater of two numbers." // fn. description
}
There is also even more advanced macro `LIB_FN_DATA_T`. It does all the same like the previous one, but also automatically generates the function type name:
*this << LIB_FN_DATA_T(
"maxInt", // exported fn. name
, // regular fn. implementation
regularMaxIntFn"Returns a greater of two numbers." // fn. description
}
*this << tLibFnData {
"aMethod", // exported fn. name
, // canonical fn. implementation
canonicalMethodFn"( TypeName -> Integer )", // fn. type
"Maps object to integer" // fn. description
}
There are macros to automatically create canonical functions for methods. The first one is `LIB_METHOD_DATA`, which creates the canonical function for the method on the fly, and prepares its registration data:
*this << LIB_METHOD_DATA(
"aMethod", // exported fn. name
::method, // regular fn. implementation
TT"( TypeName -> Integer )", // fn. type
"Maps object to integer" // fn. description
}
There is also even more advanced macro `LIB_METHOD_DATA_T`. It does all the same like the previous one, but also automatically generates the function type name:
*this << LIB_METHOD_DATA_T(
"aMethod", // exported fn. name
::method, // regular fn. implementation
TT"Maps object to integer" // fn. description
}
Note: Static methods are registered as functions, not as methods.
Please see the examples for more details.
A binary library must include libExtern/ExternLibBase.h
from Wafl project. This header file defines all the library elements
described in this file.
A binary library must link libExtern
library from Wafl
project. This library already includes the other required Wafl libraries
(libCommon
, and libMemory
).
Here we present some simple examples. For more information please see
the source for testLibExtern
project and for
libw*
sub-projects.
As a first example, we will export a sqrt
function. The
first option is to define a canonical function:
void sqrtFnC( EvCell* reserved, const EvCell* args )
{
auto result = sqrt( getArg<double>( &args[ 0 ] ) );
( reserved, result );
setResult}
This function uses the predefined marshalling template functions
getArg
and setResult
. It may be registered
using:
*this << tLibFnData {
"sqrt", // exported fn. name
, // canonical fn. implementation
sqrtFnC"( Float -> Float )", // fn. type
"Returns a square root of the number."
}
The easier option is to use generic canonical functions, defined by
wrappers. However, because the sqrt
is overloaded, we
should be more explicit on the version, like in:
inline double sqrtFn( double x ) { return sqrt( x ); }
auto sqrtFnC = wrappedFn< decltype(sqrtFn), sqrtFn >;
It is easier to use some macros, which do some of the preparations
“on the fly”. The basic macro is LIB_FN_DATA
, which creates
the canonical version and prepares it registration data:
inline double sqrtFn( double x ) { return sqrt( x ); }
...
*this << LIB_FN_DATA(
"sqrt",
,
sqrtFn"( Float -> Float )",
"Returns a square root of the number."
}
Even more advanced is macro LIB_FN_DATA_T
. It does all
the same like the previous one, but also automatically generates the
function type name:
inline double sqrtFn( double x ) { return sqrt( x ); }
...
*this << LIB_FN_DATA_T(
"sqrt",
,
sqrtFn"Returns a square root of the number."
}
…
…
This function creates and returns as a result a new binary type object:
class aBinaryType {
public:
( int n ) : Attr_( n ) {...}
aBinaryType~aBinaryType() {...}
int Attr_;
};
<aBinaryType> aBinaryTypeDesc( "aBinaryType" );
LibraryClassDescription
* createBinaryObject( int x )
aBinaryType{
return new aBinaryType( x );
}
The function uses an object of a binary type as an argument:
int useBinaryObject( const aBinaryType* obj )
{
return obj->Attr_;
}
The function computes the sum of integer array elements:
( const fArrayInt* arr )
fInteger getArraySum{
= 0;
fInteger sum for( auto i : *arr )
+= i;
sum return sum;
}
The life of array argument object is managed by Wafl evaluator. If a pointer to the object is is stored internally by a library, then the evaluator must be informed of that:
->AddReference(); arr
When such rerefence is deleted, teh evaluator must be informed of that, also:
->Free(); arr
The function creates and returns an array of size
random
integers in the range [0,range
-1]:
* getRandomArray( int range, int size )
fArrayInt{
* arr = (fArrayInt*) TheLibrary.ParentContext().CreateUninitializedArrayCellPtr( size );
fArrayIntauto set = TheLibrary.ParentContext().CreateConstIntegerCellAt;
for( auto& i : *arr )
( arr->getElementAddr( i ), rand() % range );
setreturn arr;
}
The library defined in previous examples may have the following catalog:
...
void LibraryImplementation::InitLibrary()
{
*this
<< "A Binary Library" // Library name
<< aBinaryTypeDesc // A binary type
// Functions...
<< tLibFnData{ "sqrt1", sqrtFn,
"( Float -> Float )" }
<< tLibFnData{ "maxInt", maxIntFn,
"( Integer * Integer -> Integer )" }
<< tLibFnData{ "stringAdd", stringAddFn,
"( String * String -> String )" }
<< tLibFnData{ "newBin", createBinaryObject",
"( Integer -> aBinaryType )" }
<< tLibFnData{ "useBin", useBinaryObject,
"( aBinaryType -> Integer )" };
}