All comments from the original C text are copied to generated definition modules. Their placement, however, is not preserved in some cases. The COMMENTPOS option may be used to align comments which are placed next to declarations.
C++ compilers are usually able to recognize C++-style comments (beginning with '//') even while operating in C mode. The CPPCOMMENTS option controls whether H2D recognizes such comments as well.
In most cases, H2D preserves original C identifiers. Exceptions are structure, union, and enumeration tags, which constitute a separate name space in C. If there is a constant, type, variable, or function identifier which coincides with a tag, H2D appends "_struct", "_union" or "_enum" to that tag.
In some situations, H2D itself generates additional identifiers, e.g. for unnamed function arguments, derived types, and formal types.
H2D may append digits to generated identifiers to avoid conflicts with existent ones.
Identifiers matching Modula-2 keywords are not allowed in source files. However, Modula-2 pervasive identifiers (e.g. INTEGER or HALT) are permitted.
The following C declarations were taken from the sys/stat.h file:
struct stat { ... }; int stat( const char *, struct stat * ); TYPE stat_struct = RECORD ... END; PtrChar = POINTER TO CHAR; PROCEDURE stat(arg0: PtrChar; arg1: stat_struct): SYSTEM.int;
C types are translated to Modula-2 types according to the following table:
C type | Modula-2 type |
base (int, char, etc.) | (see Base types mapping) |
void* | SYSTEM.ADDRESS |
pointer | pointer |
array | array |
enumeration | (see Enumeration) |
structure | record |
union | variant record |
pointer to function | procedure type |
Notes:
struct STRUCTURE{ TYPE STRUCTURE = RECORD int field1; field1: SYSTEM.int; char field2; field2: CHAR; double field3; field3: LONGREAL; }; END; union UNION { TYPE UNION = RECORD int field1; CASE : INTEGER OF char field2; 0: field1: SYSTEM.int; double field3; |1: field2: CHAR; }; |2: field3: LONGREAL; END; END;
For objects declared as having derived types (pointer or array) either a new Modula-2 type is introduced or a previously declared type synonym is used to improve readability. For pointers to base types, a new type is always declared.
In particular, a C structure may contain fields which type is defined as pointer to that structure. In this case H2D also automatically inserts a necessary forward pointer type declaration.
This may cause type compatibility problems. Fortunately, in XDS, compatibility rules for foreign objects are relaxed, e.g. two "C" pointer types are compatible if their base types are the same. Additional setup or postprocessing may be required when H2D is used with third-party Modula-2 compilers.
char *str1; TYPE char *str1; H2D_PtrSChar = POINTER TO CHAR; VAR str1: H2D_PtrSChar; str2: H2D_PtrSChar;
struct s { TYPE int i; s = RECORD }; i: SYSTEM.int; END; struct s *p; H2D_Ptrs = POINTER TO s; VAR p: H2D_Ptrs;
struct s { TYPE int i; s = RECORD }; i: SYSTEM.int; END; typedef struct s *ps; ps = POINTER TO s; struct s *p; VAR p: ps;
struct Node { TYPE int i; PtrNode = POINTER TO Node; struct Node *next; Node = RECORD }; i: SYSTEM.int; next: PtrNode; END;
An enumeration (enum) is not actually a distinct type in C --- it is just a convenient way to declare integer constants (but in C++ enumeration is a distinct type). Moreover, since it is possible in C to explicitly specify enumeration constant value, translation to Modula-2 enumeration type may be incorrect. H2D may translate C enumerations into either Modula-2 enumeration types or Modula-2 constant declarations, depending on the GENENUM option setting. For instance, if GENENUM is set to "Const" or "Mixed", the following C type synonym declaration:
typedef enum{ one=1, two } Number;
will be translated to
(* H2D: enumerated type: Number *) CONST one = 1; two = 2; TYPE Number = SYSTEM.int; (* H2D: End of enumerated type: Number *)
If GENENUM is set to "Enum", the same declaration will be translated unsafely (a warning comment will be added):
TYPE Number = ( one, (* H2D: integer value was 1 *) two );
C declarations of type synonyms (typedef) are translated to Modula-2 type declarations. If there are multiple synonyms declared for a type, their equivalence is preserved:
typedef char String[256]; TYPE String = ARRAY [0..255] OF CHAR; typedef String *PString; PString = POINTER TO String; typedef String *Buffer; Buffer = PString;
Note: In C, function type synonyms may be used in function declarations. These synonyms are processed in a way they are processed by a C compiler and do not appear in output files (see Function prototypes).
Variables are translated to variables. Variables declared with the const qualifier are translated to read-only variables (XDS extension). The volatile qualifier is currently ignored.
extern int i; VAR i : SYSTEM.int; extern const int j; VAR j- : SYSTEM.int;
C function prototypes declared as void are translated to proper procedure declarations; other are translated to function procedure declarations. If there is no name specified for a function parameter, argx is substituted, where x is a number unique for each unnamed parameter.
In C, a derived type (more precisely, pointer or array) may be specified for a function parameter. In Modula-2, the formal type of a procedure parameter have to be either type name or open array type. H2D translates parameters of array type to open array value parameters.
The translation procedure for pointers is more complicated. By default, H2D searches for a type synonym, previously declared via typedef. The synonym, if found, is used as formal type; otherwise H2D automatically declares one. If automatic declaration is undesirable, required synonyms may be declared in the project file. Other variants of translation may be explicitly specified by means of the #variant directive.
See also Non-standard qualifiers.
void p(int,int); PROCEDURE p ( arg0 : SYSTEM.int; arg1 : SYSTEM.int ); int f(char c); PROCEDURE f ( c : CHAR ): SYSTEM.int; void P(T *t); TYPE PtrT = POINTER TO T; PROCEDURE P ( t : PtrT ); void Q(T t[]) PROCEDURE Q ( t : ARRAY OF T ); int strlen(char *); PROCEDURE strlen ( arg0 : ARRAY OF CHAR ) #variant strlen(0) : ARRAY : SYSTEM.int;
In practice, header files are not "pure" ANSI C. The most common extension is a set of additional keywords (qualifiers) which may be used to specify calling/naming conventions used in a particular library or API.
Since XDS provides the similar mechanism called direct language specification (DLS), H2D recognizes a number of such keywords, which are translated to the following DLS strings:
C keyword | DLS String |
cdecl | none |
fortran | none |
interrupt | none |
pascal | "Pascal" for types and variables |
"StdCall" for functions | |
syscall | "Syscall" |
near, far, and huge qualifiers are recognized but ignored.
A #define C preprocessor directive may contain an object-like definition or a function-like definition which are translated differently.
#define identifier Text
If Text is a constant expression, the directive is translated to a constant declaration or a read-only variable declararion (see Preserving constant names). If Text is a type identifier, the directive is translated to a type declaration. If Text is an identifier of a function, a macro definition, or a constant, the directive is translated to a constant declaration. In all other cases, it is interpreted in a C preprocessor manner.
#define identifier"(" idenifier, { identifier } ")" Text
Translated to a proper procedure declaration with all parameters having type ARRAY OF SYSTEM.BYTE. These declarations are marked with a special comment and may be corrected after translation to reflect the actual semantics of a macro by changing parameter types and/or adding return types. See also Native code for information about macro prototype modules.
#undef identifier
Undefines identifier as it is done by a C preprocessor.
#define str_constant "Hello World!\n" #define constant 0x10 #define constant_synonym constant #define macro_with_params(p1,p2,p3) p1+p2+p3 #define macro_with_params_synonym macro_with_params int function(int); #define function_synonym function typedef int INT; #define INTEGER INT
CONST str_constant = 'Hello World!' + 12C; constant = 10H; constant_synonym = constant; <* IF __GEN_C__ THEN *> (* H2D: this procedure was generated from Macro. *) PROCEDURE macro_with_params ( p1, p2, p3: ARRAY OF SYSTEM.BYTE ); <* ELSE *> PROCEDURE / macro_with_params ( p1, p2, p3: ARRAY OF SYSTEM.BYTE ); <* END *> <* IF __GEN_C__ THEN *> CONST macro_with_params_synonym = macro_with_params; <* END *> PROCEDURE function ( arg0: SYSTEM.int ): SYSTEM.int; CONST function_synonym = function; TYPE INT = SYSTEM.int; INTEGER = SYSTEM.int;
#include <file_name> #include "file_name"
If the file specified by file_name has to be merged with the current file (see Headers merging), H2D treats this directive exactly as a C preprocessor, i.e. replaces it with contents of a specified file. Otherwise, file_name is added to the import list and the file specified by it is translated into a separate definition module. If file_name contains directories, the output files are placed to the same subdirectory.
The GENLONGNAMES option controls conversion of included header file names which contain path:
#include <sys/stat.h>
is translated to
IMPORT stat;
if GENLONGNAMES is OFF and to
IMPORT sys_stat;
if GENLONGNAMES is ON.
See also Module names.
H2D handles conditional compilation directives #if, #ifdef, #ifndef, #else, and #endif the same way as a C preprocessor does. A project file may be used to define constants which are used in arguments of these directives.
H2D recognizes and ignores #line, #error, and #pragma C preprocessor directives.
H2D recognizes two non-standard preprocessor directives: #merge and #variant. These directives are related to definiton module generation only and do not affect the C text, so they may be placed arbitrarily in a header file. Typically they are collected in project files inside a corresponding !header directive.
The advanced technique is to put these directives right into working copies of header files, next to the corresponding declarations. Then, after successful translation of all headers, these directives may be extracted with the help of GENDIRS option and moved to the project file. Now original headers may be used for translation.
#merge ( <file_name> | "file_name" )
This directive lists included header files which should be merged even if the MERGEALL option is OFF. This feature may be useful in some cases (see Headers merging).
When placed in a header file, this directive has effect only in this file. When placed in a project file, it has effect in all headers matching the surrounding !header directive.
#variant Designator ":" Type Designator = identifier { "^" | "[ ]" | "." identifier } | Parameter Parameter = identifier "(" number ")" Type = qualident
This form of the #variant directive allows to explicitly specify a Modula-2 Type for an object denoted by Designator. See Base types mapping for more information.
Designator specifies a named object or its element which is subject to the #variant directive:
"^" | pointer dereference |
"[ ]" | array indexing |
"." identifier | structure or union field selection |
Parameter specifies a function identifier and its parameter number (zero-based).
#variant Parameter ":" ( "VAR" | "ARRAY" | "VAR ARRAY" )
This form is used to control translation of a function Parameter, which has a pointer type.
By default, pointer type function parameters are translated to pointer type procedure parameters (see Function prototypes). The #variant directive allows to specify one of the following alternative rules for a particular parameter:
Modifier | T *p is translated to |
VAR | VAR p: T |
ARRAY | p: ARRAY OF T |
VAR ARRAY | VAR p: ARRAY OF T |
See also Pointer type function parameters.
#variant f(0) : VAR ARRAY void f(char*);
PROCEDURE f ( VAR arg0: ARRAY OF CHAR );
A #variant directive has effect only in the file where it is located, or, if specified in a project file, in all files matching the surrounding !header directive. Therefore, Designator should specify an object declared in this file or in one of the files which it includes. If an object specified by Designator is not present, an error message is displayed.