comp.lang.c
[Top] [All Lists]

Re: void * vs. T *

Subject: Re: void * vs. T *
From: Eric Sosman
Date: Fri, 28 Mar 2008 08:26:48 -0400
Newsgroups: comp.lang.c


Ark Khasin wrote:
Eric Sosman wrote:
Ark Khasin wrote:
Is there a way to assert at compile time that T* and void* have identical representations, so the build breaks if the assumption is invalid? IOW, is there a constant expression for that?
[...]
E.g.
(uintmax_t)(void*)123==(uintmax_t)(T*)123 && sizeof(void*)==sizeof(T*)
seem not to guarantee anything.

    Something like this couldn't be a constant expression;
constant expressions cannot use cast operators.

I mean a use of compile-time assert, like
extern int foo[(e)?1:-1];
e being the expression above. It's not for an #if.

    For a compile-time "assert" you still need a constant
expression.  In C90 you can't use a non-constant as an
array dimension at all.  You can in C99 (in some contexts),
but then you get a variable-length array and undefined
behavior -- at run time, not compile time.

Consider an example of, say, 100 disparate objects of 100 different types T1..T100 which you want to initialize from their own representation in external storage which maps to T1..T100, but there are additional things to be done on each initialization. You have 100 functions T set1(const T1*); .. T set100(const T100*); It is tempting to read each image in a well-aligned buffer, point to it with a void *pointer and pass it to each function in a loop over an array of all those functions.

    Getting the well-aligned buffer seems straightforward,
albeit tedious:

        union { T1 t1; T2 t2; ... T100 t100; } buffer;

Note, though, that if the sizes of all these Tx's vary, you'll
need a way to control how much you read each time around your
loop.  That is, you can't quite squeeze the loop down to a
purely generic form.

Alas, it's not portable. I am even thinking of having my way another way, by putting something like this in a header file:
Instead of
extern T set17(const T17 *);
Do this:
#define MYSETTER(name, type) \
extern T type_unsafe_##name(const void *);\
static inline T name(const type *arg)\
{\
    return type_unsafe_##name(arg);\
}

MYSETTER(set1, T1)
......

Implementation:
T type_unsafe_set1(const void *arg)
{
    const T1 *a = arg;
    ........
}

type_unsafe_... versions are then OK to stuff in my table; the rest of the code will use type-safe wrappers.
This looks portable but ugly and I am afraid error-prone.
Are there better ways?

    "Better" is one of those slippery words, but it seems to
me that you're expending much effort to avoid spending less ...

    One possibility would be to make all the setx functions
take a `const void*' instead of a `const Tx*':

        T set1(const void *pp) {
            const T1 *p = pp;
            ... use *p, p->frobozz, ...
        }

... which is what you're doing with the type_unsafe_ stuff.
But if you want to preserve the type-safe versions as well
and use the unsafe ones only in this decreasingly-generic
loop, perhaps you might consider dividing responsibilities
a little differently:

        T set1(const T1 *p) {
            ... use *p, p->frobozz, ...
        }

        T read1(... args for reading ...) {
            T1 buffer;
            ... read into "buffer" ...
            return set1(&buffer);
        }

        typedef T (*Reader)(... args for reading ...);
        static const Reader[] readers = {
            read1, read2, ..., read100 };
        for (i = 0;  i < 100;  ++i) {
            T result = reader[i](... args for reading ...);
            ...
        }

    It's not all that different from what you're considering,
but (1) the loop itself need no longer cope with size changes,
(2) it's easy to adopt a Tx-specific external format that's
not a byte-for-byte dump of an internal Tx, and (3) since the
readx functions are no longer equivalent-except-for-argument
to the setx functions, people won't be tempted to (ab)use them
elsewhere in the program.

--
Eric Sosman
esosman@xxxxxxxxxxxxxxxxxxxx

<Prev in Thread] Current Thread [Next in Thread>
Privacy Policy