ObjO_emC

ObjO_emC

Inhalt


Topic:.ObjO_emC.

back ==>../../emc/index.html

Legacy documentation in german ==>../../Jc/index.html

The style of Object Oriented Programming (OOP) is known since the 1980-years and usual used with C++, Java, C# etc. since the 1990-years. C was created in the years before, with structured programming (if, while, for) but with the possibility of data structures with typedef struct. C is not considered to be Object Orientierted? The OOP is not a property of a language but a style of programming. The language should deliver enough syntactical possibilities to use ObjectOrientation. For this cases C is proper. It delivers:

The goal is: Using C (for several reason) but using C in an ObjectOriented way. The goal is not: Replace C++ by flawy C constructs.

What are the reasons to use C (instead C++):


1 Plan to work ObjectOriented in C

Topic:.ObjO_emC..


2 Using a mix of C and C++

Topic:.ObjO_emC.cpp.

If you want to use C++ for your projects, then it is a proper decision. If you have some sources, proper to use, which are written in C, it can be integrated. No problem. If you have some sources, which should be used in a C environment, you can integrate it.

A bad decision would be: build a world in C++, and another world in C. Write sources necessarily in C++ though they are existing in C.

C is a subset of C++, they are less exemptions. Prevent using the exemptions. It means, you can compile any C-source with a C++ compiler as C++-source.

On Visual studio you can use the /TP option to force C++ compilation for C sources. If you use a C++ compiler on PC platform, you get more error informations, the code will be checked more on compilation time. You can use a C-compiler on the target platform nevertheless.

It may be an idea to use

extern "C"

to define operations a C-linklabel. The difference is: The C-linklabel is only the _name of the operation with an underscore before. C cannot differ operations with the same name and different arguments. The C++ label of an operation is longer, complex, contains the types of the arguments. If you have differences in arguments (for example const* and non const pointer) between the prototype in the header and the implementation, C++ builds different operation labels. Then a linker error occure ('cound not find ...'). The C++ compiler detects this drawback. You should be happy on any compiler or linker error, because it is a early error.

It means, you should not use the extern "C" possibility if it is not necessary. If you have pre-compiled sources with C (in an library), the extern "C" is necessary. But you may compile the libraries as C++ newly.


2.1 class and struct definition for the same CLASS_C

Topic:.ObjO_emC.cpp..

The topic CLASS_C is used in documentation in headerfiles to express the relationship between struct definition and operations.

You can define

typedef struct MyClass_t {
  int element;
} MyClass_s;
int myOperation_MyClass(MyClass_s* thiz, int args);
#ifdef __cplusplus
class MyClass : private MyClass_s {
  public: myOperation(int args) { myOperation_MyClass(this); }
};
#endif //__cplusplus

With this you have defined in the headerfile both, the C interface to MyClass and the C++ approach. A using application can access only the C++ form. Advantage: private encapsulation of all data. Advantage but disadvantage too: virtual operations (simple but unsafe in C++). Another application can use the C form. The implementation is in C. Note: the struct definition identifier ends with _s, but all suffixes has not this _s.


2.2 ObjectJcpp

Topic:.ObjO_emC.cpp..

this was used in the 2006 till 10, after them not maintained, but should run furthermore. TODO test and describe.


3 Abstraction and Derivation, super classes in C

Topic:.ObjO_emC.super.

The ObjectOriented approach in C should support abstraction and inheritance. Java supports only single inheritance. That is a proven concept. C++ supports multiple inheritance as a super feature from beginning, but that is partial controversial. The C ObjectOrientation is restricted to single inheritance.

The data are arranged as first struct of super data in a struct. A union helps to access ObjectJc immediately (if used, not absolutely necessary, but recommended) and helps to define interfaces:

typedef MyDerivedClass_t {
  union{ MySuperClass super; ObjectJc object; ObjectJc MyInterface1; ObjectJc MyInterface2; }; //unnamed
  int elements;
} MyDerivedClass_s;

Now you can access the super data from a derived reference using

MyDerivedClass_s* ptr;
operation_MySuperClass(&ptr->super);

without casting with type check in compile time. In difference to C++ you should write more source code, the principle is the same. Advantage: You see what you have. C++ does the casting automatically:

operation(ptr);  //C++: operation from super class is invoked, you do not see it.

If you refer the super type, you can initalize it with:

MyDerivedClass instance = {0}; //may be static instantiated.
.....
MySuperClass* ref = &instance.super;

If an algorithm knows that the instance is type of MyDerivedClass_s, you can write a simple unchecked cast. Either the algorithm knows it from other data, or it should check it, see next chapter.

struct MyDerivedClass_t* refdata = (struct MyDerivedClass_t*) ref;

Because the super struct start on beginning of the derived struct, it have the same address. In this example the forward declaration is used, which does not need to know the definition of struct {...} MyDerivedClas_s. It reduces dependencies in headers. For C++ this approach is not possible. The C++ cast should know both instances. It adjusts the value of the address, refdata and ref have different addresses if the classes have virtual methods or it is mulitple inheritance. C++ adjust it correctly, but not clearly visible. That is one of the differences between near machine code programming in C (you see what the machine code will do) and the more high level slightly obscure C++. In C++ a cast

class MyDerivedData* refdata = (class MyDerivedData*) ref;

may adjust the address value depending on the situation of virtual operations. The vtbl pointer is part of the classes. This is visible in debug, but not in view of the source code. There are some tripping points in C++ for errors (static_cast with void*, wrongly used reinterpret_cast etc.


4 The super struct ObjectJc

Topic:.ObjO_emC.ObjectJc.

All more complex data types have common properties for usage:

Therefore Java has defined java.lang.Object as the super class of all. Any instance is an Object. Any instance have reflection information, a type check can be done etc. That is a important advantage for safety code.

The disadvantage of a out of the question super class is: It needs memory. In Java primitive types are not classes. An array of 1000 integer values need 4000 Byte (int32_t) as in C. But a class with 2 elements, for example real and imagine of a complex number is based on Object already. To safe storage it is possible to use simple float[] or double[] arrays, the even index for real, the odd one for complex, as workarround.

Therefore, in C ObjectOriented with emC style, the super class ObjectJc is not necessary at all. It is only necessary for the following cases:

It means: Considerable types should based on ObjectJc. Simple struct types should not based on it.


4.1 Notation of super class ObjectJc

Topic:.ObjO_emC.ObjectJc.src.

If a struct has no other super struct than ObjectJc, you should define it as first element. Use a union though it seems as unnecessary, but for unique style and access:

typedef MyBaseStruct_t {
  union { ObjectJc object; } base;
  int someMoreElements;
} MyBaseStruct_s;

If a struct is based on another one which is guaranteed based on ObjectJc maybe in a further super struct, you can write it as union:

typedef MyDerivedStruct_t {
  union { MyBaseStruct super; ObjectJc object;} base;
  int someMoreElements;
} MyDerivedStruct_s;

You should use that identifier. It is unique and it is used for reflection generation. The super class should be the first element. It is necessary for initializer with {...} (INIZ_... macro). The super class should be named super. The union itself is named base. The union could have beeen unnamed, it would save writing time. Unnamed unions are present by K&R since 1974 already and it is standard for GNU. But the C89 standard committee has overslept this feature, C99 too. Only C11 has standardized the unnamed union. But C11 is too young (only 7 years from 2018). Unfortunately a common style cannot use non-standard extensions. Therefore you should write base:

ObjectJc* objAccess = &myRef->base.object;
MyBaseStruct_s* superAccess = &myRef->base.super;

If you have interfaces in C, see TODO. The interfaces routines have its thiz data pointer in form of ObjectJc* because the implementing class should based on ObjectJc in any case. It is the 'common denominator' (C uses often void* for adquate things).

Therefore interfaces should be defined in the union too as further elements:

typedef MyDerivedStruct_t {
  union { MyBaseStruct super; ObjectJc object;
          ObjectJc Interface_A; ObjectJc Ifc_XYZ;
        } base;
  int someMoreElements;
} MyDerivedStruct_s;

The reflection generator can detect this writing style for interfaces and can build the virtual table inside the reflection with this information.