User Tools

Site Tools


prototyping_3gl_functions_in_clarion.htm
Navigation:  Advanced Topics > Multi Language Programming >====== Prototyping Functions in Clarion ====== Previous pageReturn to chapter overviewNext page

The only thing necessary to be able to use any of the C standard library functions in Clarion code is the addition of the function's Clarion language prototype to the Clarion application's MAP structure. The Clarion prototype tells the compiler and linker what types of parameters are passed and what return data type (if any) to expect from the C function. The PROCEDURE Prototypes section in Chapter 2 of Clarion's Language Reference discusses the syntax and attributes required to create a prototype of a Clarion procedure. This same syntax is used to create Clarion prototypes of C functions.

There are four important considerations involved in creating a prototype for a C function: calling convention, naming convention, parameter passing, and return data types from functions.

The calling convention for all the SoftVelocity C standard library functions is the same register-based calling convention used by Clarion. Therefore, there is no need to use the C or PASCAL attributes in any standard C library function's Clarion prototype.

The SoftVelocity C compiler's naming convention is the normal C convention. This means an underscore is automatically prepended to the function name when compiled. The Clarion NAME attribute is usually used in the prototype to give the linker the correct reference to a C function without requiring the Clarion code to use the prepended underscore. For example, the C function “access” is actually named “_access” by the compiler. Therefore, the NAME('_access') attribute is required in the prototype (unless you want to refer to the function in Clarion code as “_access”).

Each parameter passed to a C function must appear in its Clarion prototype as the data type of the passed parameter. Parameters are passed in Clarion either “by value” or “by address.”

When a parameter is passed “by value,” a copy of the data is received by the function. The passed parameter is represented in the prototype as the data type of the parameter. When passed “by address,” the memory address of the data is received by the function. The parameter is represented in the prototype as the data type of the parameter with a prepended asterisk (*). This corresponds to passing the C function the pointer to the data.

Parameter Data Types

Parameter data type translation is the “key” to prototyping C functions in Clarion. The following is a table of C data types and the Clarion data type which should be used in the prototype:

C Data Type Clarion Data Type
Char BYTE (gets linker warnings - ignore them)
unsigned char BYTE
int SHORT
unsigned int USHORT
short SHORT
unsigned short USHORT
long LONG
unsigned long ULONG
float SREAL
double REAL
unsigned char * *BYTE
int * *SHORT
unsigned int * *USHORT
short * *SHORT
unsigned short * *USHORT
long * *LONG
unsigned long * *ULONG
float * *SREAL
double * *REAL
char * *CSTRING w/ RAW attribute
struct * *GROUP w/ RAW attribute

Since the Clarion language does not have a signed BYTE data type, linker warnings ('type inconsistency') will result when you prototype a function which receives a char parameter. As long as you are aware that the C function is expecting a signed value, and correctly adjust the BYTE field's bitmap to pass a value in the range -128 to 127, this warning may be safely ignored.

The RAW attribute must be used when a C function expects to receive the address of a CSTRING or GROUP parameter. By default, Clarion STRING, CSTRING, PSTRING, and GROUP parameters are passed (internally) to other Clarion procedures as both the address and length of the string. C functions do not usually want or need the length, and expect to receive only the address of the data. Therefore, the RAW attribute overrides this default.


If the C function returns void, there is no data returned and the function fits the definition of a Clarion PROCEDURE. If the C function does return data, it is prototyped with the actual data type returned and the function fits the definition of a Clarion PROCEDURE that returns a value and may be called as part of a condition, assignment, or parameter list.

Return Data Types

Return data types from C functions are almost the same as passed parameters:

C Return Type Clarion Return Type
char BYTE (gets linker warnings - ignore them)
unsigned char BYTE
int SHORT
unsigned int USHORT
short SHORT
unsigned short USHORT
long LONG
unsigned long ULONG
float SREAL
double REAL
unsigned char * *BYTE
int * *SHORT
unsigned int * *USHORT
short * *SHORT
unsigned short * *USHORT
long * *LONG
unsigned long * *ULONG
float * *SREAL
double * *REAL
char * CSTRING (pointer automatically dereferenced)
struct * ULONG (gets linker warnings - ignore them)

As you can see, the Clarion return type for a char * is CSTRING (not *CSTRING as you might expect). This is because the Clarion compiler automatically dereferences the pointer to the data when the function returns (as it does with all the pointer return types).

Notice that the Clarion return data type for struct * is ULONG. This will generate a “type inconsistency” linker warning. This occurs because the Clarion language does not use pointers, and the ULONG is a four-byte integer which can serve as a replacement for a pointer return type. The warning is not a problem and can be safely ignored. You would probably use memcpy() to get at the returned data.

Passing Parameters

Clarion offers two distinct methods of passing parameters to functions or procedures: “passed by value” and “passed by address.”

“Passed by value” means that the calling code passes a copy of the data to the called function or procedure. The called code can then operate on the data without affecting the caller's copy of the data. These parameters are specified by the parameter's data type in the prototype.

“Passed by address” means that the calling code passes the address of the data to the called function or procedure. With this method, the called function or procedure can modify the caller's data. These parameters are specified by prefixing the parameter's data type with an asterisk (*) in the prototype:

MAP

MODULE('My_C_Lib')

Var_Parameter(*USHORT)          ! Parameter passed by address

Val_Parameter(USHORT)           ! Parameter passed by value

END

END

These declarations represent the Clarion interface to the functions contained in the C library My_C_Lib. The following example are the equivalent C declarations:

void    Var_Parameter(CLAUSHORT *uspVal);

void    Val_Parameter(CLAUSHORT usVal);

Clarion parameters “passed by address” are equivalent to pointers to the relevant C type. Clarion “passed by value” parameters are passed in the same way as C and C++ value parameters.

The corresponding Modula-2 definition module would be:

DEFINITION MODULE M2_Code;

IMPORT Cla;

PROCEDURE Var_Parameter(VAR us: Cla.USHORT);

PROCEDURE Val_Parameter(us: Cla.USHORT);

END M2_Code.

The corresponding Pascal interface unit would be:

INTERFACE UNIT Pas_Code;

IMPORT Cla;

PROCEDURE Var_Parameter(VAR us: Cla.USHORT);

PROCEDURE Val_Parameter(us: Cla.USHORT);

END.

You cannot pass a Clarion STRING or GROUP by value. For this reason, you must pass STRINGs or GROUPs by address.

Resolving Calling Conventions

Clarion uses the SoftVelocity object code generator, so it uses the same efficient register-based parameter passing mechanism employed by all SoftVelocity languages. If differing calling conventions are used by code compiled by third-party compilers, the results may be unpredictable. Typically, the application will fail at run-time.

To use code produced by compilers other than SoftVelocity, you must ensure that either:

1) The other compiler generates code using Clarion's (SoftVelocity's) parameter passing method, or,

2)  That Clarion generates code using the other compiler's parameter method.

You must also ensure that none of the functions return floating-point data types. There is no standard of compatibility between compilers regarding this issue. For example, Microsoft C returns floating-point values in a global variable while Borland C returns them on the stack (SoftVelocity also returns them on the stack but there is no guarantee of compatibility). Therefore, any functions from non-SoftVelocity compilers which must reference floating point values and modify them should receive them “passed by address” and directly modify the value ' do not have the function return the value.

Most other compilers don't provide Clarion-compatible parameter passing conventions, but do provide standard C and Pascal parameter passing mechanisms (passed on the stack). Clarion has the C and PASCAL procedure prototype attributes to specify stack-based parameter passing.

Most non-SoftVelocity C and C++ compilers use a calling convention where parameters are pushed onto the stack from right to left (as read from the parameter list). The Clarion C attribute specifies this convention. Many C and C++ compilers also offer a Pascal calling convention where parameters are pushed left to right from the parameter list. Most other languages on the PC also use this convention. The Clarion PASCAL attribute generates calls using this convention.

In most cases, the C and PASCAL attributes are used in conjunction with the NAME attribute. This is because many compilers prepend an underscore to function names where the C convention is in use, and uppercase function names where the PASCAL convention is in use (Clarion uppercases procedure names also). For example:

MAP

MODULE('My_C_Lib')

StdC_Conv(UNSIGNED, ULONG), C, NAME('_StdC_Conv')

StdPascal_Conv(UNSIGNED, ULONG), PASCAL, NAME('STDPASCAL_CONV')

END

END

When the StdC_Conv procedure is called, the ULONG parameter is pushed on the stack followed by the UNSIGNED parameter. When StdPascal_Conv is called, the UNSIGNED parameter is pushed followed by the ULONG parameter. You should be very careful that calling conventions match, otherwise the program may behave unpredictably. When interfacing with code produced by SoftVelocity compilers, the C and PASCAL calling convention attributes are not necessary because Clarion uses the SoftVelocity register-based calling conventions.

When writing SoftVelocity C functions to be called from a Clarion program, the CLA_CONV macro (discussed above) should be used to select the correct naming conventions. The best way of achieving this is to declare any interface functions in a separate header (.H) file and to apply the conventions to these declarations. C++ functions must be declared using “Pascal” external linkage (also discussed above). Modula-2 and Pascal naming conventions are best handled by using the NAME attribute on the prototype.

Resolving Naming Conventions

When linking code produced from different programming tools, it is essential to ensure that the proper naming conventions are used. If differing naming conventions are used, the linker will not be able to resolve references to a name within code (produced by one compiler) and its definition (within code produced by another compiler). In this case, no .EXE will be generated.

Many C compilers (including SoftVelocity) prepend an underscore to the name of each function or variable name. The Clarion NAME attribute simplifies interfacing with code produced by these compilers by explicitly telling the Clarion compiler the function or procedure name to generate for the linker. This allows you to explicitly code the Clarion prototype to follow the C convention. For example:

MAP

MODULE('My_C_Lib')

StdStr_Parm(STRING), NAME('_StdStr_Parm')

END

END

When the Clarion compiler encounters the StdStr_Parm() procedure, it generates the name _StdStr_Parm in the object code. Although Clarion names are not case sensitive, the name generated using the NAME attribute will appear exactly as specified.

The following C language macro defines the Clarion naming conventions. This macro can be used when declaring C functions to interface with Clarion in order to force the C compiler to generate names following the Clarion naming convention (no prepended underscore and all upper case).

#define CLA_CONV name(prefix⇒“”, upper_case⇒on)

C++ compilers encode the return and parameter types of a procedure into the name that appears in the object code in a process known as 'name mangling'. Therefore, C++ compiled functions which may be called from Clarion can be declared within a 'extern “Pascal” {…};' modifier, which is the equivalent to the C language CLA_CONV macro (which does not affect the name mangling employed by the C++ compiler). For example:

extern “Pascal” void Clarion_Callable_Proc(void);

A more flexible form of the above, allowing for compilation by either a C or C++ compiler, is:

#ifdef cplusplus extern “Pascal” {               /* Force Clarion conventions in C++ */ #else #pragma save, CLA_CONV          /* Force Clarion conventions in C   */ #endif void Clarion_Callable_Proc(void);   /* C or C++ declaration   */ #ifdef cplusplus

}                               /* Restore C++ conventions     */

#else

#pragma restore                 /* Restore C conventions      */

#endif

This form of declaration usually appears in a header file to be included by any interface code. It ensures that the correct conventions are used when compiled with a C or C++ compiler and eliminates the need to use the NAME attribute on the Clarion language prototype of the procedure or function.

Clarion is a case-insensitive language and the compiler converts the names of all procedures to upper-case. Modula-2 and Pascal, however, are case sensitive and also prefix the name of all procedure names with the name of the module in the form: MyModule$MyProcedure. The way to resolve these differences is to use Clarion's NAME attribute to specify the full name of the Modula-2 or Pascal procedure to the Clarion compiler:

MAP

MODULE('M2_Code')

M2_Proc1(*GROUP), RAW, NAME('M2_Code$M2_Proc2')

END

MODULE('Pas_Code')

Pas_Proc1(*GROUP), RAW, NAME('Pas_Code$Pas_Proc2')

END

END

The corresponding Modula-2 definition module might be:

DEFINITION MODULE M2_Code;

TYPE

GROUP = RECORD

(* Members *)

END;

PROCEDURE M2_Proc1(VAR Data: GROUP);

END M2_Code.

The corresponding Pascal interface unit might be:

INTERFACE UNIT Pas_Code;

TYPE

GROUP = RECORD

(* Members *)

END;

PROCEDURE Pas_Proc1(VAR Data: GROUP);

END.

The naming conventions used by Clarion for data differ from those used for PROCEDURES, and are more complex. Therefore, the NAME() attribute should be used to generate a Modula-2 or Pascal-compatible name for any Clarion data that needs to be accessed between languages. Modula-2 and Pascal data names are case sensitive and prefixed with the name of the module and a '@' in the form: MyModule@MyProc.

The EXTERNAL and DLL Attributes

The EXTERNAL attribute is used to declare Clarion variables and functions that are defined in an external library. The DLL attribute declares that an EXTERNAL variable or functions is defined in a Dynamic Link Library (DLL).

These attributes provide Clarion programs with a means of accessing public data in external libraries. The compiler will not reserve space for any variables declared as EXTERNAL. For example:

typedef struct {

unsigned long   ul1;

unsigned long   ul2;

}   StructType;

#ifdef cplusplus extern “C” {     /* Use C naming conventions, which will require use */ #endif           /* of the NAME attribute in the Clarion prototype   */ StructType  Str1;         /* Define Str1                */ StructType  Str2;         /* Define Str2                */ #ifdef cplusplus

}                         /* Restore C++ conventions    */

#endif

The following Clarion declarations are all that is necessary to make Str1 and Str2 available to Clarion programs.

StructType  GROUP,TYPE  ! Declare a user defined type

ul1           ULONG

ul2           ULONG

END

! Declare Str1 and Str2 which are defined in the C module

Str1        LIKE(StructType),NAME('_Str1'),EXTERNAL

Str2        LIKE(StructType),NAME('_Str2'),EXTERNAL

The NAME attribute is used to allow the linker to use the C naming convention when referencing Str1 or Str2.

prototyping_3gl_functions_in_clarion.htm.txt · Last modified: 2021/04/15 15:57 by 127.0.0.1