Lianja/SDK C API

From Lianjapedia
Jump to: navigation, search

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


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();
}