Object-Oriented C Language Programming-DynamicLinkageGenericFunctions--Stealing Beams and Changing Columns in C Language

Article Directory

DynamicLinkageGenericFunctions

Code repository
[Code repository]:Https://github.com/zzu-andrew/linux-sys/blob/6c19498a436902120eec7e08c18e4a74d04dafa2B+j6/ooc/test/c.02/

Introduction: If you have many small modules at the bottom of the modules maintained by your company and the other modules do not need to run at all when the user chooses different modules, this method works well for you. By making all methods static, you can have different objects, but these objects call functions that process their own data through the function pointer.

Uses:
1. Object-oriented, concise programming style
2. if-lse is everywhere in the perfect solution

Constructors and Destructors

Construction and destructor

Join us in using functions like the following to define our own destructors for each type, and there is a problem with installing destructors and writing new functions for all data, as follows:

struct type {
    size_t size; /* size of an object */
    void (* dtor) (void *); /* destructor */
};
struct String {
    char * text; /* dynamic string */
    const void * destroy; /* locate destructor */
};
struct Set {
    ... information ...
    const void * destroy; /* locate destructor */
};

Initialization of new data is only part of the work of the new function, which requires pointing the dtor function to the destructor in the newly created object and implementing different functions when different parameters are passed into the new.

new(Set); /* make a set */
new(String, "text"); /* make a string */

When initialization is required, new will call different constructors to initialize the object based on different objects, where the function pointer ctor in the object is executed with the corresponding constructor pointing to the following to initialize object members other than the object.

/* Constructor */
void * new (const void * _class, ...)
{	const struct Class * class = _class;
	void * p = calloc(1, class -> size);

	assert(p);
	/* Cast to Class so that the same function is initialized only once, but all defined
		Objects can be called */
	* (const struct Class **) p = class;

	if (class -> ctor)
	{	va_list ap;

		va_start(ap, _class);
		p = class -> ctor(p, & ap);
		va_end(ap);
	}
	return p;
}
/* Destructor */
static void * String_ctor (void * _self, va_list * app)
{	struct String * self = _self;
	const char * text = va_arg(* app, const char *);

	self -> text = malloc(strlen(text) + 1);
	assert(self -> text);
	strcpy(self -> text, text);
	return self;
}

From the implementation of the above constructor, it can be seen that this constructor is not responsible for the construction of the object, but only for the construction (initialization) of the members of the object, since the construction and destruction of the object are the responsibility of the new and delete functions.

The delete function is also responsible for releasing only the memory requested by the new function, and the destructor is responsible for releasing all the memory requested by the constructor.

/* delete Function is only responsible for releasing memory requested by new function */
void delete (void * self)
{	const struct Class ** cp = self;

	if (self && * cp && (* cp) -> dtor)
		self = (* cp) -> dtor(self);
	free(self);
}
/* The memory requested in all constructors is freed by the destructor here */
static void * String_dtor (void * _self)
{	struct String * self = _self;

	free(self -> text), self -> text = 0;
	return self;
}

For a String, some memory is requested by a constructor, but a String is memory requested by a new function, and the String itself needs to be freed by a delete function.

Methods, Information, Classes, and Objects

The object pointer passed in from the delete function must be initialized before passing in, pointing the destructor pointer to the destructor of the corresponding object

struct Class {
	size_t size;
	void * (* ctor) (void * self, va_list * app);
	void * (* dtor) (void * self);
	void * (* clone) (const void * self);
	int (* differ) (const void * self, const void * b);
};

struct String {
	const void * class;	/* must be first */
	char * text;
};

Each object starts with a pointer to its own type, const void * class, through which we can get the memory size, constructor.ctor, destructor.dtor,.clone function object replicator,.differ function object comparison function that we need to request when we get a new object.

Carefully, each of our objects starts with a pointer to its own type, const void * class; the information provided in the type of calss tells us the following:

size_t size;  // Provide the size of the object, in this case the size of the String
void * (* ctor) (void * self, va_list * app); // Constructor
void * (* dtor) (void * self); // Destructor
void * (* clone) (const void * self); // Object Clone Function
int (* differ) (const void * self, const void * b); //Object comparison function

new

The new function requests the memory of the object and constructs the contents of the object through the constructor. In fact, here is a text string pointer, requests a piece of memory, and copies the string passed in from the new into the requested memory.

Note how to use the secondary pointer. A struct String is requested through new, so p is an object of type String, but class in Stirng is the pointer to the structure of a static method. To invoke a method, you need to take out the class pointer in the P pointer.The implementation is * (const struct Class **) p = class; the requirement is that the class must be the first pointer in the object.

Split:

*
(const struct Class **) 
p

From left to right * in combination with p first, then (const struct Class **)
The above is equivalent to:
   p->class ==> (*p).class
   * (const struct Class **) p ==> (const struct Class *)p->class

void * new (const void * _class, ...)
{	const struct Class * class = _class;
	void * p = calloc(1, class -> size);

	assert(p);
	/* Cast to Class so that the same function is initialized only once, but all defined
		Objects can be called */
	* (const struct Class **) p = class;

	if (class -> ctor)
	{	va_list ap;
		//Function String_to which ctor pointsCtor
		va_start(ap, _class);
		p = class -> ctor(p, & ap);
		va_end(ap);
	}
	return p;
}
static void * String_ctor (void * _self, va_list * app)
{	struct String * self = _self;
	const char * text = va_arg(* app, const char *);

	self -> text = malloc(strlen(text) + 1);
	assert(self -> text);
	strcpy(self -> text, text);
	return self;
}

delete

The delete function is a function that frees memory requested by new. It first calls the destructor to free memory requested by the constructor in self and then frees memory requested by the new function by calling free, ensuring that the incoming value is not NULL and that the constructor exists.

void delete (void * self)
{	const struct Class ** cp = self;

	if (self && * cp && (* cp) -> dtor)
		self = (* cp) -> dtor(self);
	free(self);
}

Destructor

static void * String_dtor (void * _self)
{	struct String * self = _self;

	free(self -> text), self -> text = 0;
	return self;
}

clone

Cloning of Objects

void * clone (const void * self)
{	const struct Class * const * cp = self;

	assert(self && * cp && (* cp) -> clone);
	return (* cp) -> clone(self);
}


Cloning an object by calling the new method to create a new object the same as the incoming object

static void * String_clone (const void * _self)
{	const struct String * self = _self;

	return new(String, self -> text);
}

differ

The difference comparison function enables the comparison of the object itself and whether the object string is the same. If the objects are equal, it returns 0. If the objects are different, the object description text also returns 0, indicating the same.

int differ (const void * self, const void * b)
{	const struct Class * const * cp = self;

	assert(self && * cp && (* cp) -> differ);
	return (* cp) -> differ(self, b);
}
static int String_differ (const void * _self, const void * _b)
{	const struct String * self = _self;
	const struct String * b = _b;

	if (self == b)
		return 0;
	if (! b || b -> class != String)
		return 1;
	return strcmp(self -> text, b -> text);
}

sizeOf

The sizeOf function is relatively simple, and it simply returns the values stored in the size field in the object

size_t sizeOf (const void * self)
{	const struct Class * const * cp = self;

	assert(self && * cp);
	return (* cp) -> size;
}

main

#include <stdio.h>
#include "String.h"
#include "new.h"

int main ()
{	void * a = new(String, "a"), * aa = clone(a);
	void * b = new(String, "b");

	printf("sizeOf(a) == %zu\n", sizeOf(a));
	if (differ(a, b))
		puts("ok");

	if (differ(a, aa))
		puts("differ?");

	if (a == aa)
		puts("clone?");

	delete(a), delete(aa), delete(b);
	return 0;
}

summary

What this subsection is about is a dynamic pointer. The same application can achieve different effects by pointing the function pointer to different functions.

In the main function, a, a a, b objects are created. All three objects point to the common Class structure through the calss pointer. There is a pointer to the static function in the Class structure. Different functions are loaded on the function pointer by compiling different c files at compile time.

The effect of this is to achieve the same primary function by pointing to different functions with a dynamic pointer. When it is actually executed, different functions are executed to achieve different effects and achieve different data encapsulation.

TIPS

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <stdarg.h>

void test(void);

struct String {
	const void * class;	/* must be first */
	char * text;
};

struct Class {
	size_t size;
	void  (* test) (void);
};
void test(void)
{
    printf("test for point.\n");
}

static const struct Class _String = {
	sizeof(struct String),
	test
};

const void * String = & _String;

/**
 *  
 */
int main(int argc, char const *argv[])
{
    size_t stringSize = sizeof(struct String);
    
    const struct Class * class = String;
	void * p = calloc(1, class -> size);

	assert(p);
     
	* (const struct Class **) p = class;

    struct String {
        const void * class;	/* must be first */
        char * text;
    };
    struct String * p = (struct String *)calloc(1, sizeof(struct String));
    
    return 0;
}

We have the following definitions:

struct String {
	const void * class;	/* must be first */
	char * text;
};
struct Class {
	size_t size;
	void  (* test) (void);
};

First we apply for a memory void * P = calloc (1, sizeof (struct String);, well understood here, at this point we want to make p a block of memory pointing to the struct String type, just add a cast to the struct String * p = (struct String *)calloc(1, sizeof(struct String));P is then a structure pointer to an element of type struct String.

Next, look at this usage * (const struct Class **) p = class; many people instinctively counteract the last two addressors from the previous dereference at the first glance and conclude that the actual effect is equivalent to:

(const struct Class *) p = class; if you really think so, you're wrong.

This means the following:

After doing this, the p pointer points to the String structure, and the * (const struct Class **) p = class; that is, the void * type pointer class in the String points to the String structure, note that the structure is not a type, as shown in the figure, the String structure is actually a struct Class type pointer.

static const struct Class _String = {
	sizeof(struct String),
	test
};

const void * String = & _String;
So the following absolutely must not use addressing and de-referencing to offset each other for code walking, nor do those words on the new Baidu Q&A that do not use this will not achieve the purpose of pointing the void * pointer in the String structure to the new memory application.
    const struct Class ** -- Tells the compiler that this pointer is a two-dimensional pointer. The first pointer is to a String structure. There are two more pointers in the structure. The second pointer indicates to take the pointer from the structure pointer. Which pointer to get depends on the type given earlier. This is a `struct Class` structure pointer.
    Because this is a double pointer, because p itself is a pointer, in order to correspond to the class pointer, the pointer needs to be de-referenced once, which also forms the following way, in C language, to achieve the way of taking the pointer in the structure pointer
* (const struct Class **) p = class;

Tags: github Linux Programming C

Posted on Thu, 11 Jun 2020 20:46:31 -0400 by AbsolutelyFreeW