Re: C++ more efficient than C?
Juha Nieminen said:
Richard Heathfield wrote:
lbonafide@yahoo.com said:
<snip>
And in C, there's absolutely no way to know who's fiddling around with
your struct members because you can't restrict access to them.
Wrong. Maybe *you* can't restrict access to them, but I can, and so can
quite a few others in comp.lang.c. The technique is not difficult.
I have always detested this kind of argument.
Let's see whether I can amend it to your liking.
"Maybe you can't, but we
who are better than you can",
Not what I meant. Mr lbonafide said that there's absolutely no way, so I
presume he accepts that he can't do it, and that his inability to do it is
not in dispute. That doesn't mean I'm "better" than him. It does, however,
appear to mean that I know something he doesn't - which is easily fixed,
of course.
"it's quite easy",
Aye, it is. Sorry.
and then don't even
show this "easy" solution at all
See below.
(which usually involves ugly hacks and
jumping through completely unnecessary hoops).
Um, it's not /that/ ugly a hack, and I assure you I'll only jump through
*necessary* hoops.
The technique involves a minimum of three files - the interface (.h), the
implementation (.c, which you *don't release*), and the driver, which the
user-programmer writes.
Here is our interface (which we ship):
#ifndef H_POINT
#define H_POINT 1
#include <limits.h>
#define POINT_INVALID INT_MIN
typedef struct point_ point;
point *point_make(int, int);
void point_destroy(point **);
int point_setx(point *, int);
int point_sety(point *, int);
int point_set(point *, int, int);
double point_diff(const point *, const point *);
#endif
The important point (if you'll forgive the word) is that the point type is
declared but *not* defined, so its members are not visible. More about
that shortly.
The totally unimportant point is that this is just an example, and so you
shouldn't expect to see a complete function suite here!
Implementation file (NOT shipped):
#include "point.h"
#include <stdlib.h>
#include <math.h>
struct point_
{
int x;
int y;
};
point *point_make(int x, int y)
{
point *new = malloc(sizeof *new);
if(new != NULL)
{
new->x = x;
new->y = y;
}
return new;
}
void point_destroy(point **old)
{
if(old != NULL)
{
free(*old);
*old = NULL;
}
}
int point_setx(point *p, int x)
{
int old = POINT_INVALID;
if(p != NULL)
{
old = p->x;
p->x = x;
}
return old;
}
int point_sety(point *p, int y)
{
int old = POINT_INVALID;
if(p != NULL)
{
old = p->y;
p->y = y;
}
return old;
}
int point_set(point *p, int x, int y)
{
int rc = POINT_INVALID;
if(p != NULL)
{
rc = 0;
p->x = x;
p->y = y;
}
return rc;
}
double point_diff(const point *pa, const point *pb)
{
double d = 0.0;
if(pa != NULL && pb != NULL)
{
double xd = pa->x - pb->x;
double yd = pa->y - pb->y;
d = sqrt(xd * xd + yd * yd);
}
return d;
}
We don't ship this to the user. Instead, we compile it, put the resulting
object code into a library, and ship the library.
Test driver:
#include "point.h"
#include <stdio.h>
int main(void)
{
point *p1 = point_make(1, 1);
if(p1 != NULL)
{
point *p2 = point_make(4, 5);
if(p2 != NULL)
{
double d = point_diff(p1, p2);
printf("The hypotenuse is %f\n", d);
point_destroy(&p2);
}
point_destroy(&p1);
}
return 0;
}
This works. (Try it.)
The following test driver does *not* work:
#include "point.h"
#include <stdio.h>
int main(void)
{
point *p1 = point_make(1, 1);
size_t size = sizeof *p1; /* can't do this */
point instance; /* can't do this either */
instance.x = 42; /* or this! */
if(p1 != NULL)
{
point *p2 = point_make(4, 5);
if(p2 != NULL)
{
double d = point_diff(p1, p2);
printf("The hypotenuse is %f\n", d);
point_destroy(&p2);
}
point_destroy(&p1);
}
return 0;
}
When I compile this, I get the following errors:
driver.c: In function `main':
driver.c:8: dereferencing pointer to incomplete type
driver.c:9: storage size of `instance' isn't known
Thus, you *can* restrict access to members of a struct, QED.
Reminds me of the C-hackers who insist that it's "easy" and "feasible"
to use dynamically bound "member functions" in structs, as if that
somehow made C as good as C++ (yet the big problem with that hack is
that each "member function" of the struct increases the size of the
struct, which in many cases is a completely useless waste and makes that
struct larger and more inefficient).
<shrug> The "as if that somehow made C as good as C++" bit is mere flame
bait. C and C++ are different languages with different goals and different
user bases. To claim that one is "better" than another, without specifying
in what way it is better and for whom, is to fail to understand that there
are good reasons for having more than one programming language.
Whilst it /is/ not only feasible but even easy to use dynamically bound
member functions in structs, it is certainly true that this incurs an
overhead for the pointer in every instance of the struct, and this cost
should be considered when deciding whether or not to pursue this course.
The way I resolve this myself is simple - either I'm going to be using a
lot of structs, in which case they're probably going to be in some kind of
container, so I'll put the pointers in the container instead - which is
normally okay because all the structs in any given container are generally
going to want to be processed in the same way), or I only have a few, in
which case the overhead is no big deal.
--
Richard Heathfield <http://www.cpax.org.uk>
Email: -http://www. +rjh@
Google users: <http://www.cpax.org.uk/prg/writings/googly.php>
"Usenet is a strange place" - dmr 29 July 1999