跳转到内容

Ring/Lessons/Extension using the C/C++ languages

来自 Wikibooks,开放世界中的开放书籍

使用 C/C++ 语言扩展

[编辑 | 编辑源代码]

我们可以通过添加用 C 编程语言或 C++ 编写的函数来扩展 Ring 虚拟机 (RingVM)。

RingVM 附带了许多用 C 编写的函数,我们可以像调用任何 Ring 函数一样调用它们。

我们可以通过编写新函数然后重新构建 RingVM 来扩展语言,或者我们可以创建共享库 (DLL/So) 文件来扩展 RingVM,而无需重新构建它。

Ring 语言源代码包含两个文件,用于向 RingVM 添加新模块,ring_ext.h 和 ring_ext.c

ring_ext.h

[编辑 | 编辑源代码]

文件 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.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);



Sin() 函数实现

[编辑 | 编辑源代码]

以下代码使用 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);
		}
	}



Fopen() 和 Fclose() 函数实现

[编辑 | 编辑源代码]

以下代码使用 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 - 列表函数

[编辑 | 编辑源代码]

在本节中,我们将学习 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 - 字符串函数

[编辑 | 编辑源代码]

在本节中,我们将学习 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() 函数实现

[编辑 | 编辑源代码]

以下代码展示了 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() 函数从扩展函数中返回列表。

动态/共享库 (DLL/So) 和 LoadLib() 函数

[编辑 | 编辑源代码]

在使用 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


华夏公益教科书