2013年12月5日 星期四

Private members in C.

    As being a language with great possibilities, almost every concepts, no matter old or new, could be implemented by C. A quite common difficulty would be the private member in C, especially when offering a library for co-working. Here are some notes about doing the private-like members in the C structs.



    As a note of these notes, the word `private' here focuses on letting the compiler help to check/limit the access of the member, to avoid someone who include the header does the wrong using, instead of protect the code from other's peeping: I believe it could be easily done by some common tools, such as gdb.

The most intuitional one should be using the prototype:
/* Proto.h */
struct ShareStruct;

/* Proto.c */
struct ShareStruct
{
    int PrivateMember;
}

Then someone who includes Proto.h could only treats the struct ShareStruct as an opaque object. It could be used like any other structs except deferring or measuring its size. It is noticed that the address of the instance of ShareStruct be exposed, which could causes incorrect directive modifications we don't want. A common alternative way then comes:
/* Proto.h */
struct ShareStruct
{
    void *Implement;
}

/* Proto.c */
struct ShareStruct_Imp
{
    int PrivateMember;
}

struct ShareStruct *ShareStruct_new()
{
/* Allocating stuff... */

    pShareStruct->Implement = &ShaereStruct_Imp;

    return pShareStruct;
}

Now Proto.h defines the structure ShareStruct, which only contains one pointer to the real implement instance. It successfully hides the address of the critical area. A trade off here and on is the ShareStruct instance should only be generated by a wrapped constructor - which generates the core ShareStruct_Imp, and set the pointer Implement to it. Moreover, if someone casts, inelegantly, the pointer, or more conveniently, using gdb, to discover the Implement, the access/damage could still be happend. To hide the address more completely, use the inherit pattern:
/* Proto.h */
struct ShareStruct
{
    int PublicMember;
}

/* Proto.c */
struct ShareStruct_Complete
{
    struct ShareStruct PublicPart;
    int PrivateMember;
}

Two points worthy to discuss here:

  1. The hidden property is reached by the tricky mazes: the header-includer might not have idea there is something follows the ShareStruct, rather than showing `Hey, there are somthing behind me and I don't want to let you know.' in the previous ways.
  2. The PublicPart not only being a mask makes ShareStruct more ordinary, but also provide public members, which can omit lots of method functions in get/set them in the structure.
As these base blocks be built, combinations could be done such as:

/* Proto.h */

struct ShareStruct
{
    int PublicMember;
    void *Implement;
}

/* Proto.c */
struct ShareStruct_Imp
{
    struct ShareStruct PublicMembers;
    int dummy;
    int PrivateMember;
}

struct ShareStruct *ShareStruct_new()
{
/* Allocating stuff... */

    pShareStruct->Implement = &(ShaereStruct_Imp.dummy);

    return pShareStruct;
}
Now it tends to be tricks to deceive the header-includer/library-user as to the enemies, which is far from the original purpose. But it sometimes quite useful in commercial situations, or to save the time in the misunderstanding and misusing with cooperations.



沒有留言:

張貼留言