S
Stephan Beal
Hi, all!
Before i ask my question, i want to clarify that my question is not
about the code i will show, but about what the C Standard says should
happen.
A week or so ago it occurred to me that one can implement a very basic
form of subclassing in C (the gurus certainly already know this, but
it was news to me). What i've done (shown below) seems to work all
fine and well, and does exactly what i'd expect, but i'm asking about
it because when i switched to a higher optimization level on gcc i
started getting warnings about type-punned pointers violating "strict
mode." That got me wondering, does it mean "strict C mode" or "strict
GCC mode"? i don't care much about the latter, as long as i comply
with the former. To be clear (again), my question is not GCC-specific.
My question is whether or not the approach i've taken here is legal
according to The Standard. i only ask because GCC suggests (at some
optimization levels, anyway) that i might be violating some C rule
without knowing i'm doing so.
The code (sorry for the length - it's about as short as i can make
this example in C while still keeping it readable):
// ------------------- begin code
#include <stdio.h>
#include <stdlib.h>
struct base_type; // unfortunate fwd decl
// Public API for base_type objects:
struct base_public_api
{
void (*func1)( struct base_type const * self );
long (*func2)( struct base_type const * self, int );
};
typedef struct base_public_api base_public_api;
// Base-most type of the abstract interface
struct base_type
{
base_public_api api;
};
typedef struct base_type base_type;
// Implementation of base_type abstract interface
struct sub_type
{
base_public_api api;
int member1;
};
typedef struct sub_type sub_type;
#define MARKER if(1) printf("MARKER: %s:%d:%s():
\n",__FILE__,__LINE__,__func__); if(1) printf
#define SUBP ((sub_type const *)self)
void impl_f1( base_type const * self )
{
MARKER("SUBP->member1=%d\n",SUBP->member1);
}
long impl_f2( base_type const * self, int x )
{
return SUBP->member1 * x;
}
// Now here's the part which is dubious: note the concrete types here:
static const sub_type sub_type_inst = { {impl_f1,impl_f2}, 42 };
static base_type const * sub_inst = (base_type const*) &sub_type_inst;
// ^^^^ "warning: dereferencing type-punned pointer will break strict-
aliasing rules"
int main( int argc, char const ** argv )
{
sub_inst->api.func1(sub_inst);
MARKER("func2()==%ld\n", sub_inst->api.func2(sub_inst, 2) );
return 0;
}
// ------------------- end code
On my box that looks like:
stephan@jareth:~/tmp$ ls -la inher.c
-rw-r--r-- 1 stephan stephan 1184 2008-11-05 14:43 inher.c
stephan@jareth:~/tmp$ make inher
cc inher.c -o inher
stephan@jareth:~/tmp$ ./inher
MARKER: inher.c:34:impl_f1():
SUBP->member1=42
MARKER: inher.c:48:main():
func2()==84
Am i headed down a Dark Path with this approach? Or is there a better/
more acceptable approach to simulating single inheritance in C? (i'm
not abject to changing the model, but i really do need some form of
separate interface/implementation for what i'm doing.)
Many thanks in advance for your insights.
PS (not relevant to the question, really): what's the point of all
that? i'm working on a library where i really need abstract base
interfaces (with only one level of inheritance necessary), and this
approach seems to be fairly clear (though a tad bit verbose at times).
i've used it to implement subclasses of an abstract stream interface,
for example, so my library can treat FILE handles and in-memory
buffers (or client-supplied stream types, with an appropriate wrapper)
with the same read/write API.
PS2: my appologies for the dupe post on comp.lang.c.moderated - i
inadvertently posted to that group.
Before i ask my question, i want to clarify that my question is not
about the code i will show, but about what the C Standard says should
happen.
A week or so ago it occurred to me that one can implement a very basic
form of subclassing in C (the gurus certainly already know this, but
it was news to me). What i've done (shown below) seems to work all
fine and well, and does exactly what i'd expect, but i'm asking about
it because when i switched to a higher optimization level on gcc i
started getting warnings about type-punned pointers violating "strict
mode." That got me wondering, does it mean "strict C mode" or "strict
GCC mode"? i don't care much about the latter, as long as i comply
with the former. To be clear (again), my question is not GCC-specific.
My question is whether or not the approach i've taken here is legal
according to The Standard. i only ask because GCC suggests (at some
optimization levels, anyway) that i might be violating some C rule
without knowing i'm doing so.
The code (sorry for the length - it's about as short as i can make
this example in C while still keeping it readable):
// ------------------- begin code
#include <stdio.h>
#include <stdlib.h>
struct base_type; // unfortunate fwd decl
// Public API for base_type objects:
struct base_public_api
{
void (*func1)( struct base_type const * self );
long (*func2)( struct base_type const * self, int );
};
typedef struct base_public_api base_public_api;
// Base-most type of the abstract interface
struct base_type
{
base_public_api api;
};
typedef struct base_type base_type;
// Implementation of base_type abstract interface
struct sub_type
{
base_public_api api;
int member1;
};
typedef struct sub_type sub_type;
#define MARKER if(1) printf("MARKER: %s:%d:%s():
\n",__FILE__,__LINE__,__func__); if(1) printf
#define SUBP ((sub_type const *)self)
void impl_f1( base_type const * self )
{
MARKER("SUBP->member1=%d\n",SUBP->member1);
}
long impl_f2( base_type const * self, int x )
{
return SUBP->member1 * x;
}
// Now here's the part which is dubious: note the concrete types here:
static const sub_type sub_type_inst = { {impl_f1,impl_f2}, 42 };
static base_type const * sub_inst = (base_type const*) &sub_type_inst;
// ^^^^ "warning: dereferencing type-punned pointer will break strict-
aliasing rules"
int main( int argc, char const ** argv )
{
sub_inst->api.func1(sub_inst);
MARKER("func2()==%ld\n", sub_inst->api.func2(sub_inst, 2) );
return 0;
}
// ------------------- end code
On my box that looks like:
stephan@jareth:~/tmp$ ls -la inher.c
-rw-r--r-- 1 stephan stephan 1184 2008-11-05 14:43 inher.c
stephan@jareth:~/tmp$ make inher
cc inher.c -o inher
stephan@jareth:~/tmp$ ./inher
MARKER: inher.c:34:impl_f1():
SUBP->member1=42
MARKER: inher.c:48:main():
func2()==84
Am i headed down a Dark Path with this approach? Or is there a better/
more acceptable approach to simulating single inheritance in C? (i'm
not abject to changing the model, but i really do need some form of
separate interface/implementation for what i'm doing.)
Many thanks in advance for your insights.
PS (not relevant to the question, really): what's the point of all
that? i'm working on a library where i really need abstract base
interfaces (with only one level of inheritance necessary), and this
approach seems to be fairly clear (though a tad bit verbose at times).
i've used it to implement subclasses of an abstract stream interface,
for example, so my library can treat FILE handles and in-memory
buffers (or client-supplied stream types, with an appropriate wrapper)
with the same read/write API.
PS2: my appologies for the dupe post on comp.lang.c.moderated - i
inadvertently posted to that group.