How can I emulate the constructor and destructor behavior (for specific data types) in C

I have a C (nested) structure that I would like to automatically initialize and destroy in my code.

I am compiling with GCC (4.4.3) on Linux. I dimly know about the GCC attribute constructors and the destructor, but the build / destroy they provide seems to apply to the whole program (i.e., before calling main (), etc.).

I want to have different init / cleanup functions for different data types - is this C ++ a similar behavior that I can emulate with POC?

I included the C ++ tag because it is really a C ++ behavior that I'm trying to emulate in C.

+6
source share
4 answers

There is no way to do this automatically, at least not in any portable mode. In C, you usually have functions that work as constructors and destructors - they (de) allocate memory and (de) initialize fields - except that they must be called explicitly:

typedef struct{} MyStruct; MyStruct *MyStruct_New(void); void MyStruct_Free(MyStruct *obj); 

The language was simply not intended for this, and you should not try to force it, imo. If you want automatic destruction, you should not use C.

+9
source

#define your way through the problem ...

As pointed out by previous authors, there is no automatic way to do what you ask, which, unfortunately, is obvious, since C has no way to make true OOP.

But a programmer can always crack himself through any obstacle. At the end of this post, I wrote you a sample hack to get around the problem.

There are ways to clean up the provided macro, although it will not be so portable.



- implementation of C99

 #include <stdio.h> #include <stdlib.h> 

...

 #define SCOPIFY(TYPE,NAME, ...) { \ ctor_ ## TYPE(& NAME); \ __VA_ARGS__ \ dtor_ ## TYPE(& NAME); \ } (void)0 

...

 typedef struct { int * p; } Obj; void ctor_Obj (Obj* this) { this->p = malloc (sizeof (int)); *this->p = 123; fprintf (stderr, "Obj::ctor, (this -> %p)\n", (void*)this); } void dtor_Obj (Obj* this) { free (this->p); fprintf (stderr, "Obj::dtor, (this -> %p)\n", (void*)this); } 

...

 int main (int argc, char *argv[]) { Obj o1, o2; SCOPIFY (Obj, o1, fprintf (stderr, " o1.p -> %d\n", *o1.p); SCOPIFY (Obj, o2, int a, b; fprintf (stderr, " o2.p -> %d\n", *o2.p); (*o1.p) += (*o2.p); ); fprintf (stderr, " o1.p -> %d\n", *o1.p); ); return 0; } 

output ( http://ideone.com/WYrjU )

 Obj::ctor, (this -> 0xbf8f05ac) o1.p -> 123 Obj::ctor, (this -> 0xbf8f05a8) o2.p -> 123 Obj::dtor, (this -> 0xbf8f05a8) o1.p -> 246 Obj::dtor, (this -> 0xbf8f05ac) 
+5
source

From what you write, I suggest that you already know how to write init and destroy functions that end up recursively using copies of them for individual parts.

Yes, in C there is no standard mechanism that would allow something like automatic construction or destruction.

You can replace the design somewhat by writing an initializer macro. Assigned initializers are convenient for this.

 #define TOTO_INITIALIZER(TUTU_PARAM, TATA_PARAM) \ { \ .tata_member = TATA_INITIALIZER(TATA_PARAM), \ .tutu_member = TUTU_INITIALIZER(TUTU_PARAM), \ } 

since they make this code reliable for reordering members.

For destructors, there is nothing to associate with a variable or data type. The only thing I know about what is possible is area-based resource management, which in C can be implemented using hidden for -scope local variables.

+2
source

There is no default method for automatically calling a function when creating a structure. Here is an example of a create and initialize function for a specific type of structure:

 // Simple struct that holds an ID number and a file pointer. typedef struct { int id; FILE *data; } Datum; // Function to create a Datum from a given file. Datum *create_datum(const char *fname) { // Create Datum object. Datum *d = (Datum*)malloc(sizeof(Datum)); // malloc may return NULL if we're out of memory. if(d) { // Initialise ID to something. d->id = 0; // Open filename passed. d->data = fopen(fname, "r"); } return d; } // Function to safely destroy a Datum. This function takes a pointer-pointer so // that it can set the pointer to NULL after deleting the object. Saves you // from dangling pointers. void destroy_datum(Datum **dp) { if(!dp) return; // Get a plain pointer for convenience Datum *d = *dp; if(d) { // Close the file. fclose(d->data); // Delete the object. free(d); // Set the pointer to NULL. *dp = NULL; } } // Now use these functions: int main(void) { Datum *datum = create_datum("test.txt"); if(datum) { // Do some things! } destroy_datum(&datum); // datum is now equal to NULL. } 

Hope this helps! As Homunkuls said, C is not a great language if you need to do a lot of things, but sometimes you just want to distract the process of creating a structure and also clean it. This is especially useful in a modular design, where a module can provide the create_ and destroy_ interface functions and hide the actual implementation of these functions.

+1
source

Source: https://habr.com/ru/post/905395/


All Articles