Вы находитесь на странице: 1из 10

Ring Documentation, Release 1.

ring_state_delete(pState2);
}

Output:
welcome
hello world from the ring programming language
Again from C we will call ring code
1
2
3
4
5
6
7
8
9
10
1
2
3
4
5
Now we will display the x variable value from ring code
x value : 11
x value : 6
Printing Ring variable value from C , 11
now we will set the ring variable value from C
x value after update : 20
v1 + v2 = 30
end of test

61.3. Ring State Variables 588


CHAPTER

SIXTYTWO

CODE GENERATOR FOR WRAPPING C/C++ LIBRARIES

In this chapter we will learn how to use the code generator to wrap C/C++ Libraries to use it in our Ring applications.

62.1 Using the tool

The code generator program is parsec.ring that can be executed as any ring code using the ring language.
URL : https://github.com/ring-lang/ring/tree/master/extensions/codegen
for example to read a configuration file called test.cf to generate the source code file test.c run parsec.ring as in the
next command
ring parsec.ring test.cf test.c

62.2 Configuration file

The configuration file (*.cf) is the input file that we pass to the code generator. This file determine the functions
prototypes that we need to use from a C/C++ library.
Writing configuration files is simple according to the next rules

62.3 Using the function prototype

To generate code that wraps a C function, we just write the C function prototype
Example:
ALLEGRO_DISPLAY *al_create_display(int w, int h)
void al_destroy_display(ALLEGRO_DISPLAY *display)
int al_get_new_display_flags(void)
void al_set_new_display_flags(int flags)
int al_get_new_display_option(int option, int *importance)

The previous example will guide the code generator to generate 5 functions that wraps the al_create_display(),
al_destroy_display(), al_get_new_display_flags(), al_set_new_diplay_flas() and al_get_new_display_option() func-
tions.
The generated code will be as in the next example

589
Ring Documentation, Release 1.3

RING_FUNC(ring_al_create_display)
{
if ( RING_API_PARACOUNT != 2 ) {
RING_API_ERROR(RING_API_MISS2PARA);
return ;
}
if ( ! RING_API_ISNUMBER(1) ) {
RING_API_ERROR(RING_API_BADPARATYPE);
return ;
}
if ( ! RING_API_ISNUMBER(2) ) {
RING_API_ERROR(RING_API_BADPARATYPE);
return ;
}
RING_API_RETCPOINTER(al_create_display( (int ) RING_API_GETNUMBER(1),
(int ) RING_API_GETNUMBER(2)),"ALLEGRO_DISPLAY");
}

RING_FUNC(ring_al_destroy_display)
{
if ( RING_API_PARACOUNT != 1 ) {
RING_API_ERROR(RING_API_MISS1PARA);
return ;
}
if ( ! RING_API_ISPOINTER(1) ) {
RING_API_ERROR(RING_API_BADPARATYPE);
return ;
}
al_destroy_display((ALLEGRO_DISPLAY *) RING_API_GETCPOINTER(1,"ALLEGRO_DISPLAY"));
}

RING_FUNC(ring_al_get_new_display_flags)
{
if ( RING_API_PARACOUNT != 0 ) {
RING_API_ERROR(RING_API_BADPARACOUNT);
return ;
}
RING_API_RETNUMBER(al_get_new_display_flags());
}

RING_FUNC(ring_al_set_new_display_flags)
{
if ( RING_API_PARACOUNT != 1 ) {
RING_API_ERROR(RING_API_MISS1PARA);
return ;
}
if ( ! RING_API_ISNUMBER(1) ) {
RING_API_ERROR(RING_API_BADPARATYPE);
return ;
}
al_set_new_display_flags( (int ) RING_API_GETNUMBER(1));
}

RING_FUNC(ring_al_get_new_display_option)

62.3. Using the function prototype 590


Ring Documentation, Release 1.3

{
if ( RING_API_PARACOUNT != 2 ) {
RING_API_ERROR(RING_API_MISS2PARA);
return ;
}
if ( ! RING_API_ISNUMBER(1) ) {
RING_API_ERROR(RING_API_BADPARATYPE);
return ;
}
if ( ! RING_API_ISSTRING(2) ) {
RING_API_ERROR(RING_API_BADPARATYPE);
return ;
}
RING_API_RETNUMBER(al_get_new_display_option( (int ) RING_API_GETNUMBER(1),
RING_API_GETINTPOINTER(2)));
RING_API_ACCEPTINTVALUE(2) ;
}

from the previous example we can see how much of time and effort is saved using the Code Generator.

62.4 Adding code to the generated code

To generate code directly type it between <code> and </code>


Example :
<code>
/* some C code will be written here */
</code>

We use this feature when we need to do something without the help of the code generator. for example including
header files and defining constants using Macro.

62.5 Prefix for Functions Names

To determine a prefix in all of the functions names type it between <funcstart> and </funcstart> for ex-
ample when we wrap the Allegro game programming library and we need all of the library functions
to start with al we type the next code in the configuration file
<funcstart>
al
</funcstart>

62.6 Generate function to wrap structures

To generate functions that wrap structures (create/delete/get structure members)


just type the structures names between <struct> and </struct> also after the structure name you can type the structure
members between { } separated by comma.
Example

62.4. Adding code to the generated code 591


Ring Documentation, Release 1.3

<struct>
ALLEGRO_COLOR
ALLEGRO_EVENT { type , keyboard.keycode , mouse.x , mouse.y }
</struct>

from the previous example we will generate two function to create/delete the structure ALLEGRO_COLOR Also we
will generate two functions to create/delete the structure ALLEGRO_EVENT and four functions to get the structure
ALLEGRO_EVENT members (type, keyboard.keycode, mouse.x, mouse.y).

62.7 Determine Structure Members Types

You can determine the pointer name before the strucuture member name.
Example:
SDL_Surface {flags,SDL_PixelFormat *format,w,h,pitch,void *pixels,void *userdata,locked,void *lock_da

62.8 Defining Contants

You can define constatants using <constant> and </constant>


The generator will generate the required functions to get the constant values
And will define the constants to be used with the same name in Ring code using *.rh file that will be generated too.
rh = Ring Header
Example:
<constant>
MIX_DEFAULT_FORMAT
SDL_QUIT
SDL_BUTTON_LEFT
SDL_BUTTON_MIDDLE
SDL_BUTTON_RIGHT
</constant>

Note: You will need to pass the *.rh file name to parsec.ring after the generated source file name.

Example:
ring ..\codegen\parsec.ring libsdl.cf ring_libsdl.c ring_libsdl.rh

62.9 Register New Functions

We can register functions by typing the function prototype between <register> and </register> We need this feature
only when we dont provide the function prototype as input directly where we need to write the code of this function.
Example:
<register>
void al_exit(void)
</register>

62.7. Determine Structure Members Types 592


Ring Documentation, Release 1.3

<code>
RING_FUNC(ring_al_exit)
{
if ( RING_API_PARACOUNT != 0 ) {
RING_API_ERROR(RING_API_BADPARACOUNT);
return ;
}
exit(0);
}
</code>

In the previous example we register the al_exit() function. This function is not part of the Allegro Library, its just an
extra function that we need to add. Then the code if this function is written inside <code> and </code>. This function
call the exit() function from the C language library.

62.10 Writing comments in the configuration file

To type comments just type it between <comment> and </comment>


Example:
<comment>
configuration files
</comment>

62.11 Executing code during code generation

To ask from the code generator to execute Ring code during reading the configuration file, just
write the code between <runcode> and </runcode>
Example:
<runcode>
aNumberTypes + "al_fixed"
</runcode>

The previoud line of code add the string al_fixed to the list aNumberTypes, This list contains types that can be
considered as numbers when the code generator find it in the function prototype.

62.12 Enum and Numbers

We have the list aEnumTypes to use for adding each Enumeration we uses in the functions prototype.
Example:
<runcode>
aNumberTypes + "qreal"
aNumberTypes + "qint64"
aEnumTypes + "Qt::GestureType"
aEnumTypes + "Qt::GestureFlag"
</runcode>

62.10. Writing comments in the configuration file 593


Ring Documentation, Release 1.3

62.13 Filtering using Expressions

using <filter> and </filter> we can include/exclude parts of the configuration file based on a condition, for example
<filter> iswindows()
... functions related to windows
</filter>

62.14 Configuration file for the Allegro Library

The next configuration file enable us to use the Allegro library functions. The configuration file size is less than 1000
lines. when the code generator take this file as input the generated source code file in the C language will be 12000
lines of code!
We can see this configuration file as a complete example about using the code generator Also we can use it to know
the functions that can be used from RingAllegro when you use it to create 2D games!
<code>
#define ALLEGRO_NO_MAGIC_MAIN

#include <allegro5/allegro.h>
#include "allegro5/allegro_image.h"
#include <allegro5/allegro_font.h>
#include <allegro5/allegro_ttf.h>
#include <allegro5/allegro_audio.h>
#include <allegro5/allegro_acodec.h>
#include <allegro5/allegro_opengl.h>
#include <allegro5/allegro_direct3d.h>
#include <allegro5/allegro_color.h>
#include <allegro5/allegro_memfile.h>
#include "allegro5/allegro_native_dialog.h"
#include <allegro5/allegro_physfs.h>
#include <allegro5/allegro_primitives.h>
</code>

<funcstart>
al
</funcstart>

<struct>
ALLEGRO_EVENT { type , keyboard.keycode , mouse.x , mouse.y }
ALLEGRO_TIMEOUT
ALLEGRO_SAMPLE_ID
ALLEGRO_COLOR
</struct>

<register>
void al_exit(void)
</register>

<code>
RING_FUNC(ring_al_exit)
{
if ( RING_API_PARACOUNT != 0 ) {
RING_API_ERROR(RING_API_BADPARACOUNT);
return ;

62.13. Filtering using Expressions 594


Ring Documentation, Release 1.3

}
exit(0);
}
</code>

int al_init(void)

<comment>
configuration files
</comment>

<runcode>
aNumberTypes + "al_fixed"
</runcode>

ALLEGRO_CONFIG *al_create_config(void)
void al_destroy_config(ALLEGRO_CONFIG *config)
ALLEGRO_CONFIG *al_load_config_file(const char *filename)
ALLEGRO_CONFIG *al_load_config_file_f(ALLEGRO_FILE *file)
bool al_save_config_file(const char *filename, const ALLEGRO_CONFIG *config)
bool al_save_config_file_f(ALLEGRO_FILE *file, const ALLEGRO_CONFIG *config)
void al_add_config_section(ALLEGRO_CONFIG *config, const char *name)

Note: we just provided part of the configuration file, for complete copy check the Ring source code distribution.

62.15 Threads Support

Next, another part of the configutaiton file, its important because we can learn from it how to add threads to our Ring
applications by using a threads library.
The idea is using ring_vm_mutexfunctions() and ring_vm_runcodefromthread() to execute Ring code.
<comment>
Threads
</comment>

<code>
void *al_func_thread(ALLEGRO_THREAD *thread, void *pPointer)
{
List *pList;
VM *pVM;
const char *cStr;
pList = (List *) pPointer ;
pVM = (VM *) ring_list_getpointer(pList,2);
cStr = ring_list_getstring(pList,1);
ring_vm_runcodefromthread(pVM,cStr);
ring_list_delete(pList);
return NULL;
}

RING_FUNC(ring_al_create_thread)
{
ALLEGRO_THREAD *pThread;
List *pList;
if ( RING_API_PARACOUNT != 1 ) {

62.15. Threads Support 595


Ring Documentation, Release 1.3

RING_API_ERROR(RING_API_MISS1PARA);
return ;
}
if ( ! RING_API_ISSTRING(1) ) {
RING_API_ERROR(RING_API_BADPARATYPE);
return ;
}
pList = ring_list_new(0);
ring_list_addstring(pList,RING_API_GETSTRING(1));
ring_list_addpointer(pList,pPointer);
ring_vm_mutexfunctions((VM *) pPointer,al_create_mutex,
al_lock_mutex,al_unlock_mutex,al_destroy_mutex);
pThread = al_create_thread(al_func_thread, pList);
al_start_thread(pThread);
RING_API_RETCPOINTER(pThread,"ALLEGRO_THREAD");
}

RING_FUNC(ring_al_run_detached_thread)
{
List *pList;
if ( RING_API_PARACOUNT != 1 ) {
RING_API_ERROR(RING_API_MISS1PARA);
return ;
}
if ( ! RING_API_ISSTRING(1) ) {
RING_API_ERROR(RING_API_BADPARATYPE);
return ;
}
pList = ring_list_new(0);
ring_list_addstring(pList,RING_API_GETSTRING(1));
ring_list_addpointer(pList,pPointer);
ring_vm_mutexfunctions((VM *) pPointer,al_create_mutex,
al_lock_mutex,al_unlock_mutex,al_destroy_mutex);
al_run_detached_thread(al_func_thread, pList);
}
</code>

<register>
ALLEGRO_THREAD *al_create_thread(void)
void al_run_detached_thread(void)
</register>

void al_start_thread(ALLEGRO_THREAD *thread)


void al_join_thread(ALLEGRO_THREAD *thread, void **ret_value)
void al_set_thread_should_stop(ALLEGRO_THREAD *thread)
bool al_get_thread_should_stop(ALLEGRO_THREAD *thread)
void al_destroy_thread(ALLEGRO_THREAD *thread)
ALLEGRO_MUTEX *al_create_mutex(void)
ALLEGRO_MUTEX *al_create_mutex_recursive(void)
void al_lock_mutex(ALLEGRO_MUTEX *mutex)
void al_unlock_mutex(ALLEGRO_MUTEX *mutex)
void al_destroy_mutex(ALLEGRO_MUTEX *mutex)
ALLEGRO_COND *al_create_cond(void)
void al_destroy_cond(ALLEGRO_COND *cond)
void al_wait_cond(ALLEGRO_COND *cond, ALLEGRO_MUTEX *mutex)

62.15. Threads Support 596


Ring Documentation, Release 1.3

62.16 Code Generator Rules for Wrapping C++ Classes

We can define classes between <class> and </class>


Between <class> and <class> we set attributes like name, nonew, para, parent, codename, passvmpointer and
abstract
we set the attributes using the style attributename:value or attributename only if no values are required
The name attribute determine the class name in C++ code and this name will be the default name in the Ring
code
The nonew instruction means that we dont need new/delete methods
The parent attribute determine the parent class name
The codename attribute determine another class name in C++ code
The passvmpoint instruction means passing the Ring VM pointer to the class constructor when we create new
objects, this happens when we set the codename attribute to a class the we will define and this class need the
Virtual Machine pointer (for example to use it to execute Ring code from C++ code).
The abstract instruction means that no new method is required for this class no objects will be created.
Using <nodllstartup> we can avoid #include ring.h, We need this to write our startup code.
Using <libinitfunc> we can change the function name that register the library functions
Using <ignorecpointertype> we can ignore pointer type check
Using the aStringTypes list when can defined new types that treated like const char *
Using the aBeforeReturn list when can define code that is inserted after the variable name when we return that
variable from a function
Using the aNewMethodName list we can define another method name to be used in Ring code when we call
the C++ method. this feature is required because some C++ method may be identical to Ring Keywords like
load,next,end and done.
in method prototype - when we use @ in the method name, we mean that we have the same method with different
parameters (As in C++)

62.17 Using configuration file that wrap C++ Library

To run the code generator to generate code for using C++ library in the Ring application, we can do that as we did
with using C libraries but here we will generate .cpp file instead of *.c file. Also we will determine another file to be
generated (.ring). This file will contains classes in Ring code that wraps C++ functions for using C++ classes and
objects.
ring parsec.ring qt.cf ring_qt.cpp ring_qt.ring

62.18 Configuration file for the Qt Framework

The next configuration file is used to wrap many Qt classes The configuration file is around 3500 lines and generate
C++ code around 56000 lines and generate also Ring code around 9000 lines.

62.16. Code Generator Rules for Wrapping C++ Classes 597

Вам также может понравиться