Ring/Lessons/Extension using the C/C++ languages
我们可以通过添加用 C 编程语言或 C++ 编写的函数来扩展 Ring 虚拟机 (RingVM)。
RingVM 附带了许多用 C 编写的函数,我们可以像调用任何 Ring 函数一样调用它们。
我们可以通过编写新函数然后重新构建 RingVM 来扩展语言,或者我们可以创建共享库 (DLL/So) 文件来扩展 RingVM,而无需重新构建它。
Ring 语言源代码包含两个文件,用于向 RingVM 添加新模块,ring_ext.h 和 ring_ext.c
文件 ring_ext.h 包含常量,我们可以更改这些常量以在构建过程中包含/排除模块。
#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
#endif
文件 ring_ext.c 在调用每个模块中的启动函数之前检查在 ring_ext.h 中定义的常量。
每个模块都包含一个函数,该函数在 RingVM 中注册模块函数。
#include "ring.h"
void ring_vm_extension ( RingState *pRingState )
{
/* Reflection and Meta-programming */
#if RING_VM_REFMETA
ring_vm_refmeta_loadfunctions(pRingState);
#endif
/* List Functions */
#if RING_VM_LISTFUNCS
ring_vm_listfuncs_loadfunctions(pRingState);
#endif
/* Math */
#if RING_VM_MATH
ring_vm_math_loadfunctions(pRingState);
#endif
/* File */
#if RING_VM_FILE
ring_vm_file_loadfunctions(pRingState);
#endif
/* OS */
#if RING_VM_OS
ring_vm_os_loadfunctions(pRingState);
#endif
/* MySQL */
#if RING_VM_MYSQL
ring_vm_mysql_loadfunctions(pRingState);
#endif
/* ODBC */
#if RING_VM_ODBC
ring_vm_odbc_loadfunctions(pRingState);
#endif
/* OPENSSL */
#if RING_VM_OPENSSL
ring_vm_openssl_loadfunctions(pRingState);
#endif
/* CURL */
#if RING_VM_CURL
ring_vm_curl_loadfunctions(pRingState);
#endif
/* DLL */
#if RING_VM_DLL
ring_vm_dll_loadfunctions(pRingState);
#endif
}
每个模块都从包含 Ring 头文件 (ring.h) 开始。此文件包含我们可以用来扩展 RingVM 的 Ring API。
每个模块都带有一个函数,用于在 RingVM 中注册模块函数。注册是通过使用 ring_vm_funcregister() 函数完成的。
ring_vm_funcregister() 函数接受两个参数,第一个是 Ring 程序用来调用函数的函数名。第二个参数是 C 程序中的函数指针。
例如,ring_vmmath.c 模块包含以下代码来注册模块函数
#include "ring.h"
void ring_vm_math_loadfunctions ( RingState *pRingState )
{
ring_vm_funcregister("sin",ring_vm_math_sin);
ring_vm_funcregister("cos",ring_vm_math_cos);
ring_vm_funcregister("tan",ring_vm_math_tan);
ring_vm_funcregister("asin",ring_vm_math_asin);
ring_vm_funcregister("acos",ring_vm_math_acos);
ring_vm_funcregister("atan",ring_vm_math_atan);
ring_vm_funcregister("atan2",ring_vm_math_atan2);
ring_vm_funcregister("sinh",ring_vm_math_sinh);
ring_vm_funcregister("cosh",ring_vm_math_cosh);
ring_vm_funcregister("tanh",ring_vm_math_tanh);
ring_vm_funcregister("exp",ring_vm_math_exp);
ring_vm_funcregister("log",ring_vm_math_log);
ring_vm_funcregister("log10",ring_vm_math_log10);
ring_vm_funcregister("ceil",ring_vm_math_ceil);
ring_vm_funcregister("floor",ring_vm_math_floor);
ring_vm_funcregister("fabs",ring_vm_math_fabs);
ring_vm_funcregister("pow",ring_vm_math_pow);
ring_vm_funcregister("sqrt",ring_vm_math_sqrt);
ring_vm_funcregister("unsigned",ring_vm_math_unsigned);
ring_vm_funcregister("decimals",ring_vm_math_decimals);
ring_vm_funcregister("murmur3hash",ring_vm_math_murmur3hash);
}
.. tip:: 请记住,函数 ring_vm_math_loadfunctions() 将由 ring_vm_extension() 函数(在 ring_ext.c 文件中)调用。
每个模块函数可能包含以下步骤
1 - 检查参数数量
2 - 检查参数类型
3 - 获取参数值
4 - 执行代码/调用函数
5 - 返回值
该结构与任何函数(输入 - 处理 - 输出)非常相似,但在这里我们将使用 Ring API 来完成步骤 1、2、3 和 5。
我们可以使用 RING_API_PARACOUNT 宏检查参数数量。
我们可以使用 == 或 != 运算符将 RING_API_PARACOUNT 与任何数值进行比较。
示例
if ( RING_API_PARACOUNT != 1 ) {
/* code */
}
示例
if ( RING_API_PARACOUNT == 1 ) {
/* code */
}
我们可以使用 RING_API_ERROR() 函数显示错误消息。
该函数将显示错误并结束程序的执行。
.. note:: 该函数的行为可以通过 Ring 代码使用 Try/Catch/Done 语句更改,因此在您的 C 代码中,请在此函数之后使用 Return。
语法
RING_API_ERROR(const char *cErrorMsg);
Ring API 附带了一些我们可以使用的预定义错误消息
#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!"
我们可以使用以下函数检查参数类型
int RING_API_ISNUMBER(int nParameterNumber);
int RING_API_ISSTRING(int nParameterNumber);
int RING_API_ISLIST(int nParameterNumber);
int RING_API_ISPOINTER(int nParameterNumber);
这些函数的输出将为 1(真)或 0(假)。
我们可以使用以下函数获取参数值
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);
我们可以使用以下函数从函数中返回值。
RING_API_RETNUMBER(double nValue);
RING_API_RETSTRING(const char *cString);
RING_API_RETSTRING2(const char *cString,int nStringSize);
RING_API_RETLIST(List *pList);
RING_API_RETCPOINTER(void *pValue,const char *cPointerType);
当我们定义新的函数用于 RingVM 扩展时,我们使用以下原型
void my_function_name( void *pPointer );
RING_FUNC(my_function_name);
以下代码使用 Ring API 和 sin() C 函数表示 sin() 函数的实现。
void ring_vm_math_sin ( void *pPointer )
{
if ( RING_API_PARACOUNT != 1 ) {
RING_API_ERROR(RING_API_MISS1PARA);
return ;
}
if ( RING_API_ISNUMBER(1) ) {
RING_API_RETNUMBER(sin(RING_API_GETNUMBER(1)));
} else {
RING_API_ERROR(RING_API_BADPARATYPE);
}
}
以下代码使用 Ring API 和 fopen() C 函数表示 fopen() 函数的实现。
该函数接受两个参数,第一个参数是文件名作为字符串。第二个参数是模式作为字符串。
在文件 ring_vmfile.h 中,我们有一些常量可以作为指针类型使用,例如
#define RING_VM_POINTER_FILE "file"
#define RING_VM_POINTER_FILEPOS "filepos"
ring_vmfile.c 中的函数实现
void ring_vm_file_fopen ( void *pPointer )
{
FILE *fp ;
if ( RING_API_PARACOUNT != 2 ) {
RING_API_ERROR(RING_API_MISS2PARA);
return ;
}
if ( RING_API_ISSTRING(1) && RING_API_ISSTRING(2) ) {
fp = fopen(RING_API_GETSTRING(1),RING_API_GETSTRING(2));
RING_API_RETCPOINTER(fp,RING_VM_POINTER_FILE);
} else {
RING_API_ERROR(RING_API_BADPARATYPE);
}
}
以下代码表示 fclose() 函数的实现
void ring_vm_file_fclose ( void *pPointer )
{
FILE *fp ;
if ( RING_API_PARACOUNT != 1 ) {
RING_API_ERROR(RING_API_MISS1PARA);
return ;
}
if ( RING_API_ISPOINTER(1) ) {
fp = (FILE *) RING_API_GETCPOINTER(1,RING_VM_POINTER_FILE) ;
if ( fp != NULL ) {
RING_API_RETNUMBER(fclose(fp));
RING_API_SETNULLPOINTER(1);
}
} else {
RING_API_ERROR(RING_API_BADPARATYPE);
}
}
从 fopen() 和 fclose() 实现中,我们了解到
1 - 如何使用 RING_API_RETCPOINTER() 函数返回 C 指针
2 - 如何使用 RING_API_ISPOINTER() 函数检查参数是否为指针
3 - 如何使用 RING_API_GETCPOINTER() 函数获取 C 指针值
4 - 如何使用 RING_API_SETNULLPOINTER() 函数将 C 指针变量(在 RingVM 中)设置为 NULL
在本节中,我们将学习 Ring API 提供的列表函数,以创建新列表并操作列表项。
List * ring_list_new ( int nSize ) ;
void ring_list_newitem ( List *pList ) ;
Item * ring_list_getitem ( List *pList,int index ) ;
List * ring_list_delete ( List *pList ) ;
void ring_list_deleteitem ( List *pList,int index ) ;
void ring_list_print ( List *pList ) ;
int ring_list_gettype ( List *pList, int index ) ;
void ring_list_setint ( List *pList, int index ,int number ) ;
void ring_list_addint ( List *pList,int x ) ;
void ring_list_setpointer ( List *pList, int index ,void *pValue ) ;
void ring_list_addpointer ( List *pList,void *pValue ) ;
void ring_list_setfuncpointer ( List *pList, int index ,void (*pFunc)(void *) ) ;
void ring_list_addfuncpointer ( List *pList,void (*pFunc)(void *) ) ;
int ring_list_isfuncpointer ( List *pList, int index ) ;
void ring_list_setdouble ( List *pList, int index ,double number ) ;
void ring_list_adddouble ( List *pList,double x ) ;
void ring_list_setstring ( List *pList, int index ,const char *str ) ;
void ring_list_setstring2 ( List *pList, int index ,const char *str,int nStrSize ) ;
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 ) ;
List * ring_list_getlist ( List *pList, int index ) ;
void ring_list_setlist ( List *pList, int index ) ;
void ring_list_setactiveitem ( List *pList, Items *pItems, int index ) ;
void ring_list_copy ( List *pNewList, List *pList ) ;
int ring_list_isnumber ( List *pList, int index ) ;
int ring_list_isstring ( List *pList, int index ) ;
int ring_list_islist ( List *pList, int index ) ;
int ring_list_ispointer ( List *pList, int index ) ;
void ring_list_deleteallitems ( List *pList ) ;
void ring_list_insertitem ( List *pList,int x ) ;
void ring_list_insertint ( List *pList,int nPos,int x ) ;
void ring_list_insertdouble ( List *pList,int nPos,double x ) ;
void ring_list_insertpointer ( List *pList,int nPos,void *pValue ) ;
void ring_list_insertstring ( List *pList,int nPos,const char *str ) ;
void ring_list_insertstring2 ( List *pList,int nPos,const char *str,int nStrSize ) ;
void ring_list_insertfuncpointer ( List *pList,int nPos,void (*pFunc)(void *) ) ;
List * ring_list_insertlist ( List *pList,int nPos ) ;
int ring_list_isiteminsidelist ( List *pList,Item *pItem ) ;
int ring_list_findstring ( List *pList,const char *str,int nColumn ) ;
int ring_list_finddouble ( List *pList,double nNum1,int nColumn ) ;
void ring_list_sortnum ( List *pList,int left,int right,int nColumn ) ;
void ring_list_sortstr ( List *pList,int left,int right,int nColumn ) ;
int ring_list_binarysearchnum ( List *pList,double nNum1,int nColumn ) ;
int ring_list_binarysearchstr ( List *pList,const char *cFind,int nColumn ) ;
void ring_list_swap ( List *pList,int x,int y ) ;
double ring_list_getdoublecolumn ( List *pList,int nIndex,int nColumn ) ;
char * ring_list_getstringcolumn ( List *pList,int nIndex,int nColumn ) ;
void ring_list_genarray ( List *pList ) ;
void ring_list_deletearray ( List *pList ) ;
void ring_list_genhashtable ( List *pList ) ;
void ring_list_genhashtable2 ( List *pList ) ;
void ring_list_refcopy ( List *pNewList, List *pList ) ;
void ring_list_clear ( List *pList ) ;
/* Macro */
ring_list_isdouble(pList,index)
ring_list_isint(pList,index)
ring_list_deletelastitem(x)
ring_list_gethashtable(x)
ring_list_getint(pList,index)
ring_list_getpointer(pList,index)
ring_list_getfuncpointer(pList,index)
ring_list_callfuncpointer(pList,index,x)
ring_list_getdouble(pList,index)
ring_list_getstring(pList,index)
ring_list_getstringobject(pList,index)
ring_list_getstringsize(pList,index)
ring_list_getsize(x) (x->nSize)
在本节中,我们将学习 Ring API 提供的字符串函数,用于创建新字符串和操作字符串内容。
String * ring_string_new ( const char *str ) ;
String * ring_string_new2 ( const char *str,int nStrSize ) ;
String * ring_string_delete ( String *pString ) ;
int ring_string_size ( String *pString ) ;
void ring_string_set ( String *pString,const char *str ) ;
void ring_string_set2 ( String *pString,const char *str,int nStrSize ) ;
void ring_string_add ( String *pString,const char *str ) ;
void ring_string_add2 ( String *pString,const char *str,int nStrSize ) ;
void ring_string_print ( String *pString ) ;
void ring_string_setfromint ( String *pString,int x ) ;
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 ) ;
char * ring_string_find ( char *cStr1,char *cStr2 ) ;
char * ring_string_find2 ( char *cStr1,int nStrSize1,char *cStr2,int nStrSize2 ) ;
/* Macro */
ring_string_tolower(x)
ring_string_toupper(x)
ring_string_get(x)
以下代码展示了 MySQL_Columns() 函数的实现。
此函数返回表列信息。
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 ) {
RING_API_ERROR(RING_API_MISS1PARA);
return ;
}
if ( RING_API_ISPOINTER(1) ) {
con = (MYSQL *) RING_API_GETCPOINTER(1,RING_VM_POINTER_MYSQL) ;
if ( con == NULL ) {
return ;
}
result = mysql_store_result(con);
if ( result == NULL ) {
RING_API_RETNUMBER(0);
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(pList);
ring_list_addstring(pList2,field->name);
ring_list_adddouble(pList2,field->length);
ring_list_adddouble(pList2,field->type);
ring_list_adddouble(pList2,field->flags);
}
}
mysql_free_result(result);
RING_API_RETLIST(pList);
} else {
RING_API_ERROR(RING_API_BADPARATYPE);
}
}
列表类型为 List,在前面的函数中我们声明了两个 List 类型的指针,使用 List *pList, *pList2;
.. 注意:: 该函数使用 RING_API_NEWLIST 来创建新的列表,而不是使用 ring_list_new() 来在与函数作用域相关的临时内存中创建列表。这样,我们就可以从函数中返回列表。此外,我们不会删除列表,如果它存储在 Ring 代码中的变量中,它将被保存,否则它将被 RingVM 自动删除。
列表可以包含子列表,我们使用 ring_list_newlist() 函数创建子列表。
ring_list_addstring() 函数用于向列表/子列表添加字符串项。
ring_list_adddouble() 函数用于向列表/子列表添加数值项。
.. 注意:: 从 RingVM 扩展函数返回的所有数值项都必须是 double 类型,并使用 ring_list_adddouble() 函数添加到列表中。
我们使用 RING_API_RETLIST() 函数从扩展函数中返回列表。
在使用 C/C++ 和 Ring API 编写新函数后,不必重新构建 RingVM,我们可以创建一个 DLL/So 文件,并使用 LoadLib() 函数在运行时动态地使用该文件提供的函数。
C 语言中的动态库示例
#include "ring.h"
RING_DLL __declspec(dllexport)
RING_FUNC(ring_ringlib_dlfunc)
{
printf("Message from dlfunc");
}
RING_DLL void ringlib_init(RingState *pRingState)
{
ring_vm_funcregister("dlfunc",ring_ringlib_dlfunc);
}
想法是创建 ringlib_init() 函数,当我们通过 LoadLib() 函数使用生成的 DLL 文件时,RingVM 将调用此函数。
在 ringlib_init() 函数中,我们可以注册模块函数或调用执行所有模块函数注册过程的函数。
以下 Ring 代码演示了如何在运行时使用 DLL 库。
See "Dynamic DLL" + NL
LoadLib("ringlib.dll")
dlfunc()
输出
Dynamic DLL
Message from dlfunc