Super Prev Next

Using H2D


Super Prev Next

Headers merging

A C header file may contain one or more #include directives. H2D offer the following translation variants for included headers:

If the GENSEP option is set ON, H2D separates pieces of Modula-2 text, which correspond to different merged headers, with comments containing header file names.

The #merge directive provides more flexible method of merging control than the MERGEALL option. The example illustrates situation in which this directive is very helpful.


Super Prev Next

Example

/* m1.h */                      /* m2.h */
typedef int INTEGER;            struct descriptor{
#include <m2.h>                   INTEGER handl;
#include <m3.h>                 };
#include <m4.h>                 typedef int far * RETVAL;
RETVAL handler();               /* end m2.h */
/* end m1.h */

In this example, the RETVAL declaration from m2.h is used in m1.h. On the other hand, m2.h uses the declaration from m1.h (INTEGER). Setting the MERGEALL option ON results in all headers (m1.h, m2.h, m3.h, and m4.h) being merged and translated into the single definition module m1. If this is not a desired behaviour, the #merge directive should be used instead. If the MERGEALL option is OFF and the line

#merge <m2.h>

is added to either m1.h or the corresponding #header directive in a project file, H2D produces three definition modules m1, m3 and m4, where m1 is a result of translation of two merged headers m1.h and m2.h.


Super Prev Next

Fitting a Modula-2 compiler

Some of H2D translation rules depend on the target Modula-2 compiler; even XDS-C and Native XDS require different definition modules. The BACKEND option is used to reflect the major difference: whether the target Modula-2 compiler is a native code compiler (-BACKEND = Native) or a convertor to C (-BACKEND = C). It is also possible to produce definition modules suitable for both XDS-C and Native XDS (-BACKEND = Common). In this case H2D encloses target-dependent parts with XDS conditional compilation directives.


Super Prev Next

Native code

C headers often contain a number of useful function-like macros. These macros are translated into procedure declarations with parameters having type ARRAY OF SYSTEM.BYTE, which is assignment compatible with any other type. But C macros exist only at compile-time and are not present in object files. Therefore, a Modula-2 compiler is unable to handle them properly unless it is implemented as a convertor to C. Nevertheless, H2D provides a technique which allows to use C function-like macros even with a native code Modula-2 compiler.

If the BACKEND option is set to either Native or Common and the GENMACRO option is set ON, H2D produces an additional module containing macro prototypes --- procedures corresponding to function-like macros, which bodies consist of a comment with C macro definition and are expected to be written by a programmer. These procedures are then declared as external in the main definition module. Thus, a macro prototype module need not to be imported, it should be just linked into an executable which uses the generated definition module.

A macro prototype module name is constructed from a header module name and a prefix specified by the MACPFX option.


Super Prev Next

Example

/* macro.h */
    ...
#define cube(x) (x*x*x)
    ...

(* macro.def  Sep 20  2:38:9  1996 *)
    ...
DEFINITION MODULE ["C"] macro;
    ...
PROCEDURE  / cube ( x: ARRAY OF SYSTEM.BYTE );
    ...
END macro.

(* m_macro.def  Sep 20  2:38:9  1996 *)
    ...
DEFINITION MODULE m_macro;

IMPORT SYSTEM;
    ...
PROCEDURE ["C"] cube ( x: ARRAY OF SYSTEM.BYTE );
    ...
END m_macro.

(* m_macro.mod  Sep 20  2:38:9  1996 *)
    ...
IMPLEMENTATION MODULE m_macro;

IMPORT SYSTEM;
    ...
PROCEDURE ["C"] cube ( x: ARRAY OF SYSTEM.BYTE );
(*
#define cube(x) (x*x*x)
*)
BEGIN
END cube;
    ...
END m_macro.


Super Prev Next

Convertor to C

A Modula-2 compiler implemented as a convertor to C (e.g. XDS-C) converts definition modules written by a programmer to C headers. But headers corresponding to definition modules generated by H2D already exist. To prevent them from being overridden, H2D inserts the NOHEADER XDS option, which disables header file generation, at the beginning of each definition module.

For all included header files, which are not merged (see Headers merging), H2D also sets the CSTDLIB XDS option according to the parenthesis used in the #include directive -- double quotes or angle brackets. For top-level header files, this option is set equal to the value of the CSTDLIB option.

H2D usually has to introduce a number of additional types in the definition module (see Function prototypes and Types). These types are absent in the original header file, and their usage would cause C compilation to fail. To solve this problem, H2D constructs a resulting definition module name from a header file name and a prefix specified by the DEFPFX option. Then, it produces a "wrapper" header file, which name corresponds to the name of a definition module, containing an #include directive with original header name, followed by required type declarations. Type declarations from a project file are copied to a wrapper file as well.


Super Prev Next

Example

/* type.h */

struct Node {
  struct Node *next;
  struct Node *prev;
  int          hash;
};

int Hash(char * str);

(* h2d_type.def  Sep 20  2:51:7  1996 *)
    ...
DEFINITION MODULE ["C"] h2d_type;

IMPORT SYSTEM;
    ...
<*- GENTYPEDEF *>

TYPE
  PtrNode = POINTER TO Node;

<*+ GENTYPEDEF *>

  Node = RECORD
    next: PtrNode;
    prev: PtrNode;
    hash: SYSTEM.int;
  END;

<*- GENTYPEDEF *>

  PtrSChar = POINTER TO CHAR;

PROCEDURE Hash ( str: PtrSChar ): SYSTEM.int;

END h2d_type.

/* h2d_type.h  Sep 20  2:51:7  1996 */
    ...
#include "type.h"

#ifndef h2d_type_H_
#define h2d_type_H_

typedef struct Node * PtrNode;
typedef signed char * PtrSChar;

#endif  /* h2d_type_H_ */


Super Prev Next

Modifying translation rules


Super Prev Next

Base types mapping

The CTYPE and M2TYPE options in conjunction with the #variant directive provide complete control over mapping of C base types to Modula-2 types.

The CTYPE option specifies sizes (in bytes) of C base types, and their default mapping to Modula-2:

-CTYPE = float = 4, REAL
. . .
-CTYPE = unsigned short int = 2, SYSTEM.CARD16

The M2TYPE option specifies Modula-2 types supported by a particular compiler, with their sizes and families to which they belong:

-M2TYPE = CARDINAL = 4, UNSIGNED
-M2TYPE = CHAR = 1, CHAR
. . .
-M2TYPE = SYSTEM.SET16 = 2, SET

Finally, the #variant directive allows to explicitly specify a Modula-2 type for a particular object:

void f(unsigned short mask) PROCEDURE f(mask : SYSTEM.SET16);
#variant f(0) : SYSTEM.SET16

Note: In order to keep original headers intact, #variant directives may be placed into the project file inside the corresponding !header directive.

H2D checks type mappings for correctness using the following rules:

One of the most advanced features of this mechanism is the ability to use Modula-2 set types for C objects. The C programming language, as well as C++, has no built-in set type. The common practice is to treat unsigned integer types as bit scales and to use bitwise logical operators to manipulate them. Since Modula-2 provides set types (and no bitwise operators), it would be more convenient to translate individual integer constants and types to set constants and types.


Super Prev Next

Example

struct s{
  unsigned int field;
};
typedef unsigned long BITSCALE;
int variable;
void long function(unsigned argument);
char bitarray[10];
#define constant 0x0011

#variant s.field     : BITSET
#variant BITSCALE    : BITSET
#variant variable    : BITSET
#variant function(0) : BITSET
#variant bitarray[]  : SYSTEM.SET8
#variant constant    : SYSTEM.SET16

TYPE
  s = RECORD
    field: BITSET;
  END;

  BITSCALE = BITSET;

VAR
  variable: BITSET;

PROCEDURE function ( argument: BITSET );

VAR
  bitarray: ARRAY [0..9] OF SYSTEM.SET8;

CONST
  constant = SYSTEM.SET16{0, 4};


Super Prev Next

Pointer type function parameters

In C, the actual semantics of a pointer type function parameter depends on that function and cannot be determined automatically. A function may interpret its parameter of type T* as either:

where the corresponding Modula-2 formal types are given in parenthesis.

The #variant directive may be used to explicitly point out the semantics of each pointer type parameter.

Note: In order to keep original headers intact, #variant directives may be placed into the project file inside the corresponding !header directive.


Super Prev Next

Example

#variant function(0) : VAR
#variant function(1) : ARRAY
#variant function(2) : VAR ARRAY

void function(int*, int*, int*, int*);

is translated to:

TYPE
  PtrSInt = POINTER TO SYSTEM.int;

PROCEDURE function ( VAR arg0: SYSTEM.int;
                         arg1: ARRAY OF SYSTEM.int;
                     VAR arg2: ARRAY OF SYSTEM.int;
                         arg3: PtrSInt );


Super Prev Next

Preserving constant names

By default, a #define directive introducing a constant is translated to a constant declaration:

#define ENOTEXIST 10 CONST
ENOTEXIST = 10;

This is the only way in case of a native code Modula-2 compiler, since such constants are substituted by a C preprocessor and do not appear in object files. But in case of a convertor to C, the original C headers will be used after conversion and it would be useful to refer to their names in the generated C text. Setting the GENROVARS option ON forces constants to be translated to read-only variables (Note: this is an XDS language extension). This option has no effect on generation for a native-code Modula-2 compiler.

The #variant directive may be used to specify a type for a variable:

#define ENOTEXIST 10 VAR
#variant ENOTEXIST : CARDINAL ENOTEXIST-: CARDINAL;