Os types def
From Java4c
Contents |
[edit] CRuntimeJavalike
@ident=OSAL .
[edit] os_types_def.h
@ident=os_types_def .
[edit] The necessity of a common basic header
@ident=necessity
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.
[edit] Content of os_types_def.h
@ident=content .
[edit] 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:
- bool true false: The boolean-type and its constants are defined well in C++. It is a internally representation of values for true and false. In C-applications this type and its values should be able to use in the same meaning. The ,,bool,,-type should be presented as an ,,int,, in C often. The value for false is 0 commonly. The true-value have to be the same as the presentation of a result of comparison for the current compiler. Usual a ,,!false,, presents the true-value correctly.
- int8_t int16_t int32_t int64_t uint8_t uint16_t uint64_t This are the identifiers of the fixed-width integer types which are defined in the C99-Standard. If the compiler supports C99, this definition are not necessary. In all other cases the C99-standard-types should be supported and therefore defined in the os_types_def.h header.
- ,,int8 int16 int32 int64 uint8 uint16 uint64 ,, This are the adequate identifiers of the fixed-width integer types, better able to read, usual in using, but not standardized.
- ,,UINT32,, etc.: Often adequate identifiers for the fixed.width integer are usual in use. It isn't a incompatibility to do so because the compiler understands the well-defined types as the same ones. If such user types are usual used, it should be defined in the <os_types_def.h> too. That allows to combine user-headers with all the usual fixed-width integer types without restrictions and incompatibility, if a compilation unit includes <os_types_def.h>. If the compilation unit doesn't include <os_types_def.h> but instead another header in the users space, where that types are defined, it isn't a problem. We assume, that ,,UINT32,, means a type which represents a integer value as 32 bit unsigned.
- ,,bool8 bool16,,: That both type definitions can be used in the user's application especially in struct-definition to define fixed-width boolean variables. Usual boolean values are stored as ,,int,, internally. But the ,,int,, type may have 16 or 32 bit. Therefore a ,,bool,,-Type should not be used in struct-definitions, which are used for data-interchanging. Instead, either ,,bool8,, or ,,bool16,, should be used there. Remark, a boolean value can be stored in 1 bit of a bitfield. But the bitfield should be declared as ,,int16,, ,,int32,, or adequate too.
- ,,char8 char16,, A ,,char,,-type is a byte often, but not guaranteed. A ,,char8,, is a byte guaranteed. If data for interchanging are declared in a struct, the ,,char8,,-type should be used. The ,,char16,,-type presents a character in 16 bits. Usual it is UTF16-encoding. This problem is resolved often with os-special types like ,,WCHAR,, etc. But all of this definitions are specials for some compiler platforms. Follow this commonly notification! The platform-specific ,,WCHAR,, etc. are left compatible with proper definitions. In the user sources only that platform-independent types should be used.
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>.
[edit] 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 .... #endif
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.
[edit] 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
[edit] Bit width and endian for the target processor
- ,,OSAL_BIGENDIAN,,: If this label is defined, the processor is a big-endian type. Some conditional compilation test this label to produce the correct access sequence. At user (high-) -level the big/little endian property should not be regarded. Only system routines should distinguish.
- ,,OSAL_LITTLEENDIAN,,: It is the opposite label for little endian.
- ,,OSAL_MEMWORDBOUND,,: This label should be defined, if the processor can't expand a variable over memory-word-bounds. For example if the processor is a 32-bit type, and a 32-bit-value is addressed by a odd memory address. In this case the value is located in 2 memory-words, One word contain in some bits the lo-value, the second word contains the high-bits. For a X86-architecture this isn't a problem, because that processor architecture supports the composition of the variable from any bytes in memory. But some processors have a problem with such constellations. The compiler itself prevent a splitting of variables usual by inserting fill-bytes (alignment-problem). But if a data stream comes with memory-bound-split values, an address calculation followed by a pointer casting and access (,,*(int32*)(calculatedAddress),,) causes errors. Then only more as one access to the memory and the composition of the value can help. This should be done by lo-level routines maybe in the users space too. For this routines, this label is used to control the code. The code of the routines can be written platform-independent then.
- ,,MemUnit,,: The MemUnit is a type which presents 1 word in memory. Most of the processors addresses the memory in bytes. Then this identifier should be defined as
#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.
- ,,BYTE_IN_MemUnit,,: A constant which describes how many bytes (not address-steps) are contained in a MemUnit (1 address-step). Usual it is defined as
#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.
- ,,intPTR,,: defines a integer type, that can contain an address. It allows to store a memory address (a pointer to data) and handle (transfer) it as integer. The address calculation inside the processor space is the same as calculation with a ,,MemUnit*,,, but for calculation of addresses for another processor, the usage of ,,MemUnit*,, fails. The intPtr should present the usual used addressing mode. For a 16-bit-Processor with a more-width address space (more than 64 k words) it may be a simple int, including 16 bit. That is okay, if the free memory space for data is only located in a 64-k-range, all other memory spaces are for code, file system etc. But if the user-useable space is greater than 64k, the ,,intPTR,, should be defined for example as ,,int32,,.
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.
[edit] 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__; }OS_PtrValue;
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__; }OS_PtrValue;
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:
- ,,CONST_OS_PtrValue(PTR, VAL),, This is a macro to build a constant expression to initialize, usual defined with ,,{ (char*) PTR, (int32)VAL},,.
- ,,value_OS_PtrValue(THIS),,: Gets the value, usual ,,((THIS).value__),,
- ,,PTR_OS_PtrValue(THIS, TYPE),,: Gets the pointer in form of the given type. TYPE is the base type, not the pointer (without ,,*,,), usual ,,((TYPE*)(THIS).ptr__),,
- ,,set_OS_PtrValue(THIS, PTR, INT),,: Sets all values, returns nothing (not able to use in expressions), usual ,,{ (THIS).ptr__ = (char*)(PTR); (THIS).value__ = (INT); },,
//NOTE: use a local variable to prevent twice call if SRC is a complex expression.
- ,,copy_OS_PtrValue(THIS, SRC),,: copy another OS_PtrValue into it. It is more simple as getting values from source and calling the set-macro. Hint: The SRC have to be accessed only one time, so a subroutine call can be written there. Usual: ,,{ OS_PtrValue const* src__ = &(SRC); (THIS).ptr__ = src__->ptr__; (THIS).value__ = src__->value__; },,
- ,,setValue_OS_PtrValue(THIS, INT),,: Macro to set a value, returns nothing (not able to use in expressions), usual ,,{ (THIS).value__ = (INT); },,
- ,,setPtr_OS_PtrValue(THIS, PTR),,: Macro to set a pointer, returns nothing (not able to use in expressions), usual ,, { (THIS).ptr__ = (char*)(PTR); },,.
[edit] Content of os_types_def_common.h
@ident=content
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.
[edit] 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" { #endif //...the definitions of this header #ifdef __cplusplus__ } //extern "C" #endif
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:
- ,,extern_C,,: This macro is defined for C++-compiling as ,,extern "C",, and for C-compiling as ,,extern,,. It designates ,,extern,, declarations especially for variables and static struct-incarnations.
- ,,C_TYPE,,: This macro is defined for C++-compiling as ,,extern "C",, but it is left empty for C-compilations. It should be used before a ,,typedef,, definition, especially for function-pointers, but also for the definition of struct-types.
- ,,extern_C_BLOCK_ _END_extern_C_BLOCK,,: This macros are defined as ,,extern "C" {,, and ,,},, for C++-compilation and left empty for C-compilation. It allows to write C-manner-definitions of a whole header (-block) in form
#include <dependHeaders.h> extern_C_BLOCK_ some_definitions _END_extern_C_BLOCK
[edit] Which content should not contain in os_types_def.h
@ident=noContent
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:
- Application-specific thinks shouldn't contain in this file. For example:
- Number of available ports and IP-addresses for Ehernet-Communication: This is a theme for the Ethernet-driver!
- Special hardware equipment properties: Theme for the special drivers!
- Commonly definitions which has not a reference to the platform shouldn't contain, for example:
- Definition of Pi (3.1415926...), it should contain in a <math..> header.
- The CRuntimeJavalike-platform-sources contain some definitions which are depending on the choice C/C++, the usage of 32-bit-integer for String-Length, modi of memory allocations etc. This thinks are defined in another header <fw_Platform_conventions.h> respectively <platformJc.h>. It is possible to build several appearances of CRuntimeJavalike-machinecode for the same base os/hardware-platform, therefore with the same <os_types_def.h>. On the other hand several os/hardware-platforms can use the same <platformJc.h> to defined adequate properties for Jc-appearances, but with different <os_types_def.h>-basic definitions. Therefore both headerfiles are separated together.