Yevgen Muntyan wrote On 02/09/07 09:52,:
Consider the following macro:
#define ALLOCIT(Type) ((Type*) malloc (sizeof (Type)))
The intent is to wrap raw memory allocation of N bytes
into a macro which returns allocated chunk of memory
to be used as a Type structure.
The background: I was beaten many times (it surely
is not my exclusive experience) by functions which
return and accept void*. There are such functions,
and there are cases when you can't do much about it
(third-party libraries, containers, etc.). I make
a conclusion: void* is evil and must be avoided when
possible and easy. Therefore I believe it's better
to use above macro than raw malloc. And I think this
is a valid belief, not like believing in Santa or alive
Elvis.
Compile-time type safety is lost when you use void*,
but there is a corresponding gain in flexibility. Keep
in mind that C is a statically-typed language: the type
of every expression in a program must be known at compile
time and cannot be changed afterwards. The void* type is
a way to evade this limitation when you need to.
Without void* (or some other means of "turning off
type checking"), it is hard to imagine how qsort() and
fread() and memcpy() and the whole host of other "generic"
functions could be written.
In toy programs like posted here, when you do
int *func (void)
{
int *foo = malloc (10 * sizeof *foo);
...
}
it certainly doesn't matter how you use malloc since
it's small and easy. But in big programs, when you have
lot of other things to worry about, it's nice to know
that you reduced number of function calls which return
void*. At least it's nice to know for me.
First, I disagree that the form shown above is only
good for toy programs. I have found it useful in all
kinds of programs, from throwaways and toys up through
long-lived major projects.
In "big programs" it is often desirable to invent
more machinery for memory management than raw malloc()
and friends provide. Such projects often have coding
standards that discourage using malloc() directly; one
instead calls intermediate wrappers that do additional
work like initializing the allocated objects, keeping
usage statistics, managing LRU caches, lumping "related"
objects near to each other, and so on. Often, the extra
work is highly project-specific, which is one reason that
every big project has its own memory management framework
(or set of frameworks).
But ALLOCIT and similar macros are not powerful
enough for the job! The only additional work they do
is to pre-convert the pointer to a designated type; this
is far short of what a "big program" usually needs. An
alternative along the lines of
#define ALLOCIT(type) alloc##type ()
is perhaps defensible, but why is ALLOCIT(Thing) better
than allocThing()?
To answer my own semi-rhetorical question, one way
in which it's better is that the definition might actually
look more like
#ifdef DEBUG
#define ALLOCIT(type) alloc##type(__FILE__,__LINE__)
#else
#define ALLOCIT(type) alloc##type()
#endif
.... with corresponding changes in the functions themselves,
of course. But this sort of thing can be done in other ways,
for example by using macros to intercept the function calls:
#idfef DEBUG
Thing *allocThing(const char*, int);
#define allocThing() allocThing(__FILE__,__LINE__)
#else
Thing *allocThing(void);
#endif
.... so the ALLOCIT intervention isn't an enabler or disabler
for debugging and suchlike.
Type mismatch
here is quite a usual thing (unfortunately), since
I deal a lot with "classes", with nested structures
like
struct Parent {...};
struct Child {struct Parent parent; ...};
It's extremely easy to mess it up, and avoiding using
extra void* is actually a good thing. Note that this
Child-Parent thing has nothing to do with malloc. But
it does have a lot to do with using wrong types. And
I don't try to think like "this is malloc, has nothing
to do with that, void* is fine here". I just avoid using
void*.
I don't understand how this example bears on the debate,
because there are no void* or pointers of any kind at all
in evidence. How does ALLOCIT apply to this example?
Now not all people think that ALLOCIT() buys any type safety.
Some other people do think it does.
That it buys a small amount of type safety is, I think,
beyond dispute. Whether it buys enough is the question.
Anyway, it's hard to argue with more than one person
at once, so I decided to post what I think about this
particular "yes I want cast *here*" thing. You may
say I want C++, you may say "Wrong.", you may prove
using ten steps that it's wrong, whatever. I at least
know that I am not the only person who believes in
"cast is good in this single thing" so I am not a complete
idiot. (Of course you can say that people who believe this
are all idiots or wrong, but working software proves they
aren't).
As someone else remarked, you show no lack of courage.
--
Er*********@sun.com