MI and clone()

N

Noah Roberts

I'm having a very weird problem here that's apparently caused by MI
and/or virtual inheritance that I'd like help with. I finally managed
to get the problem to appear in small form. I'll leave the code at the
end so I can explain some things more deeply before hand.

The really interesting thing is that even before the call that causes
the crash, the debugger is having troubles. For example, if I place a
breakpoint in sub_base3::do_x() I am not able to examine *this as a
sub_base3. The debugger thinks it is a sub_base1 and will not allow me
to cast it down in the watch window (even though it breaks in the
correct place and performs the correct output string).

My call stack at the crash looks like this:

scratch.exe!derived2::derived2(const derived2 & __that={...}) +
0x56 bytes C++
scratch.exe!derived2::clone() Line 38 + 0x50 bytes C++
scratch.exe!derived2::clone() + 0x2b bytes C++
scratch.exe!main() Line 61 + 0x22 bytes C++
scratch.exe!__tmainCRTStartup() Line 586 + 0x19 bytes C
scratch.exe!mainCRTStartup() Line 403 C
kernel32.dll!7c817077()
[Frames below may be incorrect and/or missing, no symbols loaded
for kernel32.dll]

Looks like some seriously F'ed up UB to me but I'm not well versed in MI
and virtual inheritance to understand the nuances I'm running into. I
thought I'd done it right.

Also, I don't know if this is meaningful but I got a C4505 for the
sub_base3::clone() = 0 override. First time I've ever seen that warning
and it blows up in my face.

CODE:

#include <string>
#include <iostream>

struct base {
virtual void do_x() { std::cout << "base::do_x()\n"; }
virtual base * clone() const = 0;
virtual int * value() = 0;
virtual ~base() {}
};
struct sub_base1 : virtual base
{
virtual void do_x() { std::cout << "sub_base1::do_x()\n"; }
//virtual sub_base1 * clone() const = 0;
};
struct sub_base2 : virtual base
{
virtual void do_x() { std::cout << "sub_base2::do_x()\n"; }
//virtual sub_base2 * clone() const = 0;
};
struct derived : sub_base1, sub_base2
{
virtual void do_x() { std::cout << "derived::do_x()\n"; }
virtual derived * clone() const { return new derived; }
int * value() { return 0; }
//void do_y() { }
};
struct sub_base3 : sub_base1
{
void do_x()
{
std::cout << "sub_base3::do_x() -> " << *value() << "\n";
}
virtual sub_base3 * clone() const = 0;
};
struct derived2 : sub_base3
{
void do_y() {}
virtual derived2 * clone() const { return new derived2(*this); }
int * value() { return val; }

derived2(int * v) : val(v) {}
private:
int * val;
};


int main()
{
int x = 5;
derived2 d(&x);
d.do_x();
//d.clone()->do_x();
base * b = &d;
b->do_x();

base * b2 = b->clone();
b2->do_x();

sub_base3 * sb3 = dynamic_cast<sub_base3*>(b2);
sb3->do_x();
sub_base3 * sb32 = sb3->clone(); // this call explodes.
sb32->do_x();

std::cin.get();
}

ENDCODE

The reason sub_base3 has a clone() override is that it is called from
code that knows the type is sub_base3 and needs to get a clone pointer
to that level. I've done stuff like that a million times before but not
with MI.
 
N

Noah Roberts

I'm having a very weird problem here that's apparently caused by MI
and/or virtual inheritance that I'd like help with. I finally managed
to get the problem to appear in small form. I'll leave the code at the
end so I can explain some things more deeply before hand.

The really interesting thing is that even before the call that causes
the crash, the debugger is having troubles. For example, if I place a
breakpoint in sub_base3::do_x() I am not able to examine *this as a
sub_base3. The debugger thinks it is a sub_base1 and will not allow me
to cast it down in the watch window (even though it breaks in the
correct place and performs the correct output string).

My call stack at the crash looks like this:

scratch.exe!derived2::derived2(const derived2 & __that={...}) +
0x56 bytes C++
scratch.exe!derived2::clone() Line 38 + 0x50 bytes C++
scratch.exe!derived2::clone() + 0x2b bytes C++
scratch.exe!main() Line 61 + 0x22 bytes C++
scratch.exe!__tmainCRTStartup() Line 586 + 0x19 bytes C
scratch.exe!mainCRTStartup() Line 403 C
kernel32.dll!7c817077()
[Frames below may be incorrect and/or missing, no symbols loaded
for kernel32.dll]

Looks like some seriously F'ed up UB to me but I'm not well versed in MI
and virtual inheritance to understand the nuances I'm running into. I
thought I'd done it right.

Also, I don't know if this is meaningful but I got a C4505 for the
sub_base3::clone() = 0 override. First time I've ever seen that warning
and it blows up in my face.

CODE:

#include <string>
#include <iostream>

struct base {
virtual void do_x() { std::cout << "base::do_x()\n"; }
virtual base * clone() const = 0;
virtual int * value() = 0;
virtual ~base() {}
};
struct sub_base1 : virtual base
{
virtual void do_x() { std::cout << "sub_base1::do_x()\n"; }
//virtual sub_base1 * clone() const = 0;
};
struct sub_base2 : virtual base
{
virtual void do_x() { std::cout << "sub_base2::do_x()\n"; }
//virtual sub_base2 * clone() const = 0;
};
struct derived : sub_base1, sub_base2
{
virtual void do_x() { std::cout << "derived::do_x()\n"; }
virtual derived * clone() const { return new derived; }
int * value() { return 0; }
//void do_y() { }
};
struct sub_base3 : sub_base1
{
void do_x()
{
std::cout << "sub_base3::do_x() -> " << *value() << "\n";
}
virtual sub_base3 * clone() const = 0;
};
struct derived2 : sub_base3
{
void do_y() {}
virtual derived2 * clone() const { return new derived2(*this); }
int * value() { return val; }

derived2(int * v) : val(v) {}
private:
int * val;
};


int main()
{
int x = 5;
derived2 d(&x);
d.do_x();
//d.clone()->do_x();
base * b = &d;
b->do_x();

base * b2 = b->clone();
b2->do_x();

sub_base3 * sb3 = dynamic_cast<sub_base3*>(b2);
sb3->do_x();
sub_base3 * sb32 = sb3->clone(); // this call explodes.
sb32->do_x();

std::cin.get();
}

ENDCODE

The reason sub_base3 has a clone() override is that it is called from
code that knows the type is sub_base3 and needs to get a clone pointer
to that level. I've done stuff like that a million times before but not
with MI.

Oh, and I'm running into this trouble on MSVC 8. Problem doesn't seem
to occur in g++ 3.4.5
 
J

Joshua Maurice

I'm having a very weird problem here that's apparently caused by MI
and/or virtual inheritance that I'd like help with.  I finally managed
to get the problem to appear in small form.  I'll leave the code at the
end so I can explain some things more deeply before hand.
[snip example with code]

The last time I checked, MSVC does not support covariant return types
with multiple inheritance. It actually gave me compiler errors IIRC,
saying something like "ambiguous type".
 
J

Joshua Maurice

and/or virtual inheritance that I'd like help with.  I finally managed
to get the problem to appear in small form.  I'll leave the code at the
end so I can explain some things more deeply before hand.

[snip example with code]

The last time I checked, MSVC does not support covariant return types
with multiple inheritance. It actually gave me compiler errors IIRC,
saying something like "ambiguous type".

Here's a simple example I just checked vs Comeau Online, gcc version
3.4.3 20050227 (Red Hat 3.4.3-22.1), and msvc 2008.

struct A { virtual A* clone() =0; };
struct B : virtual A { virtual B* clone() =0; };
struct C : virtual A { virtual C* clone() =0; };
struct D : B, C { virtual D* clone() =0; };

Compiles with Comeau Online and gcc 3.4.3. Trying to compile on msvc
2008 gives a compiler error.

1>c:\documents and settings\jmaurice\desktop\cpp_tester\foo.cpp(4) :
error C2250: 'D' : ambiguous inheritance of 'B *A::clone(void)'

I wonder how your example even compiles on msvc 2008. (It does on my
computer.) I find it most odd. My \guess\ is that it's producing
garbage output as it doesn't support covariant return types with
multiple inheritance, and your slightly more complex example hits a
compiler bug, a bug in the sense that it doesn't fail like the
developers of msvc intended to as they don't support covariant return
types with multiple inheritance.
 
T

Thomas J. Gritzan

Am 23.02.2010 23:42, schrieb Noah Roberts:
I'm having a very weird problem here that's apparently caused by MI
and/or virtual inheritance that I'd like help with. I finally managed
to get the problem to appear in small form. I'll leave the code at the
end so I can explain some things more deeply before hand.

There's no MI involved. You don't even use class derived and sub_base2.

I made a simpler example that fails on my MSVC 2008 SP1:

#include <string>
#include <iostream>

struct base {
virtual base * clone() const = 0;
};
struct sub : virtual base {
virtual sub * clone() const = 0;
};
struct derived : sub {
virtual derived * clone() const {
return new derived(*this); // <- here
}
int val;
};
int main()
{
derived d;
sub* s = &d;
sub * s2 = s->clone(); // AV in copy constructor
}

Looking in the debugger, the this pointer in the clone function is
wrong. The problem seems to be a combination of virtual inheritance and
covariant return types. If all the clone functions return base*, it
works. Obviously a bug in the compiler.
 
A

Alf P. Steinbach

* Joshua Maurice:
I'm having a very weird problem here that's apparently caused by MI
and/or virtual inheritance that I'd like help with. I finally managed
to get the problem to appear in small form. I'll leave the code at the
end so I can explain some things more deeply before hand.
[snip example with code]

The last time I checked, MSVC does not support covariant return types
with multiple inheritance. It actually gave me compiler errors IIRC,
saying something like "ambiguous type".

Noah may try the following code to work around the covariance issue:


<code>
#include <string>
#include <iostream>

class base {
protected:
virtual base * virtual_clone() const = 0;
public:
virtual void do_x() { std::cout << "base::do_x()\n"; }
virtual int * value() = 0;
virtual ~base() {}
base* clone() const { return virtual_clone(); }
};

class sub_base1
: public virtual base
{
public:
virtual void do_x() { std::cout << "sub_base1::do_x()\n"; }
};

class sub_base2
: public virtual base
{
public:
virtual void do_x() { std::cout << "sub_base2::do_x()\n"; }
};

class derived
: public sub_base1
, public sub_base2
{
protected:
virtual base * virtual_clone() const { return new derived; }
public:
virtual void do_x() { std::cout << "derived::do_x()\n"; }
derived* clone() const
{ return dynamic_cast<derived*>( virtual_clone() ); }
int * value() { return 0; }
};

class sub_base3
: public sub_base1
{
public:
void do_x()
{
std::cout << "sub_base3::do_x() -> " << *value() << "\n";
}
sub_base3* clone() const
{ return dynamic_cast<sub_base3*>( virtual_clone() ); }
};

class derived2
: public sub_base3
{
protected:
virtual base * virtual_clone() const { return new derived2(*this); }
public:
void do_y() {}
derived2 * clone() const
{ return dynamic_cast<derived2*>( virtual_clone() ); }
int * value() { return val; }

derived2(int * v) : val(v) {}
private:
int * val;
};


int main()
{
int x = 5;
derived2 d(&x);
d.do_x();
//d.virtual_clone()->do_x();
base * b = &d;
b->do_x();

base * b2 = b->clone();
b2->do_x();

sub_base3 * sb3 = dynamic_cast<sub_base3*>(b2);
sb3->do_x();
sub_base3 * sb32 = sb3->clone(); // this call doesn't explode.
sb32->do_x();
}
</code>


Cheers & hth.,

- Alf
 
N

Noah Roberts

and/or virtual inheritance that I'd like help with.  I finally managed
to get the problem to appear in small form.  I'll leave the code at the
end so I can explain some things more deeply before hand.

[snip example with code]

The last time I checked, MSVC does not support covariant return types
with multiple inheritance. It actually gave me compiler errors IIRC,
saying something like "ambiguous type".

Here's a simple example I just checked vs Comeau Online, gcc version
3.4.3 20050227 (Red Hat 3.4.3-22.1), and msvc 2008.

struct A { virtual A* clone() =0; };
struct B : virtual A { virtual B* clone() =0; };
struct C : virtual A { virtual C* clone() =0; };
struct D : B, C { virtual D* clone() =0; };

Compiles with Comeau Online and gcc 3.4.3. Trying to compile on msvc
2008 gives a compiler error.

1>c:\documents and settings\jmaurice\desktop\cpp_tester\foo.cpp(4) :
error C2250: 'D' : ambiguous inheritance of 'B *A::clone(void)'

I wonder how your example even compiles on msvc 2008. (It does on my
computer.) I find it most odd. My \guess\ is that it's producing
garbage output as it doesn't support covariant return types with
multiple inheritance, and your slightly more complex example hits a
compiler bug, a bug in the sense that it doesn't fail like the
developers of msvc intended to as they don't support covariant return
types with multiple inheritance.

Well, the problem I'm running into doesn't actually use covariant return
types with multiple inheritance. You'll notice that my derived2 class
never inherits from more than one base anywhere along it's path to the
root base. What it does is inherit from that base virtually so that a
totally different branch in the tree can inherit from that base more
than once. So I suspect the fact that I'm not actually inheriting
multiply at that point is the real reason it didn't vomit some error.

Interestingly though both covariance, virtual inheritance, and multiple
inheritence live together just fine if I only attempt to override in the
most derived classes. So in your above example take out the B and C
redefs and leave the D...in my sample it works.

I'm also not using 2008 but 2005 that could be part of the diff maybe.
Without the service pack even because it irreparably destroyed the only
computer we tried to apply it to.
 
N

Noah Roberts

* Joshua Maurice:
I'm having a very weird problem here that's apparently caused by MI
and/or virtual inheritance that I'd like help with. I finally managed
to get the problem to appear in small form. I'll leave the code at the
end so I can explain some things more deeply before hand.
[snip example with code]

The last time I checked, MSVC does not support covariant return types
with multiple inheritance. It actually gave me compiler errors IIRC,
saying something like "ambiguous type".

Noah may try the following code to work around the covariance issue:


<code>
#include <string>
#include <iostream>

class base {
protected:
virtual base * virtual_clone() const = 0;
public:
virtual void do_x() { std::cout << "base::do_x()\n"; }
virtual int * value() = 0;
virtual ~base() {}
base* clone() const { return virtual_clone(); }
};

I like this. Thanks.
 
J

Jorgen Grahn

Am 23.02.2010 23:42, schrieb Noah Roberts:

There's no MI involved.

It would be better if both of you spelled out "MI". I'm *guessing* it
means "multiple inheritance" but it's certainly not an abbreviation
you see often. (Especially since most people rarely have a need for
it.)

/Jorgen
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,067
Latest member
HunterTere

Latest Threads

Top