Difference between revisions of "Lianja/SDK C API"

From Lianjapedia
Jump to: navigation, search
m (1 revision)
(Using the 'C' functions and classes)
 
(28 intermediate revisions by 2 users not shown)
Line 1: Line 1:
'C' functions and classes defined in dynamic link libraries (DLLs) can be used on Windows.
+
The Lianja/SDK C API provides an extensive API for extending Lianja with 'C' functions and classes. It is a very concise API that provides features and functionality similar to the PHP  and Python C API -- but much much simpler to use.
  
==Defining the C functions==
+
If you are building extensions on Windows there is a sample Visual Studio 2010 solution that builds the '''example.c''' extension in:
The main source file for the DLL must include the following four elements:
+
  
====The include file "dbapi.h"====
+
C:\lianja\extensions\lianja_extension_example
  
 +
If you are building extensions on Linux there is a sample extension called '''example.c''' together with its makefile '''makefile.example''' in:
 +
 +
/opt/lianja/extensions
 +
 +
After building your extension be sure to enable it in the user_extensions.ini file (see below) so that it will be loaded automatically.
 +
 +
Alternatively you can use the [[LOADLIBRARY()]] function to manually load the extension.
 +
 +
==Defining the C functions==
 +
The main source file for the extension must include the following:
  
 
<code lang="c">
 
<code lang="c">
#include "lianjaapi.h"
+
 
 +
#include "lianja_api.h"
 +
 
 +
static struct LIANJA_EXTENSION_TABLE lianja_extension_table[] =
 +
{
 +
        {"example_character","fnExampleCharacter", API_FUNCTION},
 +
        {"example_type",    "fnExampleType",      API_FUNCTION},
 +
        {"example_logical",  "fnExampleLogical",  API_FUNCTION},
 +
        {"example_numeric",  "fnExampleNumeric",  API_FUNCTION},
 +
        {"example_execute",  "fnExampleExecute",  API_FUNCTION},
 +
        {"example_evaluate", "fnExampleEvaluate",  API_FUNCTION},
 +
        {"example_class",    "clsMyClass",        API_CLASS},
 +
  {NULL, NULL, -1}
 +
};
 +
 
 +
 
 +
//================================================================================
 +
// *** REQUIRED *** only once in each extension library and *must* come after the
 +
// declaration for LIANJA_EXTENSION_TABLE.
 +
//--------------------------------------------------------------------------------
 +
LIANJA_INIT_API;
 +
 
 
</code>
 
</code>
  
  
====The API_SHARED_FUNCTION_TABLE structure====
+
==Using the 'C' functions and classes==
Used to define the 'C' functions and classes included in the library:
+
Extensions are loaded automatically by placing directives in the '''user_extensions.ini''' file in the Lianja extensions directory.
 +
The distribution includes the file '''extensions.ini'''.  This should be used as a template for your '''user_extensions.ini''' file.  The '''extensions.ini''' will be overwritten by the Lianja installer; your '''user_extensions.ini''' will not.
 +
 
 +
Here is an example of a user_extensions.ini file:
 +
 
 +
<pre>
 +
[windows]
 +
library=example.dll
 +
[linux]
 +
#library=example.so
 +
[mac]
 +
#library=example.dylib
 +
</pre>
 +
 
 +
Note: for the Lianja Cloud Server, the extensions directory is a sub-directory of the wwwroot directory.  By default, this is:
 +
 
 +
* Windows
 +
<pre>C:\lianja\cloudserver\tenants\public\wwwroot</pre>
 +
 
 +
* Linux
 +
<pre>/opt/lianja/cloudserver/tenants/public/wwwroot</pre>
 +
 
 +
The [[LIST PROCEDURE]] and [[DISPLAY PROCEDURE]] commands include loaded extension library function names in their listings and the [[LIST CLASSES]] and [[DISPLAY CLASSES]] commands include loaded extension library class names in their listings.
 +
 
 +
Extension library functions can be used in the same way as a internal functions.  Extension library classes can be used in the same way as system classes using [[CREATEOBJECT()]].
  
 +
Here is a complete example as provided in the distribution:
  
 
<code lang="c">
 
<code lang="c">
// API function address table
+
//================================================================================
static struct API_FUNCTION_ADDRESS_TABLE *api_function_address_table = NULL;
+
//
 +
// Using the Lianja/SDK C API to build extensions
 +
// ----------------------------------------------
 +
//
 +
// These extensions reside in the "extensions" directory which is typically
 +
// located at:
 +
//
 +
// Windows
 +
// -------
 +
// c:\lianja\extensions
 +
//
 +
// Linux
 +
// ------
 +
//
 +
// /opt/lianja/extensions
 +
//
 +
// An extension library can contain functions and/or classes that can be used in
 +
// Lianja just like the in-built functions and classes.
 +
//
 +
// The Lianja/SDK C API is organized into 5 levels of functionality.
 +
//
 +
// Level 1:
 +
//
 +
// Accessing parameters passed to functions
 +
//
 +
// Leval 2;
 +
//
 +
// Execute Lianja commands from C/C++
 +
// Evaluate Lianja expressions from C/C++
 +
// Throw errors back into the Lianja engine
 +
//
 +
// Level 3:
 +
//
 +
// NoSQL-style data access such as APPEND, DELETE, UPDATE, SKIP, SEEK, etc
 +
// Many common VFP-style functions available to C/C++
 +
// Lookup and work with memory variables and arrays
 +
//
 +
// Level 4:
 +
//
 +
// Helper functions for working with objects and classes
 +
//
 +
// Level 5:
 +
//
 +
// Complete set of DAO compatible functions for working with Lianja native tables and/or LianjaSQL
 +
//
 +
//--------------------------------------------------------------------------------
  
// Add all functions to this structure as follows:
+
 
// Name, C Function Name , Type
+
//================================================================================
 
//
 
//
// Make sure the last entry is NULL.
+
// This is an example template file to show you how to write an a Lianja extension
 +
// library.
 
//
 
//
static struct API_SHARED_FUNCTION_TABLE api_function_table[7] = {
+
// Extension libraries that you want automatically loaded should be specified in
{"schar", "fnSamplesCharacter", API_FUNCTION},
+
// the extensions.ini text file which is located in the extensions directory.
{"stype", "fnSamplesType", API_FUNCTION},
+
//
{"slog", "fnSamplesLogical", API_FUNCTION},
+
// lianja_api.h is a required include file. Best way to learn about the
{"snum", "fnSamplesNumeric", API_FUNCTION},
+
// Lianja/SDK C API is to read this file.
{"sopen", "fnSamplesOpen", API_FUNCTION},
+
//
{"myclass", "clsMyClass", API_CLASS},
+
//--------------------------------------------------------------------------------
{NULL, NULL, -1}
+
#include "lianja_api.h"
} ;
+
</code>
+
  
 +
/* example.h just contains a data structure that we will attach to our example_class */
 +
#include "example.h"
  
====The initAPI() function====
 
  
 +
//================================================================================
 +
// *** REQUIRED *** only once in each shared extensions library
 +
//
 +
// Declare the function/class dispatch table with the name "lianja_extension_table".
 +
//
 +
// Lianja name, C function name, API type
 +
//
 +
//--------------------------------------------------------------------------------
 +
static struct LIANJA_EXTENSION_TABLE lianja_extension_table[] =
 +
{
 +
  {"example_character", "fnExampleCharacter", API_FUNCTION},
 +
  {"example_type", "fnExampleType", API_FUNCTION},
 +
  {"example_logical", "fnExampleLogical", API_FUNCTION},
 +
  {"example_numeric", "fnExampleNumeric", API_FUNCTION},
 +
  {"example_execute", "fnExampleExecute", API_FUNCTION},
 +
  {"example_evaluate", "fnExampleEvaluate", API_FUNCTION},
 +
  {"example_class", "clsMyClass", API_CLASS},
 +
  {NULL, NULL, -1}
 +
};
  
<code lang="c">
+
 
// This function is used to define function addresses for API calls.
+
//================================================================================
// C++ example
+
// *** REQUIRED *** only once in each extension library and *must* come after the
extern "C" int WINAPI EXPORT initAPI(struct API_FUNCTION_ADDRESS_TABLE *function_address_table)
+
// declaration for LIANJA_EXTENSION_TABLE.
 +
//--------------------------------------------------------------------------------
 +
LIANJA_INIT_API;
 +
 
 +
 
 +
//================================================================================
 +
// This is an example of passing a character parameter and returning one.
 +
//--------------------------------------------------------------------------------
 +
LIANJA_FUNCTION fnExampleCharacter(void)
 
{
 
{
api_function_address_table = function_address_table;
+
_retc(_parc(1));
return 0;
+
 
}
 
}
</code>
 
  
 +
//================================================================================
 +
// This is an example of passing a numeric parameter and returning one.
 +
//--------------------------------------------------------------------------------
 +
LIANJA_FUNCTION fnExampleNumeric(void)
 +
{
 +
_retni(_parni(1));
 +
}
  
====The getFunctions() function====
+
//================================================================================
 +
// This is an example returns the privateObjectData type of the parameter passed.
 +
//--------------------------------------------------------------------------------
 +
LIANJA_FUNCTION fnExampleType(void)
 +
{
 +
char result[10];
  
<code lang="c">
+
switch (_parinfo(1)) {
// This function is used to return the function names of the API.
+
case API_CTYPE:
// C++ example
+
    strcpy(result, "Character");
extern "C" struct API_SHARED_FUNCTION_TABLE *getFunctions(void)
+
    break;
 +
case API_NTYPE:
 +
    strcpy(result, "Numeric");
 +
    break;
 +
case API_LTYPE:
 +
    strcpy(result, "Logical");
 +
    break;
 +
case API_DTYPE:
 +
    strcpy(result, "Date");
 +
    break;
 +
case API_TTYPE:
 +
    strcpy(result, "DateTime");
 +
    break;
 +
case API_YTYPE:
 +
    strcpy(result, "Currency");
 +
    break;
 +
case API_OTYPE:
 +
    strcpy(result, "Object");
 +
    break;
 +
case API_ATYPE:
 +
    strcpy(result, "Array");
 +
    break;
 +
default:
 +
    strcpy(result, "Unknown");
 +
    break;
 +
}
 +
 
 +
_retc(result);
 +
}
 +
 
 +
//================================================================================
 +
// This is an example of passing a logical parameter and return "True" or "False".
 +
//--------------------------------------------------------------------------------
 +
LIANJA_FUNCTION fnExampleLogical(void)
 
{
 
{
return api_function_table;
+
char result[10];
 +
 
 +
if (_parl(1)) strcpy(result, "True");
 +
else strcpy(result, "False");
 +
 
 +
_retc(result);
 
}
 
}
</code>
 
  
 +
//================================================================================
 +
// This example executes a Lianja command
 +
//--------------------------------------------------------------------------------
 +
LIANJA_FUNCTION fnExampleExecute(void)
 +
{
 +
if (_parinfo(1) == API_CTYPE) {
 +
    _retni(LIANJA_EXECUTE(_parc(1)));
 +
} else {
 +
    _retni(-1);
 +
}
 +
}
  
==Using the 'C' functions and classes==
+
//================================================================================
Shared libraries are loaded using the [[SET LIBRARY|SET LIBRARY TO <library> [ADDITIVE]]] command or the [[REQUIRE()]], [[REQUIRE_ONCE()]], [[INCLUDE()]] or [[INCLUDE_ONCE()]] functions. By default library files are accessed from the directory defined by the [[DB_LIBDIR]] environment variable.
+
// This example evaluates a Lianja expression
 +
//--------------------------------------------------------------------------------
 +
LIANJA_FUNCTION fnExampleEvaluate(void)
 +
{
 +
struct LIANJA_EXPRESSION result;
 +
int rc;
 +
 +
if (_parinfo(1) == API_CTYPE)
 +
{
 +
    rc = LIANJA_EVALUATE(&result, _parc(1));
 +
    if (rc != 0) _retl(0);
 +
    switch (result.type)
 +
    {
 +
case 'C':
 +
_retc(result.character);
 +
 +
case 'N':
 +
_retnd(result.number);
 +
 +
case 'L':
 +
_retl(result.logical == 'T');
  
If full path information is specified, the shared library files can be loaded from other directories, e.g.
+
case 'T':
 +
 +
case 'Y':
 +
 +
case 'D':
 +
 +
default:
 +
_retl(0);
 +
    }
 +
} else {
 +
    _retl(0);
 +
}
 +
}
  
<code lang="recital">
 
// pdf.dll is in the DB_LIBDIR directory
 
set library to pdf
 
   
 
//mylib.dll is in C:\Program Files\Lianja\Myapplibs\myapplibs
 
set library to "C:\Program Files\Lianja\Myapplibs\mylib"
 
</code>
 
  
 +
//================================================================================
 +
DEFINE_METHOD(clsMyClass, Constructor)
 +
{
 +
/* example_data is defined in example.h */
 +
struct example_data *privateObjectData;
  
The function [[LOADLIBRARY()|LOADLIBRARY(<library>)]] can also be used to load additional shared libraries.  The name of the shared library file including the full path and file extension must be specified.
+
/* Allocate memory for the privateObjectData area */
 +
privateObjectData = (struct example_data *) malloc(sizeof(struct example_data));
 +
if (privateObjectData == NULL) RETURN_ERROR();
  
The [[SET LIBRARY|SET LIBRARY TO]] or [[RELEASE LIBRARY|RELEASE LIBRARY <library>]] commands are used to close all or specified shared libraries.
+
/* Assign the default property values to our privateObjectData */
 +
strcpy(privateObjectData->prop_charvalue, "Test Lianja/SDK C API object");
 +
privateObjectData->prop_numvalue = 15.2827;
 +
privateObjectData->prop_logvalue = 1;
 +
strcpy(privateObjectData->prop_datevalue, DATE_DATE());
 +
strcpy(privateObjectData->prop_datetimevalue, DATE_DATETIME());
 +
strcpy(privateObjectData->prop_currvalue, "15.2827");
 +
strcpy(privateObjectData->object_name, "myobject");
 +
privateObjectData->prop_objvalue = NULL;
 +
 +
//
 +
// we can dynamically create other lianja named objects like this:
 +
//
 +
// privateObjectData->prop_objvalue = LIANJA_CREATEOBJECT("objectname", "classname");
 +
//
 +
// or anonymous objects like this:
 +
//
 +
// privateObjectData->prop_objvalue = LIANJA_CREATEOBJECT(NULL, "classname");
 +
//
 +
 +
/* Attach the data to the object */
 +
LIANJA_OBJECT_SETDATA(privateObjectData);
  
The [[LIST PROCEDURE]] and [[DISPLAY PROCEDURE]] commands include loaded shared library function names in their listings and the [[LIST CLASSES]] and [[DISPLAY CLASSES]] commands include loaded shared library class names in their listings.
+
RETURN_SUCCESS();
 +
}
 +
 
 +
//================================================================================
 +
DEFINE_METHOD(clsMyClass, Destructor)
 +
{
 +
struct example_data *privateObjectData = (struct example_data *)LIANJA_OBJECT_GETDATA();
  
Loaded shared library functions can be used in the same way as an internal function. Loaded shared library classes can be used in the same way as system classes.
+
if (privateObjectData != NULL)
 +
{
 +
    if (privateObjectData->prop_objvalue != NULL) LIANJA_DELETEOBJECT(privateObjectData->prop_objvalue);
 +
    free(privateObjectData);
 +
    privateObjectData = NULL;
 +
}
 +
 
 +
RETURN_SUCCESS();
 +
}
 +
 
 +
//================================================================================
 +
DEFINE_METHOD(clsMyClass, Echo)
 +
{
 +
struct example_data *privateObjectData = (struct example_data *)LIANJA_OBJECT_GETDATA();
 +
struct LIANJA_EXPRESSION result;
 +
 
 +
if (METHOD_GETARGC() != 1)
 +
{
 +
    return OBJECT_ERROR_PARAMETER_COUNT;
 +
}
 +
 
 +
METHOD_GETPARAMETER(1, &result);
 +
RETURN_RESULT(&result);
 +
}
 +
 
 +
//================================================================================
 +
DEFINE_METHOD(clsMyClass, Sayhello)
 +
{
 +
    RETURN_CHARACTER("Hello world");
 +
}
 +
 
 +
//================================================================================
 +
DEFINE_PROPERTYGET(clsMyClass, NumValue)
 +
{
 +
struct example_data *privateObjectData = (struct example_data *)LIANJA_OBJECT_GETDATA();
 +
 
 +
if (privateObjectData == NULL) RETURN_ERROR();
 +
 
 +
RETURN_PROPERTY_NUMERIC(privateObjectData->prop_numvalue);
 +
}
 +
 
 +
//================================================================================
 +
DEFINE_PROPERTYGET(clsMyClass, LogValue)
 +
{
 +
struct example_data *privateObjectData = (struct example_data *)LIANJA_OBJECT_GETDATA();
 +
 
 +
if (privateObjectData == NULL) RETURN_ERROR();
 +
 
 +
RETURN_PROPERTY_LOGICAL(privateObjectData->prop_logvalue);
 +
}
 +
 
 +
//================================================================================
 +
DEFINE_PROPERTYGET(clsMyClass, DateValue)
 +
{
 +
struct example_data *privateObjectData = (struct example_data *)LIANJA_OBJECT_GETDATA();
 +
 
 +
if (privateObjectData == NULL) RETURN_ERROR();
 +
 
 +
RETURN_PROPERTY_DATE(privateObjectData->prop_datevalue);
 +
}
 +
 
 +
//================================================================================
 +
DEFINE_PROPERTYGET(clsMyClass, DateTimeValue)
 +
{
 +
struct example_data *privateObjectData = (struct example_data *)LIANJA_OBJECT_GETDATA();
 +
 
 +
if (privateObjectData == NULL) RETURN_ERROR();
 +
 
 +
RETURN_PROPERTY_DATETIME(privateObjectData->prop_datetimevalue);
 +
}
 +
 
 +
//================================================================================
 +
DEFINE_PROPERTYGET(clsMyClass, CurrValue)
 +
{
 +
struct example_data *privateObjectData = (struct example_data *)LIANJA_OBJECT_GETDATA();
 +
 
 +
if (privateObjectData == NULL) RETURN_ERROR();
 +
 
 +
RETURN_PROPERTY_CURRENCY(privateObjectData->prop_currvalue);
 +
}
 +
 
 +
//================================================================================
 +
DEFINE_PROPERTYGET(clsMyClass, CharValue)
 +
{
 +
struct example_data *privateObjectData = (struct example_data *)LIANJA_OBJECT_GETDATA();
 +
 
 +
if (privateObjectData == NULL) RETURN_ERROR();
 +
 
 +
RETURN_PROPERTY_CHARACTER(privateObjectData->prop_charvalue);
 +
}
 +
 
 +
//================================================================================
 +
DEFINE_PROPERTYGET(clsMyClass, ObjValue)
 +
{
 +
struct example_data *privateObjectData = (struct example_data *)LIANJA_OBJECT_GETDATA();
 +
 
 +
if (privateObjectData == NULL) RETURN_ERROR();
 +
 
 +
if (privateObjectData->prop_objvalue == NULL)
 +
{
 +
    RETURN_PROPERTY_UNDEFINED();
 +
}
 +
else
 +
{
 +
    RETURN_PROPERTY_OBJECT(privateObjectData->prop_objvalue);
 +
}
 +
}
 +
 
 +
//================================================================================
 +
DEFINE_PROPERTYSET(clsMyClass, NumValue)
 +
{
 +
struct example_data *privateObjectData = (struct example_data *)LIANJA_OBJECT_GETDATA();
 +
double n;
 +
 +
PROPERTY_GETVALUE_NUMERIC(n);
 +
privateObjectData->prop_numvalue = n;
 +
RETURN_SUCCESS();
 +
}
 +
 
 +
//================================================================================
 +
DEFINE_PROPERTYSET(clsMyClass, LogValue)
 +
{
 +
struct example_data *privateObjectData = (struct example_data *)LIANJA_OBJECT_GETDATA();
 +
struct LIANJA_EXPRESSION result;
 +
 
 +
PROPERTY_GETVALUE(&result);
 +
if (result.errnumber == 0 && result.type == 'L')
 +
{
 +
    privateObjectData->prop_logvalue = result.logical == 'T';
 +
RETURN_SUCCESS();
 +
}
 +
 
 +
RETURN_ERROR();
 +
}
 +
 
 +
//================================================================================
 +
DEFINE_PROPERTYSET(clsMyClass, DateValue)
 +
{
 +
struct example_data *privateObjectData = (struct example_data *)LIANJA_OBJECT_GETDATA();
 +
struct LIANJA_EXPRESSION result;
 +
 
 +
PROPERTY_GETVALUE(&result);
 +
if (result.errnumber == 0 && result.type == 'D')
 +
{
 +
    strcpy(privateObjectData->prop_datevalue, DATE_DTOS(result.date));
 +
    RETURN_SUCCESS();
 +
}
 +
 
 +
RETURN_ERROR();
 +
}
 +
 
 +
//================================================================================
 +
DEFINE_PROPERTYSET(clsMyClass, DateTimeValue)
 +
{
 +
struct example_data *privateObjectData = (struct example_data *)LIANJA_OBJECT_GETDATA();
 +
struct LIANJA_EXPRESSION result;
 +
 
 +
PROPERTY_GETVALUE(&result);
 +
if (result.errnumber == 0 && result.type == 'T')
 +
{
 +
    strcpy(privateObjectData->prop_datetimevalue, DATE_TTOS(result.datetime));
 +
    RETURN_SUCCESS();
 +
}
 +
 
 +
RETURN_ERROR();
 +
}
 +
 
 +
//================================================================================
 +
DEFINE_PROPERTYSET(clsMyClass, CurrValue)
 +
{
 +
struct example_data *privateObjectData = (struct example_data *)LIANJA_OBJECT_GETDATA();
 +
struct LIANJA_EXPRESSION result;
 +
 
 +
PROPERTY_GETVALUE(&result);
 +
if (result.errnumber == 0 && result.type == 'Y')
 +
{
 +
    strcpy(privateObjectData->prop_currvalue, CURR_YTOS(result.currency));
 +
    RETURN_SUCCESS();
 +
}
 +
 
 +
RETURN_ERROR();
 +
}
 +
 
 +
//================================================================================
 +
DEFINE_PROPERTYSET(clsMyClass, CharValue) 
 +
{
 +
struct example_data *privateObjectData = (struct example_data *)LIANJA_OBJECT_GETDATA();
 +
char value[MAX_CHARACTER_LENGTH+1];
 +
 
 +
PROPERTY_GETVALUE_CHARACTER(value);
 +
strcpy(privateObjectData->prop_charvalue, value);
 +
RETURN_SUCCESS();
 +
}
 +
 
 +
//================================================================================
 +
DEFINE_PROPERTYSET(clsMyClass, ObjValue)
 +
{
 +
struct example_data *privateObjectData = (struct example_data *)LIANJA_OBJECT_GETDATA();
 +
LIANJA_OBJECT objvalue;
 +
 
 +
if (PROPERTY_GETTYPE() == 'O')
 +
{
 +
    PROPERTY_GETVALUE_OBJECT(objvalue);
 +
    privateObjectData->prop_objvalue = OBJECT_ASSIGN(objvalue, privateObjectData->object_name);
 +
    RETURN_SUCCESS();
 +
}
 +
 
 +
RETURN_ERROR();
 +
}
 +
 
 +
 
 +
//================================================================================
 +
//
 +
// ** REQUIRED ** must be placed at the bottom of the file after all the
 +
// property and method handlers.
 +
//
 +
// This example declares a class that can be instantiated in Lianja. It is the
 +
// equivalent of the following Lianja code.
 +
//
 +
// DEFINE CLASS example_class
 +
//    ...
 +
// ENDDEFINE
 +
//
 +
// To use this class in Lianja you just need to:
 +
//
 +
// myObject = createObject("example_class")
 +
//
 +
// Then you call methods like this:
 +
//
 +
// myObject.echo("Hello world")
 +
//
 +
// and set/get properties like this:
 +
//
 +
// myObject.numvalue = 10
 +
// if myObject.numvalue = 10
 +
//  ....
 +
// endif
 +
//
 +
// Note that the example_data structure for this class is defined in example.h
 +
//
 +
//--------------------------------------------------------------------------------
 +
DEFINE_CLASS(clsMyClass)
 +
{
 +
TRACE_MESSAGES(clsMyClass);
 +
 
 +
/*------------------*/
 +
/* method handlers  */
 +
/*------------------*/
 +
DISPATCH_METHOD(clsMyClass, Constructor); /**  REQUIRED **/
 +
DISPATCH_METHOD(clsMyClass, Destructor); /**  REQUIRED **/
 +
DISPATCH_METHOD(clsMyClass, Echo);
 +
DISPATCH_METHOD(clsMyClass, Sayhello);
 +
 
 +
/*-------------------------------*/
 +
/* SET and GET property handlers */
 +
/*-------------------------------*/
 +
DISPATCH_PROPERTY(clsMyClass, NumValue);
 +
DISPATCH_PROPERTY(clsMyClass, LogValue);
 +
DISPATCH_PROPERTY(clsMyClass, DateValue);
 +
DISPATCH_PROPERTY(clsMyClass, DateTimeValue);
 +
DISPATCH_PROPERTY(clsMyClass, CurrValue);
 +
DISPATCH_PROPERTY(clsMyClass, CharValue);
 +
DISPATCH_PROPERTY(clsMyClass, ObjValue);
 +
 
 +
/*----------------------------*/
 +
/* Unknown property or method */
 +
/*----------------------------*/
 +
RETURN_ERROR();
 +
}
 +
 
 +
</code>
  
 
[[Category:Documentation]]
 
[[Category:Documentation]]
 
[[Category:SDK]]
 
[[Category:SDK]]
 
[[Category:Reference]]
 
[[Category:Reference]]

Latest revision as of 12:18, 16 February 2021

The Lianja/SDK C API provides an extensive API for extending Lianja with 'C' functions and classes. It is a very concise API that provides features and functionality similar to the PHP and Python C API -- but much much simpler to use.

If you are building extensions on Windows there is a sample Visual Studio 2010 solution that builds the example.c extension in:

C:\lianja\extensions\lianja_extension_example

If you are building extensions on Linux there is a sample extension called example.c together with its makefile makefile.example in:

/opt/lianja/extensions

After building your extension be sure to enable it in the user_extensions.ini file (see below) so that it will be loaded automatically.

Alternatively you can use the LOADLIBRARY() function to manually load the extension.

Defining the C functions

The main source file for the extension must include the following:

#include "lianja_api.h"
 
static struct LIANJA_EXTENSION_TABLE lianja_extension_table[] = 
{
        {"example_character","fnExampleCharacter", API_FUNCTION},
        {"example_type",     "fnExampleType",      API_FUNCTION},
        {"example_logical",  "fnExampleLogical",   API_FUNCTION},
        {"example_numeric",  "fnExampleNumeric",   API_FUNCTION},
        {"example_execute",  "fnExampleExecute",   API_FUNCTION},
        {"example_evaluate", "fnExampleEvaluate",  API_FUNCTION},
        {"example_class",    "clsMyClass",         API_CLASS}, 
   	{NULL, NULL, -1}
};
 
 
//================================================================================
// *** REQUIRED *** only once in each extension library and *must* come after the
// declaration for LIANJA_EXTENSION_TABLE.
//--------------------------------------------------------------------------------
LIANJA_INIT_API;


Using the 'C' functions and classes

Extensions are loaded automatically by placing directives in the user_extensions.ini file in the Lianja extensions directory. The distribution includes the file extensions.ini. This should be used as a template for your user_extensions.ini file. The extensions.ini will be overwritten by the Lianja installer; your user_extensions.ini will not.

Here is an example of a user_extensions.ini file:

[windows]
library=example.dll
[linux]
#library=example.so
[mac]
#library=example.dylib

Note: for the Lianja Cloud Server, the extensions directory is a sub-directory of the wwwroot directory. By default, this is:

  • Windows
C:\lianja\cloudserver\tenants\public\wwwroot
  • Linux
/opt/lianja/cloudserver/tenants/public/wwwroot

The LIST PROCEDURE and DISPLAY PROCEDURE commands include loaded extension library function names in their listings and the LIST CLASSES and DISPLAY CLASSES commands include loaded extension library class names in their listings.

Extension library functions can be used in the same way as a internal functions. Extension library classes can be used in the same way as system classes using CREATEOBJECT().

Here is a complete example as provided in the distribution:

//================================================================================
//
// Using the Lianja/SDK C API to build extensions
// ----------------------------------------------
//
// These extensions reside in the "extensions" directory which is typically
// located at:
//
// Windows
// -------
// c:\lianja\extensions
//
// Linux
// ------
//
// /opt/lianja/extensions
//	
// An extension library can contain functions and/or classes that can be used in
// Lianja just like the in-built functions and classes.
//
// The Lianja/SDK C API is organized into 5 levels of functionality.
//
// Level 1:
//
//	Accessing parameters passed to functions
//
// Leval 2;
//
//	Execute Lianja commands from C/C++
//	Evaluate Lianja expressions from C/C++
//	Throw errors back into the Lianja engine
//
// Level 3:
//
//	NoSQL-style data access such as APPEND, DELETE, UPDATE, SKIP, SEEK, etc
//	Many common VFP-style functions available to C/C++
//	Lookup and work with memory variables and arrays
//
// Level 4:
//
//	Helper functions for working with objects and classes
//
// Level 5:
//
//	Complete set of DAO compatible functions for working with Lianja native tables and/or LianjaSQL
//
//--------------------------------------------------------------------------------
 
 
//================================================================================
//
// This is an example template file to show you how to write an a Lianja extension 
// library.
//
// Extension libraries that you want automatically loaded should be specified in
// the extensions.ini text file which is located in the extensions directory.
//
// lianja_api.h is a required include file. Best way to learn about the 
// Lianja/SDK C API is to read this file.
//
//--------------------------------------------------------------------------------
#include "lianja_api.h"
 
/* example.h just contains a data structure that we will attach to our example_class */
#include "example.h"
 
 
//================================================================================
// *** REQUIRED *** only once in each shared extensions library
//
// Declare the function/class dispatch table with the name "lianja_extension_table".
//
// 	Lianja name, 		C function name,		API type
//
//--------------------------------------------------------------------------------
static struct LIANJA_EXTENSION_TABLE lianja_extension_table[] = 
{
   	{"example_character",	"fnExampleCharacter",		API_FUNCTION},
   	{"example_type",	"fnExampleType",		API_FUNCTION},
   	{"example_logical",	"fnExampleLogical",		API_FUNCTION},
   	{"example_numeric",	"fnExampleNumeric",		API_FUNCTION},
   	{"example_execute",	"fnExampleExecute",		API_FUNCTION},
   	{"example_evaluate",	"fnExampleEvaluate",		API_FUNCTION},
   	{"example_class",	"clsMyClass",			API_CLASS}, 
   	{NULL,			NULL,				-1}
};
 
 
//================================================================================
// *** REQUIRED *** only once in each extension library and *must* come after the
// declaration for LIANJA_EXTENSION_TABLE.
//--------------------------------------------------------------------------------
LIANJA_INIT_API;
 
 
//================================================================================
// This is an example of passing a character parameter and returning one.
//--------------------------------------------------------------------------------
LIANJA_FUNCTION fnExampleCharacter(void)
{
	_retc(_parc(1));
}
 
//================================================================================
// This is an example of passing a numeric parameter and returning one.
//--------------------------------------------------------------------------------
LIANJA_FUNCTION fnExampleNumeric(void)
{
	_retni(_parni(1));
}
 
//================================================================================
// This is an example returns the privateObjectData type of the parameter passed.
//--------------------------------------------------------------------------------
LIANJA_FUNCTION fnExampleType(void)
{
	char	result[10];
 
	switch (_parinfo(1)) {
	case API_CTYPE:
	    strcpy(result, "Character");
	    break;
	case API_NTYPE:
	    strcpy(result, "Numeric");
	    break;
	case API_LTYPE:
	    strcpy(result, "Logical");
	    break;
	case API_DTYPE:
	    strcpy(result, "Date");
	    break;
	case API_TTYPE:
	    strcpy(result, "DateTime");
	    break;
	case API_YTYPE:
	    strcpy(result, "Currency");
	    break;
	case API_OTYPE:
	    strcpy(result, "Object");
	    break;
	case API_ATYPE:
	    strcpy(result, "Array");
	    break;
	default:
	    strcpy(result, "Unknown");
	    break;
	}
 
	_retc(result);
}
 
//================================================================================
// This is an example of passing a logical parameter and return "True" or "False".
//--------------------------------------------------------------------------------
LIANJA_FUNCTION fnExampleLogical(void)
{
	char	result[10];
 
	if (_parl(1)) strcpy(result, "True");
	else strcpy(result, "False");
 
	_retc(result);
}
 
//================================================================================
// This example executes a Lianja command
//--------------------------------------------------------------------------------
LIANJA_FUNCTION fnExampleExecute(void)
{
	if (_parinfo(1) == API_CTYPE) {
	    _retni(LIANJA_EXECUTE(_parc(1)));
	} else {
	    _retni(-1);
	}
}
 
//================================================================================
// This example evaluates a Lianja expression
//--------------------------------------------------------------------------------
LIANJA_FUNCTION fnExampleEvaluate(void)
{
	struct LIANJA_EXPRESSION result;
	int rc;
 
	if (_parinfo(1) == API_CTYPE) 
	{
	    rc = LIANJA_EVALUATE(&result, _parc(1));
	    if (rc != 0) _retl(0);
	    switch (result.type)
	    {
		case 'C':
			_retc(result.character);
 
		case 'N':
			_retnd(result.number);
 
		case 'L':
			_retl(result.logical == 'T');
 
		case 'T':
 
		case 'Y':
 
		case 'D':
 
		default:
			_retl(0);
	    }
	} else {
	    _retl(0);
	}
}
 
 
//================================================================================
DEFINE_METHOD(clsMyClass, Constructor) 
{
	/* example_data is defined in example.h */
	struct example_data *privateObjectData;
 
	/* Allocate memory for the privateObjectData area */
	privateObjectData = (struct example_data *) malloc(sizeof(struct example_data));
	if (privateObjectData == NULL) RETURN_ERROR();
 
	/* Assign the default property values to our privateObjectData */
	strcpy(privateObjectData->prop_charvalue, "Test Lianja/SDK C API object");
	privateObjectData->prop_numvalue = 15.2827;
	privateObjectData->prop_logvalue = 1;
	strcpy(privateObjectData->prop_datevalue, DATE_DATE());
	strcpy(privateObjectData->prop_datetimevalue, DATE_DATETIME());
	strcpy(privateObjectData->prop_currvalue, "15.2827");
	strcpy(privateObjectData->object_name, "myobject");
	privateObjectData->prop_objvalue = NULL;
 
	//
	// we can dynamically create other lianja named objects like this:
	//
	// privateObjectData->prop_objvalue = LIANJA_CREATEOBJECT("objectname", "classname");
	//
	// or anonymous objects like this:
	//
	// privateObjectData->prop_objvalue = LIANJA_CREATEOBJECT(NULL, "classname");
	//
 
	/* Attach the data to the object */
	LIANJA_OBJECT_SETDATA(privateObjectData);
 
	RETURN_SUCCESS();
}
 
//================================================================================
DEFINE_METHOD(clsMyClass, Destructor) 
{
	struct example_data *privateObjectData = (struct example_data *)LIANJA_OBJECT_GETDATA();
 
	if (privateObjectData != NULL) 
	{
	    if (privateObjectData->prop_objvalue != NULL) LIANJA_DELETEOBJECT(privateObjectData->prop_objvalue);
	    free(privateObjectData);
	    privateObjectData = NULL;
	}
 
	RETURN_SUCCESS();
}
 
//================================================================================
DEFINE_METHOD(clsMyClass, Echo) 
{
	struct	example_data *privateObjectData = (struct example_data *)LIANJA_OBJECT_GETDATA();
	struct	LIANJA_EXPRESSION result;
 
	if (METHOD_GETARGC() != 1) 
	{
	    return OBJECT_ERROR_PARAMETER_COUNT;
	}
 
	METHOD_GETPARAMETER(1, &result);
	RETURN_RESULT(&result);
}
 
//================================================================================
DEFINE_METHOD(clsMyClass, Sayhello) 
{
    RETURN_CHARACTER("Hello world");
}
 
//================================================================================
DEFINE_PROPERTYGET(clsMyClass, NumValue) 
{
	struct example_data *privateObjectData = (struct example_data *)LIANJA_OBJECT_GETDATA();
 
	if (privateObjectData == NULL) RETURN_ERROR();
 
	RETURN_PROPERTY_NUMERIC(privateObjectData->prop_numvalue);
}
 
//================================================================================
DEFINE_PROPERTYGET(clsMyClass, LogValue) 
{
	struct example_data *privateObjectData = (struct example_data *)LIANJA_OBJECT_GETDATA();
 
	if (privateObjectData == NULL) RETURN_ERROR();
 
	RETURN_PROPERTY_LOGICAL(privateObjectData->prop_logvalue);
}
 
//================================================================================
DEFINE_PROPERTYGET(clsMyClass, DateValue) 
{
	struct example_data *privateObjectData = (struct example_data *)LIANJA_OBJECT_GETDATA();
 
	if (privateObjectData == NULL) RETURN_ERROR();
 
	RETURN_PROPERTY_DATE(privateObjectData->prop_datevalue);
}
 
//================================================================================
DEFINE_PROPERTYGET(clsMyClass, DateTimeValue) 
{
	struct example_data *privateObjectData = (struct example_data *)LIANJA_OBJECT_GETDATA();
 
	if (privateObjectData == NULL) RETURN_ERROR();
 
	RETURN_PROPERTY_DATETIME(privateObjectData->prop_datetimevalue);
}
 
//================================================================================
DEFINE_PROPERTYGET(clsMyClass, CurrValue) 
{
	struct example_data *privateObjectData = (struct example_data *)LIANJA_OBJECT_GETDATA();
 
	if (privateObjectData == NULL) RETURN_ERROR();
 
	RETURN_PROPERTY_CURRENCY(privateObjectData->prop_currvalue);
}
 
//================================================================================
DEFINE_PROPERTYGET(clsMyClass, CharValue) 
{
	struct example_data *privateObjectData = (struct example_data *)LIANJA_OBJECT_GETDATA();
 
	if (privateObjectData == NULL) RETURN_ERROR();
 
	RETURN_PROPERTY_CHARACTER(privateObjectData->prop_charvalue);
}
 
//================================================================================
DEFINE_PROPERTYGET(clsMyClass, ObjValue) 
{
	struct example_data *privateObjectData = (struct example_data *)LIANJA_OBJECT_GETDATA();
 
	if (privateObjectData == NULL) RETURN_ERROR();
 
	if (privateObjectData->prop_objvalue == NULL) 
	{
	    RETURN_PROPERTY_UNDEFINED();
	}
	else
	{
	    RETURN_PROPERTY_OBJECT(privateObjectData->prop_objvalue);
	}
} 
 
//================================================================================
DEFINE_PROPERTYSET(clsMyClass, NumValue) 
{
	struct example_data *privateObjectData = (struct example_data *)LIANJA_OBJECT_GETDATA();
	double n;
 
	PROPERTY_GETVALUE_NUMERIC(n);
	privateObjectData->prop_numvalue = n;
	RETURN_SUCCESS();
}
 
//================================================================================
DEFINE_PROPERTYSET(clsMyClass, LogValue) 
{
	struct example_data *privateObjectData = (struct example_data *)LIANJA_OBJECT_GETDATA();
	struct LIANJA_EXPRESSION result;
 
	PROPERTY_GETVALUE(&result);
	if (result.errnumber == 0 && result.type == 'L') 
	{
	    privateObjectData->prop_logvalue = result.logical == 'T';
		RETURN_SUCCESS();
	}
 
	RETURN_ERROR();
}
 
//================================================================================
DEFINE_PROPERTYSET(clsMyClass, DateValue) 
{
	struct example_data *privateObjectData = (struct example_data *)LIANJA_OBJECT_GETDATA();
	struct LIANJA_EXPRESSION result;
 
	PROPERTY_GETVALUE(&result);
	if (result.errnumber == 0 && result.type == 'D') 
	{
	    strcpy(privateObjectData->prop_datevalue, DATE_DTOS(result.date));
	    RETURN_SUCCESS();
	}
 
	RETURN_ERROR();
}
 
//================================================================================
DEFINE_PROPERTYSET(clsMyClass, DateTimeValue) 
{
	struct example_data *privateObjectData = (struct example_data *)LIANJA_OBJECT_GETDATA();
	struct LIANJA_EXPRESSION result;
 
	PROPERTY_GETVALUE(&result);
	if (result.errnumber == 0 && result.type == 'T') 
	{
	    strcpy(privateObjectData->prop_datetimevalue, DATE_TTOS(result.datetime));
	    RETURN_SUCCESS();
	}
 
	RETURN_ERROR();
}
 
//================================================================================
DEFINE_PROPERTYSET(clsMyClass, CurrValue) 
{
	struct example_data *privateObjectData = (struct example_data *)LIANJA_OBJECT_GETDATA();
	struct LIANJA_EXPRESSION result;
 
	PROPERTY_GETVALUE(&result);
	if (result.errnumber == 0 && result.type == 'Y') 
	{
	    strcpy(privateObjectData->prop_currvalue, CURR_YTOS(result.currency));
	    RETURN_SUCCESS();
	}
 
	RETURN_ERROR(); 
}
 
//================================================================================
DEFINE_PROPERTYSET(clsMyClass, CharValue)  
{
	struct example_data *privateObjectData = (struct example_data *)LIANJA_OBJECT_GETDATA();
	char value[MAX_CHARACTER_LENGTH+1];
 
	PROPERTY_GETVALUE_CHARACTER(value);
	strcpy(privateObjectData->prop_charvalue, value);
	RETURN_SUCCESS();
}
 
//================================================================================
DEFINE_PROPERTYSET(clsMyClass, ObjValue) 
{
	struct example_data *privateObjectData = (struct example_data *)LIANJA_OBJECT_GETDATA();
	LIANJA_OBJECT	objvalue;
 
	if (PROPERTY_GETTYPE() == 'O') 
	{
	    PROPERTY_GETVALUE_OBJECT(objvalue);
	    privateObjectData->prop_objvalue = OBJECT_ASSIGN(objvalue, privateObjectData->object_name);
	    RETURN_SUCCESS();
	}
 
	RETURN_ERROR();
}
 
 
//================================================================================
//
// ** REQUIRED ** must be placed at the bottom of the file after all the
// property and method handlers.
//
// This example declares a class that can be instantiated in Lianja. It is the
// equivalent of the following Lianja code.
//
// DEFINE CLASS example_class
//     ...
// ENDDEFINE
//
// To use this class in Lianja you just need to:
//
// myObject = createObject("example_class")
//
// Then you call methods like this:
//
// myObject.echo("Hello world")
//
// and set/get properties like this:
//
// myObject.numvalue = 10
// if myObject.numvalue = 10
//   ....
// endif
//
// Note that the example_data structure for this class is defined in example.h 
//
//--------------------------------------------------------------------------------
DEFINE_CLASS(clsMyClass)
{
	TRACE_MESSAGES(clsMyClass);
 
	/*------------------*/
	/* method handlers  */
	/*------------------*/
	DISPATCH_METHOD(clsMyClass, Constructor);	/**  REQUIRED **/
	DISPATCH_METHOD(clsMyClass, Destructor); 	/**  REQUIRED **/
	DISPATCH_METHOD(clsMyClass, Echo);
	DISPATCH_METHOD(clsMyClass, Sayhello);
 
	/*-------------------------------*/
	/* SET and GET property handlers */
	/*-------------------------------*/
	DISPATCH_PROPERTY(clsMyClass, NumValue);
	DISPATCH_PROPERTY(clsMyClass, LogValue);
	DISPATCH_PROPERTY(clsMyClass, DateValue);
	DISPATCH_PROPERTY(clsMyClass, DateTimeValue);
	DISPATCH_PROPERTY(clsMyClass, CurrValue);
	DISPATCH_PROPERTY(clsMyClass, CharValue); 
	DISPATCH_PROPERTY(clsMyClass, ObjValue);
 
	/*----------------------------*/
	/* Unknown property or method */
	/*----------------------------*/
	RETURN_ERROR();
}