Private members in C structures
This week, here at work had one more proof that programming paradigm is something completely language independent, ie it is not because you're programming in C + +, compiled with g + + that your code will be object-oriented so little, if you program in your code ANSI C must be structured or you will be disqualified from object-oriented programming.
I'm reading right, Object Orientation in ANSI C?
Yes and no!
Things like inheritance, polymorphism and overloading are complicated to make / emulate in C, but you can program using a style that behaved similarly to object orientation. The libdfb is written in C but "object oriented", so that you create, manipulate it destroys elements that behave much like objects.
Here at work, we have a system Hardware Abstraction in C that connects with a GUI in C + +. The lowest layer, was also written in C (and very well written, say by the way) with these techniques, simulating an object orientation. One such technique, called my attention by wearing one of those footnotes of books C.
When compiling C code, each symbol is only visible within the compilation unit in which it was declared, unless it is again declared as extern in other units. Thus, you can "hide" certain symbols within its compilation unit, making them inaccessible to the outside world. We have with this package.
The compilation unit is the set of files that after pre-processed and generate a single compiled object code. Basically (but not exactly), we can take for each compilation unit implementation file of source code (*. c, *. cpp. Etc.). More detail on the steps of compiling C and C + + can be found on the blog Caloni .
Using this information, we can create a struct in ANSI C in which its internal members are "private." The magic is in the definition of trap states within the compilation unit and create access methods for these members. For this we use the footnotes that show us the difference between declaration and definition of elements in C:
Declaration or manifesto: the compiler presents an identifier without saying much about its meaning, ie, tells the compiler that the identifier XXX exists, but little is known about what it represents.
Eg
a ; extern int a; void ) ; void blah (void); struct st_data;
Definition and implementation: Tells the compiler that the identifier is given, such as how much memory should be allocated to it and what the memory address where we can find it, among other things.
Eg
int a; void ) { /* Do anything. void blah (void) {/ * Do anything. * /} /* Some members. st_data struct {/ * Add members. * /}
When we talk about structures and types, without defining the compiler can not allocate memory for them because nothing is known about how much space you need a variable of that type. On the other hand, sometimes, without a claim, the linker can not know that symbol exists.
When we declare a structure in a header, usually we have also set their members there and all this time we add a header to our source, we have included in the compilation unit that both the source statement as the definition of this structure, making the members of the public estutura to this compilation unit. This allows us to access its members directly.
If we separate statement of the definition only symbol that represents the structure is available, but not its members. Thus, any attempt to direct access to a member, will generate a compilation error. An interesting side effect is that as the compiler knows nothing about the size of the structure, you can not directly define a variable of type structure, only a pointer to it, because all pointers have the same size and the compiler needs only the name of the symbol the pointer type to create.
We then mytype.h header like this:
# Ifndef MY_TYPE_H # Define MY_TYPE_H / * The typedef is not just to keep repeating the word struct. * / / * The statement is only the passage: * / / * _MYTYPE Struct * / typedef struct _MYTYPE my_type; my_type ** ) ; void create_my_type (my_type **); my_type ** ) ; void destroy_my_type (my_type **); my_type * , int ) ; void set_data (my_type *, int); my_type * ) ; int get_data (my_type *); my_type * , const char * ) ; void set_text (my_type *, const char *); get_text ( my_type * ) ; get_text char * (* my_type); # Endif
The implementation mytype.c:
# Include "mytype.h" Fopen # Include # Include <string.h> /* Aqui fica a definição da estrtura. _MYTYPE struct {/ * Here is the definition of estrtura. * / /* Somente depois disso é que o com- */ int data / * Only after that, with-* / 21 ] ; /* pilador vai saber como alocá-la. int text [21] / * pilador will know how to allocate it. * / /* Tente um sizeof(my_type) no main. ;} / * Try a sizeof (my_type) in main. * / my_type ** my_ptr ) { /* some code... create_my_type void (** my_type my_ptr) {/ * some code ... * /} my_type ** my_ptr ) { /* some code... destroy_my_type void (** my_type my_ptr) {/ * some code ... * /} my_type * my_ptr , int d ) { /* some code... set_data void (* my_type my_ptr, int d) {/ * some code ... * /} my_type * my_ptr ) { /* some code... int get_data (my_type my_ptr *) {/ * some code ... * /} my_type * my_ptr , const char * text ) { /* some code... void set_text (my_type my_ptr *, const char * text) {/ * some code ... * /} get_text ( my_type * my_ptr ) { /* some code... get_text char * (* my_type my_ptr) {/ * some code ... * /}
The complete source code example can be found here .
Since you can not directly create such variables, we need to define a "builder" and a "destructor".
Attempts at direct access to members generate compilation error:
private-struct-members$ gcc -o teste mytype.h mytype.c main.c user @ host: ~ / private-struct-members $ gcc-o test main.c mytype.h mytype.c main.c: In function 'main': main.c: 22: error: dereferencing pointer to incomplete type private-struct-members$ user @ host: ~ / private-struct-members $
Thus, with a little creativity and with the concepts of both language paradigms, it is possible to implement codes really interesting. In this silly example might not clear the usefulness of forcing an emulation package or the use of constructor / destructor in C, but in systems where the circumstances do not allow a C + +, or the complexity tends to reach critical levels, these techniques are show of great value. In our case, this technique specifically allowed an experienced programmer who did not attend the entire project, discovered that his attempt to direct access to a member of a structure, was contextually inappropriate. Without it, a bug Kabul logic would appear only at runtime, making the software probably explode in the face of the customer.
Useful links (or not ...):
http://www.directfb.org
http://www.caloni.com.br
http://www.numaboa.com.br/informatica/c/
Book of Linux Kernel Development
Comments
- Diogo V. Kersting
- blab
- blab
- blab
- Rodrigo Martins (Witch)
- blab
- Carcara

