Private members in C structures

May 18, 2008 · Posted in C / C + +

This week, here at work had more proof that programming paradigm is something completely language independent, ie it is not because you're programming in C + +, compiling with g + + that your code will be object oriented, so little, if you program in ANSI C your code must be structured or you will be unable to object-oriented programming.

I'm reading right, Object Orientation in ANSI C?

Yes and no!

Things like inheritance, polymorphism and overloading are harder to make / emulate in C, but you can program using a style that behaves similarly to object orientation. The libdfb is written in C but "object oriented", so that you create, manipulate to destroy elements that behave very similarly to objects.

Here at work, we have a system of hardware abstraction in C that connects with a GUI in C + +. The lower layer, was also written in C (and very well written, tell by the way) with these techniques, simulating an object orientation. One of these techniques, I was struck by wearing one of those footnotes of the books of 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 the other units. Thus, it is possible to "hide" certain symbols within your compilation unit, making them inaccessible to the outside world. We have with this package.

A compilation unit is the set of files that once processed and pre-compiled code generates a single object. Basically (but not exactly), we may take as each compilation unit implementation file source code (*. c, *. cpp. Etc). Further details on the steps of compiling C and C + + can be found on the blog Caloni .

Using this information, we can create an ANSI C struct in which their 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 he represents.
Eg:

  a ; extern int a;
 void ) ; void blah (void);
 struct st_data; 

Definition or implementation: Tells the compiler that the identifier is determined, 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 speak of structures and types, without setting the compiler can not allocate memory for them because nothing is known about how much space a variable of that type need. On the other hand, sometimes without the declaration, the linker can not know what that symbol exists.

When we declare a structure in a header, usually we also define its members there and every time we add this header to a source of ours, we included in the compilation unit that both the source statement as the definition of this structure, making the members of our entire public to this compilation unit. This allows us to access its members directly.

If we separate the statement of the definition, only symbol that represents the structure 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 set a variable of type structure, only a pointer to it, because pointers have all the same size and the compiler only needs the name of the symbol type to create the pointer.

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 words: * /
 / * Struct _mytype * /
 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 * ) ; char * get_text (my_type *);

 # Endif 

The implementation mytype.c:

  # Include "mytype.h"

 # <stdio.h>
 # Include <stdlib.h>
 # 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 date; / * 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 the main.  * /

 my_type ** my_ptr ) { /* some code... void create_my_type (my_type my_ptr **) (/ * some code ...  * /)
 my_type ** my_ptr ) { /* some code... void destroy_my_type (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 variables of this kind, we must define a "builder" and a "destructor".

Attempts to 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 taking the concepts of language as both paradigms, you can implement codes really interesting. In this silly example might not clear the usefulness of forcing an emulation of encapsulation 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, such techniques are show of great value. In our case, this technique specifically allowed an experienced programmer, who missed the entire project, he 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/
Paper Linux Kernel Development

Comments

    blog comments powered by Disqus