Extension using the C/C++ languages

We can extend the Ring Virtual Machine (RingVM) by adding new functions written in the C programming language or C++. The RingVM comes with many functions written in C that we can call like any Ring function.

We can extend the language by writing new functions then rebuilding the RingVM again, or we can create shared library (DLL/So/Dylib) file to extend the RingVM without the need to rebuild it.

The Ring language source code comes with two files to add new modules to the RingVM, ring_ext.h and ring_ext.c


The file ring_ext.h contains constants that we can change to include/exclude modules during the build process.

#ifndef ringext_h
#define ringext_h
/* Constants */
#define RING_VM_LISTFUNCS       1
#define RING_VM_REFMETA         1
#define RING_VM_MATH            1
#define RING_VM_FILE            1
#define RING_VM_OS              1
#define RING_VM_MYSQL           1
#define RING_VM_ODBC            1
#define RING_VM_OPENSSL         1
#define RING_VM_CURL            1
#define RING_VM_DLL             1


The file ring_ext.c check constants defined in ring_ext.h before calling the start-up function in each module.

Each module contains a function that register the module functions in the RingVM.

#include "ring.h"

void ring_vm_extension ( RingState *pRingState )
        /* Reflection and Meta-programming */
        #if RING_VM_REFMETA
        /* List Functions */
        /* Math */
        #if RING_VM_MATH
        /* File */
        #if RING_VM_FILE
        /* OS */
        #if RING_VM_OS
        /* MySQL */
        #if RING_VM_MYSQL
        /* ODBC */
        #if RING_VM_ODBC
        /* OPENSSL */
        #if RING_VM_OPENSSL
        /* CURL */
        #if RING_VM_CURL
        /* DLL */
        #if RING_VM_DLL

Module Organization

Each module starts by include the ring header file (ring.h). This files contains the Ring API that we can use to extend the RingVM.

Each module comes with a function to register the module functions in the RingVM The registration is done by using RING_API_REGISTER() function.

The RING_API_REGISTER() function takes two parameters, the first is the function name that will be used by Ring programs to call the function. The second parameter is the function pointer in the C program.

for example, the ring_vmmath.c module contains the next code to register the module functions

#include "ring.h"

void ring_vm_math_loadfunctions ( RingState *pRingState )


Remember that the function ring_vm_math_loadfunctions() will be called by the ring_vm_extension() function (in the ring_ext.c file).

Function Structure

Each module function may contains the next steps

1 - Check Parameters Count

2 - Check Parameters Type

3 - Get Parameters Values

4 - Execute Code/Call Functions

5 - Return Value

The structure is very similar to any function (Input - Process - Output) But here we will use the Ring API for the steps 1,2,3 and 5.

Check Parameters Count

We can check the parameters count using the RING_API_PARACOUNT macro.

We can compare RING_API_PARACOUNT with any numeric value using == or != operators.


if ( RING_API_PARACOUNT != 1 ) {
        /* code */


if ( RING_API_PARACOUNT == 1 ) {
        /* code */

Display Error Message

We can display error messages using the RING_API_ERROR() function.

The function will display the error and end the execution of the program.


the behaviour of this function can be changed by the Ring code using Try/Catch/Done statements, so in your C code, use Return after this function.


RING_API_ERROR(const char *cErrorMsg);

The Ring API comes with some of predefined error messages that we can use

#define RING_API_MISS1PARA "Bad parameters count, the function expect one parameter"
#define RING_API_MISS2PARA "Bad parameters count, the function expect two parameters"
#define RING_API_MISS3PARA "Bad parameters count, the function expect three parameters"
#define RING_API_MISS4PARA "Bad parameters count, the function expect four parameters"
#define RING_API_BADPARATYPE    "Bad parameter type!"
#define RING_API_BADPARACOUNT   "Bad parameters count!"
#define RING_API_BADPARARANGE   "Bad parameters value, error in range!"
#define RING_API_NOTPOINTER     "Error in parameter, not pointer!"
#define RING_API_NULLPOINTER    "Error in parameter, NULL pointer!"
#define RING_API_EMPTYLIST      "Bad parameter, empty list!"

Check Parameters Type

We can check the parameter type using the next functions

int RING_API_ISNUMBER(int nParameterNumber);
int RING_API_ISSTRING(int nParameterNumber);
int RING_API_ISLIST(int nParameterNumber);
int RING_API_ISCPOINTER(int nParameterNumber);
int RING_API_ISPOINTER(int nParameterNumber);  // List or C Pointer

The output of these functions will be 1 (True) or 0 (False).

Get Parameters Values

We can get parameters values using the next functions

double RING_API_GETNUMBER(int nParameterNumber);
const char *RING_API_GETSTRING(int nParameterNumber);
int RING_API_GETSTRINGSIZE(int nParameterNumber);
List *RING_API_GETLIST(int nParameterNumber);
void *RING_API_GETCPOINTER(int nParameterNumber, const char *cPoinerType);
int RING_API_GETPOINTERTYPE(int nParameterNumber);

If we would like to get pointers to char, int, float & double

We can use the next functions

char *RING_API_GETCHARPOINTER(int nParameterNumber);
int *RING_API_GETINTPOINTER(int nParameterNumber);
void RING_API_ACCEPTINTVALUE(int nParameterNumber);
float *RING_API_GETFLOATPOINTER(int nParameterNumber);
void RING_API_ACCEPTFLOATVALUE(int nParameterNumber);
double *RING_API_GETDOUBLEPOINTER(int nParameterNumber);

Return Value

We can return values from our function using the next functions.

RING_API_RETNUMBER(double nValue);
RING_API_RETSTRING(const char *cString);
RING_API_RETSTRING2(const char *cString,int nStringSize);
RING_API_RETCPOINTER(void *pValue,const char *cPointerType);
RING_API_RETMANAGEDCPOINTER(void *pValue,const char *cPointerType,
                                void (* pFreeFunc)(void *,void *))

Function Prototype

When we define new function to be used for RingVM extension, we use the next prototype

void my_function_name( void *pPointer );

or we can use the RING_FUNC() Macro


Sin() Function Implementation

The next code represents the sin() function implementation using the Ring API and the sin() C function.

void ring_vm_math_sin ( void *pPointer )
        if ( RING_API_PARACOUNT != 1 ) {
                return ;
        if ( RING_API_ISNUMBER(1) ) {
        } else {

Fopen() and Fclose() Functions Implementation

The next code represents the fopen() function implementation using the Ring API and the fopen() C Function.

The function takes two parameters, the first parameter is the file name as string. The second parameter is the mode as string.

In the file ring_vmfile.h we have some constants to use as the pointer type like

#define RING_VM_POINTER_FILE    "file"
#define RING_VM_POINTER_FILEPOS "filepos"

The function implementation in ring_vmfile.c

void ring_vm_file_fopen ( void *pPointer )
        FILE *fp  ;
        if ( RING_API_PARACOUNT != 2 ) {
                return ;
        if ( RING_API_ISSTRING(1) && RING_API_ISSTRING(2) ) {
                fp = fopen(RING_API_GETSTRING(1),RING_API_GETSTRING(2));
        } else {

The next code represents the fclose() function implementation

void ring_vm_file_fclose ( void *pPointer )
        FILE *fp  ;
        if ( RING_API_PARACOUNT != 1 ) {
                return ;
        if ( RING_API_ISCPOINTER(1) ) {
                if ( fp != NULL ) {
        } else {

From fopen() and fclose() implementation we learned

1 - how to return C pointer using RING_API_RETCPOINTER() function

2 - how to check if the parameter is a pointer using the RING_API_ISCPOINTER() function

3 - how to get C pointer value using the RING_API_GETCPOINTER() function

4 - how to set the C pointer variable (in RingVM) to NULL using the RING_API_SETNULLPOINTER() function

Ring API - List Functions

In this section we will learn about the list functions provided by the Ring API to create new lists and manipulate the list items.

This section contains the most important functions required for common use-cases

For a complete list of function, including functions that store and process integers, pointers & objects, see this file: ring/language/include/rlist.h

If your list will be modified by Ring code then it’s required to use the _gc version of the functions

Lists created using ring_list_new()/ring_list_new_gc() must be deleted using ring_list_delete()/ring_list_delete_gc().

If we created a list using RING_API_NEWLIST then deleting the list is not required and may cause problems, because Ring VM will delete it automatically after the end of the caller scope.

Using RING_API_NEWLISTUSINGBLOCKS1D and RING_API_NEWLISTUSINGBLOCKS2D is similar to RING_API_NEWLIST but we have better performance when we create large lists.

// Create and delete lists

List * RING_API_NEWLISTUSINGBLOCKS2D(int nRows,int nColumns)
List * ring_list_new_gc ( void *pState,unsigned int nSize ) ;
List * ring_list_delete_gc ( void *pState,List *pList ) ;

List * ring_list_new ( unsigned int nSize ) ;
List * ring_list_delete ( List *pList ) ;

// Add Items

void ring_list_adddouble_gc ( void *pState,List *pList,double x ) ;
void ring_list_addstring_gc ( void *pState,List *pList,const char *str ) ;
void ring_list_addstring2_gc ( void *pState,List *pList,const char *str,int nStrSize ) ;
List * ring_list_newlist_gc ( void *pState,List *pList ) ;

void ring_list_adddouble ( List *pList,double x ) ;
void ring_list_addstring ( List *pList,const char *str ) ;
void ring_list_addstring2 ( List *pList,const char *str,int nStrSize ) ;
List * ring_list_newlist ( List *pList ) ;

// Get List Size

int ring_list_getsize( List *pList );

// Check Item Type

int ring_list_isdouble ( List *pList, unsigned int index ) ;
int ring_list_isstring ( List *pList, unsigned int index ) ;
int ring_list_islist ( List *pList, unsigned int index ) ;
int ring_list_isobject ( List *pList ) ;

// Get Items

double ring_list_getdouble ( List *pList, unsigned int index ) ;
char * ring_list_getstring ( List *pList, unsigned int index ) ;
int ring_list_getstringsize ( List *pList, unsigned int index ) ;
String * ring_list_getstringobject ( List *pList, unsigned int index ) ;
List * ring_list_getlist ( List *pList, unsigned int index ) ;

// Insert Items

void ring_list_insertdouble_gc ( void *pState,List *pList,unsigned int nPos,double x ) ;
void ring_list_insertstring_gc ( void *pState,List *pList,unsigned int nPos,const char *str ) ;
void ring_list_insertstring2_gc ( void *pState,List *pList,unsigned int nPos,const char *str,int nStrSize ) ;
List * ring_list_insertlist_gc ( void *pState,List *pList,unsigned int nPos ) ;

void ring_list_insertdouble ( List *pList,unsigned int nPos,double x ) ;
void ring_list_insertstring ( List *pList,unsigned int nPos,const char *str ) ;
void ring_list_insertstring2 ( List *pList,unsigned int nPos,const char *str,int nStrSize ) ;
List * ring_list_insertlist ( List *pList,unsigned int nPos ) ;

// Set Items

void ring_list_setdouble_gc ( void *pState,List *pList, unsigned int index ,double number ) ;
void ring_list_setstring_gc ( void *pState,List *pList, unsigned int index ,const char *str ) ;
void ring_list_setstring2_gc ( void *pState,List *pList, unsigned int index ,const char *str,int nStrSize ) ;
void ring_list_setlist_gc ( void *pState,List *pList, unsigned int index ) ;

void ring_list_setdouble ( List *pList, unsigned int index ,double number ) ;
void ring_list_setstring ( List *pList, unsigned int index ,const char *str ) ;
void ring_list_setstring2 ( List *pList, unsigned int index ,const char *str,int nStrSize ) ;
void ring_list_setlist ( List *pList, unsigned int index ) ;

// Delete Items

void ring_list_deleteitem_gc ( void *pState,List *pList,unsigned int index ) ;
void ring_list_deleteallitems_gc ( void *pState,List *pList ) ;

void ring_list_deleteitem ( List *pList,unsigned int index ) ;
void ring_list_deleteallitems ( List *pList ) ;

// Copy Lists

void ring_list_copy_gc ( void *pState,List *pNewList, List *pList ) ;
void ring_list_copy ( List *pNewList, List *pList ) ;

// Find Items

int ring_list_findstring ( List *pList,const char *str,unsigned int nColumn ) ;
int ring_list_finddouble ( List *pList,double nNum1,unsigned int nColumn ) ;

// Swap

void ring_list_swap ( List *pList,unsigned int x,unsigned int y ) ;
void ring_list_swaptwolists ( List *pList1, List *pList2 ) ;

// Print

void ring_list_print ( List *pList ) ;
void ring_list_print2 ( List *pList,unsigned int nDecimals ) ;
void ring_list_printobj ( List *pList, unsigned int nDecimals ) ;


To modify or delete the Lists and Strings created using Ring code, We have to use specific Lists/Strings functions. These functions requires that the Ring State to be passed as the first parameter.

Also, these functions add _gc to the function name which means that it uses the Ring Garbage Collector.

Some of these functions are

void ring_list_setdouble_gc ( void *pState, List *pList, int index , double number ) ;
void ring_list_adddouble_gc ( void *pState, List *pList, double x ) ;
void ring_list_deleteitem_gc ( void *pState, List *pList, int index ) ;


The next C code implement the filterList() function

We pass a list and a number to this function and it will check each item in the list.

If the item value is not greater than the passed number, then the item will be deleted.

In the implementation we uses ring_list_deleteitem_gc() function to delete the item and we pass Ring state as the first parameter using RING_API_STATE.

        List *pList;
        int x;
        // Check Parameters Count
                if (RING_API_PARACOUNT != 2) {
        // Check Parameters Type
                if ( ! ( RING_API_ISLIST(1) && RING_API_ISNUMBER(2) ) ) {
        // Filter List Items
                pList = RING_API_GETLIST(1);
                for(x = ring_list_getsize(pList) ; x >= 1 ; x--)
                        if ( ring_list_isdouble(pList,x) )
                                if ( ! (ring_list_getdouble(pList,x) >
                                                 RING_API_GETNUMBER(2)) )
                                        ring_list_deleteitem_gc(RING_API_STATE,pList,x) ;
        // Return Output

Ring API - String Functions

In this section we will learn about the string functions provided by the Ring API to create new string and manipulate the string content.

As in List functions, the GC version of these functions (which add _gc to the name) exist.

Here, Using the GC version of the functions is not necessary for stability but it could be helpful for performance.

Why it’s not necessary for stability as we have in the List functions?

Because Ring API uses List * to pass lists from Ring VM to C code and also uses List * to return lists from C code to Ring. These lists are passed by pointers, They are the same lists, and we need the same memory functions to process the list items.

While when using Strings, Ring API create new strings when using RING_API_GETSTRING() or RING_API_RETSTRING()

For a complete list of function, including functions that uses the GC, see this file: ring/language/include/rstring.h

// Create and delete strings

String * ring_string_new ( const char *str ) ;
String * ring_string_new2 ( const char *str,int nStrSize ) ;
String * ring_string_delete ( String *pString ) ;

// Get string

char * ring_string_get ( String *pString ) ;
int ring_string_size ( String *pString ) ;

// Add text to the string

void ring_string_add ( String *pString,const char *str ) ;
void ring_string_add2 ( String *pString,const char *str,int nStrSize ) ;

// Change the string

void ring_string_set ( String *pString,const char *str ) ;
void ring_string_set2 ( String *pString,const char *str,int nStrSize ) ;
void ring_string_setfromint ( String *pString,int x ) ;

// Print the string

void ring_string_print ( String *pString ) ;

// Convert to lower/UPPER case

void ring_string_tolower(String *pString)
void ring_string_toupper(String *pString)
char * ring_string_lower ( char *cStr ) ;
char * ring_string_upper ( char *cStr ) ;
char * ring_string_lower2 ( char *cStr,int nStrSize ) ;
char * ring_string_upper2 ( char *cStr,int nStrSize ) ;

// Find

char * ring_string_find ( char *cStr1,char *cStr2 ) ;
char * ring_string_find2 ( char *cStr1,int nStrSize1,char *cStr2,int nStrSize2 ) ;

MySQL_Columns() Function Implementation

The next code presents the MySQL_Columns() function implementation.

This function returns table columns information.

void ring_vm_mysql_columns ( void *pPointer )
        MYSQL *con  ;
        MYSQL_RES *result  ;
        int nColumns,x  ;
        MYSQL_ROW row  ;
        MYSQL_FIELD *field  ;
        List *pList, *pList2  ;
        if ( RING_API_PARACOUNT != 1 ) {
                return ;
        if ( RING_API_ISCPOINTER(1) ) {
                if ( con == NULL ) {
                        return ;
                result = mysql_store_result(con);
                if ( result == NULL ) {
                        return ;
                pList = RING_API_NEWLIST ;
                nColumns = mysql_num_fields(result);
                if ( row = mysql_fetch_row(result) ) {
                        while ( field = mysql_fetch_field(result) ) {
                                pList2 = ring_list_newlist_gc(RING_API_STATE,pList);
        } else {

Lists are of type List, in the previous function we declared two pointers of type List using List *pList, *pList2;


The function uses RING_API_NEWLIST to create new list instead of ring_list_new() to create the list in Temp. Memory related to the function scope. This way we can return the list from the function. Also we don’t delete the list, if it’s stored in a variable by Ring Code it will be saved, if not it will be automatically deleted by RingVM.

The list can contains sub lists, we used the function ring_list_newlist_gc() to create a sublist.

The function ring_list_addstring() is used to add string items to the list/sublist.

The function ring_list_adddouble() is used to add numeric items to the list/sublist.


All numeric items in lists returned from RingVM extension functions must be of type double and added to the list using ring_list_adddouble() function.

We return the list from the extension function using the RING_API_RETLIST() function.

Dynamic/Shared Libraries (DLL/So/Dylib) and LoadLib() function

Instead of rebuilding the RingVM after writing new functions using C/C++ and the Ring API, we can create a DLL/So/Dylib file and dynamically use the functions provided by this file in the runtime using the LoadLib() function.

Dynamic library example in C

#include "ring.h"

RING_DLL __declspec(dllexport)

        printf("Message from dlfunc");


the idea is to create the RING_LIBINIT function, this function will be called by the RingVM when we use the generated DLL file through the LoadLib() function.

Inside the RING_LIBINIT function we can register the module function or call a function that do the registration process for all of the module functions.

The next Ring code demonstrates how to use the DLL library during the runtime.

See "Dynamic DLL" + NL


Dynamic DLL
Message from dlfunc


Using RING_API_RETMANAGEDCPOINTER() the Ring extensions written in C/C++ languages can return a managed pointer to Ring. This pointer can be controlled by the Ring VM using reference counting.

This is important to avoid the need to write code that free the unmanaged resources like QPixMap objects in RingQt.

Also the Code Generator for extensions is updated to automatically use RING_API_RETMANAGEDCPOINTER() based on need.


RING_API_RETMANAGEDCPOINTER(void *pValue,const char *cPointerType,
                                void (* pFreeFunc)(void *,void *))


The next example from ring_qt.cpp - QPixMap Class - Scaled() Method.

        QPixmap *pObject ;
        if ( RING_API_PARACOUNT != 5 ) {
                return ;
        if ( ! RING_API_ISCPOINTER(1) ) {
                return ;
        pObject = (QPixmap *) RING_API_GETCPOINTER(1,"QPixmap");
        if ( ! RING_API_ISNUMBER(2) ) {
                return ;
        if ( ! RING_API_ISNUMBER(3) ) {
                return ;
        if ( ! RING_API_ISNUMBER(4) ) {
                return ;
        if ( ! RING_API_ISNUMBER(5) ) {
                return ;
                QPixmap *pValue ;
                pValue = new QPixmap() ;
                *pValue = pObject->scaled( (int ) RING_API_GETNUMBER(2),
                         (int ) RING_API_GETNUMBER(3),
                         (Qt::AspectRatioMode )  (int) RING_API_GETNUMBER(4),
                         (Qt::TransformationMode )  (int) RING_API_GETNUMBER(5));

The function that will free the memory takes two parameters (Ring State and the allocated Memory Pointer)


void ring_QPixmap_freefunc(void *pState,void *pPointer)
        QPixmap *pObject ;
        pObject = (QPixmap *) pPointer;
        delete pObject ;

Memory Functions

Ring API provides the next functions for Memory Management

These functions uses the Memory pool provided by Ring VM

  • Use RING_API_MALLOC() instead of the malloc() function.

  • Use RING_API_CALLOC() instead of the calloc() function.

  • Use RING_API_REALLOC() instead of the realloc() function.

  • Use RING_API_FREE() instead of the free() function.

  • RING_API_FREEFUNC provides a function pointer to the ring_state_free() function