Public/private in C

J

jacob navia

As you may have noticed, when I proposed the iterator object I splitted
the object into a public part (the first 3 fields) an an undisclosed
part specific to each iterator.

This inheritance implementation looks like this:

In the header file:

typedef struct PublicStruct {
// public fields
} Public;

In the implementation file (.c) we do:

typedef struct Inherited {
Public p;
// follow the private fields
} Private;

The interface for all functions is just the public structure:

void fn1(Public *);
void fn2(Public *);

The implementation of fn1 however, does this:

void fn1(Public *p) {
Private *pri = (Private *)p;

// Work with the private struct.
// It is assumed that the pointer
// handed down is actually a private
// pointer

}

Obviously this doesn't have any compiler support
but works quite well in practice. You can add a
"magic" number somewhere in the private struct
in the debug version to catch mistakes when passing
the structures.

Obviously you aren't supposed to copy the public object since you do not
know what is behind. A way to avoid that would be to add a VLA:

typedef struct PublicStruct {
// public fields
// ...
// private fields follow:
char private[];
} Public;


YES I KNOW.

C++/Java/C# do this MUCH better, but they have other drawbacks
that make a pure C solution MUCH better. Thank you.
 
I

Ian Collins

As you may have noticed, when I proposed the iterator object I splitted
the object into a public part (the first 3 fields) an an undisclosed
part specific to each iterator.

Obviously this doesn't have any compiler support
but works quite well in practice. You can add a
"magic" number somewhere in the private struct
in the debug version to catch mistakes when passing
the structures.

Obviously you aren't supposed to copy the public object since you do not
know what is behind. A way to avoid that would be to add a VLA:

typedef struct PublicStruct {
// public fields
// ...
// private fields follow:
char private[];

This isn't a VLA, its an example of the "struct hack".
} Public;

You can still copy these. It's not a good idea, but there's nothing to
prevent the copy.
YES I KNOW.

C++/Java/C# do this MUCH better, but they have other drawbacks
that make a pure C solution MUCH better. Thank you.

In this instance, please elaborate!
 
K

Keith Thompson

Ian Collins said:
On 03/22/10 11:20 AM, jacob navia wrote: [...]
typedef struct PublicStruct {
// public fields
// ...
// private fields follow:
char private[];

This isn't a VLA, its an example of the "struct hack".
} Public;
[...]

More precisely, it's a "flexible array member", C99's replacement for
the "struct hack".

Incidentally, jacob's declaration for the struct was properly
indented, but the indentation vanished in Ian's followup, probably
because of the use of tabs. At least for Usenet posts, spaces are
better than tabs for indentation, since news software doesn't always
deal sanely with tabs.
 
H

Hamiral

jacob said:
As you may have noticed, when I proposed the iterator object I splitted
the object into a public part (the first 3 fields) an an undisclosed
part specific to each iterator.

Interesting.
Do you have something similar for inheritance, or... polymorphism ? That
would be great :)

Ham
 
I

ImpalerCore

As you may have noticed, when I proposed the iterator object I splitted
the object into a public part (the first 3 fields) an an undisclosed
part specific to each iterator.

This inheritance implementation looks like this:

In the header file:

typedef struct PublicStruct {
        // public fields

} Public;

In the implementation file (.c) we do:

typedef struct Inherited {
        Public p;
        // follow the private fields

} Private;

The interface for all functions is just the public structure:

void fn1(Public *);
void fn2(Public *);

The implementation of fn1 however, does this:

void fn1(Public *p) {
        Private *pri = (Private *)p;

        // Work with the private struct.
        // It is assumed that the pointer
        // handed down is actually a private
        // pointer

}

Obviously this doesn't have any compiler support
but works quite well in practice. You can add a
"magic" number somewhere in the private struct
in the debug version to catch mistakes when passing
the structures.

Obviously you aren't supposed to copy the public object since you do not
know what is behind. A way to avoid that would be to add a VLA:

typedef struct PublicStruct {
        // public fields
        // ...
        // private fields follow:
        char private[];

} Public;

YES I KNOW.

C++/Java/C# do this MUCH better, but they have other drawbacks
that make a pure C solution MUCH better. Thank you.

Personally I think implementing some public/private separation is
outside the scope of C. Grafting this OO paradigm into C is purely
arbitrary at best, confusing and complex at worst. C wants people to
get their hands dirty, and doesn't do a whole lot to protect them.

Again, you say a pure C solution is MUCH better, but I haven't seen a
really cool use-case that says "Yeah, I want to try it out!". If I
wanted to make something private in C, I'd just give the "private"
members a hideously long name so that most people will just be annoyed/
bored to type it out and use the "public" parts ;)

Best regards,
John D.
 
J

jacob navia

ImpalerCore a écrit :
Personally I think implementing some public/private separation is
outside the scope of C. Grafting this OO paradigm into C is purely
arbitrary at best, confusing and complex at worst. C wants people to
get their hands dirty, and doesn't do a whole lot to protect them.

Well, as you may have noticed, I proposed a common iterator object for
iterating through ANY container in my library. Obviously that object
can't be a SINGLE type, it must be several since it must have the specific
knowledge of each container, AND it must offer a COMMON interface, so user
code is independent of the container.

It is not just "a matter of protecting people from themselves", it is
just that is impossible to make a COMMON type for all container iterators
if each iterator exposes its interface.

The solution is then, to define a common interface with a few
function pointers that is shared by all containers. User code uses that
interface

typedef struct _iterator {
void (*Getnext)(structb_iterator *);
// Some other public function pointers
char private[]; // Document that this is a variable length structure
} Iterator;

In the file of the container implementation (say list.c) I
define

typedef struct tagListIterator {
Iterator it;
// Private fields follow
List *list;
unsigned timestamp;
// etc
} ListIterator;

Now, I can define

static void *GetNext(Iterator *it)
{
ListIterator *li = (ListIterator *)it;

// And now I can use the private fields
// in this implementation.
}

The advantage of this is that I can EXTEND a public structure with private
parts, something similar to simple inheritance in C.
Again, you say a pure C solution is MUCH better, but I haven't seen a
really cool use-case that says "Yeah, I want to try it out!".

When you write a library, it is very handy since it allows to present a common
interface for a set of different objects
If I
wanted to make something private in C, I'd just give the "private"
members a hideously long name so that most people will just be annoyed/
bored to type it out and use the "public" parts ;)

Best regards,
John D.

It is not so much a "private" vs public stuff but it is a thing of
interfacing different objects of different types through a common interface.
 
I

ImpalerCore

ImpalerCore a écrit :
Personally I think implementing some public/private separation is
outside the scope of C.  Grafting this OO paradigm into C is purely
arbitrary at best, confusing and complex at worst.  C wants people to
get their hands dirty, and doesn't do a whole lot to protect them.

Well, as you may have noticed, I proposed a common iterator object for
iterating through ANY container in my library. Obviously that object
can't be a SINGLE type, it must be several since it must have the specific
knowledge of each container, AND it must offer a COMMON interface, so user
code is independent of the container.

It is not just "a matter of protecting people from themselves", it is
just that is impossible to make a COMMON type for all container iterators
if each iterator exposes its interface.

The solution is then, to define a common interface with a few
function pointers that is shared by all containers. User code uses that
interface

typedef struct _iterator {
        void (*Getnext)(structb_iterator *);
        // Some other public function pointers
        char private[]; // Document that this is a variable length structure

} Iterator;

In the file of the container implementation (say list.c) I
define

typedef struct tagListIterator {
        Iterator it;
        // Private fields follow
        List *list;
        unsigned timestamp;
        // etc

} ListIterator;

Now, I can define

static void *GetNext(Iterator *it)
{
        ListIterator *li = (ListIterator *)it;

        // And now I can use the private fields
        // in this implementation.

}

The advantage of this is that I can EXTEND a public structure with private
parts, something similar to simple inheritance in C.

I can see your viewpoint better now, between this and your other
responses.
When you write a library, it is very handy since it allows to present a common
interface for a set of different objects

Fair enough, I'll wait to see how it all works when you release
something.
It is not so much a "private" vs public stuff but it is a thing of
interfacing different objects of different types through a common interface.

I suppose I took the private/public too literally. Probably a by-
product of learning C++ then coming to C I suppose.

Best regards,
John D.
 
A

Anand Hariharan

As you may have noticed, when I proposed the iterator object I splitted
the object into a public part (the first 3 fields) an an undisclosed
part specific to each iterator.

This inheritance implementation looks like this:

In the header file:

typedef struct PublicStruct {
        // public fields

} Public;

In the implementation file (.c) we do:

typedef struct Inherited {
        Public p;
        // follow the private fields

} Private;

The interface for all functions is just the public structure:

void fn1(Public *);
void fn2(Public *);

The implementation of fn1 however, does this:

void fn1(Public *p) {
        Private *pri = (Private *)p;

        // Work with the private struct.
        // It is assumed that the pointer
        // handed down is actually a private
        // pointer

}

Obviously this doesn't have any compiler support
but works quite well in practice. You can add a
"magic" number somewhere in the private struct
in the debug version to catch mistakes when passing
the structures.

Obviously you aren't supposed to copy the public object since you do not
know what is behind. A way to avoid that would be to add a VLA:

typedef struct PublicStruct {
        // public fields
        // ...
        // private fields follow:
        char private[];

} Public;

YES I KNOW.

C++/Java/C# do this MUCH better, but they have other drawbacks
that make a pure C solution MUCH better. Thank you.


Am not sure if there is a question here. Also, you may already know
this, but you can control/restrict access by using the 'pimpl' idiom
(also known as the 'opaque structure'). Herb Sutter has written an
article or two regarding this (yes, they are C++ based, but the ideas
can be applied to C).

Basically you forward declare a structure that you want to be private
like so:

struct MyStructPrivates; /* forward declaration */

struct MyStruct
{
/* All your "public" stuff here */

struct MyStructPrivates *PtrToPrivate;
};

In the declaration of MyStructPrivates, you include the declaration of
MyStruct so you have access to the public parts of your struct.

The declaration of MyStructPrivates does not even have to be delivered
to the library user. Further, if anything changes in the
implementation of MyStructPrivates, your library users will remain
unaffected.

I haven't followed the other thread or read your iterator design, it
is possible I am way off-tangent here.

- Anand
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,994
Messages
2,570,223
Members
46,812
Latest member
GracielaWa

Latest Threads

Top