Software for embedded systems should be run under different conditions:
very fast realtime
small memory available
C or C++ usage
elaborately error checks
safety or fast
The user software itself should not be changed for this challenges, that is the concept of emC: embedded multiplatform C/C++
The variants can be controlled by:
a) choice of a proper include path for a given
b) apply a specific
<applstdef_emC.h>(which can contain some defines and then include one of the standard ones)
c) Set defines as compiler call arguments combined with a) and b)
All three possibilities of control are adequate in effect.
The b) documents the selected variant in the application specific file
It is the best case because the file is version controlled. It should be favored.
For variant c) the make script may be version controlled, but if more as one IDE is used
for (more) target systems and PC-simulation, the variant selection may be more obscure.
The emC basic headers regard the variants of compiling conditions. The user software can call operations, which may be macros specified to the variants. The user software should not regard the variants.
One of the basic idea of emC is: Test in a PC-based high level C++ compilation environment - and deploy the well tested software on target with the target conditions. All sophisticated software checks are done on PC testing, the software should be widely error-free after them and can deploy without these checks on target to run fastly - without adapting the sources. It means the software can be run and checked again on PC for enhancements, without change the sources, and without sophisticated #ifdef constructs.
In conclusion the target compilation and the PC test compilation can or should use different variants, for example on PC with exception handling in C++ which may not available on target.
__cplusplus: Selecting *.c-sources for C++ compilation. This can be done by compiler switch only. If C++-Projects are mixed with c-compiled sources, a correct handling of
extern "C"is necessary.
DEF_CPP_COMPILE: This compiler switch have to be set if C-Sources should be compiled with C++. That should be matching with compiler options to force CPP-compilation. It is
/TPfor Visual Studio and
-x c++for GNU compiler.
All Functions which are implemented in C-Sources should be declared in the header with
extern_C Type myOperation_Type(...); //always as C-functions .... extern_CCpp Type myOperation_Type(...); //defined depending DEF_CPP_COMPILE
extern_C is replaced by
extern "C" for C++-compilation.
That prefix forces a C-like Linker label with only the name of the operation,
without argument-sensitivity (signature of the operation). A C-compiled object file
contains this simple label only. The C++ linker regards it.
The second form regards the compiler switch
DEF_CPP_COMPILE. Only if it is not set
then the macro
extern_CCpp is defined as
Elsewhere it assumes that the associated c-source is compiled with C++.
The call of the operation in a C++ environment expects a linker label with argument
sensitivity (signature of the operation). It should be offered by the C-routine
and requests the C++ compilation for C-sources.
There may be some source which are never compiled with C++ because there are very simple
(then C++ is never necessary) or they define
const-data for a const memory segment
(for example immediately contained and read from a Flash Memory in an embedded device).
For that sources
extern_C should be used anyway.
ObjectJc is possible as base for some data.
With this unique base the type and size of data can be evaluate in debugging situations
outside an IDE (data view on target), for evaluating extracted data, and for safety
in an application using type and size checks.
In cohesion with Reflection.en.html the data can be evaluated
without knowledge of the data structure especially for unknown software versions,
for example in data evaluating situations on legacy software.
ClassJc const* reflection.
ObjectJc as base
struct also for classes can be used, but does not need to be used
for all data. For the reflection concept only the root class needs the root reference
to reflection, and especially via interface references the implementation type
can be gotten.
DEF_ObjectJc_SIMPLE: If that compiler switch is set firstly before other files are included (in the user specific
<applstdef_emC.h>or as compiler switch), Then the ObjectJc contains only 4 Bytes for a type and size identification. The reflection concept is not supported. This variant should be used if very less RAM is available on the target, but the full ObjectJc capability should be used on another target or for PC test. Then
ObjectJcis used as base class, for some data, but it does not need significant RAM space. The application can check the type and size of the data object, but only the implementation type, not a supported base type on runtime. But note that a proper base type check is supported on compile time in C++ via
DEF_ObjectJc_REFLREF: If this compiler switch is set than the
structcontains the reference to its
ClassJcreflection data. See chapter Reflection handling. Only then a base type can be detected for pointer type casting from a more basically type, especially
ObjectJc*, to a base type of an instance for a C-like or
reinterpret_cast<..>(..)It is a similar capability as
dynamic_cast<…>but without the necessity of RunTimeTypeInformation on compiling level. It works for
structtoo and it works in C. For symbolic access (reflection handling) all information are accessible. Activating this switch needs additionally 4 bytes for
DEF_ObjectJcpp_REFLECTION: Using reflection in
classan additional offset between the position of the partial
ObjectJcdata and the start address of an instance is necessary. If C++ is used with interfaces or multiple inheritance and with reflection this switch have to be set. It needs additional 4 bytes. But in complex C++ systems this amount of memory should be available.
DEF_ObjectJc_SYNCHANDLE: If this switch is set then the operations
notify_ObjectJc(..)and its C++ variants defined in
ObjectJccontains an index for mutex and notify objects. It is a Java-adequate concept which associates the handle of operation system resources for this operations to the data instance. It needs the same additional 4 byte as
DEF_ObjectJcpp_REFLECTIONbecause both information are stored as two 16 bit words in a 4 byte location, for assurance of 4-byte-boundary.
DEF_ObjectJc_OWNADDRESS: If this switch is set, any ObjectJc partial data contains its own address. This is helpfully if data are stored as memory map and evaluated. references between data contains the real hardware address references. Using the information about the hardware address of own instance the references can be associated though the data are contained as memory map on other addresses.
DEF_ObjectJc_FULL: This is a simple switch which forces all capability of
ObjectJc. The other switches can be additionally set, but do not need. If
DEF_ObjectJc_SIMPLEis set at once, an
#error messageis forced.
DEF_NO_StringJcCapabilities: Some DSP processors (Digital Signal Processor) do not handle String operations in a proper way. Only simple `char const*`can be stored. A String processing is not desired. For that variant this define is set. It reduces String handling in the basic emC functionality.
For exception handling see ThCxtExc_emC.html: Stacktrace, ThreadContext and Exception handling.
The Strategy is: Test on PC with full exception handling, deploy maybe in a poor target
well tested without exception, but with unchanged sources.
To control the exception handling and meaning of
THROW, macros are used.
The following compiler switches in
<applstdef_emC.h> or as compiler definition settings
DEF_ThreadContext_SIMPLE: If this is set,
DEF_Exception_TRYCpp: The C++ Compilation should be used, especially on PC-Test. The emC-Exception handling with
TRY CATCH THROWuses the C++
try catch throwKeywords. A catch is always implemented as the 'native C++
catch(…)’ which is necessary for the Visual Studio 'asynchronous exceptions'. They are handled if the compiler switch `/EHais set. See https://docs.microsoft.com/de-de/cpp/build/reference/eh-exception-handling-model It means, memory errors because faulty pointers, division by zero etc. forces catching this exception too. It may be substantial for programs in test. (TODO clarify this topic for gcc compilation.)
longjmpmechanism is used for the emC-Exception handling with
TRY CATCH THROW. C or C++ compilation is possible, whereby for C++ sources destructors are not invoked on
THROW. Hence this mode should only be used if temporary class instances are not used or all of their destructors are empty. On a target system this condition may be true, the
longjmpexception handling may be some time faster.
DEF_Exception_NO: In this case a
THROWmacro does only invoke
logSimple_ExceptionJc(…)which is implemented either in …TODO
DEF_ThreadContext_SIMPLE: The thread context is a memory area which is thread specific, or interrupt execution specific too (this is also a thread). The `
DEF_HandlePtr64: In some 64-bit-Applications, for example S-Functions in Simulink, all memory addresses of instances (…of Function Blocks) are held in a global accessable address table. The references are handles - index to the table, as
uint32word. For deployment the code to a 32-bit-System the same
uint32words as connection data between function blocks (aggregations in UML-slang) contains the really memory addresses, for fast access. This is regarded by code generation in Simulink (® Mathworks) - via specific tlc files (tlc = target language control).
struct ClassJc contains the type information for any data, it is referenced in
ClassJc can have a full capability to present full symbolic access
to all data (then
DEF_REFLECTION_FULL is present).
Or it is a small
struct only to support type check capability.
See ObjectJc.en.html, chapter Reflection and Types.
DEF_REFLECTION_FULL: The reflection (see Reflection.en.html) contains the possibility of symbolic access to all data. This feature allows dynamic programming in C and C++, for example find out a data element because of its textual identifier name gotten via a communication telegram, or executed an operation by symbolic specification. For example symbolic data access via Inspector.en.html can be used. That features are proper not only for PC programming but for rich powerful embedded applications. For that the symbolic information (reflection) should be generated from the header file information with the tool CHeader2Reflection.en.html.
DEF_REFLECTION_OFFS: To prevent effort on target but allow symbolic data access via the Inspector.en.html tool a inspector target proxy can be used. That proxy contains the textual information and communicates with the target via simple memory accesses. The target should contain generated Reflection information which contains only the offsets# to all data in a struct, because the offsets may be specific on target compilation (cannot be presumed by a compiler- and situation-independent tool). For that this compiler switch can be set.
DEF_REFLECTION_SIMPLE: If this macro is set, only type information is contained in reflection data (Type
ClassJc) to support safety type checks.
DEF_REFLECTION_NO: Whether type nor symbolic access can be done. This is the simplest form - no reflection usage. Instances of
ClassJccannot be defined.
ClassJc definition does not depend on the Reflection definition but depends
on the capability of ObjectJc. But it should be match to the reflection usage.
DEF_ObjectJc_FULLas opposite to
DEF_ObjectJc_Simple: In this case the
ClassJcis referred from
ObjectJcand it is able to present the
DEF_REFLECTION_FULL- full reflection information. But it the other compiler switches are set for reflection generation, the
ClassJcmay contains only type information, type information about base classes too, or no information.
DEF_ObjectJc_SIMPLE: Then the
ClassJcdefinition is only simple too. It cannot contain full qualified Reflection (
DEF_REFLECTION_FULL), because this definition is proper for a poor target system with less resources. But any instance of data based on
ObjectJccontains a type information if initialized. In the poorest form this is a 16 bit identification number in the only one 32-bit data member
ObjectJc. It is the low part of the address of a given
reflection_MyTypeinstance. If all reflection-Type-instances are localized in one memory section of maximal 64 k memory address range, it is unique.
DEF_ObjectJc_SIMPLE: The ClassJc contains the index number of the generated Reflection offset data. In this case it is possible to check whether a data object refers the correct reflection offset information, respectively the reflection information gotten from information of the Inspector target proxy matches to the data object. If
DEF_ObjectJc_REFLREFis not defined, then the 16-bit-part of the only one information
ObjectJccontains this index of the reflection offset table.
DEF_ObjectJc_SIMPLE: Then the simple definition of
ObjectJccontains a dedicated reference to its
ClassJctype information. The
ClassJccontains a reference to a possible super class (only for simple inheritance). Hence it can be tested whether a given data instance based on
ObjectJcis type of or has base data which are type of a given reflection (instance of
ClassJc). This enables type checks for derived data structures or classes in poor embedded applications. This is independently of given generated reflection-offset data (
DEF_REFLECION_OFFSis set or not).
ClassJccontains a String given type name if
DEF_NO_StringJcCapabilitiesis not set. For simple numeric applications without any string processing it is not necessary to spend memory space for identification strings because there are never compared or processed otherwise. For such applications the
DEF_NO_StringJcCapabilitiescan be set in the <applstdef_emC.h> for generally. If
DEF_NO_StringJcCapabilitiesis not set, the string literal in
ClassJccontains the plain text name of the type. Hence more as one instances of
ClassJcwhich presents the same type, especially in different independent compiling units (with
static-keyword definition) or in different dynamic linked parts of the application can be existing, and the unique recognition of the type is assured. The plain text type name may be helpful in debugging situations too.
If the reflection are generated via the CHeader2Reflection.en.html tool, there are generated files. The following form are preferred to include it:
With them the type of an instance can be checked whether it is from a given type:
bool ok = instanceof_ObjectJc(myData, &reflection_MyDataType);
#ifdef DEF_REFLECTION_FULL #include "genRefl/MyType.crefl" #elif defined(DEF_REFLECTION_OFFS) #include <emC/Base/genRefl/Time_emC.crefloffs> #else //DEF_REFLECTION_SIMPLE, or DEF_REFLECTION_NO ClassJc const reflection_Clock_MinMaxTime_emC = INIZ_ClassJc(reflection_Clock_MinMaxTime_emC, "Clock_MinMaxTime_emC"); #endif