os_types_def - basic types and definitions

os_types_def - basic types and definitions




1 The necessity of a common basic header


C-Sources are used and should be used in several projects and environments in unchanged form. But often there are incompatibilities especially while using user-defined types for fixed-with integer types (for example INT32) or other language-special (not application-special) details. Another problem are some incompatibilities between C++- and C-language. Often sources should be deployed in C-environments but should be reused in C++ too.

Prevent "#ifdef MyPlatform" in applications

The conditional compilation is a often-used construct to avoid incompatibilities. For example a sequence of inline-assembling for the target-platform is fenced and replaced by a proper expression for a simulation environment. But in re-used sources such project- and platform-specific conditionals causes a distension of code for all possibilities. Such a source-code isn't able to read far. The source-code should be changed, a new revision should created, only because a next platform-project-condition is incompatible with the current conditionals.

The better way to do is: Using of a macro in the common sources, defining the macro in a substantial platform-project-specific header and include that header.

Usage of a platform- or application-specific headers in more as one appearances in several directories

An unchanged re-used header- or C-file includes a header by name. The content of the included header should depend on the target platform etc. It is possible to have more as one header with the same file-name, but located in several directories. The platform is associated in any case either with the specific compiler or with specific make files. In the make-file or as command-line-options for the compiler, the include-path is specified. The include-path should refer to that proper directories, where the correct platform-depending header is located. In this kind commonly written sources are compiled with platform-depending properties. That is the philosopher's stone.

One of this platform-depending header is the <os_types_def.h>. It contains only basic definitions to adapt the operation system basic-features, the special compiler features for the platform and the basicly hardware features for the commonly reuse-able sources.

It is necessary to define which macros are defined in this <os_types_def.h> header. The user should be sure about the use-able macros. But the responsibility to the realization is taken in the specific header.

2 Content of os_types_def.h



2.1 Enhanced common types


The C-language-standard doesn't define all necessities of types. Independing of the used compiler and options (C/C++), the following types should be present for usage:

All types should be defined using a #define-statement, not using a typedef. The reason is: Sometimes (especially for the operation-system-adaption layer) other header-files should be included, which defines the same identifier in a adequate way (compatible for usage in compiling) but incompatible while compiling the definitions itself. If the first-included <os_types_def.h> defines the types with #define, an #undef statement can be written before including the other necessary header-files. But ff a typedef is used in <os_types_def.h>, the difference can only be resolved by changing the other headerfiles (remove the unecessary definitions). But the other included headerfiles are originals, which should not be changed often. Typical it may be necessary to write:

#include <os_types_def.h>
#include "someHeadersOfUser"  //using definition of os_types_def.h
#undef int32
#undef uint32
#undef int16
#undef uint16
#include <specialPlatformHeader.h>  //defines this types in another way but compatible
...implementation using the platformheader.h
...and the someHeadersOfUser including os_types_def.h-properties

This construct is not typical for the application-part of the software. The application parts should not depend on special platform headers. But it is typical for the os-adaption layer and for drivers, which have to be use the <specialPlatformHeader.h>.

2.2 Notification of used compiler and platform


Two defined labels allows conditional compiling in user-sources. The conditional compiling is not recommended. But if it is necessary or desired, it should be done in a unified schema. The defines are platform- and maybe project-depending. They should be queried only in a positive way (#ifdef) not negative (#ifndef). For usage on Windows with Visual Studio 6, the labels are named:

#define __OS_IS_WINDOWS__
#define __COMPILER_IS_MSC6__

Using that both labels, a special user routine can query for example:

#ifdef __OS_IS_WINDOWS__
  //some statements for simulation ....

The distinction between os- and compiler label is: Usual the os-platform should be query. Only in special cases the compiler may be query, maybe for specific examination of errors etc.

That labels should not be used to force conditional compilation for commonly problems for example little/big endian, alignment requests etc.

2.3 pragmas to prevent warnings


In generally, any warning may be a hint to an error. But some warnings are ignorable. If such warnings are switched off, the critical warnings are visible better.

Warnings can be switched off individually by pragmas. The commonly valid pragmas to disable uncritical warnings should be included in the os_types_def.h. But only commonly and uncritical! The os_types_def.h can be adapted individually. In this case an individual setting of warning-pragma for a C-compiling project is possible.

The following example shows some warnings, which are switch off for the microsoft-visual-studio-compiler:

#pragma warning(disable:4100) //unused argument
#pragma warning(disable:4127) //conditional expression is constant
#pragma warning(disable:4214) //nonstandard extension used : bit field types other than int
#pragma warning(disable:4189) //local variable is initialized but not referenced
#pragma warning(disable:4201) //nonstandard extension used : nameless struct/union

2.4 Bit width and endian for the target processor


#define MemUnit char

That is usual but not valid in any case. Some processor architectures are oriented to full-integer and float numerical information and saving hardware-effort for the memory access. Therefore they address the memory in 32-bit-words for example. In that case character values are not presented effective, but this may not be a problem. But the MemUnit is a int then:

#define MemUnit int

The user can use a MemUnit* pointer for address calculations. Mostly a char* is used instead in user-sources, submitting that a memory-word is a byte. But that is wrong in some cases.

#define BYTE_IN_MemUnit 1

But in the case of an abbreviated MemUnit the number of bytes per MemUnit may be 2 or 4. A Byte is 8 bit always. This constant is necessary to calculate space while interchanging of data for example via a Dual-Port-RAM, where a processor with another memory address-mechanism is the partner.

For 32-bit-architectures it may be possible that an address consists of a 32-bit-address and an additional segment information. In that case a intPTR may need to contain the segment too, it means it needs more than 32 bit. But in most of cases the address can be stored in 32 bit. In that kind it may be possible that a address will be condensed to 32 bit by truncation of (unused) address bits. Special operation are possible to do that. Then the intPTR should present the condensed address for commonly usage.

2.5 OS_PtrValue


This structure is used to hold a pointer and an associated integer value to return it per value. It should be organized in a kind, that forces the usage of registers for the returned values. Normally, struct data, which are returned per value are copied from the stack in another stack location while execution the return machine instructions, after them they may be copied a second time into its destination struct-variable if the return-value is assigned to any one. The usage of register is much more effective. Because the usage of register may depend on some compiler specialities, the definition of this base struct is organized in this header. Frequently the definition of this struct is equal like shown in the example. But sometimes special constructs may be necessary.

The struct is defined as (pattern, frequently form)

typedef struct OS_PtrValue_t
{ char* ptr__;
  int32 value__;

The pointer may be a void* in theory, but a char* allows to visit a referenced string while debugging. It may be opportune too to write

typedef struct OS_PtrValue_t
{ union{ char* c; int32Array* a;} ptr__;
  int32 value__;

to see a int-array while debugging. - It may be able to adjust, which int-type is stored and in which form the pointer is stored (segmentation? see intPTR). Especially for simple 16-bit-Processors a proper definition should be find out.

There are defined some macros to access values and build constans:

//NOTE: use a local variable to prevent twice call if SRC is a complex expression.

3 Content of os_types_def_common.h


The header-file <os_types_def_common.h> should be included normally in <os_types_def.h> It contains definitions, which are valid and proper for all operation systems and compiler variants, but necessary respectively recommended at low level programming in C and C++. The OSAL-source package contains a version of this header-file for commonly usage. But it is possible for special requirements to adjusts nevertheless some properties, by including a changed variant of this file which is contained in the user's source-space. As a rule, the originally version should be used.

3.1 Specials for C++: extern "C" - usage


Generally, all sources should be use-able both for C and C++ compiling. It is an advantage that that programming languages are slightly compatible. The extern "C" -expression allows the usage of C-compiled library-parts in a C++-environment. But the extern "C" expression is understand only in C++-compiling. Usual headers of C-like-functions are encapsulated in

#ifdef __cplusplus__
extern "C" {
//...the definitions of this header
#ifdef __cplusplus__
}  //extern "C"

This form allows the usage of the same header for C-compiling, without activating of this definition, and for C++-compiling. It is also proper to write a extern "C" to any extern declaration. In some cases a extern "C"-declaration is helpfull in C++, but in C there shouldn't be a extern instead. For example typedef can be designated with extern "C" for the C++-compilation to designate the type as C-type. But it can't be replaced by a extern in C.

The effect of extern "C" is: Labels for linking are build in the simply C-manner: Functions are designated with its simple name, usual with a prefix-_. The label doesn't depend on the function-signature (argument types). The same effect is taken for forward-declared variables. In opposite, in C++ the labels for linking are build implicitly with some additional type information, argument types for functions respectively methods, const or volatile information for variables etc. It is a advantage in C++, that the labels for linking contains some additional information about the element, not only the simple name. Therefore incompatibilities are able to detect in link time. But this advantage prevents the compatibility to C, and it is more difficult to correct errors, which are checked more strong than necessary in C++. Therfore a extern "C"-declaration in C++ makes sense in some cases.

To support a simple usage of extern "C" in sources, which are used both for C and C++, the following macros are declared:

#include <dependHeaders.h>



4 Which content should not contain in os_types_def.h


Because the <os_types_def.h> is included in any C-file, some definitions which are used as basics for an application are inclined to find entrance in this file. But the effect or disadvantage is: The <os_types_def.h> is not more a file for the platform and compiler but for the application. It contains to much different thinks. Therefore a re-using for other applications with the adequate platform is aggravated. Therefore: