IBM
Contents Index Previous Next



C/C++ to SDL Translation Rules


The general idea behind the CPP2SDL tool is to take a set of C/C++ header files, preprocess them, and translate some or all of the declarations in these headers into SDL/PR representations. This section describes the rules for this translation process.

Each C/C++ construct is described in a subsection of its own. First, a general rule for the translation of the construct is presented. Then follows a description of exceptions to this rules, and rationals for these exceptions.

Before proceeding, it should be noted that the translation rules have been designed to support both C and C++ target compilers. To a large extent the translation rules are actually independent of whether a C or C++ target compiler is used. However, there are some differences, so when CPP2SDL executes in "C mode" (i.e. with the -c option set) a few translation rules are slightly modified. These modifications are described in Special Translation Rules for C Compilers.

Names

Rule: The name of a C/C++ identifier is the same in SDL.

The naming rules of identifiers in SDL and C/C++ are rather similar but differs in two important aspects:

To overcome these differences tool specific extensions have been made in the supported SDL dialect. The Analyzer has an option to handle case sensitive SDL (see Set-Case-Sensitive), and most of the restrictions with underscores have been removed. However, the rule that a name that ends with an underscore should be concatenated with the following name, makes it necessary to modify such names in the SDL mapping. This is done by appending a string suffix to such names. This string is by default "uscore" but may be configured to an arbitrary string be means of the CPP2SDL option
-suffix.

Another case where a name in C/C++ cannot be retained in the SDL translation is when the name is an SDL keyword. Such names are prefixed with a user-configurable string that by default is keyword_. The option -prefix can be used to configure this string.

Example 91 below gives some examples of the translation rules for names.

Example 91 : Translation of names

C++:

int ABC, abc; 	 	 // Case sensitivity
char u__sc, _w, x_; // Unrestricted use of underscores
double signal; // SDL keyword

SDL:

DCL ABC int; EXTERNAL 'C++';
DCL abc int; EXTERNAL 'C++';
DCL u__sc char; EXTERNAL 'C++';
DCL _w char; EXTERNAL 'C++';
DCL x_uscore /*#REFNAME 'x_'*/ char; EXTERNAL 'C++';
DCL keyword_signal /*#REFNAME 'signal'*/ double; EXTERNAL 'C++';

Note the #REFNAME directive that passes the original C/C++ name to the Code Generator for names that are modified in the SDL translation.

Fundamental Types

Rule: A fundamental C/C++ type is mapped to an SDL sort with the same name.

The SDL sorts that represent fundamental C/C++ types are not generated by CPP2SDL but are defined in special SDL/PR files that are included if the -generatecpptypes option is set. The SDL sorts in these files are normally syntypes of predefined SDL sorts. Refer to SDL Library for Fundamental C/C++ Types for more information.

The table below shows how the fundamental C/C++ types are translated to SDL sorts, and what predefined SDL sort that correspond to these SDL sorts.

C/C++ Fundamental Type SDL Sort Predefined SDL Sort
signed int

int

	 int



Integer

unsigned int

unsigned	 

unsigned_int



Integer



signed long int

signed long 

long int

long

long_int



Integer

unsigned long int

unsigned long

unsigned_long_int



Integer

signed short int

signed short

short int

short

short_int



Integer

unsigned short int

unsigned short

unsigned_short_int



Integer

signed long long int

signed long long

long long int

long long

long_long_int



Integer

unsigned long long int

unsigned long long

unsigned_long_long_int



Integer

char

char

Character

signed char 

signed_char

Character

unsigned char

unsigned_char

Octet

wchar_t

wchar_t

N/A

float

float

Real

double

long double

double



Real

bool

bool

Boolean

void

N/A

N/A

Note that the wchar_t type has no counterpart in SDL, and thus is represented by a newtype rather than a syntype of a predefined sort.

Also note that the special void type is not represented explicitly in SDL. Instead this type is translated by omitting input and result arguments to operators as described in Functions.

Type Declarators

There are two different type declarators in C; pointer (*), and array ([]). C++ has one additional type declarator; reference (&).

Pointers

Rule: A type with a pointer declarator is translated by applying the Ref generator on the SDL sort that corresponds to that type.

The name of the generated newtype for the pointer is prefixed with a user-configurable string that by default is ptr_. The option -prefix can be used to configure this string.

Untyped pointers (void*) are translated to a special SDL sort called ptr_void. See SDL Library for Fundamental C/C++ Types for more information about the Ref generator and the special ptr_void sort.

Example 92 : Translation of pointers

C++:

typedef int* p_int;
extern void* generalp;

SDL:

NEWTYPE ptr_int Ref( int);
OPERATORS
ptr_int : -> ptr_int;
ptr_int : ptr_int -> ptr_int;
ENDNEWTYPE ptr_int;EXTERNAL 'C++';
SYNTYPE p_int = ptr_int
ENDSYNTYPE p_int;EXTERNAL 'C++';
DCL generalp ptr_void; EXTERNAL 'C++';

Arrays

Rule: A type with an array declarator is translated by applying the
CArray generator on the SDL sort that corresponds to that type.

There is one important exception to this rule. Array declarators that do not specify the size of the array are translated in the same way as pointers (see Pointers).

The name of the generated newtype for an array type with a specified size is prefixed with a user-configurable string that by default is "arr_". The option -prefix can be used to configure this string. The name also contains the size of the array, since the size is used in the CArray generator instantiation and thus is significant in SDL. This makes SDL array sorts of different sizes type incompatible, but this is normally not a big problem since the elements of the arrays are type compatible.

Note:

SDL array sorts corresponding to C/C++ arrays with different sizes are normally type incompatible.

Example 93 : Translation of arrays

C++:

extern char c_arr1[20];
extern char c_arr2[];

SDL:

NEWTYPE arr_20_char CArray( 20, char);
ENDNEWTYPE arr_20_char;EXTERNAL 'C++';
DCL c_arr1 arr_20_char; EXTERNAL 'C++';
NEWTYPE ptr_char Ref( char);
OPERATORS
ptr_char : -> ptr_char;
ptr_char : ptr_char -> ptr_char;
ENDNEWTYPE ptr_char;EXTERNAL 'C++';
DCL c_arr2 ptr_char; EXTERNAL 'C++';

References

Rule: A type with a reference declarator is translated as normal, i.e. the reference declarator is not translated to SDL.

A C++ reference can be looked upon as a constant pointer that is automatically de-referenced each time it is used. This makes a reference an alternative name for an object. Since no difference will be made between an object and a reference to an object in the SDL mapping, references will appear to be objects in SDL.

Example 94 : Translation of references

C++:

extern int i; /* i is initialized elsewhere */
extern int& r; /* r is initialized to i elsewhere
(int& r = i;), i.e. r and i refers to
the same int. */

SDL:

DCL i int; EXTERNAL 'C++';
DCL r int; EXTERNAL 'C++';/* N.B. C++ reference! */

Note that if r in this example is assigned a value in SDL, it is in fact the object that r refers to (i.e. i) that gets a new value. This could be confusing if only the SDL translation of r is considered, and to avoid this a comment is attached to the declaration of r that tells that it is a reference in C++.

References could also appear as specifiers for formal function arguments. Such arguments will be translated to operator arguments marked with the IN/OUT keyword if they are non-constant (see Argument Passing and Return Value).

Enumerated Types

Rule: An enumerated type is translated to a newtype with literals corresponding to the enum literals.

A special case is when the enumerated type has no literals. Such a type can be treated as an integer in C/C++, and is consequently translated to a syntype of int.

Example 95 : Translation of enumerated types

C/C++:

enum {} v;
enum E2 {};
enum E1 {a, b, c=10};

SDL:

DCL v int; EXTERNAL 'C++';
SYNTYPE E2 = int
ENDSYNTYPE E2;EXTERNAL 'C++';
NEWTYPE E1
LITERALS a, b, c;
OPERATORS
IntToEnum /*#REFNAME '(E1)'*/ : int -> E1;
EnumToInt : E1 -> int; /*#OP(PY)*/
ORDERING;
ENDNEWTYPE E1;EXTERNAL 'C++';

By using the "type conversion" operators EnumToInt and IntToEnum integer arithmetic and comparisons become available in SDL also for enumerations. As can be seen from the #REFNAME directive in the example above, the generated code for calls to the IntToEnum operator will be a C style cast from int to enum. This explicit type conversion should be acceptable by all target compilers. Also note that the #OP(PY) directive means that there will be no generated code for calls to the EnumToInt operator, which is desired since that type conversion is implicit in C/C++.

If the enumerated type is incomplete, i.e. if the enum tag is missing, the translation rule is slightly modified according to the translation rules for incomplete types (see Incomplete Types). For enumerations, these rules have the following impact:

· The name of the generated newtype follows the naming rules for incomplete types described in Incomplete Types.

· The newtype will not be external, since it does not correspond to a C/C++ type that may be referred to.

· The IntToEnum operator will not be generated for the same reason.

Typedef Declarations

Rule: A typedef declaration is translated to an SDL syntype declaration.

There are two exceptions to this rule:

In these cases the typedef declarations do not define new typenames, and thus no syntypes need to be generated.

Another special case is when a synonym for void is introduced by means of a typedef declaration. Such a typedef declaration is not translated, but the typedef name will be remembered. References to the typedef name will then be translated in the same way as void would have been translated in that context.

Example 96 : Translation of typedef declarations

C++:

typedef int MyInt;
typedef struct r {
int a;
} r; // Typedef name is the same as the tag name!
typedef struct s {
MyInt a;
}; // Omitted typedef name - legal but rare!
typedef void myvoid;
typedef myvoid myvoid2;
myvoid f(myvoid2);

SDL:

NEWTYPE global_namespace_ImpSpec /*#NOTYPE*/
OPERATORS
f :;
ENDNEWTYPE global_namespace_ImpSpec;EXTERNAL 'C++';
SYNTYPE MyInt = int
ENDSYNTYPE MyInt;EXTERNAL 'C++';
NEWTYPE ptr_r Ref( r);
OPERATORS
ptr_r : -> ptr_r;
ptr_r : ptr_r -> ptr_r;
ENDNEWTYPE ptr_r;EXTERNAL 'C++';
NEWTYPE r
STRUCT
a int;
OPERATORS
r : -> r;
r : r -> r;
ENDNEWTYPE r;EXTERNAL 'C++';
NEWTYPE ptr_s Ref( s);
OPERATORS
ptr_s : -> ptr_s;
ptr_s : ptr_s -> ptr_s;
ENDNEWTYPE ptr_s;EXTERNAL 'C++';
NEWTYPE s
STRUCT
a MyInt;
OPERATORS
s : -> s;
s : s -> s;
ENDNEWTYPE s;EXTERNAL 'C++';

Note:

Typedefs of function types are not supported by CPP2SDL, and will not be translated to SDL.

Functions

Rule: A function prototype is translated to an SDL operator signature.

This rule is valid both for member and non-member functions. Operators that result from functions that are members of a class will be placed in the newtype that is the translation of that class. Operators that result from non-member functions will be placed in a special newtype called global_namespace_ImpSpec2.

Member functions are described in Members, and the rest of this section will focus on non-member functions.

Example 97 : Translation of non-member functions

C++:

char myfunc1(char);
int myfunc1();
void myfunc2();
void myfunc2(int);

SDL:

NEWTYPE global_namespace_ImpSpec /*#NOTYPE*/
OPERATORS
myfunc1 : char -> char;
myfunc1 : -> int;
myfunc2 :;
myfunc2 : int;
ENDNEWTYPE global_namespace_ImpSpec;EXTERNAL 'C++';

Note that functions without input arguments or return value, will be translated to operators without input arguments or return value. Such operators are not allowed according to the SDL96 standard, but are accepted by the SDL Analyzer as a tool specific language extension.

Overloaded Functions

Rule: Overloaded functions are translated to overloaded SDL operators.

The semantics of overloaded functions in C++ differs slightly from the semantics of overloaded operators in SDL. For example, C++ allows overloading on constant arguments which is not possible in SDL. A C++ header file may therefore contain overloaded functions that cannot be translated to SDL. Normally this is not a problem since the C++ compiler resolves generated calls to these functions correctly anyway.

Example 98 : Translation of overloaded functions

C++:

int f0();
int f0(double);
int f1(int&);
int f1(const int&);

SDL:

NEWTYPE global_namespace_ImpSpec /*#NOTYPE*/
OPERATORS
f0 : -> int;
f0 : double -> int;
f1 : int -> int;
ENDNEWTYPE global_namespace_ImpSpec;EXTERNAL 'C++';

Argument Passing and Return Value

Rule: Function arguments that are passed by reference are translated to IN/OUT operator arguments in SDL.

There is one exception to this rule; arguments that are references to constants do not translate to IN/OUT arguments since C++ allows these arguments to take variables as well as constant values.

Example 99 : Translation of function arguments and return value

C++:

int f1 (int p1, int &p2, const int &p3, const int 
*p4, int *const p5);
int &f2();
const int &f3();

SDL:

NEWTYPE global_namespace_ImpSpec /*#NOTYPE*/
OPERATORS
f1 : int, IN/OUT int, int, ptr_int, ptr_int -> int;
f2 : -> int;
f3 : -> int;
ENDNEWTYPE global_namespace_ImpSpec;EXTERNAL 'C++';
NEWTYPE ptr_int Ref( int);
OPERATORS
ptr_int : -> ptr_int;
ptr_int : ptr_int -> ptr_int;
ENDNEWTYPE ptr_int;EXTERNAL 'C++';

The example shows that information about constant arguments is lost in the SDL mapping. Also note that no difference will be made in SDL between functions that return data by value, data by reference, or constant data by reference.

Finally note that IN/OUT arguments to operators is a tool specific SDL extension.

Default Arguments

Rule: A function with default arguments are translated to several overloaded SDL operators.

This translation is reasonable given the fact that each C++ function with default arguments may be rewritten to an equivalent set of overloaded C++ functions.

Example 100 : Translation of functions with default arguments

C++:

int func(int a, int b = 5, int c = 7);
int func(int a); // Ambiguous function!

SDL:

NEWTYPE global_namespace_ImpSpec /*#NOTYPE*/
OPERATORS
func : int, int, int -> int;
func : int, int -> int;
func : int -> int;
ENDNEWTYPE global_namespace_ImpSpec;EXTERNAL 'C++';

In C++, ambiguities between overloaded functions are allowed provided that the functions are never called. SDL is, however, more strict, and operator resolution is made from the declarations of the operators. The second version of func in the example above is therefore not accessible in the SDL mapping.

Unspecified Arguments

Rule: The translation of a function with unspecified arguments (a.k.a. an ellipsis function) requires the usage of an import specification that specifies the types of the unknown arguments.

See Prototypes for Ellipsis Functions for information about how an import specification can be used to "expand" ellipsis functions.

Inline Functions

Rule: A function that is declared to be inline is translated as an ordinary function.

This is natural since the inline keyword on functions can be seen as a directive to the C++ compiler, which only affects the way that calls to these functions are generated. This is of course nothing that needs to be visible in SDL.

Example 101 : Translation of inline functions

C++:

inline int fac(int n){/*...*/};

SDL:

NEWTYPE global_namespace_ImpSpec /*#NOTYPE*/
OPERATORS
fac : int -> int;
ENDNEWTYPE global_namespace_ImpSpec;EXTERNAL 'C++';

Function Pointers

Rule: A function pointer is translated to an untyped pointer in SDL, i.e. to ptr_void.

This translation rule makes it possible to represent a function pointer in SDL, but it is not possible to call the function that it points to, or to assign the address of another function to it. That has to be done with inline C/C++ code, for example by means of the #CODE operator.

Example 102 : Translation of function pointers

C++:

typedef int (*fp)(int, char);
fp g(double);

SDL:

NEWTYPE global_namespace_ImpSpec /*#NOTYPE*/
OPERATORS
g : double -> fp;
ENDNEWTYPE global_namespace_ImpSpec;EXTERNAL 'C++';
SYNTYPE fp = ptr_void
ENDSYNTYPE fp;EXTERNAL 'C++';

The special ptr_void sort is described in SDL Library for Fundamental C/C++ Types.

Scope Units

Rule: A C/C++ scope unit is translated to an SDL newtype.

Note that the global scope (known as the global namespace in C++) is also translated to an SDL newtype. This newtype is called global_namespace_<ImpSpec> (where <ImpSpec> is the name of the import specification file) and is a container for all operators that are the translation of non-member or global functions in the program. Other global declarations are however placed directly in the SDL scope that is the context of the translation.

Example 103 : Translation of the global namespace

C++:

int i;
void op(unsigned int);

SDL:

NEWTYPE global_namespace_ImpSpec /*#NOTYPE*/
OPERATORS
op : unsigned_int;
ENDNEWTYPE global_namespace_ImpSpec;EXTERNAL 'C++';
DCL i int; EXTERNAL 'C++';

The most important scope units that may be found in a C/C++ header file are:

In C++ these scope units may be nested to arbitrary depth, but since nested newtypes are not allowed in SDL, the translation of a nested scope unit will be a newtype that has a name that is prefixed with a scope qualification prefix. This prefix consists of the names of all enclosing scope units separated by underscores ("_").

Example 104 : Translation of nested scope units

C++:

class C {
public:
int ci;
class CC {
public:
int op();
};
};

SDL:

NEWTYPE ptr_C_CC Ref( C_CC);
OPERATORS
ptr_C_CC : -> ptr_C_CC;
ptr_C_CC : ptr_C_CC -> ptr_C_CC;
ENDNEWTYPE ptr_C_CC;EXTERNAL 'C++';
NEWTYPE C_CC /*#REFNAME 'C::CC'*/
OPERATORS
op : C_CC -> int;
C_CC /*#REFNAME 'CC'*/ : -> C_CC;
C_CC /*#REFNAME 'CC'*/ : C_CC -> C_CC;
ENDNEWTYPE C_CC;EXTERNAL 'C++';
NEWTYPE ptr_C Ref( C);
OPERATORS
ptr_C : -> ptr_C;
ptr_C : ptr_C -> ptr_C;
ENDNEWTYPE ptr_C;EXTERNAL 'C++';
NEWTYPE C
STRUCT
ci int;
OPERATORS
C : -> C;
C : C -> C;
ENDNEWTYPE C;EXTERNAL 'C++';

Compare the name "C_CC" of the nested class CC in this example, with the fully qualified name "C::CC" of this class in C++. The latter name is provided in a #REFNAME directive as information to the Code Generator.

Namespaces

Rule: A namespace is translated to a newtype that may not be instantiated in SDL.

Classes, structs, unions and template classes not only define scope units, but also types. They may thus be instantiated in for example variable declarations. Namespaces, on the other hand, are plain scope units and may not be instantiated. This is indicated in the SDL mapping by means of a Code Generator directive called #NOTYPE. This directive enables the Code Generator to catch attempts to instantiate newtypes that originates from namespaces.

Example 105 : Translation of namespaces

C++:

namespace N {
const int ci;
class CC {
public:
int op();
};
  int f(char);
}

SDL:

SYNONYM N_ci /*#REFNAME 'N::ci'*/ int = EXTERNAL 
'C++';
NEWTYPE ptr_N_CC Ref( N_CC);
OPERATORS
ptr_N_CC : -> ptr_N_CC;
ptr_N_CC : ptr_N_CC -> ptr_N_CC;
ENDNEWTYPE ptr_N_CC;EXTERNAL 'C++';
NEWTYPE N_CC /*#REFNAME 'N::CC'*/
OPERATORS
op : N_CC -> int;
N_CC /*#REFNAME 'CC'*/ : -> N_CC;
N_CC /*#REFNAME 'CC'*/ : N_CC -> N_CC;
ENDNEWTYPE N_CC;EXTERNAL 'C++';
NEWTYPE N /*#NOTYPE*/
OPERATORS
N_f /*#REFNAME 'N::f'*/ : char -> int;
ENDNEWTYPE N;EXTERNAL 'C++';

Note that the newtype that corresponds to the namespace only contains the operators that are the translation of the functions declared in that namespace. This is analogous to how the global namespace is translated (see Example 103). Other declarations in the namespace will appear outside the newtype. All SDL declarations that are generated from namespace declarations will be prefixed with the name of the newtype that is the translation of that namespace. Also, their fully qualified C++ name is given in #REFNAME directives.

Variables

Rule: A variable is translated to an external variable if it is a non-member or global variable, or to a newtype field if it is a member variable.

Newtype fields that result from member variables of a class will be placed in the newtype that is the translation of that class. Member variables are described in Members, and the rest of this section will focus on non-member variables.

Example 106 : Translation of non-member variables

C++:

int ivar, jvar;
class X {
int j;
public:
int Get() { return j;};
} xvar;

SDL:

DCL ivar int; EXTERNAL 'C++';
DCL jvar int; EXTERNAL 'C++';
NEWTYPE ptr_X Ref( X);
OPERATORS
ptr_X : -> ptr_X;
ptr_X : ptr_X -> ptr_X;
ENDNEWTYPE ptr_X;EXTERNAL 'C++';
NEWTYPE X
OPERATORS
Get : X -> int;
X : -> X;
X : X -> X;
ENDNEWTYPE X;EXTERNAL 'C++';
DCL xvar X; EXTERNAL 'C++';

External variables are a tool-specific SDL extension that are similar to external synonyms.

External variables may only be declared in a process, procedure, service or operator diagram. Since CPP2SDL does not know the SDL context where the translation takes place, it has a command line option called -novariables that tells whether external variables may be generated or not. When CPP2SDL is executed from the Organizer, this option is set automatically. If the option is set, and a C/C++ construct is found that would map to an external variable, CPP2SDL will print a warning.

Constants

Rule: A constant is translated to an external synonym.

This rule applies for all true C/C++ constants, i.e. constants that have been declared using the const type specifier. It is not uncommon, especially in older C API:s, to use macros to represent constants. Such constants will not be directly accessible in SDL since the preprocessor expands them before CPP2SDL begins the translation. However, simple macro constants may often be accessed by using inline target code, for example by means of the #CODE operator. As an alternative external synonyms could be declared to represent such macros.

Note:

With the option -extsyn the translation of constants differs some. For constants with numeric expressions that can be calculated during translation, the default transformation rule is that also the constant value is translated. If -extsyn is switched on, translation is always an external synonym without its value.

Example 107 Translation using -extsyn

C++:

const int FOO = 1;
const float ScoobieDoo = FOO/4;
const bool YOU;

SDL without -extsyn option (default behavior):

SYNONYM FOO int = 1; 
SYNONYM ScoobieDoo float = 0.25; 
SYNONYM YOU bool = EXTERNAL 'C++';

SDL with -extsyn option:

SYNONYM FOO int = EXTERNAL 'C++';
SYNONYM ScoobieDoo float = EXTERNAL 'C++';
SYNONYM YOU bool = EXTERNAL 'C++';

Example 108 : Translation of constants

C++:

class MyClass;
const double pi = 3.1415;
const MyClass m(7, 'x');

SDL:

SYNONYM pi double = 3.1415; 
NEWTYPE MyClass /*#NOTYPE*/
ENDNEWTYPE MyClass;EXTERNAL 'C++';
SYNONYM m MyClass = EXTERNAL 'C++';

Constant Expressions

Rule: Constant expressions are evaluated while translated to SDL.

Constant expressions may be encountered at a number of places in a C/C++ header, for example as constant initializers, or as size specifiers of array declarators or bitfields. If a constant expression has to be translated to SDL, CPP2SDL attempts to evaluate it during the translation in order to simplify its representation in SDL.

Example 109 : Translation of constant expressions

C++:

enum e {a, b, c=10};
const int i = (2+c)*b;
struct s{
int f1 : (2+c)*b;
};
typedef int intarr[sizeof(int)+1];

SDL:

NEWTYPE e
LITERALS a, b, c;
OPERATORS
IntToEnum /*#REFNAME '(e)'*/ : int -> e;
EnumToInt : e -> int; /*#OP(PY)*/
ORDERING;
ENDNEWTYPE e;EXTERNAL 'C++';
SYNONYM i int = 12;
NEWTYPE ptr_s Ref( s);
OPERATORS
ptr_s : -> ptr_s;
ptr_s : ptr_s -> ptr_s;
ENDNEWTYPE ptr_s;EXTERNAL 'C++';
NEWTYPE s
STRUCT
f1 int : 12;
OPERATORS
s : -> s;
s : s -> s;
ENDNEWTYPE s;EXTERNAL 'C++';
NEWTYPE arr_2_int CArray( 2, int);
ENDNEWTYPE arr_2_int;EXTERNAL 'C++';
SYNTYPE intarr = arr_2_int
ENDSYNTYPE intarr;EXTERNAL 'C++';

Note that not all the constant expressions in this example are visible in the SDL translation, and thus need not be evaluated by CPP2SDL.

Most constant expressions can be evaluated by CPP2SDL, but not all. In particular, expressions containing the sizeof() operator are difficult to evaluate since CPP2SDL has no information about what compiler that will be used to compile the generated C/C++ code. Some standard assumptions are therefore used when a sizeof() operator is encountered, and a warning will be issued to encourage manual inspection of the translation.

Classes, Structs and Unions

Rule: A class, struct or union is translated to an SDL newtype.

This rule follows from the fact that classes, structs and unions are scope units (see Scope Units).

This section mainly uses classes in the discussions and examples, but since the translation rules make no difference between classes, structs and unions, the same is valid for structs and unions. Example 110 shows the translation of an empty class, struct and union.

Example 110 : Translation of classes, structs and unions

C++:

class C {};
struct S {};
union U {};

SDL:

NEWTYPE ptr_C Ref( C);
OPERATORS
ptr_C : -> ptr_C;
ptr_C : ptr_C -> ptr_C;
ENDNEWTYPE ptr_C;EXTERNAL 'C++';
NEWTYPE C
OPERATORS
C : -> C;
C : C -> C;
ENDNEWTYPE C;EXTERNAL 'C++';
NEWTYPE ptr_S Ref( S);
OPERATORS
ptr_S : -> ptr_S;
ptr_S : ptr_S -> ptr_S;
ENDNEWTYPE ptr_S;EXTERNAL 'C++';
NEWTYPE S
OPERATORS
S : -> S;
S : S -> S;
ENDNEWTYPE S;EXTERNAL 'C++';
NEWTYPE ptr_U Ref( U);
OPERATORS
ptr_U : -> ptr_U;
ptr_U : ptr_U -> ptr_U;
ENDNEWTYPE ptr_U;EXTERNAL 'C++';
NEWTYPE U
OPERATORS
U : -> U;
U : U -> U;
ENDNEWTYPE U;EXTERNAL 'C++';

In the example above three C++ types translate to six SDL sorts. The reason for this is that when CPP2SDL generates a newtype for a class, it will also, by default, generate a newtype that represents a pointer type for this class. This is convenient since pointers to a class often are needed. If the class inherits other classes this pointer newtype is in fact necessary, since it then holds cast operators to the base types of the class (see Type Compatibility between Pointers to Inherited Classes). If the command line option -optclasspointers has been set, CPP2SDL will not generate this extra newtype unless a pointer to the class is explicitly present in the input code.

If the class, struct, or union has no tag, it is an incomplete type declaration. The translation rules for incomplete types are described in Incomplete Types.

When importing a C/C++ union, the imported PR code will contain a /*#UNIONC*/ directive for the newtype representing that union.

Anonymous Unions

Rule: An anonymous union is translated by making its members become fields of the newtype that represents the enclosing scope unit.

This translation rule is natural since an anonymous union is no scope unit.

Example 111 : Translation of Anonymous Unions

C++:

struct S {
int i;
union {
int j;
int k;
};
};

SDL:

NEWTYPE ptr_S Ref( S);
OPERATORS
ptr_S : -> ptr_S;
ptr_S : ptr_S -> ptr_S;
ENDNEWTYPE ptr_S;EXTERNAL 'C++';
NEWTYPE S
STRUCT
j int;/* member of anonymous union */
k int;/* member of anonymous union */
i int;
OPERATORS
S : -> S;/*implicit parameter-less constructor*/
S : S -> S;/* implicit copy constructor */
ENDNEWTYPE S;EXTERNAL 'C++';

Note that an anonymous union is not an incomplete type declaration, although the syntax is similar. An anonymous union is not used to declare a type nor a variable, and does not define a type at all. Consequently, the translation rules for anonymous unions and incomplete types differ significantly. Compare with Incomplete Types.

Constructors

Rule: A constructor for a class is translated to an operator with the same name as the newtype that represents the class.

The return sort of the operator will be the sort defined by the newtype for the class, and the operator will of course also be placed in that newtype.

There are two different kinds of constructors in C++:

While a class may contain an arbitrary number of user-defined constructors, it may at the most contain two auto-generated ones; a parameter-less (or default) constructor and a copy constructor. A parameter-less constructor is available only if the class has no user-defined constructors, and a copy constructor is available only if no user-defined copy constructor is declared.

CPP2SDL will generate operators both for user-defined and implicit constructors. Example 112 below shows a class with three user-defined constructors, and one implicit copy constructor.

Example 112 : Translation of constructors

C++:

class C {
public:
C();
C(int i);
C(char c);
~C();
};

SDL:

NEWTYPE ptr_C Ref( C);
OPERATORS
ptr_C : -> ptr_C;
ptr_C : ptr_C -> ptr_C;
ENDNEWTYPE ptr_C;EXTERNAL 'C++';
NEWTYPE C
OPERATORS
C : -> C;
C : char -> C;
C : int -> C;
C : C -> C;/* implicit copy constructor */
ENDNEWTYPE C;EXTERNAL 'C++';

Destructors

Rule: A destructor is not translated to SDL.

The reason why a class destructor is not made accessible in SDL, is that it normally should not be called explicitly. Instead it will be called automatically when an object of the class goes out of scope or is deleted. See Example 112 for an example of how a destructor disappears in the SDL mapping.

Members

Rule: Member variables of a C++ class are translated to fields in the newtype that is the translation of that class, and member functions are translated to operators in the same newtype.

Other declarations than variables and functions in a class, for example type declarations, are also sometimes called members of the class, but they are not translated according to the translation rule above. Instead they are considered to be declarations on their own, but defined in an enclosing scope unit (i.e. the class). See Scope Units for more information.

Example 113 : Translation of class members

C++:

class C {
public:
int mv1; // Member variable
void mf1(long long p1); // Member function
enum e {a,b,c}; // "Member" type declaration
};

SDL:

NEWTYPE C_e /*#REFNAME 'C::e'*/
LITERALS a, b, c;
OPERATORS
IntToEnum /*#REFNAME '(C::e)'*/ : int -> C_e;
EnumToInt : C_e -> int; /*#OP(PY)*/
ORDERING;
ENDNEWTYPE C_e;EXTERNAL 'C++';
NEWTYPE ptr_C Ref( C);
OPERATORS
ptr_C : -> ptr_C;
ptr_C : ptr_C -> ptr_C;
ENDNEWTYPE ptr_C;EXTERNAL 'C++';
NEWTYPE C
STRUCT
mv1 int;
OPERATORS
mf1 : C, long_long_int;
C : -> C;/* implicit parameter-less constructor */
C : C -> C;/* implicit copy constructor */
ENDNEWTYPE C;EXTERNAL 'C++';

Note that the operator that represents a member function will have an additional initial formal argument. This argument has the sort of the newtype that represents the class where the member function is declared. Member functions are called from SDL in a functional style, where the first actual argument to the member function operator is the class instance on which the member function is to be invoked.

Member Access Specifier

Rule: Only members with public access specifier are translated to SDL.

This rule follows from the fact that public members of a class are the only members that are accessible from outside that class or its derived classes.

Example 114 : Translation of members with different access specifiers

C++:

class C {
private:
int i;
protected:
int j;
public:
int k;
int GetI();
int GetJ();
int Calc (int x, int y);
};

SDL:

NEWTYPE ptr_C Ref( C);
OPERATORS
ptr_C : -> ptr_C;
ptr_C : ptr_C -> ptr_C;
ENDNEWTYPE ptr_C;EXTERNAL 'C++';
NEWTYPE C
STRUCT
k int;
OPERATORS
Calc : C, int, int -> int;
GetI : C -> int;
GetJ : C -> int;
C : -> C;
C : C -> C;
ENDNEWTYPE C;EXTERNAL 'C++';

Virtual Member Functions

Rule: A virtual member function is translated in the same way as an ordinary member function.

In C++, virtual functions of a base class may be redefined in derived classes. Although this means that there is only one version of a particular virtual function in a derived class, the version defined in the base class may still be called by means of explicit qualification. Both versions of the function must thus be present in the SDL translation, exactly as is the case for non-virtual functions. See Inheritance for more about how C++ inheritance is represented in SDL.

Example 115 : Translation of virtual member functions

C++:

class CPen {
public:
virtual void Draw(); // Virtual member function
double GetRep(); // Non-virtual member function
};
class CPenD : public CPen {
public:
virtual void Draw(); // Redefinition of CPen::Draw()
};

SDL:

NEWTYPE ptr_CPen Ref( CPen);
OPERATORS
ptr_CPen : -> ptr_CPen;
ptr_CPen : ptr_CPen -> ptr_CPen;
ENDNEWTYPE ptr_CPen;EXTERNAL 'C++';
NEWTYPE CPen
OPERATORS
Draw : CPen;
GetRep : CPen -> double;
CPen : -> CPen;
CPen : CPen -> CPen;
ENDNEWTYPE CPen;EXTERNAL 'C++';
NEWTYPE ptr_CPenD Ref( CPenD);
OPERATORS
cast : ptr_CPenD -> ptr_CPen; /*#OP(PY)*/
ptr_CPenD : -> ptr_CPenD;
ptr_CPenD : ptr_CPenD -> ptr_CPenD;
ENDNEWTYPE ptr_CPenD;EXTERNAL 'C++';
NEWTYPE CPenD
OPERATORS
Draw : CPenD;
CPen_Draw /*#REFNAME 'CPen::Draw'*/ : CPenD;/* Inherited from CPen */
GetRep : CPenD -> double;/* Inherited from CPen */
CPenD : -> CPenD;/
CPenD : CPenD -> CPenD;
ENDNEWTYPE CPenD;EXTERNAL 'C++';

Pure Virtual Member Functions

Rule: A pure virtual member function is translated in the same way as an ordinary member function.

Although "pure virtuality" does not affect the translation of the member function itself, it will have impact on how the containing class, which is an abstract class, is translated. The reason is that special translation rules apply for abstract classes. See Abstract Classes for more information and an example on how pure virtual member functions are translated.

Static Members

Rule: A static member is translated both as an ordinary member, and as if it was declared in the global namespace.

There will thus be two representations in SDL of a static C++ member. The additional representation is caused by the fact that a static member is accessible without having an instance of the class where it is defined.

As shown in Example 116 below, a static member variable will be translated both to a newtype field and an external variable (see Variables), while a static member function will result in both an operator in the newtype for the class and an operator in the special global_namespace_ImpSpec newtype (see Functions).

Example 116 : Translation of static members

C++:

class C {
public:
static int k;
static void InitI(int);
};

SDL:

NEWTYPE global_namespace_ImpSpec /*#NOTYPE*/
OPERATORS
C_InitI /*#REFNAME 'C::InitI'*/ : int;
ENDNEWTYPE global_namespace_ImpSpec;EXTERNAL 'C++';
DCL C_k /*#REFNAME 'C::k'*/ int; EXTERNAL 'C++';
NEWTYPE ptr_C Ref( C);
OPERATORS
ptr_C : -> ptr_C;
ptr_C : ptr_C -> ptr_C;
ENDNEWTYPE ptr_C;EXTERNAL 'C++';
NEWTYPE C
STRUCT
k int;
OPERATORS
InitI : C, int;
C : -> C;
C : C -> C;
ENDNEWTYPE C;EXTERNAL 'C++';

If the resulting SDL declarations are to be inserted in an SDL context where external variables are not allowed (i.e. if CPP2SDL executes with the -novariables option set), static member variables cannot be translated to external variables. In that case only the standard translation of class members can be applied. Naturally, CPP2SDL will issue a warning if this happens.

Constant Members

Rule: A constant member is translated as an ordinary member, but with a #CONSTANT directive attached.

The semantics of a constant member variable is that it may not be written to after its initialization, and a constant member function may not change the state of its object. There is no way to express these restrictions in SDL, so the Analyzer will not be able to detect if they are violated. However, by attaching the #CONSTANT directive to SDL declarations that result from constant members, the Code Generator can do the necessary checks.

Example 117 : Translation of constant members

C++:

class C {
public:
const int cm; // constant member
C(int k) : cm(k) {};
void Do(double);
void Undo(double) const; // constant member function
};

SDL:

NEWTYPE ptr_C Ref( C);
OPERATORS
ptr_C : -> ptr_C;
ptr_C : ptr_C -> ptr_C;
ENDNEWTYPE ptr_C;EXTERNAL 'C++';
NEWTYPE C
STRUCT
cm /*#CONSTANT*/ int;
OPERATORS
C : int -> C;
Do : C, double;
Undo : C, double; /*#CONSTANT*/
C : C -> C;
ENDNEWTYPE C;EXTERNAL 'C++';

Member Constants

Rule: A member constant is translated both as an ordinary member with a #CONSTANT directive attached, and as if it was declared in the global namespace.

This translation rule is a combination of the translation rules for static and constant members, which is natural since a member constant is declared both to be constant and static in C++.

Example 118 : Translation of member constants

C++:

class X {
public:
static const int i = 99; // member constant
};
const int X::i; // definition of i

SDL:

SYNONYM X_i /*#REFNAME 'X::i'*/ int = EXTERNAL 
'C++';
NEWTYPE ptr_X Ref( X);
OPERATORS
ptr_X : -> ptr_X;
ptr_X : ptr_X -> ptr_X;
ENDNEWTYPE ptr_X;EXTERNAL 'C++';
NEWTYPE X
STRUCT
i /*#CONSTANT*/ int;
OPERATORS
X : -> X;
X : X -> X;
ENDNEWTYPE X;EXTERNAL 'C++';

Mutable Member Variables

Rule: A mutable member variable is translated as an ordinary member variable.

The mutable keyword in C++ can be looked upon as some kind of compiler directive, and needs therefore not be visible in the SDL translation.

Bitfield Member Variables

Rule: A bitfield member variable is translated to an SDL bitfield.

This rule applies for all bitfields that have a name. Bitfields without name are not translated to SDL.

It would have been possible to translate bitfields to ordinary newtype fields. However, by including the bitfield size in the SDL translation, the Analyzer is given a possibility to check that these fields are not assigned values that will not fit in the corresponding bitfield.

Example 119 : Translation of bitfields

C++:

struct A {
unsigned int i : 12;
int : 3;
bool dirty : 1;
};

SDL:

NEWTYPE ptr_A Ref( A);
OPERATORS
ptr_A : -> ptr_A;
ptr_A : ptr_A -> ptr_A;
ENDNEWTYPE ptr_A;EXTERNAL 'C++';
NEWTYPE A
STRUCT
dirty bool : 1;
i unsigned_int : 12;
OPERATORS
A : -> A;
A : A -> A;
ENDNEWTYPE A;EXTERNAL 'C++';

Note that bitfields are a tool-specific SDL extension.

Friends

Rule: Friend declarations will not be translated to SDL.

Friendship between a class C and another declaration D only affects what members of C that the implementation of D may access. It is therefore uninteresting to supply this information in the SDL translation of the class C.

Inheritance

Rule: C++ inheritance is represented in SDL by adding the translation of all public base class members to the newtype that represents a derived class.

This rule simply means that the C++ inheritance hierarchy is flattened in the SDL newtype representation. The reason for choosing this translation strategy, instead of using SDL inheritance between newtypes, is that the semantics of C++ and SDL inheritance is quite different.

All public member variables and member functions (but not constructors) of direct or indirect bases of a class will be generated in the newtype that is the translation of that class. Such an inherited field or operator will normally have the same name in SDL as in C++, but in some cases it is necessary to prefix the name with the name of the class from which it is inherited3. This happens when the name of the inherited member is the same as the name of one of the members in the derived class. Example 120 below shows how such ambiguities between inherited members are handled.

Example 120 : Translation of inheritance

C++:

class A {
public:
int am;
A(char);
};
class B : public A {
public:
char bm;
virtual void calc();
void set();
};
class C : public B {
public:
int am;
double cm;
void calc(); // Redefines B::calc()
void set();
};

SDL:

NEWTYPE ptr_A Ref( A);
OPERATORS
ptr_A : -> ptr_A;
ptr_A : ptr_A -> ptr_A;
ENDNEWTYPE ptr_A;EXTERNAL 'C++';
NEWTYPE A
STRUCT
am int;
OPERATORS
A : char -> A;
A : A -> A;
ENDNEWTYPE A;EXTERNAL 'C++';
NEWTYPE ptr_B Ref( B);
OPERATORS
cast : ptr_B -> ptr_A; /*#OP(PY)*/
ptr_B : -> ptr_B;
ptr_B : ptr_B -> ptr_B;
ENDNEWTYPE ptr_B;EXTERNAL 'C++';
NEWTYPE B
STRUCT
am int;/* Inherited from A */
bm char;
OPERATORS
calc : B;
keyword_set /*#REFNAME 'set'*/ : B;
B : B -> B;
ENDNEWTYPE B;EXTERNAL 'C++';
NEWTYPE ptr_C Ref( C);
OPERATORS
cast : ptr_C -> ptr_A; /*#OP(PY)*/
cast : ptr_C -> ptr_B; /*#OP(PY)*/
ptr_C : -> ptr_C;
ptr_C : ptr_C -> ptr_C;
ENDNEWTYPE ptr_C;EXTERNAL 'C++';
NEWTYPE C
STRUCT
am int;
B_am /*#REFNAME 'B::am'*/ int;/* Inherited from A */
bm char;/* Inherited from B */
cm double;
OPERATORS
calc : C;
B_calc /*#REFNAME 'B::calc'*/ : C;/* Inherited from B */
keyword_set /*#REFNAME 'set'*/ : C;
B_keyword_set /*#REFNAME 'B::keyword_set'*/ : C;/* Inherited from B */
C : C -> C;
ENDNEWTYPE C;EXTERNAL 'C++';

In C++, it is always possible to use a fully qualified name when accessing a class member, even if the name of the member is unambiguous without qualification. In the example above, the member variable bm in C that is inherited from B, may be referred to both as bm and B::bm. To avoid getting too many fields and operators in the generated newtypes, only the unqualified name can be used from SDL. This is natural since qualification in C++ normally only is done when necessary to resolve ambiguities.

There are more cases where C++ allows a member to be accessed by more than one name, while the SDL translation only supplies one of these possible names. For example, this applies for inherited types and static members as shown in Example 121 below.

Example 121 : Translation of inherited types and static members

C++:

class B {
public:
static int mv;
static char mf(double);
struct s {
int y;
} ms;
};
class D : public B {
};

SDL:

NEWTYPE global_namespace_ImpSpec /*#NOTYPE*/
OPERATORS
B_mf /*#REFNAME 'B::mf'*/ : double -> char;
ENDNEWTYPE global_namespace_ImpSpec;EXTERNAL 'C++';
NEWTYPE ptr_B Ref( B);
OPERATORS
ptr_B : -> ptr_B;
ptr_B : ptr_B -> ptr_B;
ENDNEWTYPE ptr_B;EXTERNAL 'C++';
NEWTYPE B
STRUCT
ms B_s;
mv int;
OPERATORS
mf : B, double -> char;
B : -> B;
B : B -> B;
ENDNEWTYPE B;EXTERNAL 'C++';
DCL B_mv /*#REFNAME 'B::mv'*/ int; EXTERNAL 'C++';
NEWTYPE ptr_B_s Ref( B_s);
OPERATORS
ptr_B_s : -> ptr_B_s;
ptr_B_s : ptr_B_s -> ptr_B_s;
ENDNEWTYPE ptr_B_s;EXTERNAL 'C++';
NEWTYPE B_s /*#REFNAME 'B::s'*/
STRUCT
y int;
OPERATORS
B_s /*#REFNAME 's'*/ : -> B_s;
B_s /*#REFNAME 's'*/ : B_s -> B_s;
ENDNEWTYPE B_s;EXTERNAL 'C++';
NEWTYPE ptr_D Ref( D);
OPERATORS
cast : ptr_D -> ptr_B; /*#OP(PY)*/
ptr_D : -> ptr_D;
ptr_D : ptr_D -> ptr_D;
ENDNEWTYPE ptr_D;EXTERNAL 'C++';
NEWTYPE D
STRUCT
ms B_s;/* Inherited from B */
mv int;/* Inherited from B */
OPERATORS
mf : D, double -> char;/* Inherited from B */
D : -> D;
D : D -> D;
ENDNEWTYPE D;EXTERNAL 'C++';

The declarations B::mv, B::mf and B::s in this example may in C++ also be referred to by means of the names D::mv, D::mf and D::s. This is not possible in the SDL translation, i.e. there are no declarations called D_mv, D_mf or D_s. CPP2SDL will choose the first version of the names since the members are declared in B.

Multiple Inheritance

The translation rule for C++ inheritance works also when a class inherits from more than one base class. However, the naming strategy described in Inheritance for handling ambiguous inherited members have to be generalized to also cover the case when a class inherits the same base class more than once.

Example 122 : Translation of multiple inheritance

C++:

class A {
public:
int m;
};
class B {
public:
int m;
int n;
};
class C: public A, public B {
};

SDL:

NEWTYPE ptr_A Ref( A);
OPERATORS
ptr_A : -> ptr_A;
ptr_A : ptr_A -> ptr_A;
ENDNEWTYPE ptr_A;EXTERNAL 'C++';
NEWTYPE A
STRUCT
m int;
OPERATORS
A : -> A;
A : A -> A;
ENDNEWTYPE A;EXTERNAL 'C++';
NEWTYPE ptr_B Ref( B);
OPERATORS
ptr_B : -> ptr_B;
ptr_B : ptr_B -> ptr_B;
ENDNEWTYPE ptr_B;EXTERNAL 'C++';
NEWTYPE B
STRUCT
m int;
n int;
OPERATORS
B : -> B;
B : B -> B;
ENDNEWTYPE B;EXTERNAL 'C++';
NEWTYPE ptr_C Ref( C);
OPERATORS
cast : ptr_C -> ptr_B; /*#OP(PY)*/
cast : ptr_C -> ptr_A; /*#OP(PY)*/
ptr_C : -> ptr_C;
ptr_C : ptr_C -> ptr_C;
ENDNEWTYPE ptr_C;EXTERNAL 'C++';
NEWTYPE C
STRUCT
A_m /*#REFNAME 'A::m'*/ int;/* Inherited from A */
B_m /*#REFNAME 'B::m'*/ int;/* Inherited from B */
n int;/* Inherited from B */
OPERATORS
C : -> C;
C : C -> C;
ENDNEWTYPE C;EXTERNAL 'C++';

The names of the generated fields and operators correspond to the qualified names that must be used in C++ to access the members in question. The rule is to qualify an ambiguous member with the most specialized base class that makes the name of the member unique. In most cases this base class is the class where the ambiguous member is declared, but when the inheritance hierarchy forms a graph rather than a tree (see Example 123) it might be necessary to qualify with the name of a class further down on the inheritance path.

Note that in some extraordinary inheritance hierarchies, it is possible that a member of a base class is inaccessible in a derived class. This happens when the inherited member cannot be unambiguously qualified according to the naming rule described above. If this happens, CPP2SDL will not translate the member to SDL, and a warning will be printed.

Virtual and Non-Virtual Inheritance

Rule: Virtual inheritance is translated in the same way as ordinary inheritance.

Virtual inheritance affects the way data is replicated when multiple inheritance is used. As shown in Multiple Inheritance members that are inherited more than once from the same base class need to be prefixed in SDL.

Since data from a base class is not replicated in derived classes that inherit from the base class with virtual inheritance, it would be possible to avoid prefixing the name of the members that are virtually inherited from the base class. However, since a virtually inherited member in general may be accessed using many alternative prefixes (corresponding to possible paths for reaching the member in the inheritance graph), and none of these prefixes can be said to be more natural to use than the others, all versions of the member's name are included in the SDL translation. This is the reason why no difference is made between virtual and non-virtual inheritance in SDL.

Example 123 : Translation of virtual inheritance

C++:

class A {
public:
int a;
};
class C : public A {
};
class D : public virtual A {
};
class E : public virtual A {
};
class G : public C, public D, public E {
};

SDL:

NEWTYPE ptr_A Ref( A);
OPERATORS
ptr_A : -> ptr_A;
ptr_A : ptr_A -> ptr_A;
ENDNEWTYPE ptr_A;EXTERNAL 'C++';
NEWTYPE A
STRUCT
a int;
OPERATORS
A : -> A;
A : A -> A;
ENDNEWTYPE A;EXTERNAL 'C++';
NEWTYPE ptr_C Ref( C);
OPERATORS
cast : ptr_C -> ptr_A; /*#OP(PY)*/
ptr_C : -> ptr_C;
ptr_C : ptr_C -> ptr_C;
ENDNEWTYPE ptr_C;EXTERNAL 'C++';
NEWTYPE C
STRUCT
a int;/* Inherited from A */
OPERATORS
C : -> C;
C : C -> C;
ENDNEWTYPE C;EXTERNAL 'C++';
NEWTYPE ptr_D Ref( D);
OPERATORS
cast : ptr_D -> ptr_A; /*#OP(PY)*/
ptr_D : -> ptr_D;
ptr_D : ptr_D -> ptr_D;
ENDNEWTYPE ptr_D;EXTERNAL 'C++';
NEWTYPE D
STRUCT
a int;/* Inherited from A */
OPERATORS
D : -> D;
D : D -> D;
ENDNEWTYPE D;EXTERNAL 'C++';
NEWTYPE ptr_E Ref( E);
OPERATORS
cast : ptr_E -> ptr_A; /*#OP(PY)*/
ptr_E : -> ptr_E;
ptr_E : ptr_E -> ptr_E;
ENDNEWTYPE ptr_E;EXTERNAL 'C++';
NEWTYPE E
STRUCT
a int;/* Inherited from A */
OPERATORS
E : -> E;
E : E -> E;
ENDNEWTYPE E;EXTERNAL 'C++';
NEWTYPE ptr_G Ref( G);
OPERATORS
cast : ptr_G -> ptr_E; /*#OP(PY)*/
cast : ptr_G -> ptr_D; /*#OP(PY)*/
cast : ptr_G -> ptr_C; /*#OP(PY)*/
ptr_G : -> ptr_G;
ptr_G : ptr_G -> ptr_G;
ENDNEWTYPE ptr_G;EXTERNAL 'C++';
NEWTYPE G
STRUCT
C_a /*#REFNAME 'C::a'*/ int;/* Inherited from A */
D_a /*#REFNAME 'D::a'*/ int;/* Inherited from A */
E_a /*#REFNAME 'E::a'*/ int;/* Inherited from A */
OPERATORS
G : -> G;
G : G -> G;
ENDNEWTYPE G;EXTERNAL 'C++';

Inheritance Access Specifier

Rule: Only members that are inherited using public inheritance are translated to SDL.

When the inheritance is private or protected, the members of the base class are not accessible from outside the class. It is therefore natural to exclude members, that are inherited from private and protected bases, in the newtype that represents the derived class.

Example 124 : Translation of inheritance with different access specifiers

C++:

class X {
public:
int a;
void f();
};
class Y1 : public X {};
class Y2 : protected X {};
class Y3 : private X {};

SDL:

NEWTYPE ptr_X Ref( X);
OPERATORS
ptr_X : -> ptr_X;
ptr_X : ptr_X -> ptr_X;
ENDNEWTYPE ptr_X;EXTERNAL 'C++';
NEWTYPE X
STRUCT
a int;
OPERATORS
f : X;
X : -> X;
X : X -> X;
ENDNEWTYPE X;EXTERNAL 'C++';
NEWTYPE ptr_Y1 Ref( Y1);
OPERATORS
cast : ptr_Y1 -> ptr_X; /*#OP(PY)*/
ptr_Y1 : -> ptr_Y1;
ptr_Y1 : ptr_Y1 -> ptr_Y1;
ENDNEWTYPE ptr_Y1;EXTERNAL 'C++';
NEWTYPE Y1
STRUCT
a int;/* Inherited from X */
OPERATORS
f : Y1;/* Inherited from X */
Y1 : -> Y1;
Y1 : Y1 -> Y1;
ENDNEWTYPE Y1;EXTERNAL 'C++';
NEWTYPE ptr_Y2 Ref( Y2);
OPERATORS
ptr_Y2 : -> ptr_Y2;
ptr_Y2 : ptr_Y2 -> ptr_Y2;
ENDNEWTYPE ptr_Y2;EXTERNAL 'C++';
NEWTYPE Y2
OPERATORS
Y2 : -> Y2;
Y2 : Y2 -> Y2;
ENDNEWTYPE Y2;EXTERNAL 'C++';
NEWTYPE ptr_Y3 Ref( Y3);
OPERATORS
ptr_Y3 : -> ptr_Y3;
ptr_Y3 : ptr_Y3 -> ptr_Y3;
ENDNEWTYPE ptr_Y3;EXTERNAL 'C++';
NEWTYPE Y3
OPERATORS
Y3 : -> Y3;
Y3 : Y3 -> Y3;
ENDNEWTYPE Y3;EXTERNAL 'C++';

The inheritance access specifier also affects how casting from the derived type to the base type can be done. This is described in Type Compatibility between Inherited Classes and in Type Compatibility between Pointers to Inherited Classes.

Type Compatibility between Inherited Classes

Rule: An object of a derived class may be assigned to an object of a base class by using an explicit cast operator in SDL.

The above assignment (known as slicing) is type-compatible in C++ without the use of a cast operator. Since only the common members are copied in the assignment, this operation is somewhat dangerous and is not generally recommended. Therefore, the cast operators that are needed in SDL to do slicing between objects, are only generated when the
-slicing option is set.

Example 125 : Generation of operators for slicing

C++:

class C {};
class D {};
class CD : public C, public D {};

SDL:

NEWTYPE ptr_C Ref( C);
OPERATORS
ptr_C : -> ptr_C;
ptr_C : ptr_C -> ptr_C;
ENDNEWTYPE ptr_C;EXTERNAL 'C++';
NEWTYPE C
OPERATORS
C : -> C;
C : C -> C;
ENDNEWTYPE C;EXTERNAL 'C++';
NEWTYPE ptr_D Ref( D);
OPERATORS
ptr_D : -> ptr_D;
ptr_D : ptr_D -> ptr_D;
ENDNEWTYPE ptr_D;EXTERNAL 'C++';
NEWTYPE D
OPERATORS
D : -> D;
D : D -> D;
ENDNEWTYPE D;EXTERNAL 'C++';
NEWTYPE ptr_CD Ref( CD);
OPERATORS
cast : ptr_CD -> ptr_D; /*#OP(PY)*/
cast : ptr_CD -> ptr_C; /*#OP(PY)*/
ptr_CD : -> ptr_CD;
ptr_CD : ptr_CD -> ptr_CD;
ENDNEWTYPE ptr_CD;EXTERNAL 'C++';
NEWTYPE CD
OPERATORS
cast : CD -> D; /*#OP(PY)*/
cast : CD -> C; /*#OP(PY)*/
CD : -> CD;
CD : CD -> CD;
ENDNEWTYPE CD;EXTERNAL 'C++';

Note that the inheritance access specifier affects how these cast operators are generated. A cast operator from a class D to a class B will only be generated if B is a public unambiguous base of D. If it is private or protected, or is an ambiguous base for D, it is not allowed to cast from D to B.

Type Compatibility between Pointers to Inherited Classes

Rule: A pointer to an object of a derived class may be assigned to a pointer to an object of a base class by using an explicit cast operator in SDL.

The above assignment is type-compatible in C++, i.e. "up-casts" in a class hierarchy are implicit in C++. This is an important property of object-oriented languages that support for example polymorphism. In SDL, however, the newtypes for a base class and a derived class will be unrelated and thus type incompatible. To support up-casting in SDL, explicit cast operators are generated in the newtype that represents the pointer type to a derived class.

Example 126 : Generation of cast operators for up-casting

C++:

class C {};
class D {};
class CD : public C, public D {};

SDL:

NEWTYPE ptr_C Ref( C);
OPERATORS
ptr_C : -> ptr_C;
ptr_C : ptr_C -> ptr_C;
ENDNEWTYPE ptr_C;EXTERNAL 'C++';
NEWTYPE C
OPERATORS
C : -> C;
C : C -> C;
ENDNEWTYPE C;EXTERNAL 'C++';
NEWTYPE ptr_D Ref( D);
OPERATORS
ptr_D : -> ptr_D;
ptr_D : ptr_D -> ptr_D;
ENDNEWTYPE ptr_D;EXTERNAL 'C++';
NEWTYPE D
OPERATORS
D : -> D;
D : D -> D;
ENDNEWTYPE D;EXTERNAL 'C++';
NEWTYPE ptr_CD Ref( CD);
OPERATORS
cast : ptr_CD -> ptr_D; /*#OP(PY)*/
cast : ptr_CD -> ptr_C; /*#OP(PY)*/
ptr_CD : -> ptr_CD;
ptr_CD : ptr_CD -> ptr_CD;
ENDNEWTYPE ptr_CD;EXTERNAL 'C++';
NEWTYPE CD
OPERATORS
CD : -> CD;
CD : CD -> CD;
ENDNEWTYPE CD;EXTERNAL 'C++';

Note that the inheritance access specifier is taken into consideration so that a cast operator from Ref(D) to Ref(B) only will be generated if the class B is a public unambiguous base of the class D.

Sometimes it is necessary to do down-casts, or even cross-casts, in a class hierarchy. Such casts (known as dynamic casts) are explicit both in C++ and SDL. See Run-Time Type Information and Dynamic Cast for more information.

Abstract Classes

Rule: An abstract class is translated to a newtype without constructor operators.

This translation rule makes it possible to declare pointers to an abstract class, but no objects of such a class may be allocated since there are no constructor operators that can be used as argument to the new operator (see Dynamic Memory Management).

Example 127 : Translation of abstract classes

C++:

class C {
public:
virtual int f(int) = 0; // pure virtual member function
C() {};
};
class D : public C {
};

SDL:

NEWTYPE ptr_C Ref( C);
ENDNEWTYPE ptr_C;EXTERNAL 'C++';
NEWTYPE C
OPERATORS
f : C, int -> int;
ENDNEWTYPE C;EXTERNAL 'C++';
NEWTYPE ptr_D Ref( D);
OPERATORS
cast : ptr_D -> ptr_C; /*#OP(PY)*/
ENDNEWTYPE ptr_D;EXTERNAL 'C++';
NEWTYPE D
OPERATORS
f : D, int -> int;/* Inherited from C */
ENDNEWTYPE D;EXTERNAL 'C++';

Note from the example above that abstractness is inherited to a derived class if not each pure virtual member function of all its base classes are redefined in the derived class.

Run-Time Type Information and Dynamic Cast

Rule: A pointer to an object of a base class may be assigned to a pointer to an object of a derived class, or to a pointer to an object of a base class of such a derived class, by using an explicit cast operator in SDL.

The above assignments require a dynamic cast in C++, which is done with an explicit cast operator that supports down-casts and cross-casts in an inheritance hierarchy. Since these casts require run-time type information (RTTI) about the dynamic type of an object, most C++ compilers have an option that must be set to safely support dynamic casts. For the same reason, CPP2SDL also has such an option called -rtti. If it is set, cast operators will be generated that enable the type conversions that are possible in C++ using dynamic casts.

The source type of a dynamic cast must be polymorphic, i.e. contain one or more virtual member functions, possibly inherited ones. For each such polymorphic class X, cast operators will be generated that convert from Ref(X) to Ref(Y), for each class Y that either inherits from X (down-casts), or is a public base class of a class that inherits from X (cross-casts).

Example 128 below illustrates this translation rule. It is assumed that all classes in the example contain virtual functions and thus are polymorphic.

Example 128 : Generation of cast operators for dynamic casting

C++:

class A {};
class B: public A {};
class E {};
class D: protected E {};
class C: public B, public D {};

SDL:

NEWTYPE ptr_A Ref( A);
OPERATORS
ptr_A : -> ptr_A;
ptr_A : ptr_A -> ptr_A;
ENDNEWTYPE ptr_A;EXTERNAL 'C++';
NEWTYPE A
OPERATORS
A : -> A;
A : A -> A;
ENDNEWTYPE A;EXTERNAL 'C++';
NEWTYPE ptr_B Ref( B);
OPERATORS
cast /*#REFNAME 'dynamic_cast<B*>'*/ : ptr_A -> ptr_B;
cast : ptr_B -> ptr_A; /*#OP(PY)*/
ptr_B : -> ptr_B;
ptr_B : ptr_B -> ptr_B;
ENDNEWTYPE ptr_B;EXTERNAL 'C++';
NEWTYPE B
OPERATORS
B : -> B;
B : B -> B;
ENDNEWTYPE B;EXTERNAL 'C++';
NEWTYPE ptr_E Ref( E);
OPERATORS
ptr_E : -> ptr_E;
ptr_E : ptr_E -> ptr_E;
ENDNEWTYPE ptr_E;EXTERNAL 'C++';
NEWTYPE E
OPERATORS
E : -> E;
E : E -> E;
ENDNEWTYPE E;EXTERNAL 'C++';
NEWTYPE ptr_D Ref( D);
OPERATORS
ptr_D : -> ptr_D;
ptr_D : ptr_D -> ptr_D;
ENDNEWTYPE ptr_D;EXTERNAL 'C++';
NEWTYPE D
OPERATORS
D : -> D;
D : D -> D;
ENDNEWTYPE D;EXTERNAL 'C++';
NEWTYPE ptr_C Ref( C);
OPERATORS
cast /*#REFNAME 'dynamic_cast<A*>'*/ : ptr_D -> ptr_A;
cast /*#REFNAME 'dynamic_cast<B*>'*/ : ptr_D -> ptr_B;
cast /*#REFNAME 'dynamic_cast<D*>'*/ : ptr_A -> ptr_D;
cast /*#REFNAME 'dynamic_cast<D*>'*/ : ptr_B -> ptr_D;
cast /*#REFNAME 'dynamic_cast<C*>'*/ : ptr_D -> ptr_C;
cast : ptr_C -> ptr_D; /*#OP(PY)*/
cast /*#REFNAME 'dynamic_cast<C*>'*/ : ptr_A -> ptr_C;
cast : ptr_C -> ptr_A; /*#OP(PY)*/
cast /*#REFNAME 'dynamic_cast<C*>'*/ : ptr_B -> ptr_C;
cast : ptr_C -> ptr_B; /*#OP(PY)*/
ptr_C : -> ptr_C;
ptr_C : ptr_C -> ptr_C;
ENDNEWTYPE ptr_C;EXTERNAL 'C++';
NEWTYPE C
OPERATORS
C : -> C;
C : C -> C;
ENDNEWTYPE C;EXTERNAL 'C++';

Note that no cast operators are generated that cast to ptr_E since E is a protected base of D. But in fact there are no cast operators that cast from ptr_E neither. The somewhat subtle reason for this is that those operators cannot be used in practice, since the protected inheritance makes it impossible to have a variable with ptr_E as static type and ptr_C as dynamic type. CPP2SDL will therefore not generate these cast operators.

In C++ dynamic casts work both for pointers and references to objects. In SDL, however, it is only possible to do dynamic casts between pointers, since references are not explicitly represented in the translation (see References).

Forward Declarations

Rule: A forward declaration is not translated to SDL.

This rule is valid for all forward declarations for which there are definitions later on in the header file. This is the most common case, and the purpose of such forward declarations is simply to make an identifier known to the C/C++ compiler so that it may be used before it is defined.

However, it is possible to make a forward declaration for which no definition exists in the header file. In that case CPP2SDL must generate an extra newtype to represent the missing definition. Since this extra newtype does not correspond to a real C/C++ type, it is marked with a
#NOTYPE directive.

Example 129 below contains two forward declarations, one of which has no corresponding definition (class C).

Example 129 : Translation of forward declarations

C++:

typedef struct S *fwdS;
class C *fwdC;
struct S {
int a;
};

SDL:

SYNTYPE fwdS = ptr_S
ENDSYNTYPE fwdS;EXTERNAL 'C++';
NEWTYPE C /*#NOTYPE*/
ENDNEWTYPE C;EXTERNAL 'C++';
NEWTYPE ptr_C Ref( C);
OPERATORS
ptr_C : -> ptr_C;
ptr_C : ptr_C -> ptr_C;
ENDNEWTYPE ptr_C;EXTERNAL 'C++';
DCL fwdC ptr_C; EXTERNAL 'C++';
NEWTYPE ptr_S Ref( S);
OPERATORS
ptr_S : -> ptr_S;
ptr_S : ptr_S -> ptr_S;
ENDNEWTYPE ptr_S;EXTERNAL 'C++';
NEWTYPE S
STRUCT
a int;
OPERATORS
S : -> S;
S : S -> S;
ENDNEWTYPE S;EXTERNAL 'C++';

Incomplete Types

Rule: An incomplete type declaration is translated to a newtype that may not be instantiated in SDL. This rule applies to all incomplete types even if they are declared within complete types. For example a tagless type within a container type will not be correctly instantiated in SDL.

Compare this translation rule with the one for namespaces (see Namespaces). While a namespace does not define a type at all, an incomplete type declaration defines an incomplete type that may not be referred to. That is the reason why such a newtype must not be instantiated in SDL.

C/C++ allows declarations of incomplete classes, structs, unions and enums, i.e. all types having a tag. Incomplete types are therefore also called tag-less types.

Incomplete types can be used in

Example 130 : Translation of incomplete types

C++:

typedef struct {
  int i;
} r;
struct S {
int i;
struct {
int j;
} ss1, *ss2, ss3[2]; // Data declarations
};
typedef enum {
a, b, c
} ss1, *ss2, ss3[2]; // Type declarations
typedef struct {
int i;
}; // Missing type name - "useless" declaration
struct {
int i;
}; // Missing variable name - "useless" declaration

SDL:

NEWTYPE r
  STRUCT
    i int;
ENDNEWTYPE r;EXTERNAL 'C++';
NEWTYPE S_incomplete_ss3
STRUCT
j int;
ENDNEWTYPE S_incomplete_ss3;
NEWTYPE ptr_S_incomplete_ss3 Ref( S_incomplete_ss3);
OPERATORS
ptr_S_incomplete_ss3 : -> ptr_S_incomplete_ss3;
ptr_S_incomplete_ss3 : ptr_S_incomplete_ss3 ->
ptr_S_incomplete_ss3;
ENDNEWTYPE ptr_S_incomplete_ss3;EXTERNAL 'C++';
NEWTYPE arr_2_S_incomplete_ss3 CArray(2, 
S_incomplete_ss3);
ENDNEWTYPE arr_2_S_incomplete_ss3;EXTERNAL 'C++';
NEWTYPE ptr_S Ref( S);
OPERATORS
ptr_S : -> ptr_S;
ptr_S : ptr_S -> ptr_S;
ENDNEWTYPE ptr_S;EXTERNAL 'C++';
NEWTYPE S
STRUCT
i int;
ss1 S_incomplete_ss3;
ss2 ptr_S_incomplete_ss3;
ss3 arr_2_S_incomplete_ss3;
OPERATORS
S : -> S;
S : S -> S;
ENDNEWTYPE S;EXTERNAL 'C++';
NEWTYPE incomplete_ss3
LITERALS a, b, c;
OPERATORS
EnumToInt : incomplete_ss3 -> int; /*#OP(PY)*/
ORDERING;
ENDNEWTYPE incomplete_ss3;EXTERNAL 'C++';
NEWTYPE ptr_incomplete_ss3 Ref( incomplete_ss3);
OPERATORS
ptr_incomplete_ss3 : -> ptr_incomplete_ss3;
ptr_incomplete_ss3 : ptr_incomplete_ss3 ->
ptr_incomplete_ss3;

ENDNEWTYPE ptr_incomplete_ss3;EXTERNAL 'C++';
SYNTYPE ss2 = ptr_incomplete_ss3
ENDSYNTYPE ss2;EXTERNAL 'C++';
NEWTYPE arr_2_incomplete_ss3 CArray( 2, 
incomplete_ss3);
ENDNEWTYPE arr_2_incomplete_ss3;EXTERNAL 'C++';
SYNTYPE ss3 = arr_2_incomplete_ss3
ENDSYNTYPE ss3;EXTERNAL 'C++';

As can be seen in the example, incomplete types that are used in data or type declarations will have the name of the last declared variable or type, prefixed with a user-configurable string that by default is "incomplete_". The option -prefix can be used to configure this string.

Incomplete types in "useless" declarations will not be translated to SDL, and CPP2SDL will issue warnings that the declarations were ignored.

Note that the translation of incomplete enum declarations differs from the normal translation rule of an enum declaration. The differences are listed in Enumerated Types.

Finally note that incomplete classes, structs and unions define scope units although they are incomplete. Their names thus follows the rules for naming of scope units described in Scope Units.

Dynamic Memory Management

Rule: The C/C++ primitives for dynamic memory management is represented in SDL by means of special operators.

Dynamic memory management is done differently in C and C++. How C or C++ data is dynamically allocated and deallocated in SDL therefore depends on whether CPP2SDL executes in C or C++ mode (controlled by the option -c). In both cases dynamic memory management is done by means of special SDL operators that are defined in the Ref generator. However, the definition of the Ref generator is different in C and C++ mode (see SDL Library for Fundamental C/C++ Types).

C Mode

The following operators are used to support dynamic memory management of C data from SDL:

Example 131 : Dynamic memory management of C data from SDL

C:

struct S {
int i;
double j;
};

SDL:

NEWTYPE ptr_S Ref( S);
ENDNEWTYPE ptr_S;EXTERNAL 'C';
NEWTYPE S /*#REFNAME 'struct S'*/
STRUCT
i int;
j double;
ENDNEWTYPE S;EXTERNAL 'C';

SDL Usage:

dcl var s, ptrs ptr_s;
task { 
ptrs := (. var .);
ptrs*>!i := 4;
  free(ptrs);
};

C++ Mode

The following operators are used to support dynamic memory management of C++ data from SDL:

Example 132 : Dynamic memory management of C++ data from SDL

C++:

struct S {
int i;
double j;
};

Import Specification:

TRANSLATE {
S**
int*
}

SDL:

NEWTYPE ptr_int Ref( int);
OPERATORS
ptr_int : -> ptr_int;
ptr_int : ptr_int -> ptr_int;
ENDNEWTYPE ptr_int;EXTERNAL 'C++';
NEWTYPE ptr_ptr_S Ref( ptr_S);
OPERATORS
ptr_ptr_S : -> ptr_ptr_S;
ptr_ptr_S : ptr_ptr_S -> ptr_ptr_S;
ENDNEWTYPE ptr_ptr_S;EXTERNAL 'C++';
NEWTYPE ptr_S Ref( S);
OPERATORS
ptr_S : -> ptr_S;
ptr_S : ptr_S -> ptr_S;'
ENDNEWTYPE ptr_S;EXTERNAL 'C++';
NEWTYPE S
STRUCT
i int;
j double;
OPERATORS
S : -> S;
S : S -> S;
ENDNEWTYPE S;EXTERNAL 'C++';

SDL Usage:

dcl ptrs ptr_s, ptrptrs ptr_ptr_s, ptri ptr_int;
task {
ptrs := new(s);
ptrs*>!i := 4;
ptrptrs := new(ptr_s);
ptri := new(int);
  delete(ptrs);
delete(ptrptrs);
delete(ptri);
  ptri := newArray(int, 5);
  deleteArray(ptri);
};

The input to the new and new_array operators must be an operator that corresponds to a constructor in C++. To enable dynamic allocation of data with non-class types, there must thus exist constructor operators for these types. These operators correspond to the implicit parameter-less and copy constructors, which exist for each C++ type. The definition of these constructor operators are part of the non-generated SDL declarations that are included when the -generatecpptypes option is set (see SDL Library for Fundamental C/C++ Types).

Overloaded Operators

Rule: An overloaded C++ operator is translated to a corresponding overloaded SDL operator.

Since the sets of operators that may be overloaded are different in C++ and SDL, not all overloaded C++ operators can be translated to SDL.

The table below shows which C++ operators that can be translated to SDL

:
C++ operator Description SDL operator

+

(binary) Addition

+

-

(binary) Subtraction

-

*

(binary) Multiplication

*

*

(unary prefix) Dereference

*>

/

(binary) Division

/

%

(binary) Modulo

rem

!

(unary prefix) Not

not

<

(binary) Less

<

>

(binary) Greater

>

<<

(binary) Left Shift

<

>>

(binary) Right Shift

>

==

(binary) Equal

=

!=

(binary) Not Equal

/=

<=

(binary) Less Equal

<=

>=

(binary) Greater Equal

>=

&&

(binary) And

and

||

(binary) Or

or

Note that even if there is a corresponding SDL operator to translate to, a C++ operator could be declared in a way that would make it necessary to qualify its SDL name, which is not possible. This happens for example if the operator is declared to be static, or declared inside a namespace (see Scope Units for more about scope name qualifications). It may also happen due to name qualification rules for inherited members (see Multiple Inheritance). CPP2SDL will issue a warning if it encounters an overloaded operator that cannot be translated.

The table above shows that the translation of the less and greater operators (<, >) is the same as the translation of the shift operators (<<, >>). Obviously, this may lead to ambiguities if both these operator pairs are overloaded in a class. In that case, the less and greater operators will have precedence, and CPP2SDL will issue a warning that the overloaded shift operators cannot be translated to SDL.

Example 133 : Translation of overloaded operators

C++:

class ostream {
public:
ostream& operator<(const char* p1);
ostream& operator<<(const char* p1);
ostream& operator>>(const char* p1);
static int operator%(int p1);
bool operator!();
};

SDL:

NEWTYPE ptr_char Ref( char);
OPERATORS
ptr_char : -> ptr_char;
ptr_char : ptr_char -> ptr_char;
ENDNEWTYPE ptr_char;EXTERNAL 'C++';
NEWTYPE ptr_ostream Ref( ostream);
OPERATORS
ptr_ostream : -> ptr_ostream;
ptr_ostream : ptr_ostream -> ptr_ostream;
ENDNEWTYPE ptr_ostream;EXTERNAL 'C++';
NEWTYPE ostream
OPERATORS
"not" /*#REFNAME 'operator!'*/ : ostream -> bool;
"rem" /*#REFNAME 'operator%'*/ : ostream, int -> int;
"<" /*#REFNAME 'operator<'*/ : ostream, ptr_char -> ostream;
ostream : -> ostream;
ostream : ostream -> ostream;
ENDNEWTYPE ostream;EXTERNAL 'C++';

Conversion Operators

Rule: A conversion operator is translated to a special conv operator in SDL.

In C++, a conversion operator is implicitly called by the compiler when a matching type conversion is needed. The conv operator, however, must be called explicitly in SDL.

Example 134 : Translation of conversion operators

C++:

class Tiny {
public:
operator int(); // Implicit conversion from Tiny to int
};

SDL:

NEWTYPE ptr_Tiny Ref( Tiny);
OPERATORS
ptr_Tiny : -> ptr_Tiny;
ptr_Tiny : ptr_Tiny -> ptr_Tiny;
ENDNEWTYPE ptr_Tiny;EXTERNAL 'C++';
NEWTYPE Tiny
OPERATORS
conv : Tiny -> int; /*#OP(PY)*/
Tiny : -> Tiny;
Tiny : Tiny -> Tiny;
ENDNEWTYPE Tiny;EXTERNAL 'C++';

Note that the conv operator returns the target type of the type conversion specified by the conversion operator. The #OP(PY) directive tells the Code Generator that the operator is implicitly called in C++.

Templates

Rule: A template declaration is translated to SDL by instantiating it.

A template declaration as such cannot be translated to SDL. Only specified instantiations of the template can be translated. CPP2SDL will print a warning about this when a template declaration is encountered.

A template instantiation may of course be present in the input headers. In that case CPP2SDL will translate the template instantiation by substituting all formal template arguments in the template declaration with the actual template arguments used in the template instantiation. If the input headers contain no suitable instantiation of a certain template, an import specification may be used to provide such an instantiation. See Template Instantiations to learn more about that.

There are two main kinds of template declarations in C++; class templates and function templates.

Class Templates

Rule: An instantiation of a class template is translated in the same way as the non-template class that is obtained if all formal arguments of the class template declaration are substituted with the actual arguments of the class template instantiation.

This translation rule implies that class template instantiations will become newtypes in SDL. The name of such a newtype will consist of the name of the template class, followed by the names of all actual template arguments of the template instantiation. The name will also be prefixed with a string that by default is "tpl_". The option -prefix can be used to configure this string.

Example 135 : Translation of class template instantiations

C++:

template <class T> class C {
public:
T t;
T f();
C(T v);
};
typedef C<int> mytype; // Class template 
instantiation

SDL:

NEWTYPE ptr_tpl_C_int Ref( tpl_C_int);
OPERATORS
ptr_tpl_C_int : -> ptr_tpl_C_int;
ptr_tpl_C_int : ptr_tpl_C_int -> ptr_tpl_C_int;
ENDNEWTYPE ptr_tpl_C_int;EXTERNAL 'C++';
NEWTYPE tpl_C_int /*#REFNAME 'C<int >'*/
STRUCT
t int;
OPERATORS
tpl_C_int /*#REFNAME 'C'*/ : int -> tpl_C_int;
f : tpl_C_int -> int;
tpl_C_int /*#REFNAME 'C'*/ : tpl_C_int -> tpl_C_int;
ENDNEWTYPE tpl_C_int;EXTERNAL 'C++';
SYNTYPE mytype = tpl_C_int
ENDSYNTYPE mytype;EXTERNAL 'C++';

Note that a #REFNAME directive passes the C++ name of the class template instantiation to the Code Generator.

Function Templates

Rule: An instantiation of a function template is translated in the same way as the non-template function that is obtained if all formal arguments of the function template declaration are substituted with the actual arguments of the function template instantiation.

This translation rule implies that function template instantiations will become operators in SDL. The name of such an operator will consist of the name of the template function, followed by the names of all actual template arguments of the template instantiation. The name will also be prefixed with a string that by default is "tpl_". The option -prefix can be used to configure this string.

Since a function template is instantiated when called, a C++ header file will normally not contain any function template instantiations. Instead an import specification should be used to provide the necessary instantiations (see Template Instantiations). In Example 136 below an import specification is used to instantiate the template function with the type int*.

Example 136 : Translation of function template instantiations

C++:

template <class T> T func(T t);

Import Specification:

TRANSLATE {
func<int*>
}

SDL:

NEWTYPE global_namespace_ImpSpec /*#NOTYPE*/
OPERATORS
tpl_func_ptr_int /*#REFNAME 'func<int* >'*/ : ptr_int
-> ptr_int;
ENDNEWTYPE global_namespace_ImpSpec;EXTERNAL 'C++';
NEWTYPE ptr_int Ref( int);
OPERATORS
ptr_int : -> ptr_int;
ptr_int : ptr_int -> ptr_int;
ENDNEWTYPE ptr_int;EXTERNAL 'C++';

As can be seen from the translation of the example above, the #REFNAME directive contains the C++ name of the template instantiation written on the so called explicit form5. This makes the Code Generator use this explicit form when the template function is called from SDL.

Note:

Calling a function template from SDL requires that the target C++ compiler can handle calls using the explicit form of the function template instantiation.

Default Template Arguments

Rule: An instantiation of a template declaration with default arguments is translated in the same way as an ordinary template instantiation, where omitted actual arguments in the instantiation are substituted with the specified default types or values.

This translation rule is very similar to the one used for functions with default arguments (see Default Arguments).

Example 137 : Translation of templates with default arguments

C++:

template <class T, class U = char, int i = 5> class 
C {
public:
T t[i];
T f();
C(U p1);
};
C<int> var1; // Using all the default values
C<int, bool> var2; // Using the default value for i
C<int, bool, 5> var3; // Not using any default value

SDL:

NEWTYPE ptr_tpl_C_int_char_5 Ref( tpl_C_int_char_5);
OPERATORS
ptr_tpl_C_int_char_5 : -> ptr_tpl_C_int_char_5;
ptr_tpl_C_int_char_5 : ptr_tpl_C_int_char_5 ->
ptr_tpl_C_int_char_5;
ENDNEWTYPE ptr_tpl_C_int_char_5;EXTERNAL 'C++';
NEWTYPE tpl_C_int_char_5 /*#REFNAME 'C<int, char, 5 
>'*/
STRUCT
t arr_5_int;
OPERATORS
tpl_C_int_char_5 /*#REFNAME 'C'*/ : char ->
tpl_C_int_char_5;
f : tpl_C_int_char_5 -> int;
tpl_C_int_char_5 /*#REFNAME 'C'*/ : tpl_C_int_char_5
-> tpl_C_int_char_5;
ENDNEWTYPE tpl_C_int_char_5;EXTERNAL 'C++';
DCL var1 tpl_C_int_char_5; EXTERNAL 'C++';
DCL var2 tpl_C_int_bool_5; EXTERNAL 'C++';
NEWTYPE arr_5_int CArray( 5, int);
ENDNEWTYPE arr_5_int;EXTERNAL 'C++';
NEWTYPE ptr_tpl_C_int_bool_5 Ref( tpl_C_int_bool_5);
OPERATORS
ptr_tpl_C_int_bool_5 : -> ptr_tpl_C_int_bool_5;
ptr_tpl_C_int_bool_5 : ptr_tpl_C_int_bool_5 ->
ptr_tpl_C_int_bool_5;
ENDNEWTYPE ptr_tpl_C_int_bool_5;EXTERNAL 'C++';
NEWTYPE tpl_C_int_bool_5 /*#REFNAME 'C<int, bool, 5 
>'*/
STRUCT
t arr_5_int;
OPERATORS
tpl_C_int_bool_5 /*#REFNAME 'C'*/ : bool ->
tpl_C_int_bool_5;
f : tpl_C_int_bool_5 -> int;
-> tpl_C_int_bool_5;
ENDNEWTYPE tpl_C_int_bool_5;EXTERNAL 'C++';
DCL var3 tpl_C_int_bool_5; EXTERNAL 'C++';

Note that although the template instantiations of var2 and var3 in the example above look different, they evaluate to the same template type both in C++ and in the SDL translation.

Miscellaneous

This section covers some miscellaneous issues that have not been discussed so far. They are divided into constructs that are part of the C or C++ languages, and constructs that are not part of the languages as such, but that nevertheless may be found in an input C/C++ header file.

Language Constructs

Volatile

Rule: A volatile declaration is translated in the same way as an ordinary declaration.

The volatile specifier can be looked upon as some kind of compiler directive, and needs therefore not be visible in the SDL translation.

Linkage

Rule: The linkage of a C/C++ identifier is not visible in the SDL translation of the identifier.

There is one important exception to this rule; static linkage of class members affects their translation as described in Static Members.

In general, the linkage of a C/C++ identifier can be specified to be internal or external using the keywords static or extern (although the former is a deprecated feature in C++ for all declarations but class members). In C++ it is also possible to use the extern keyword to specify that a set of declarations have C linkage, i.e. belong to a translation unit that is compiled with a C compiler.

Example 138 : Translation of identifiers with different linkage

C++:

extern int a; // Declaration of a
extern int a; // Legal redeclaration of a
int a; // Definition of a
extern "C" {
struct S {
int x;
};
}

SDL:

DCL a int; EXTERNAL 'C++';
NEWTYPE ptr_S Ref( S);
OPERATORS
ptr_S : -> ptr_S;
ptr_S : ptr_S -> ptr_S;
ENDNEWTYPE ptr_S;EXTERNAL 'C++';
NEWTYPE S
STRUCT
x int;
OPERATORS
S : -> S;
S : S -> S;
ENDNEWTYPE S;EXTERNAL 'C++';

Note that the extern "C" directive in this example does not affect the mapping of S at all. For example, it will be possible to instantiate S using the new operator.

Non-Language Constructs

Macros

Rule: Macros are not translated to SDL.

The reason for not translating macros is that they are not part of the C or C++ languages. Macros are expanded and removed by the preprocessor before CPP2SDL performs the translation.

Some header files (especially C headers) contain numerous macros that could be useful or even essential to access in SDL. Fortunately most macros can actually be accessed from SDL, although they are not translated by CPP2SDL. Refer to Constants for more information.

SDL Sorts in C/C++

Rule: A C/C++ type called "SDL_<sort>", where <sort> is a predefined SDL sort, is translated to that SDL sort.

Since this translation rule restricts the way ordinary C/C++ types may be named, it is only respected by CPP2SDL if the -sdlsorts option is set.

Example 139 : Translation of types referring to SDL sorts

C++:

SDL_Real func(SDL_Integer, int);

SDL:

NEWTYPE global_namespace_ImpSpec /*#NOTYPE*/
OPERATORS
func : Integer, int -> Real;
ENDNEWTYPE global_namespace_ImpSpec;EXTERNAL 'C++';

The feature of referring to SDL sorts from a C/C++ header file may be useful if the header has been designed to be used from SDL exclusively.

1

A tagged type is a class, struct, union or enum type with a tag.

2

This name indicates that the newtype represents the global scope in C/C++. In C++ terminology this scope is often called the global namespace.

3

This naming rule is generalized in Multiple Inheritance.

4

In fact the Make! operator can also be used in C++ mode. In that case it behaves exactly like the new operator.

5

In the explicit form of a function template instantiation, all actual template arguments are provided explicitly in the instantiation rather than being deduced from the types of the actual arguments in a call to the function template.


http://www.ibm.com/rational
Contents Index Previous Next