Ir al contenido principal

Ralsina.Me — El sitio web de Roberto Alsina

Now I remember why I don't like C++

I have refac­tored IUP/Qt in­to a love­ly ob­ject hi­er­ar­chy. And I now re­mem­ber why I dis­like C++.

Here's the ba­sic prob­lem (code may be wrong, I don´t have the bro­ken ver­sions any­more):

I have a base class IOb­jec­t, that has stub set­ter­s/get­ters for all the IUP at­tributes.

For each kind of wid­get, I cre­ate a class that in­her­its from IOb­ject and the match­ing Qt wid­get (ex­am­ple: QDi­alog).

These have to be hooked in­to an Ihan­dle struc­ture through the use of a void * (re­mem­ber, that's C, I have that too ;-)

Now, this was my first naive ap­proach:

Ihandle *n;
IQDialog *d=new IQDialog();
n->handle=d; //handle is a void*

That com­piles ok. How­ev­er, when you try to call a d mem­ber lat­er (and I know I am us­ing old C cast­s... they had worked for me un­til to­day!) ...

((IObject *)(n->handle))set_title("Title here");

Blam­mo, seg­fault. Why? Be­cause in one of the as­sign­ments to/from void *, the point­er for some rea­son starts to point else­where.

Here´s what Glip­tic at #C++ sug­gest­ed, and it does work:

Ihandle *n;
IQDialog *d=new IQDialog();
n->handle=static_cast <void *>(static_cast < IObject *> (d));

Right, I have to cast it twice.

And here is how you get it back:

(static_cast < IObject * >(n->handle))->set_title ("Some title");

Is­n't this just ridicu­lous­ly weird for any­one com­ing from any oth­er lan­guage?

Or am I miss­ing some­thing com­plete­ly?

But the good news is, it works ok, and the Qt back­end now has a de­cent struc­ture to hack on.

Ponto / 2006-04-22 21:17:

This is not a fault of C++. The problem arises because you use the C idiom to store untyped objects in void * variables.

In any sane language and also in C++ your n->handle would be of type or interface IObject * and then you can write:

n->handle = d;
n->handle->set_tilte("Some title");

AC / 2006-04-22 21:20:

eh.. perhaps you should initialize n to point somewhere? (or did you just drop that line of code from your example?)

Roberto Alsina / 2006-04-22 21:50:

Ponto: I have to do that because that piece of the code **is** C.

The goal is interfacing with that C library. So, if I start converting it to C++, it kinda defeats the point, doesn't it? ;-)

From every other language, when you convert to a void* and back, you get the same thing. And I had never seen a need to cast twice before :-P

AC: dropped of the example.

bced / 2006-04-22 22:23:

if your IQDialog inherit FIRST from QDialog and then from IObject, when you do :
n->handle=d
you put a pointer to a QDialog object in d !

and then, when you get it, you cast a pointer to a QDialog in a IObject, and than, you method call fail with a segfault.

for that raison, if you cast it to a IObject when you put it in n->handle, the QDialog part of the object is skipped and then you get a "true" IObject.

I think the solution in your case is to change the inheritance order in your objects. First inherit from IObject and then from the QT widget.

Remember, multiple inheritance can create more problem that it solve !

Roberto Alsina / 2006-04-22 22:27:

bced: that would only cause trouble in other places where I need to access the "qdialogness" of a iqdialog :-)

Anyway, I am happy I got through this thanks to the great help of #C++ but it got me really frustrated for a while.

I mean, I never expected casting to change where the pointer points to. That's of course due to my ignorance of C++.

panos / 2006-04-23 15:54:

It's weird, what you describe. If you cast a SomeObject* to AnyPtr*, nothing is ever lost. Casting to SomeObject* back brings the whole thing. C included.
Except if you haven't initialized n :S ..
Furthermore, trying to dynamic_cast(anyptr), will (at some expense) verify that the pointer really holds the right thing.

Anonymous / 2006-04-23 18:56:

If you have an object with multiple interfaces, when you cast to same of the interfaces the compiler must adjust the offset of the pointer.

class A: public B, C
{
...
};

A a
B* pb = &a;
C* pc = &a;

Then may result that pb is not the same pointer as pc. The offset depend if B hast data members, in first place came B data members then C data members.

Now

pc = static_cast(pb);

This can fail because the compiler can't deduce the right offset for C* because B y C are not related (only trough A).

To correct this you need the double cast.

pc=static_cast(static_cast(pb));

Roberto Alsina / 2006-04-24 01:45:

Anonymous: that sounds like a reasonable explanation for the phenomenon, but doesn't it mean that we are working around an implementation issue?

After all, there is no reason why the casting should be done by changing the pointer to another location, or that the multiple inheritance should be implemented by having one interface for each ancestor.

An object could have a single interface with the combined interfaces of all ancestors.

In such an implementation, the naive approach would work, and it would all be much simpler.

Unless of course the standard dictates how an object must be laid out in memory, in which case I say the standard is way too detailed ;-)

Nicolai Hähnle / 2006-04-25 14:14:

It's not that simple. Consider again the previous poster's example.

Suppose you're interfacing with a library that has been compiled already, and that library *only knows about class C*. When you pass a C* to that library, the pointer *must* point to the beginning of the C part of your A object.

Now, if the C part of your A object happens to be the first part of the A object, then everything will work fine even with C style casts. This is why you'll never have problems without multiple inheritance.

But with multiple inheritance, it becomes obvious that both ancestors cannot be the first part of an A object at the same time - one of the ancestors needs to be moved into the interior of the A object. And in that case, the pointer must change, there's simply no way around it.

So there really is no way around the explicit C++ style casts when converting to/from void*.

Roberto Alsina / 2006-04-25 15:26:

Well, there would be if C++ didn't rely on a simple vtable to find the methods. Ok, that would hurt performance, I supppose.

phone number lookup / 2011-12-03 22:42:

this is really interesting viewpoint on the subject i might add

employment background check / 2011-12-27 23:32:


Well, the write-up is truly the freshest on this laudable topic. 


Contents © 2000-2020 Roberto Alsina