473,753 Members | 7,825 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

Pointer to "base" type - what does the Standard say about this?

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_f 2}, 42 };
static base_type const * sub_inst = (base_type const*) &sub_type_in st;
// ^^^^ "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.mod erated - i
inadvertently posted to that group.
Nov 5 '08 #1
18 2454
On 5 Nov 2008 at 15:34, Stephan Beal wrote:
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.)
Having subtype as "base class struct + some extra fields" and casting
back to the base when necessary is completely ubiquitous in networking
code based on Berkeley sockets. So if your code is meant to run on
Windows or a *nix that does networking, then this approach will
certainly work.

If your interested in head-on-a-pin discussions about whether it will
work on embedded C for a coffee machine with half the standard library
missing, the "regulars" will no doubt be along soon with their usual
grandstanding answers.

Nov 5 '08 #2
On Nov 5, 5:25 pm, Antoninus Twink <nos...@nospam. invalidwrote:
Having subtype as "base class struct + some extra fields" and casting
back to the base when necessary is completely ubiquitous in networking
code based on Berkeley sockets.
That's good to hear, thanks :).
So if your code is meant to run on
Windows or a *nix that does networking, then this approach will
certainly work.
The code is intended to be platform neutral (but "C" below...), and it
now sounds like the approach i chose is also platform neutral. i was
worried there for a while.

As a baseline i'm trying to make sure it also compiles (and behaves
properly) with tcc (TinyC Compiler).
If your interested in head-on-a-pin discussions about whether it will
work on embedded C for a coffee machine with half the standard library
missing, the "regulars" will no doubt be along soon with their usual
grandstanding answers.
i'm only interested in conforming to The Standard. My programming
won't allow me to sleep at night if i knowingly make use of a compiler-
specific extension (with the exception of a couple very common
extensions, like free placement of var decls in functions, instead of
all at the front).

Thanks for the answer - it sounds like i'm not treading any
particularly dangerous ground here (assuming the API is used properly,
of course).
Nov 5 '08 #3
On Nov 5, 5:44 pm, Stephan Beal <sgb...@googlem ail.comwrote:
The code is intended to be platform neutral (but "C" below...), and it
now sounds like the approach i chose is also platform neutral. i was
worried there for a while.
Doh, i spoke to soon:

http://www.cellperformance.com/mike_..._aliasing.html

Says:

"In C99, it is illegal to create an alias of a different type than the
original. This is often refered to as the strict aliasing rule."

Now i'm at an impasse - a few parts of my code rely on C99 features,
all but 1 of them (vsscanf()) i could probably easily do without. But
if i require C99 mode then i'm knowingly using undefined behaviour.
That's a tough call, considering that i don't think i can reasonably
reimplement around this limitation. Damn.
Nov 5 '08 #4
Antoninus Twink <no****@nospam. invalidwrites:
On 5 Nov 2008 at 15:34, Stephan Beal wrote:
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.)

Having subtype as "base class struct + some extra fields" and casting
back to the base when necessary is completely ubiquitous in networking
code based on Berkeley sockets. So if your code is meant to run on
Windows or a *nix that does networking, then this approach will
certainly work.

If your interested in head-on-a-pin discussions about whether it will
work on embedded C for a coffee machine with half the standard library
missing, the "regulars" will no doubt be along soon with their usual
grandstanding answers.
The problem isn't with a coffee machine -- the compiler for which will
probably have a simple optimizer -- but with a complex optimizer which uses
the aliasing rules to drives the optimisation. And so code will
works... until the optimizer see an opportunity to use the fact that two
things shouldn't alias.

In the OP case, there is hope. Replacing

static base_type const * sub_inst = (base_type const*) &sub_type_in st;

by

static base_type const * sub_inst = &sub_type_inst. api;

will remove complains about this line. Now the only problematic part is

((sub_type const *)self)

but this isn't problematic AFAIK (6.7.2.1/14 in C99 and I seem to remember
that C90 has the same language: A pointer to a structure object, suitably
converted, points to its initial member (or if that member is a bit-field,
then to the unit in which it resides), and vice versa.) BTW, that reference
shows that there isn't a need to complains about the first line.

Yours,

--
Jean-Marc
Nov 5 '08 #5
Stephan Beal wrote:
....
// ------------------- 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 ;
You can combine those two statements:

typedef struct base_public_api
{
// details
} 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;
....
// Now here's the part which is dubious: note the concrete types here:
static const sub_type sub_type_inst = { {impl_f1,impl_f 2}, 42 };
static base_type const * sub_inst = (base_type const*) &sub_type_in st;
// ^^^^ "warning: dereferencing type-punned pointer will break strict-
aliasing rules"
There's no problem there. Section 6.7.2.1p13 says "A pointer to a
structure object, suitably converted, points to its initial member (or
if that member is a bit-field, then to the unit in which it resides),
and vice versa."

Strictly speaking, you have to apply that rule twice, and that implies
that the correct conversion has to be:

static_base_typ e const* sub_inst = (base type const*)
(base_public_ap i*)&sub_type_in st;

If we are given the fact that, for a specific set of types A, B, and
C, where p is object of type C*, the conversion (A*)(B*)p is legal,
the standard says nothing that allows us to conclude that (A*)(B*)p ==
(A*)p. It doesn't even guarantee that (A*)p is also legal. However,
in practice, I would expect this to work, and I believe that the
intent was that it should work.

Nov 5 '08 #6
On Nov 5, 6:36 pm, Jean-Marc Bourguet <j...@bourguet. orgwrote:
The problem isn't with a coffee machine -- the compiler for which will
probably have a simple optimizer -- but with a complex optimizer which uses
the aliasing rules to drives the optimisation. And so code will
works... until the optimizer see an opportunity to use the fact that two
things shouldn't alias.
Optimization is certainly a potential problem. i can conceive that for
some reason a compiler might optimize or pad these differently:

struct sub1
{
base_api api;
int m1;
double m2;
};

struct sub1
{
base_api api;
double m1;
char const * m2;
};

but my knowledge of explicit optimizations done by any given compiler
is pretty minimal. i'm much better versed in C++ than C.
In the OP case, there is hope. Replacing

static base_type const * sub_inst = (base_type const*) &sub_type_in st;

by

static base_type const * sub_inst = &sub_type_inst. api;
That was my original thought, but the point of passing (base_type
[const]*) as the "self" argument of base_type_api was to give me a
level of indirection which i want (for storage of subtype-specific
data without requiring subclasses to literally redeclare the whole
public API), and i lose that (and features based off of it) if i pass
a (base_type_api* ) and cast it to a (sub_type*) (which in my eyes is
just plain wrong, even if it might work in this case).
will remove complains about this line. Now the only problematic part is

((sub_type const *)self)

but this isn't problematic AFAIK (6.7.2.1/14 in C99 and I seem to remember
that C90 has the same language: A pointer to a structure object, suitably
converted, points to its initial member (or if that member is a bit-field,
then to the unit in which it resides), and vice versa.)
Coincidentally, i spent the last half hour reading up on that topic.
The reading here was enlighting:

http://www.cellperformance.com/mike_..._aliasing.html

After reading that, i'm very confused about whether using the
"restrict" keyword might in some way help me here.

My current conclusion is:

a) it's technically illegal.
b) it's largely assumed to be safe on "most" platforms.
c) my subclasses are all private implementations (file-level scope)
and are never referenced using their subtype outside of a single file,
so the compiler might have a chance of knowing what i'm hoping for.
BTW, that reference
shows that there isn't a need to complains about the first line.
gcc doesn't complain until i turn on -O2 or higher or turn on the -
fstrict-aliasing flag. tcc doesn't complain at all.

Thanks for your input :).
Nov 5 '08 #7
On Nov 5, 6:47 pm, jameskuyper <jameskuy...@ve rizon.netwrote:
You can combine those two statements:

typedef struct base_public_api
{
// details

} base_public_api ;
Thanks for that. i'm far more used to C++ than C (been away from C
since 1995 or so), so i'm not all caught up yet.
There's no problem there. Section 6.7.2.1p13 says "A pointer to a
structure object, suitably converted, points to its initial member (or
if that member is a bit-field, then to the unit in which it resides),
and vice versa."
AND vice versa? That changes things. So that means that i could cast
the (base_type_api* ) itself back to the original subclass which
contains it (provided i use it as we've shown here)?

So does that imply that the following is actually legal? (warning -
uncompiled)

typedef struct API
{
int (*func1)( struct API * );
// ... more API ...
} API;

typedef struct Foo
{
API api;
int val;
} Foo;

int func1_Foo( API *x )
{
return (Foo*)x;
}

....

Foo foo;
foo.api.func1 = func1_Foo;
printf("%d\n",f oo.api.func1( &foo ));
If that's legal then i'm happy (though a bit perplexed as to why it
would be allowed).
If we are given the fact that, for a specific set of types A, B, and
C, where p is object of type C*, the conversion (A*)(B*)p is legal,
the standard says nothing that allows us to conclude that (A*)(B*)p ==
(A*)p. It doesn't even guarantee that (A*)p is also legal.
Those last two parts: fair enough, and i think i understand why that
must be so, but it is philosophically unsettling nonetheless.
However,
in practice, I would expect this to work, and I believe that the
intent was that it should work.
MY intent is for it to work, certainly ;). i just hope i'm not
violating anyone's bits by doing this.

Thanks for your insightful reply :).
Nov 5 '08 #8
jameskuyper wrote:
Stephan Beal wrote:
...
// ------------------- 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;

...
// Now here's the part which is dubious: note the concrete types here:
static const sub_type sub_type_inst = { {impl_f1,impl_f 2}, 42 };
static base_type const * sub_inst = (base_type const*) &sub_type_in st;
// ^^^^ "warning: dereferencing type-punned pointer will break strict-
aliasing rules"

There's no problem there.
[Nonsense]

Sorry about that. I got mixed up because I misread your code. I was
somehow got confused while reading your code, and was under the
impression that sub_type_inst had a member of type base_public_api
(which was correct), and that base_public_api had a member of type
base_type (which is exactly backwards). You can only perform such a
conversion by defining a union of both types, and only when the
original pointer points to one of the members of such a union.
Nov 5 '08 #9
On Nov 5, 7:23 pm, jameskuyper <jameskuy...@ve rizon.netwrote:
Sorry about that. I got mixed up because I misread your code. I was
somehow got confused while reading your code, and was under the
impression that sub_type_inst had a member of type base_public_api
(which was correct), and that base_public_api had a member of type
base_type (which is exactly backwards).
It was a good try, though :).
You can only perform such a
conversion by defining a union of both types, and only when the
original pointer points to one of the members of such a union.
i'm considering the union option. However, that would require that i
use all the concrete subtypes in the public union. Right now my
subtypes are all static/file-scope implementations , so that wouldn't
be possible without revealing those types, some of which require
optional add-on functionality like sqlite3 (which the subclasses hide
in the current API).

In the off chance that you'd like to see the actual code, visit here:

http://fossil.wanderinghorse.net/rep...1n.net/c11n/io

give in the user name "anonymous" with password "anonymous" (that's an
anti-bot feature of the hosting SCM software), click Login, and you'll
see the files. The base types are declared in c11n_io.h (search for
"_api api;") and a couple concrete impls of the c11n_stream base are
in c11n_stream_*.c . (c11n_io_handle r_*.c are also relevant but
significantly more complex because they are grammar parser
implementations .)

Again, thanks for the insights!
Nov 5 '08 #10

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

11
2101
by: Joseph Turian | last post by:
Fellow hackers, I have a class BuildNode that inherits from class Node. Similarly, I have a class BuildTree that inherits from class Tree. Tree includes a member variable: vector<Node> nodes; // For clarity, let this be "orig_nodes" BuildTree includes a member variable:
4
1511
by: Alex Sedow | last post by:
Method invocation will consist of the following steps: 1. Member lookup (14.3) - evaluate method group set (Base.f() and Derived.f()) .Standart say: "The compile-time processing of a method invocation of the form M(A), where M is a method group and A is an optional argument-list, consists of the following steps:  The set of candidate methods for the method invocation is constructed. Starting with the set of methods associated with M,...
2
1717
by: Wade | last post by:
Hi all, We have created some "Base" class pages for our WebForms and UserControls. For instance, when we create a WebForm called "WebForm1.aspx", instead of inheriting from "System.Web.UI.Page" we implement from our "Base" class page which itself inherits from "System.Web.UI.Page" -- I know, pretty standard. We do the same with our UserControls, instead they inherit from "System.Web.UI.UserControl". Now, there are some methods that we...
7
2249
by: relient | last post by:
Question: Why can't you access a private inherited field from a base class in a derived class? I have a *theory* of how this works, of which, I'm not completely sure of but makes logical sense to me. So, I'm here for an answer (more of a confirmation), hopefully. First let me say that I know people keep saying; it doesn't work because the member "is a private". I believe there's more to it than just simply that... Theory: You inherit,...
2
1462
by: reckless2k | last post by:
Client side; knows nothing of Derived: class Base { ... virtual void do_something() } #include "Base.h" void main() {
3
1587
by: Rob | last post by:
I have these classes (elided methods): class Base { public: Base(string name) {...} }; class Derived : public Base {
10
3599
by: Dom Jackson | last post by:
I have a program which crashes when: 1 - I use static_cast to turn a base type pointer into a pointer to a derived type 2 - I use this new pointer to call a function in an object of the derived type 3 - this function then 'grows' the derived type object (by pushing onto a vector).
0
9072
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However, people are often confused as to whether an ONU can Work As a Router. In this blog post, we’ll explore What is ONU, What Is Router, ONU & Router’s main usage, and What is the difference between ONU and Router. Let’s take a closer look ! Part I. Meaning of...
0
9653
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers, it seems that the internal comparison operator "<=>" tries to promote arguments from unsigned to signed. This is as boiled down as I can make it. Here is my compilation command: g++-12 -std=c++20 -Wnarrowing bit_field.cpp Here is the code in...
1
9421
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows Update option using the Control Panel or Settings app; it automatically checks for updates and installs any it finds, whether you like it or not. For most users, this new feature is actually very convenient. If you want to control the update process,...
0
8328
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing, and deployment—without human intervention. Imagine an AI that can take a project description, break it down, write the code, debug it, and then launch it, all on its own.... Now, this would greatly impact the work of software developers. The idea...
0
6151
by: conductexam | last post by:
I have .net C# application in which I am extracting data from word file and save it in database particularly. To store word all data as it is I am converting the whole word file firstly in HTML and then checking html paragraph one by one. At the time of converting from word file to html my equations which are in the word document file was convert into image. Globals.ThisAddIn.Application.ActiveDocument.Select();...
0
4771
by: TSSRALBI | last post by:
Hello I'm a network technician in training and I need your help. I am currently learning how to create and manage the different types of VPNs and I have a question about LAN-to-LAN VPNs. The last exercise I practiced was to create a LAN-to-LAN VPN between two Pfsense firewalls, by using IPSEC protocols. I succeeded, with both firewalls in the same network. But I'm wondering if it's possible to do the same thing, with 2 Pfsense firewalls...
0
4942
by: adsilva | last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?
1
3395
by: 6302768590 | last post by:
Hai team i want code for transfer the data from one system to another through IP address by using C# our system has to for every 5mins then we have to update the data what the data is updated we have to send another system
3
2284
bsmnconsultancy
by: bsmnconsultancy | last post by:
In today's digital era, a well-designed website is crucial for businesses looking to succeed. Whether you're a small business owner or a large corporation in Toronto, having a strong online presence can significantly impact your brand's success. BSMN Consultancy, a leader in Website Development in Toronto offers valuable insights into creating effective websites that not only look great but also perform exceptionally well. In this comprehensive...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.