5

I have a dynamic cast which is failing. The class layout is like this:

class A1
{
    public:
virtual int foo1()=0;
};

class A2
{
    public:
    virtual int foo2();
};

class A3
{
public:
   virtual int foo3();
};

class B : public A1, public A2, public A3 
{
   int bar();
};

Now I use pointers ( so no slicing can occur ) for the downcast.

main()
{
   B b;

   A1* a1 = dynamic_cast<A1*> (&b); // ok
   B*  b1 = dynamic_cast<B*>  (a1); // ok

   A2* a2_1 = dynamic_cast<A2*> (a1); // OSX 10.7 ok, OSX 10.9 fail
   A2* a1_2 = dynamic_cast<A2*> (b1); // ok

};

The downcast works, the upcast works but the sidecast does not work always. Under OSX 10.7 the sidecast works, under OSX 10.9 it does not ( GCC 4.2 using dynamic c++ stdlib). Looking at the vtable using gdb I can clearly see A2 methods and members listed.

My questions:

a) Is the sidecast technically correct? Should it work or is this a runtime bug?
b) If the sidecast depends on the runtime, where is this runtime defined? I always thought this is a part of the binary?
c) How does a failing dynamic_cast look like (when looking at assembly), and how can you track down such problems?

Update: this is what i see in the system console

11/01/15 14:16:27,435 APPNAMECHANGED[15280]: dynamic_cast error 1: Both of the following type_info's should have public visibility. At least one of them is hidden. 10A1, 15A2.

So c) is answered, at least for OSX 10.9. Have a look at the console. (Linux, someone?) It looks like a problem about symbol visibility. Doc says (gcc.gnu.org/wiki/Visibility)

However, this isn't the full story - it gets harder. Symbol visibility is "default" by default but if the linker encounters just one definition with it hidden - just one - that typeinfo symbol becomes permanently hidden (remember the C++ standard's ODR - one definition rule). This is true for all symbols, but is more likely to affect you with typeinfos; typeinfo symbols for classes without a vtable are defined on demand within each object file that uses the class for EH and are defined weakly so the definitions get merged at link time into one copy.

This leads to the next question,

d) how can we track down which symbol get at least once marked hidden, and where and why? Is there a tool to inspect .o files?

Heiner
  • 165
  • 1
  • 11
  • do you get a compile-time or runtime error and if so, what does it look like? – Michael Dautermann Jan 10 '15 at 16:05
  • 3
    A1 is pure virtual, and foo1 is never implemented. You can't create a B. Typo ? – tux3 Jan 10 '15 at 16:08
  • FWIW -- the sidecast works fine with gcc 4.9.2 on Linux. This is purely a compiler issue, and I would not expect the OS/platform to be a factor in any way. gcc 4.2 is ancient, I wouldn't waste any time on it. Upgrade to current gcc, and call it a day. If you need a workaround, try downcasting to the base class, then upcasting. – Sam Varshavchik Jan 10 '15 at 16:40
  • @Michael, the sidecast simply results in 0x0, no exception etc. – Heiner Jan 10 '15 at 16:41
  • @tux3 i simplified the case, of course the classes are implemented and work work properly – Heiner Jan 10 '15 at 16:43
  • @Sam 4.2 is old, agreed. But how does a compiler issue explain that it works under some OS and not under other OS Versions? – Heiner Jan 10 '15 at 16:44
  • If you mean the same exact compiler, it would probably come down to the compiler doing different thing depending on the OS due to some platform-specific issues. However, there is really no such version of gcc as "4.2". There have been several minor dot releases of the 4.2 compiler branch. Do you have the same, exact, identical compiler build on both OSes? – Sam Varshavchik Jan 10 '15 at 16:46
  • @Sam yes, it is the gcc delivered with XCode 3.2.6 (also ancient, agreed). Interestingly switching to clang (also comes with XC3.2.6) does not change the issue. But switching from 'dynamic' to 'static' c++ stdlib solves the issue. – Heiner Jan 10 '15 at 16:54
  • Ok, well, again, do you know for a fact that both builds of Xcode come with identical gcc build, for both platforms? Just because it's the same version of xcode wouldn't necessarily mean an identical version of gcc on two different platforms. – Sam Varshavchik Jan 10 '15 at 17:10
  • It seems you might have simplified the case too much. Are you sure that the above code would, when made valid with minor changes, reproduce the error? – Columbo Jan 10 '15 at 17:19
  • @Sam Yes, i a) copied the binary (App) from one system to another to test -and it failed- and b) copied the XCode suite including gcc from one system to another, rebuild - and it failed. – Heiner Jan 10 '15 at 17:29
  • I am not familiar with binary compatibility properties of OS-X; however I would not be surprised if this was a result of running code on an OS version different than the one it was built on. – Sam Varshavchik Jan 10 '15 at 18:16

2 Answers2

1

The issue was finally resolved after we found that the executable linked against a dylib which was itself linked statically against the c++ runtime but was compiled with a different compiler (gcc 4.8 vs clang).

Heiner
  • 165
  • 1
  • 11
0

Disclaimer: I am only gonna address question a).

First of all, B is clearly a polymorphic type according to [class.virtual]/1 as it inherits a virtual function (to be precise, B inherits three distinct ones).
Now consider [expr.dynamic.cast]/8:

If C is the class type to which T points or refers, the run-time check logically executes as follows:

  • If, in the most derived object pointed (referred) to by v, v points (refers) to a public base class subobject of a C object, and if only one object of type C is derived from the subobject pointed (referred) to by v the result points (refers) to that C object.
  • Otherwise, if v points (refers) to a public base class subobject of the most derived object, and the type of the most derived object has a base class, of type C, that is unambiguous and public, the result points (refers) to the C subobject of the most derived object.
  • Otherwise, the run-time check fails.

So yes, it should work.

Columbo
  • 60,038
  • 8
  • 155
  • 203