I have recently read "Everything you ever wanted to know about C types" by
Peter Seebach (1), and learned about incomplete types. Now, I realise the
value of encapsulation, but I wonder whether it is proper C style to use
that much of it. By using incomplete types you end up with something like
this:

object_set_colour(object, RED);
colour = object_get_colour(object);

instead of:

object->colour = RED;
colour = object->colour;

If the type of "object" is incomplete, you could not write object->colour or
something like that, because accessing data of incomplete type is not
possible. Only the file which contains the get/set rountines defines the
type. You end up with something that looks very much like OOP + a terrible
overhead. Keep in mind that you need to keep the get/set functions + the
actual type definition in a seperate file to get the encapsulation effect.
That means that no mainstream C compiler will be able to optimize those
function calls away (AFAIK neither GNU, nor MS, nor Borland can inline
functions from other files).

So do you consider that proper C style?
Personally, I am not sure what to think of this. It seems to scream "Why do
you not use C++ already?!".

I have recently read "Everything you ever wanted to know about C types" by
Peter Seebach (1), and learned about incomplete types. Now, I realise the
value of encapsulation, but I wonder whether it is proper C style to use
that much of it. By using incomplete types you end up with something like
this:

object_set_colour(object, RED);
colour = object_get_colour(object);

instead of:

object->colour = RED;
colour = object->colour;

If the type of "object" is incomplete, you could not write object->colour or
something like that, because accessing data of incomplete type is not
possible. Only the file which contains the get/set rountines defines the
type. You end up with something that looks very much like OOP + a terrible
overhead. Keep in mind that you need to keep the get/set functions + the
actual type definition in a seperate file to get the encapsulation effect.
That means that no mainstream C compiler will be able to optimize those
function calls away (AFAIK neither GNU, nor MS, nor Borland can inline
functions from other files).

MS VC++ can do it.
And if you do the cheesy, sleezy include trick all of them can do it:

On the other hand, I have no problem reading or even enjoying C code
written in a C++ style.

On the other, other hand, I have seen programs where everything is
typedef-ed into some abstract type (even things like loop indexes) and
find those programs to be a lot less transparent to understand than
one more simply written.

I have recently read "Everything you ever wanted to know about C types" by
Peter Seebach (1), and learned about incomplete types. Now, I realise the
value of encapsulation, but I wonder whether it is proper C style to use
that much of it. By using incomplete types you end up with something like
this:

object_set_colour(object, RED);
colour = object_get_colour(object);

If you end up with this you don't really *have* any encapsulation.
All you've done invent new syntax for:

instead of:

object->colour = RED;
colour = object->colour;

as you have spotted.

Incomplete types are at their most useful when the interface you have
to define is small compared to the complexity (or probable
variability) of the implementation that is hidden behind it.

I have recently read "Everything you ever wanted to know about C types" by
Peter Seebach (1), and learned about incomplete types. Now, I realise the
value of encapsulation, but I wonder whether it is proper C style to use
that much of it. By using incomplete types you end up with something like
this:

object_set_colour(object, RED);
colour = object_get_colour(object);

instead of:

object->colour = RED;
colour = object->colour;

If the type of "object" is incomplete, you could not write object->colour or
something like that, because accessing data of incomplete type is not
possible. Only the file which contains the get/set rountines defines the
type. You end up with something that looks very much like OOP + a terrible
overhead. Keep in mind that you need to keep the get/set functions + the
actual type definition in a seperate file to get the encapsulation effect.
That means that no mainstream C compiler will be able to optimize those
function calls away (AFAIK neither GNU, nor MS, nor Borland can inline
functions from other files).

So do you consider that proper C style?

Yes, and it's a very common idiom (have you ever manipulated a FILE
object?). Sometimes you want to use encapsulation to present a
consistent interface over a number of platforms. Other times you may
wish to provide a stable interface to an object that is liable to change
between releases of you application.

Personally, I am not sure what to think of this. It seems to scream "Why do
you not use C++ already?!".

Nothing to do with C++, unless you are using a similar idiom with opaque
types, C++ classes lay their innards bare for all to see.

I have recently read "Everything you ever wanted to know about C types" by
Peter Seebach (1), and learned about incomplete types. Now, I realise the
value of encapsulation, but I wonder whether it is proper C style to use
that much of it. By using incomplete types you end up with something like
this:

object_set_colour(object, RED);
colour = object_get_colour(object);

instead of:

object->colour = RED;
colour = object->colour;

If the type of "object" is incomplete, you could not write object->colour or
something like that, because accessing data of incomplete type is not
possible. Only the file which contains the get/set rountines defines the
type. You end up with something that looks very much like OOP + a terrible
overhead. Keep in mind that you need to keep the get/set functions + the
actual type definition in a seperate file to get the encapsulation effect.
That means that no mainstream C compiler will be able to optimize those
function calls away (AFAIK neither GNU, nor MS, nor Borland can inline
functions from other files).

So do you consider that proper C style?

"Proper" is a judgmental term I won't try to address.
As for the pattern, yes: It's a perfectly good and useful
way to use C. Let's modify your example a little bit:

That is, suppose there is a colour object that supports several
different perceptual color models: RGB, CMY, CMYK, CIE, ... You
can set the components of any model, and read back the components
of any other model: the object takes care of the transformations.
You can even do mixed-mode operations:

Now: How are you going to accomplish this with the struct->element
approach? By using mutators and accessors, you give yourself an
opportunity to do some processing at interesting moments, to
defend against bogus settings (colour_set_rgb(&hue, RED, -42e9)),
and in general to change the internal representation of the object.

This freedom isn't always important, and it's silly to be a
slave to encapsulation orthodoxy for no purpose. But it can also
be a potent tool, capable of simplifying designs and easing
debugging. The trick is to know when to indulge and when to
abstain.

Personally, I am not sure what to think of this. It seems to scream "Why do
you not use C++ already?!".

Because you'll go straight to Hell, of course. Where your
body, like your name, will be mangled.

>I have recently read "Everything you ever wanted to know about C types" byPeter Seebach (1), and learned about incomplete types. Now, I realise thevalue of encapsulation, but I wonder whether it is proper C style to usethat much of it. By using incomplete types you end up with something likethis:

object_set_colour(object, RED);
colour = object_get_colour(object);

instead of:

object->colour = RED;
colour = object->colour;

If the type of "object" is incomplete, you could not write object->colour
or something like that, because accessing data of incomplete type is not
possible. Only the file which contains the get/set rountines defines the
type.

That's one way to do it, in particular if you're writing a library to be
used by others and there's a substantial risk that versions will get out of
sync (for instance, you change your library code but they don't recompile
their application).

The other way to do it is to define function-like macros or static inline
functions in the header file that the application includes, which allows the
compiler to optimize away the encapsulation. This is safe as long as you
trust coders using your interface not to peek at the implementation and both
sets of code will always be recompiled if you change the internals.

You end up with something that looks very much like OOP + a terrible
overhead.

There is no guarantee it will perform worse if you do end up with a
function-call interface. Sure, it's likely, but (a) compilers continually
get better at optimizing, (b) CPUs get better every year at executing bad
code, and (c) you shouldn't worry about optimization until your program is
correct and you can prove there's a performance problem that merits writing
less-maintainable code.

Keep in mind that you need to keep the get/set functions + the actual type
definition in a seperate file to get the encapsulation effect.

That means that no mainstream C compiler will be able to optimize those
function calls away

There do exist compilers that can optimize, including inlining, across
translation units. I don't know the status of the ones you list; ask Google
about "Whole-Program Optimization". My understanding is that's fairly rare
today; at best you get inter-procedural optimizations within a single
translation unit with most compilers. That argues for (today) sticking with
the latter encapsulation strategy I describe above, rather than the one
you're assuming, if you can show the latter causes performance problems.

S

--
Stephen Sprunk "Those people who think they know everything
CCIE #3723 are a great annoyance to those of us who do."
K5SSS --Isaac Asimov
--
Posted via a free Usenet account from http://www.teranews.com

>If the type of "object" is incomplete, you could not write object->colour orsomething like that, because accessing data of incomplete type is notpossible.

[...]

>So do you consider that proper C style?

Yes, and it's a very common idiom (have you ever manipulated a FILEobject?).

Unfortunately you frequently see generous 'un-encapsulation' in C
code. Structs are defined completely in the header file but only
pointers to functions are used in the function declarations (other
'private' elements (defines, macros, enums) are also unnecessarily
placed into the header). Many programmers seem to be unaware of gratis
encapsulation.

>Personally, I am not sure what to think of this. It seems to scream "Why doyou not use C++ already?!".

Nothing to do with C++, unless you are using a similar idiom with opaquetypes, C++ classes lay their innards bare for all to see.

but not for all to access. Encapsulation is independent of language
and paradigm (it's not an original OO feature).

--
Roland Pibinger
"The best software is simple, elegant, and full of drama" - Grady Booch

This freedom isn't always important, and it's silly to be a
slave to encapsulation orthodoxy for no purpose. But it can also
be a potent tool, capable of simplifying designs and easing
debugging. The trick is to know when to indulge and when to
abstain.

>I have recently read "Everything you ever wanted to know about C types" byPeter Seebach (1), and learned about incomplete types. Now, I realise thevalue of encapsulation, but I wonder whether it is proper C style to usethat much of it. By using incomplete types you end up with something likethis:

object_set_colour(object, RED);colour = object_get_colour(object);

If you end up with this you don't really *have* any encapsulation.All you've done invent new syntax for:

>instead of:

object->colour = RED;colour = object->colour;

as you have spotted.

Not quite. With the first variation, you don't need access to the
components of object, or even to know their names or other properties.

>Incomplete types are at their most useful when the interface you haveto define is small compared to the complexity (or probablevariability) of the implementation that is hidden behind it.

>>I have recently read "Everything you ever wanted to know about C types" byPeter Seebach (1), and learned about incomplete types. Now, I realise thevalue of encapsulation, but I wonder whether it is proper C style to usethat much of it. By using incomplete types you end up with something likethis:

object_set_colour(object, RED);colour = object_get_colour(object);

If you end up with this you don't really *have* any encapsulation.All you've done invent new syntax for:

>>instead of:

object->colour = RED;colour = object->colour;

as you have spotted.

Not quite. With the first variation, you don't need access to the
components of object, or even to know their names or other
properties.

Good point. And you also have a place to put checks and debug tests,
which can be quite handy. But you are still not getting the most from
the *idea* of encapsulation if this is what most of the interface
looks like.

I have recently read "Everything you ever wanted to know about C types" byPeter Seebach (1), and learned about incomplete types. Now, I realise thevalue of encapsulation, but I wonder whether it is proper C style to usethat much of it. By using incomplete types you end up with something likethis:

object_set_colour(object, RED);colour = object_get_colour(object);

If you end up with this you don't really *have* any encapsulation.All you've done invent new syntax for:

instead of:

object->colour = RED;colour = object->colour;

as you have spotted.

Not quite. With the first variation, you don't need access to thecomponents of object, or even to know their names or otherproperties.

Good point. And you also have a place to put checks and debug tests,which can be quite handy. But you are still not getting the most fromthe *idea* of encapsulation if this is what most of the interfacelooks like.

Right. The "get/set" routines are often needed, but hopefully there
are higher-level aspects to the interface.

>>Yes, and it's a very common idiom (have you ever manipulated a FILEobject?).

Unfortunately you frequently see generous 'un-encapsulation' in C
code. Structs are defined completely in the header file but only
pointers to functions are used in the function declarations (other
'private' elements (defines, macros, enums) are also unnecessarily
placed into the header). Many programmers seem to be unaware of gratis
encapsulation.

Virtually all the code on my website is written in the FILE * paradigm form.
I don't like the term "object" because it gets confused with "object
oriented", which my paradigm isn't. However I always have a constructor, a
destructor, and functions which operate on the object.

However I always expose the structure in the header. That is so that people
can bypass the encapsulation easily. For instance whilst debugging it is
often handy to be able to print out or set a field directly without touching
the file that contains the member functions.

>
but not for all to access. Encapsulation is independent of language
and paradigm (it's not an original OO feature).

But this is C++ (or something suspiciously like it). You could tidy up
the syntax a lot -- that is one thing that C++ will let you do.

You also seem to be comparing apples and oranges. It looks simpler to
write f(... x, y, ...) rather than f(... getx(cursor), gety(cursor),
....) but do you really have a pair of variables (global?) that track
the cursor position? (To keep on topic I have changed the code to be
a bit more C-like.)

The second is what object type code quickly ends up looking
like. There's nothing difficult going on. It purely the fact that the
symbols are nested too deeply.

Often, people don't use the full range of what is on offer in C. I
remember with joy when C acquired the ability to pass (and return)
structures. For small things like points and colours, this can help a
lot (with minimal cost):

But this is C++ (or something suspiciously like it). You could tidy up
the syntax a lot -- that is one thing that C++ will let you do.

You also seem to be comparing apples and oranges. It looks simpler to
write f(... x, y, ...) rather than f(... getx(cursor), gety(cursor),
...) but do you really have a pair of variables (global?) that track
the cursor position? (To keep on topic I have changed the code to be
a bit more C-like.)

>The second is what object type code quickly ends up lookinglike. There's nothing difficult going on. It purely the fact that thesymbols are nested too deeply.

Often, people don't use the full range of what is on offer in C. I
remember with joy when C acquired the ability to pass (and return)
structures. For small things like points and colours, this can help a
lot (with minimal cost):

Let's say we want to take an image. We then want to query it for the "light
spot", which is an application-specific algorithm for the point of greatest
luminance. Then when the cursor passes over that spot, we want to change it
to a cross.
Perfectly unexceptional requirement. The lightspot function has been
provided for us by some clever person. We are humble UI programmers.

Now.

Point2 WinSystem_getcursorpos()

great.

So
Point2 lightspot(Image *img)

OK.

In lightspot,h

#include <Winsystem.h>

Ah, not so good.

Let's make that

Point2 WinSystem_getcursorpos().

typedef struct
{
int x;
int y;
} Point 2D;

Point2D lightspot(Image *img).

Now lightspot() can be ported to a system not produced by the evil WinSystem
corp. But now we've got Point2 and Point2D swilling about our code, both
doing effectively the same thing, confusing everyone.
--
Free games and programming goodies.http://www.personal.leeds.ac.uk/~bgy1mm

But this is C++ (or something suspiciously like it). You could tidy upthe syntax a lot -- that is one thing that C++ will let you do.

You also seem to be comparing apples and oranges. It looks simpler towrite f(... x, y, ...) rather than f(... getx(cursor), gety(cursor),...) but do you really have a pair of variables (global?) that trackthe cursor position? (To keep on topic I have changed the code to bea bit more C-like.)

>>The second is what object type code quickly ends up lookinglike. There's nothing difficult going on. It purely the fact that thesymbols are nested too deeply.

Often, people don't use the full range of what is on offer in C. Iremember with joy when C acquired the ability to pass (and return)structures. For small things like points and colours, this can help alot (with minimal cost):

Let's say we want to take an image. We then want to query it for the
"light spot", which is an application-specific algorithm for the point
of greatest luminance. Then when the cursor passes over that spot, we
want to change it to a cross.
Perfectly unexceptional requirement. The lightspot function has been
provided for us by some clever person. We are humble UI programmers.

Now.

Point2 WinSystem_getcursorpos()

great.

So
Point2 lightspot(Image *img)

OK.

In lightspot,h

#include <Winsystem.h>

Ah, not so good.

Let's make that

Point2 WinSystem_getcursorpos().

typedef struct
{
int x;
int y;
} Point 2D;

Point2D lightspot(Image *img).

Now lightspot() can be ported to a system not produced by the evil
WinSystem corp. But now we've got Point2 and Point2D swilling about
our code, both doing effectively the same thing, confusing everyone

I've left everything unsnipped because I am not really sure where you
are going with this and I fear I'll delete come critical part. There
are some typos in the code and will have to paraphrase to see if I've
got you point.

Are you saying that in some cases when porting code one often has to
duplicate structures rather that simply mimicking them? If so, I am
not entirely sure why (my reading assumes that "Point 2D" should have
been "Point2D" and that it is distinct from "Point2").

Even if this is not what you are saying, I think you point will relate
to those situations where someone else has designed part of the API.
I was talking about those situation (sadly rather rare in practice)
where one gets to choose.

Are you saying that in some cases when porting code one often has to
duplicate structures rather that simply mimicking them? If so, I am
not entirely sure why (my reading assumes that "Point 2D" should have
been "Point2D" and that it is distinct from "Point2").

Even if this is not what you are saying, I think you point will relate
to those situations where someone else has designed part of the API.
I was talking about those situation (sadly rather rare in practice)
where one gets to choose.

Bool break libraries.

Why? Because a graphics library has no business providing such a fundamental
type. IsMonochrome() is a function which naturally returns a boolean, but
unless the entire world agrees that GLBool_t is the name for it, it is just
a nuisance to everyone.

You can't do that so easily if your structure is a 3D point. In any sort of
geometrical application points are a fundamental data structure. I've
rewritten any number of 3d graphics routines, simply because there was no
standard point type, which means no standard simple functions like length(),
dotproduct() and crossproduct().
--
Free games and programming goodies.http://www.personal.leeds.ac.uk/~bgy1mm

>
Why? Because a graphics library has no business providing such a
fundamental type. IsMonochrome() is a function which naturally
returns a boolean, but unless the entire world agrees that
GLBool_t is the name for it, it is just a nuisance to everyone.

In C the general attitude is that zero represents false, and that
anything else represents true. If you want bit arrays you can then
complicate this.