跳轉到內容

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


華夏公益教科書