Re: incorrect prototypes??

Guido.van.Rossum@cwi.nl
Fri, 18 Jun 1993 16:58:14 +0200

[Lance sent me a private reply, probably by mistake (since I first
sent him a private reply by mistake). I quote liberally from his reply.]

> I do not use 'object *' at all in my code. If I did, all would
> work fine, but then the compiler would not warn me that I am
> passing things where they should not be. Currently I am
> having to cast everything because they are NOT prototyped using
> 'listobject *'. :( Looks like different programmig styles have
> clashed. Oh well..

I don't see how you can live without "object*" in general. (Of course
I don't know how much code you have and how general it is.) Take for
instance a C function called from Python that takes a list of integers
as argument, and has to access the element. Python's type system
makes it impossible to enforce that the list items are indeed
integers, so the function has to check this. The code would look more
or less like this:

object *
sum_list(dummy, arg)
object *dummy, *arg;
{
int i, n;
long sum;
if (arg == NULL || !is_listobject(args)) {
err_set(TypeError);
return NULL;
}
n = getlistsize(arg);
sum = 0;
for (i = 0; i < n; i++) {
object *item = getlistitem(arg, i);
if (!is_intobject(item)) {
err_set(TypeError);
return NULL;
}
sum = sum + getintitem(item);
}
return newintobject(sum);
}

(Suppose this can be called directly as a function in a built-in
module.)

Now we don't *know* that arg is a list object until we've tested it,
so it would be lying to declare it as such. The same holds for item.
Also, note that the function has to be declared object* even though
we know it will always return an intobject* (or NULL), since it has to
conform to the general prototype for built-in functions. This means
that if newintobject() were declared to return intobject* instead of
object*, we would need another cast for the return value.

If Python were implemented in C++, things would be different: explicit
casts from specialized object types to generic object* would be
unnecessary, and casts from object* to any particular object type
could be handled differently (there's quite a nice C++ idiom for this
that combines the cast and the check for object type). But we're
using plain C, and I maintain that having most functions take and
return object* and do some checking is the best compromise. (It's
different in parts of the interpreter where objects' members are
accessed frequently; there the correct type must be used; but this is
not for general consumption...)

> All functions should test for the correct type being passed in.
> This is a given. I would feel the code was VERY broken if it did NOT
> check for this.

No. Functions shouldn't have to check when the type has already been
checked by the compiler. The point is if every routine checked its
arguments, there would be a lot of redundant checking going on (since
often there are several levels of function calls passing each other
objects that have already been verified). The function that casts an
object* to listobject* is responsible for only applying the cast if it
is valid.

> I think this is wrong. newlistobject() should return a 'listobject *'
> instead of a 'object *'. It may be consistent, but I think it is wrong.
> If I am the only one in the world that does feel this way, I will
> shut my mouth and keep quiet.

Actually, I enjoy discussions like this, but so far few people have
joined in, so I am here on my own defending some questional
practice...

(Maybe they are still waiting for my revised name changing proposal?
Hold your breath, I'm working on it...)

--Guido van Rossum, CWI, Amsterdam <Guido.van.Rossum@cwi.nl>