company logo

Implementation suggestions

In order to simplify conversion from OSI implementations to C++ typed access, one my follow the subsequent suggestions.

Used OSI keywords

Several OSI keywords are C++ compatible or supported by C++ defines. Thus, for elementary data types one should always use the C++ notation (i.e. int32 instead of INT(10)). The following OSI keywords are C++ compatible:

  • elementary data types: bool, int, int16, int32, int64, uint16, uint32, uint64, float, double,
  • data types supported by defines: DATE (odaba::Date), TIME (odaba::Time), DATETIME (odaba::DateTime), STRING (odaba::String), CHAR (odaba::String)
  • Collection types: SET<...> (other collection supported in OSI (LIST, BAG) are not supported in C++)
  • Process flow: if, else, while, for, switch, break, continue, return, extern, ERROR, LEAVE

Keywords, which are not known as C++ keywords are usually supported in its capitalized version.

Assignment by reference

The use of reference variables in OSI differs slightly from the way, reference variables are used in C++. In order to refer to global collections (extents), one typically declares a reference variable in the OSI functions variable section:

    SET<Person>&person = Person;

In C++, this will not work properly, since Person is not known as global variable. In order to get access to global collections, one has to open the extent explicitly. This is, however, not so easy, since for opening the global collection, a database or object space handle is required, which is, usually, not available in a C++ database instance.

In order to get access to a database handle, one may declare a global (external) object instance, which contains global information. Such a global instance may be initialized after opening the database, i.e. from within the application program. A better way, however is, to initialize the global instance in the DatabaseContext::doAfterOpen() handler for the database (see example below).

Functions called in database context handlers, have to make sure, that property handle selections in the global instance are not changed, since this might be used by the event generating function. Hence, when referring to global instance property handles while running in a database context handler, it will be more save to create a cursor copy, when the selection is going to be changed.

// C++ header file for global data

#ifndef   P_Person_HPP

#define   P_Person_HPP

#include  <SET>

class       Person;

#endif

class  GlobalData {

public     :         odaba::Database                               base;

public     :         odaba::Dictionary                             dict;

public     :         SET< Person >                                 person;

public     :

public     :                                       void Initialize (odaba::Database &db );

};

void GlobalData :: Initialisieren (odaba::Database &db ) {

  base.open(db);

  dict.open(base.dictionary());

  persons.open(base,"Person",Update);

}

float Person :: AvarageAge ( )

{

  extern  GlobalData          gd;

  int                         count = 0;

  float                       sum_age = 0;

  SET< Person >              &person = gd.persons; // shares the cursor

// create cursor copy

//SET< Person >               person;

//person.cursorCopy(gd.persons);  

  persons.top();

  while ( persons.next() )

    ++count;

    sum_age += person->age;

  }

  return(sum_age/count);

}

Notes:

Property handles are not thread save. In order to access global data in a multi-thread application, one has to create thread-global instances instead. Since each thread requires its own object space, those should be initialized in the ObjectSpaceContext::doAfterOpen() handler.

Referring to class members

There is no problem referring to class members of the class or its base types. When, however, referring to members in other classes (e.g. person.children.name), this cannot resolve in C++. However, SET provides a -> operator, which extracts the selected instance from the property handle (e.g. the person instance from SET<Person> person).

    employees->children->name

When type members have not been defined as public (default in ClassEditor is protected), member access functions have to be defined (private) or are generated when creating the header file (for protected members). In this case, get-functions may be used instead:

    person->get_children()->get_name()

Instead of -> operator the template class function iPtr() might be called as well, which also returns an instance pointer. Both, the function and the operator, will check, whether the instance is selected or try to select instances in single references (Property::provideArea()). When no instance could be selected, an exception is thrown. An exception is also thrown, when the data type of the selected instance does not correspond to the data type of the template class. In order to avoid exceptions, instances have to be selected, when the path contains collections with more than one instance (see example below)

In general, operation and property paths will not work properly in C++ and have to be rewritten. This can be avoided by calling class functions rather than using property or operation paths.

When calling functions, one may also use the -> operator in OSI. This makes a difference only, when using iteration paths (person()->TotalIncom()), in which case OSI calls the function for each instance in the collection (person s in this case). Since C++ does not support iteration operands, those operand paths have to be transformed into corresponding iterations:

    while ( person.next() )

        person->TotalIncom()

// enshure accessibility

.... Company::fragment() {

  if ( employees.selected() )

    if ( employees->children.tryGet(0) )

      Application::output(employees->children->name);

}

Operation paths

Referring to operation paths in C++ programs is not a simple as in OSI. When planning to transform OSI programs to C++, one should try to avoid operation paths with more than two elements (except passed referring to properties in complex attributes.

// osi path in class Car

  users(0).company(0).name

// C++ program

  

String data

In order to simplify C++ transformations, 8-Bit string encoding types are supported properly, only. Referring to UTF16 or UTF32 strings. String data will be defined as char data types with the size defined in the database.

String data defined in complex data types is presented as character (char[]) data type and has to be accessed by appropriate functions (memcpy, strcpy). Another way is creating a odaba::String from text fields and operate on the String variable.

When using string values, those may be enclosed in '...' or "...". In order to make transformation as simple as possible, it is suggested using "", always. When using string values for referring to enumerators, one may use ' ', instead, since this will caus compile errors when compiling the C++ program, which can be solved simply by removing the quotes.

Enumerators

Passing enumerator values differs in C++ from OSI. While OSI accepts string values or scoped enumerator names, C++ only allows simple enumerator names. Hence. the best way is using string values in OSI. After porting code to C++, the quotes have to be removed, only.

In contrast to OSI, one cannot directly access enumerator attributes. In order to obtain the defined condition for the current enumerator value set in a variable, instead of writing

    enum_var.condition.toInteger()

in OSI, one has to insert the following statements:

TypeDefinition td = dict.typeDefinition(enum_name);

EnumeratorDefinition ed = td.enumeratorDefinition(enum_var);

ed.condition().toInteger()

In a similar way one may obtain enumerator attributes for name, label, title etc.

Syntax problems

There are several syntax extension in OSI, which are not available in C++:

  • Case in switch blocks allow operands, which is not supported in C++. Hence, one should avoid switching by operand values and using constants (numbers) as case operands, only. Otherwise, switch block have to be transformed in a sequence of if ... else statements.
  • Functions without parameters: OSI allows function calls without parameter lists. Since this is not supported by C++, it is suggested to write empty parameter lists, when no parameters are required (like function()).