Programming Considerations

Top  Previous  Next

 

Using C++ Class Libraries

There are some limitations that apply to accessing C++ code and data from Clarion. C++ is an object oriented language and includes language features to support classes and objects, polymorphism, operator and function overloading, and class inheritance. None of these features are supported in Clarion as they are in C++. This does not prevent you from taking advantage of these features in a mixed Clarion and C++ application, but it does dictate the nature of the interface code.

Clarion cannot directly access C++ classes, or objects of a class type. Therefore, Clarion programs do not have direct access to the data or functions contained within those classes. To access them, it is necessary to provide a "C-like" interface to the C++ functionality. A C style function can be called from Clarion, which would then be able to access the C++ classes and objects defined within the code, including their public data and methods.

The following example code fragment demonstrates how to code a C++ function that calls a C++ class library. The MakeFileList function may be called directly from Clarion — the DirList constructors and the ReOrder class member may not. The DirList class implements a directory list whose entries may be ordered by name, size or date. The class definition and Clarion callable entry point declarations are as follows (note the use of the ‘extern "C"’ linkage specifier to force C naming conventions for the Clarion callable functions):

 

     //*** DirList Class Definition

     class DirList: public List {

     public:

         DirList(char *Path, CLAUSHORT Attr, CLAUSHORT Order);

         DirList();

         void ReOrder(int Order);

     };

 

     //*** Clarion Entrypoint Declarations

     extern "C" {

     void MakeFileList(char *Path, CLAUSHORT Attr, CLAUSHORT Order);

     }

The following code does nothing more than provide entry points for the Clarion code to access the functionality of the DIRLIST class library. Since Clarion performs no name-mangling and cannot access classes or their members, this API is necessarily fairly simple.

 

     DirList *FileList = NULL;              // The directory list object

 

     void MakeFileList(char *Path, CLAUSHORT Attr, CLAUSHORT Order)

     {   if (FileList != NULL)              // If we have a list  

       {   delete FileList;                 // invoke class destructor

           FileList = NULL;                 // so we can start again

       }

       FileList = new DirList(Path, Attr, Order);

     }

The following is the corresponding MAP structure prototype to allow Clarion to call the MakeFileList interface function:

 

         MAP

           MODULE('DirList')

             MakeFileList(*CSTRING,USHORT,USHORT),RAW,NAME('_MakeFileList')

           END

         END

One disadvantage of this is that, given a large class library, it appears to involve a lot of extra work to create a suitable interface. In practice, however, it should only be necessary to provide a very small interface to begin taking advantage of an existing C++ class library.

It is not possible to call C++ code compiled using non-SoftVelocity C++ compilers from a Clarion application. C++ modules usually require special initialization — constructors for all static objects must be invoked in the correct order. This initialization process must be performed by the Clarion start-up code. Clarion’s startup code automatically performs the necessary initialization for any SoftVelocity C++ modules that are present, but it will not initialize modules compiled with other C++ compilers. Even if the modules did not require initialization, other C++ compilers use different calling and naming conventions, and adopt different internal class structures. This makes it impossible to use C++ class libraries in Clarion applications compiled with a compiler other than SoftVelocity C++.

 

Summary:

The Clarion API provides a number of features to assist developers who need to interface to code written in other programming languages. With a little care, it is possible to create Clarion interfaces to some extremely powerful external libraries.

 

When preparing interfaces to libraries written in other languages you should consider the following suggestions:

* Don’t write C, C++, Pascal, or Modula-2 functions to return CSTRING variables to Clarion. Have the other language routine place the CSTRING value in a public variable, or pass a *CSTRING (by address) parameter to the C routine to receive the value.

* Don’t call Clarion procedures that return STRING variables from other language functions. Have the Clarion procedure place the return value in a public variable or pass a *CSTRING (by address) parameter to the other language procedure.

* For simplicity and efficiency, STRING and GROUP parameters should usually be passed by address with the RAW attribute to ensure only the address is passed.

* Test the application in XLARGE memory model first.

 

C and C++ Considerations

* If a C or C++ function takes a pointer parameter, the corresponding parameter in the Clarion prototype for that function should be declared as "passed by address" by prefixing the data type with an asterisk (*).

* If a C or C++ function takes a pointer to a GROUP, STRING, PSTRING or CSTRING, you should use the RAW attribute in the Clarion prototype.

* If a C or C++ function takes an ASCIIZ string as a parameter, the corresponding parameter in the Clarion prototype should be *CSTRING.

* If a C or C++ function takes a pointer to a structure as a parameter, the corresponding parameter in the Clarion prototype should be *GROUP.

* Use the header (.H) files as a template for developing a Clarion interface to a C or C++ library that eliminates the need to use the NAME attribute on the Clarion prototype to specify names.

* Use the NAME attribute on the Clarion prototype to specify names for C library functions that do not use the CLA_CONV macro - remember that C names are case sensitive and start with an underscore (_).

 

Modula-2 and Pascal Considerations

* If a Modula-2 or Pascal procedure takes a VAR parameter, the corresponding parameter in the Clarion prototype for that procedure should be declared as "passed by address" by prefixing the data type with an asterisk (*).

* If a Modula-2 or Pascal procedure takes a VAR parameter for a GROUP, STRING, PSTRING or CSTRING, you should use the RAW attribute in the Clarion prototype.

* If a Modula-2 or Pascal procedure takes a VAR record as a parameter, the corresponding parameter in the Clarion prototype should be *GROUP and the RAW attribute should be used in the prototype.

 

Additional C++ Considerations

* Use the "Pascal" external linkage specification for your C++ interface functions. This eliminates the need to use the Clarion NAME attribute on the prototype.

* Don’t call C++ class member functions from your Clarion code.

* Don’t try to access C++ objects of class type from your Clarion code.

* Don’t try to access C++ code compiled with a C++ compiler other than SoftVelocity.

 

Additional Modula-2 Considerations

* Use the definition (.DEF) module as a template for developing a Clarion interface to a Modula-2 library.

* If a Modula-2 procedure takes an ASCIIZ string as a parameter, the corresponding parameter in the Clarion prototype should be *CSTRING.

* Use the NAME attribute to specify names for Modula-2 library procedures -remember that Modula-2 names are prefixed with the module name followed by a ‘$’ and are case-sensitive.

 

Additional Pascal Considerations

* Use the interface (.ITF) files as a template for developing a Clarion interface to a Pascal library.

* Use the NAME attribute to specify names for Pascal library procedures -remember that Pascal names are prefixed with the module name followed by a ‘$’ and are upper-case.