Use C language struct to implement C + + class

Source: Internet
Author: User
Tags define function
How do you implement C + + class in your language?
Some low-level devices such as embedded, or some of the underlying drivers, operating systems, can not use the C + + language. There are a lot of explanations on the web, unless you make some settings or modifications to the compiler, but this greatly increases the difficulty of development. and by modifying compiler parameters to compile, still cannot take advantage of C + +. For example, C + + virtual functions, garbage collection, abnormal, in the bottom of the development of the use, but will cause a lot of unnecessary trouble. For example, C + + compiler to overload a function, its compiled function names are changed to include the form of arguments, and each compiler has its own internal prefix and suffix, which can be especially problematic in operating system authoring because system calls to the operating system use assemblies, such as interrupt implementations, You need to invoke the assembly interrupt service, and then callback the C function of the operating system kernel, and if you use C + + functions, you cannot specify the callback function name correctly. So how do you make this procedural language more encapsulated without the code looking "lazy" if you can only use the C language to implement it?
C language can be the type of analogy with the class is struct, there are also union, but the Union does not have the conditions of class. You cannot define functions in struct, which can be compared under Microsoft Visual Studio and Linux gcc:
typedef struct A {
int data;
int Val () {
return data;
}
}a;
A A;
A.val ();
Under VS, this struct can be compiled, and a. Val () can fetch the value because the C + + compiler can support inline functions when compiling the struct for compatible C languages by struct the public class. However, GCC is a compiler that supports only the C language, and the error occurs at compile time. So, if you use the C language, how can you make struct comparable to class?
In fact, C-class languages support the definition of function pointers, and function pointer definitions are also supported in struct. Like what
int func (int a, int b);
The function pointer defined can make such a:
Int (*pfunc) (int, int);
When you define PFUNC = func, the following two calls are the same:
Func (10, 20); and Pfunc (10, 20);
That, as mentioned above, defines the function pointer in struct:
typedef struct A {
int data;
Int (*val) (int a);
}a;
int Val (int a) {
return A;
}
A A;
A.val = Val;
A.val (10);
This will result in 10. We know that a this pointer is implied in class, so how do we get this in the Val function? Yes, it's passed through the parameters:
typedef struct A;
struct a{
int data;
Int (*val) (A * that, int A);
};
int Val (A * that, int A) {
return That->data + A;
}
A A;
A.val = Val;
A.val (&a, 10);
The result of A.data + 10 can be obtained above. We use that to replace this, so if the code is below the C + + compiler, it will not conflict with the this implied in struct. This defines struct as a substitute for class, and the only drawback is that after it is defined, it is unavoidable to pass the object pointer in every call to the function. You can define the following macros to prevent inconsistent objects specified two times:
#define __call (o, F, ...) o.f (&o, __va_args__)
__call (A, Val, 10);
Where __va_args__ is the C language keyword, which is used to pass the macro parameter to the specified location. The macro is already expanded at compile time, so Val is already a member of a (function), so don't worry about val This parameter is not defined when __CALL this macro call.
Advanced 1: Constructors
In the previous step, A.val = Val; written out, if there are several members (functions), can cause code bloat, you need to define a constructor.
A * _A (* that, int data) {
That->data = data;
That->val = Val;
return to that;
}
A A;
_a (&a, 20);
A.val (&a, 10);
It takes two lines of code to define an object, and you can still define a macro to implement the new object, but it is completely unnecessary if it is a new object.
A * a = _a (new A, 10);
In addition, the constructor can only be a normal function, not as a member (function), because the constructor is called only once, not as a member, and if the constructor is a member, it cannot know what the constructor is before it is constructed, so it can only be specified externally. If you do not define a member (function), this becomes two lines of code:
A.init = _a;
A.init (&a, 20);
Haha, but if you want to reset the value of object A, defining the init member is a different matter, but it's best to define it in normal function _a.
Step 2: Inheritance
We already have a good "class":
typedef struct A;
struct a{
int data;
Int (*val) (A * that, int A);
};
int Val (A * that, int A) {
return That->data + A;
}
A * _a (a * that, int data) {
That->data = data;
That->val = Val;
return to that;
}
A A;
_a (&a, 20);
A.val (&a, 10);
Next, we want to implement inheritance. Because if you just need the above code, there is no need to use the class, and implementing inheritance is the ultimate goal of using the class. There is no need to consider this, for the moment, the virtual function is not implemented. Implementation inheritance requires the union mentioned above, such as inheriting a:
typedef struct b b;
struct B {
Union {
A Super;
struct {
int data;
Int (*val) (A * that, int A);
};
};
int Val;
};
b* _b (b* that, int val) {
_a (&that->super val);
That->val = val;
}
b b;
_b (&b, 30);
In Union, the base class A is defined and the members of base class A are copied to an anonymous struct. In the C specification, a variable name is used after the anonymous struct of the Union. A member can get a variable, and if there is no variable name, the variable can be obtained directly using the member name, as follows:
Union {
float f[2];
struct {
float F1;
float F2;
}uf;
}um;
To get f[1] use UM.FU.F2 can be obtained, while
Union {
float f[2];
struct {
float F1;
float F2;
};
}um;
Only use UM.F2 can get f[1]. Our class uses this to make the members of the base class become members of the inheriting class. The super part of the inheritance class is the base class, and it defines Val as a member variable, it belongs to the base class, and more interestingly, in B's constructor, you can construct a by that->super directly, and after the constructor is finished, B.data and B.val are the members after the construction of a, which equals B.super.data and B.super.val respectively.
Advanced 3: Partial inheritance and overrides (overload)
In addition, we can use this structure to implement partial inheritance. Look at the definitions of A and B below (only definitions, forward declarations, and calls are omitted):
struct a{
int data;
Int (*val) (A * that, int A);
int Val;
};
struct B {
Union {
A Super;
struct {
int data;
Int (*val) (A * that, int A);
};
};
int Val;
};
Among them, B.val and A.val are different members. If you want to get a.val use B.super.val, this is the union of the characteristics to determine. This does not inherit from the base class, which is called partial inheritance.
So how to implement rewriting it. See the constructor for B:
b* _b (b* that, int val) {
_a (&that->super, Val);
Override
That->val = Myval;
That->val = val;
}
Just point the Val pointer of the inheriting class to a custom function. Note, however, that it must be completed after a construct, otherwise it will be overwritten.
So, to sum up, the order of the constructors is:
Base class Construction
overriding functions
Subclass constructs (function pointer initialization)
Child class data initialization
Other initialization
where everything before "other initialization" can be likened to the constructor of a C + + class:
B:a (Val), Val (val) {}
Overloaded functions are directly overloaded when they are defined inline functions in a C + + class.
Step 4: Define macros to make the structure simpler
C language Analog C + + class as a general example. However, if you want to create a class that's easier, you'll need some help with macro definitions.
In any case, we should know whether we define this class as a base class in the future. So, for a, we know it's a base class, and you can rewrite it as a "template," but this template is not C + + template, but a macro definition that simplifies the writing of the base class in the inheriting class:
typedef struct A {
#define A_TEMPLATE \
int data;\
Int (*val) (A * that, int A);
#define TEMPLATE_A A_template
Template_a
int Val;
}a;
Although there are two more #define, the definition of a has not changed. int Val; it's not inherited by subclasses so it's not written in #define. #define包括在花括号内是为了让代码更加美观. #define将会在下面宏中使用:
#define __super (Base) \
Union {\
Base Super; \
struct {\
template_# #Base \
}; \
}
This is the union part of B, and we refine it so that all future classes can be inherited without losing the general call to macros.
typedef struct B {
__super (A);
int Val;
}b;
Look, so you don't have to rewrite the members of the base class, all the base class members are defined once in the base class, and are expanded through the macros in subclasses.
Advanced 5: Template.
With the instructions above, we can quickly write an example of a class, a C code (Linux GCC) that compiles and runs:
Class.c
//
#include <stdio.h>
#include <stdlib.h>

//////////////////////////////////////////////////////
#define __super (Base) \
Union {\
Base Super; \
struct {\
template_# #Base \
}; \
}
//////////////////////////////////////////////////////
typedef struct A;
struct A {
#define A_TEMPLATE \
int data;\
Int (*val) (A * that, int A);
#define TEMPLATE_A A_template
Template_a
int Val;
};
int Val (A * that, int A) {
return That->data + A;
}
A * _a (a * that, int data) {
That->data = data;
That->val = Val;
return to that;
}
//////////////////////////////////////////////////////
typedef struct B {
__super (A);
int Val;
}b;
int Myval (b* that, int a) {
return That->data * A;
}
b* _b (b* that, int val) {
_a (&that->super, Val);
That->val = Myval;
That->val = val;
}
//////////////////////////////////////////////////////
int main () {
A A;
_a (&a, 10);
A.val = 1;
printf ("%d%d\n", A.val, A.val (&a, 20));

b b;
_b (&b, 20);
B.val = 2;
printf ("%d%d\n", B.val, B.val (&b, 30));

return 0;
}
You can see that the results are 1 20 and 2 600, which indicates success. However, the compiler reported a warning: assignment from incompatible pointer type, which is because the base class Val is a type a parameter, whereas the overload in B is given a type B parameter, because it is an inheritance relationship and is a pointer, so there is no problem. But if you really want to be serious, you need to change the #define:
struct A {
#define A_TEMPLATE (T) \
int data;\
Int (*val) (t* that, int a);
#define TEMPLATE_A (t) a_template (struct T)
Template_a (A)
int Val;
};
Above _super and below B call the place that __super also get rid of, do not repeat. As you can see, when in a, Val uses the type a parameter, and the B type parameter is used in B, there should be no problem. --------But this is not a template. Because of the limitations of struct, we passed the "pointer" instead of the this pointer by adding parameters to the function, we defined the type T to eliminate the problem of mismatched pointer types when inheriting, but did not introduce a template.
We use macros in the Inheritance class to expand the base class, which is similar to the template, why not make a template. Haha, we can imitate the template, but the real template is not like this:
Class.c
//
#include <stdio.h>
#include <stdlib.h>

//////////////////////////////////////////////////////
#define __super (Base, Type, ...) \
Union {\
Base Super; \
struct {\
template_# #Base (Type, __va_args__) \
}; \
}
//////////////////////////////////////////////////////
typedef struct A;
struct A {
#define A_template (T, Type) \
Type data;\
Int (*val) (t* that, int a);
#define TEMPLATE_A (t, type) a_template (struct T, type)
Template_a (A, int)
int Val;
};
int Val (A * that, int A) {
return That->data + A;
}
A * _a (a * that, int data) {
That->data = data;
That->val = Val;
return to that;
}
//////////////////////////////////////////////////////
typedef struct B {
__super (A, B, int);
int Val;
}b;
int Myval (b* that, int a) {
return That->data * A;
}
b* _b (b* that, int val) {
_a (&that->super, Val);
That->val = Myval;
That->val = val;
}
//////////////////////////////////////////////////////
int main () {
A A;
_a (&a, 10);
A.val = 1;
printf ("%d%d\n", A.val, A.val (&a, 20));

b b;
_b (&b, 20);
B.val = 2;
printf ("%d%d\n", B.val, B.val (&b, 30));

return 0;
}
Look, the template is #define template, and a defines the template as an int type, and B defines the template as an int. The result remains the same. But this is still not a template, and the template is specific when defining the object, which is already special when defining the type.
Since member functions are not inline (so called inline, that is, each object contains functions that are extended within the object), the functions must be written externally, and the external must ensure that the struct is fully defined, so C is not able to do the actual template.
6. Inheritance Chain
Macros are not nested according to the macro definition of C. So if you want to implement the inheritance chain, __super can cause nesting. So, add a ___super to the level two inheritance chain, and the definition is exactly the same as __super, so that ___super calls template and then invokes __super, which forms the inheritance chain. If more inheritance is available, continue to define ____super. In addition, because the anonymous union is used, the super of the base class is exposed, and a duplicate super definition appears in the inheritance chain, which can be defined as the Super+ base class name. The second-level inheritance chain is defined as follows, and the first-level inheritance chain is also modified to the following definition:
#define ___super (Base, ...) \
Union {\
Base super# #Base; \
struct {\
template_# #Base (__va_args__) \
}; \
}
OK, so you can implement the multi-level inheritance chain. In the _b constructor, change the super to SuperA, and then add Class C to inherit Class B (Level two inheritance):
struct C {
___super (B, C);
};
C * _c (c* that) {
_b (&that->superb, 77);
}
This constitutes a complete chain of inheritance. The complete and operational code is as follows:
Class.c
//
#include <stdio.h>
#include <stdlib.h>

typedef struct A;
typedef struct b b;
typedef struct C C;
//////////////////////////////////////////////////////
#define __super (Base, ...) \
Union {\
Base super# #Base; \
struct {\
template_# #Base (__va_args__) \
}; \
}
#define ___super (Base, ...) \
Union {\
Base super# #Base; \
struct {\
template_# #Base (__va_args__) \
}; \
}
//////////////////////////////////////////////////////
struct A {
#define A_TEMPLATE (T) \
int data;\
Int (*val) (t* that, int a);
#define TEMPLATE_A (t) a_template (t)
Template_a (A)
int Val;
};
int Val (A * that, int A) {
return That->data + A;
}
A * _a (a * that, int data) {
That->data = data;
That->val = Val;
return to that;
}
//////////////////////////////////////////////////////
struct B {
#define B_TEMPLATE (T) \
__super (A, T);
int Val;
#define TEMPLATE_B (t) b_template (t)
Template_b (B)
};
int Myval (b* that, int a) {
return That->data * A;
}
b* _b (b* that, int val) {
_a (&that->supera, Val);
That->val = Myval;
That->val = val;
}
//////////////////////////////////////////////////////
struct C {
___super (B, C);
};
C * _c (c* that) {
_b (&that->superb, 77);
}
//////////////////////////////////////////////////////
int main () {
A A;
_a (&a, 10);
A.val = 1;
printf ("%d%d\n", A.val, A.val (&a, 20));

b b;
_b (&b, 20);
B.val = 2;
printf ("%d%d\n", B.val, B.val (&b, 30));

c C;
_c (&AMP;C);
C.val = 3;
printf ("%d%d\n", C.val, C.val (&c, 30));

return 0;
}
Output Result: 1 30 2 600 3 2310, the result is correct.

7. Concluding remarks
Although not able to do the template is regrettable, but can ensure that some class elements can be used in the C language, already very good. The application of this class is to look at the operating system kernel, looking for the C language to solve class problems, some of these research results, and in practice has been proved to be able to use----although some obscure and not a good programming experience (such as the following recorded, It's really hard to change for someone who's used to class.
But need to emphasize a few points, this is also a legacy of the problem, with the current ability or no way to solve, look forward to the expert to solve it:
1. When using and defining class members (functions), be sure to take the class object pointer with that to replace this
2.struct inheritance is very different from C + + inheritance, because the struct is done by redefining the base class in the subclass, so the base class design must pay attention to the member order, for example, the Val of a in the example above is put at the end rather than the top, Subclasses do not inherit Val from the past when inheriting from subclasses.
3. Because of some of the characteristics of the macro definition, which causes the inheritance chain to define multiple levels of __super, you need to be clear about what level the current inheritance is, and then determine which __super to use
4. The 2nd brings trouble more than that, if it is continuous inheritance, all subclasses must have all the members of the base class, the more the inheritance chain, the more the class will become bloated, this is only refers to the space occupied by the class, but the use of __super in code does not see this amplification effect
5. As seen above, all member functions are not inline functions, but merely pointers to functions, pointing to a function outside the struct (in gcc it is not allowed to write functions directly in struct, so you can only define function pointers, which are actually equivalent to a member variable), So member functions do not have the means to do inline optimization.
End (* ^_^ *)

Stophin 2016/11/26
Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.