views:

353

answers:

3

I'm building an open source 2D game engine called YoghurtGum. Right now I'm working on the Android port, using the NDK provided by Google.

I was going mad because of the errors I was getting in my application, so I made a simple test program:

class Base
{

public:

    Base() { }
    virtual ~Base() { }


}; // class Base

class Vehicle : virtual public Base
{

public:

    Vehicle() : Base() { }
    ~Vehicle() { }


}; // class Vehicle

class Car : public Vehicle
{

public:

    Car() : Base(), Vehicle() { }
    ~Car() { }

}; // class Car

int main(int a_Data, char** argv)
{
    Car* stupid = new Car();

    return 0;
}

Seems easy enough, right? Here's how I compile it, which is the same way I compile the rest of my code:

/home/oem/android-ndk-r3/build/prebuilt/linux-x86/arm-eabi-4.4.0/bin/arm-eabi-g++
-g 
-std=c99 
-Wall 
-Werror 
-O2 
-w 
-shared 
-fshort-enums 
-I ../../YoghurtGum/src/GLES 
-I ../../YoghurtGum/src 
-I /home/oem/android-ndk-r3/build/platforms/android-5/arch-arm/usr/include 
-c src/Inheritance.cpp 
-o intermediate/Inheritance.o

(Line breaks are added for clarity). This compiles fine. But then we get to the linker:

/home/oem/android-ndk-r3/build/prebuilt/linux-x86/arm-eabi-4.4.0/bin/arm-eabi-gcc
-lstdc++ 
-Wl,
--entry=main,
-rpath-link=/system/lib,
-rpath-link=/home/oem/android-ndk-r3/build/platforms/android-5/arch-arm/usr/lib,
-dynamic-linker=/system/bin/linker,
-L/home/oem/android-ndk-r3/build/prebuilt/linux-x86/arm-eabi-4.4.0/lib/gcc/arm-eabi/4.4.0,
-L/home/oem/android-ndk-r3/build/platforms/android-5/arch-arm/usr/lib,
-rpath=../../YoghurtGum/lib/GLES 
-nostdlib 
-lm 
-lc 
-lGLESv1_CM  
-z 
/home/oem/android-ndk-r3/build/platforms/android-5/arch-arm/usr/lib/crtbegin_dynamic.o 
/home/oem/android-ndk-r3/build/platforms/android-5/arch-arm/usr/lib/crtend_android.o
intermediate/Inheritance.o 
../../YoghurtGum/bin/YoghurtGum.a 
-o bin/Galaxians.android

As you can probably tell, there's a lot of cruft in there that isn't really needed. That's because it doesn't work. It fails with the following errors:

intermediate/Inheritance.o:(.rodata._ZTI3Car[typeinfo for Car]+0x0): undefined reference to `vtable for __cxxabiv1::__si_class_type_info'
intermediate/Inheritance.o:(.rodata._ZTI7Vehicle[typeinfo for Vehicle]+0x0): undefined reference to `vtable for __cxxabiv1::__vmi_class_type_info'
intermediate/Inheritance.o:(.rodata._ZTI4Base[typeinfo for Base]+0x0): undefined reference to `vtable for __cxxabiv1::__class_type_info'
collect2: ld returned 1 exit status
make: *** [bin/Galaxians.android] Fout 1

These are the same errors I get from my actual application.

If someone could explain to me where I went wrong in my test or what option or I forgot in my linker, I would be very, extremely grateful.

Thanks in advance.

UPDATE:

When I make my destructors non-inlined, I get new and more exciting link errors:

intermediate/Inheritance.o:(.rodata+0x78): undefined reference to `vtable for __cxxabiv1::__si_class_type_info'
intermediate/Inheritance.o:(.rodata+0x90): undefined reference to `vtable for __cxxabiv1::__vmi_class_type_info'
intermediate/Inheritance.o:(.rodata+0xb0): undefined reference to `vtable for __cxxabiv1::__class_type_info'
collect2: ld returned 1 exit status
make: *** [bin/Galaxians.android] Fout 1
+6  A: 

Use g++ to drive the linker, not gcc:

/home/oem/android-ndk-r3/build/prebuilt/linux-x86/arm-eabi-4.4.0/bin/arm-eabi-g++
-Wl,
--entry=main,
...

Update: another problem appears to be your use use of -nostdlib. This is stopping the compiler from adding all the standard libraries (such as the runtime library that provides your missing externals). Is there a reason you need this?

R Samuel Klatchko
Doesn't work. Same errors.
knight666
@knight666 - noticed another problem and updated answer.
R Samuel Klatchko
Unfortunately, this also doesn't solve the problem. This may also be due to the fact that Android doesn't supply an implementation of the standard template library.
knight666
@knight666 - try adding `-lgcc_s` and/or `-lsupc++`
R Samuel Klatchko
I love you. Removing `-lnostdlib` did fix the problem, I just had to remove it in the linker instead of the compiler. Thank you! :D EDIT: Waaaait... if I remove `-lnostdlib`, I get `crt0.p: No such file: No such file or directory`. In order to remove that, I have to... include `-nosdtlib`. :\
knight666
@knight666 - you're welcome.
R Samuel Klatchko
+2  A: 

You tried to instantiate in your initialization list a base you didn't inherit from. Car only inherits from Vehicle and not also Base. From your code I assume that was your intention.

I personally prefer to use composition rather than multiple inheritance - it's cleaner and better performance.

DeadMG
That's very "C with classes" instead of "C++". In my humble opinion, of course. Composition is basically abstracting away the implementation, right? Other than that, I really need inheritance. I need an Object class that is the grandparent of everything, an Entity class that is derived from Object and an in-game object and stuff like Player, Bullet, Alien, etc, that is derived from Entity.
knight666
No. You use inheritance when you want virtual functions. If you don't want virtual functions, don't use inheritance. Composition is much cleaner than regular inheritance and you gain much more control, as well as solves issues with MI. Inheritance is not an all-solving solution, and there are definitely times when it's not correct. The entire Java language shows that.However, you still didn't inherit Car from Base, and tried to instantiate a base class that you didn't inherit from. I'm amazed that even compiles.
DeadMG
A: 

To quote from http://gcc.gnu.org/faq.html#vtables

"The ISO C++ Standard specifies that all virtual methods of a class that are not pure-virtual must be defined, but does not require any diagnostic for violations of this rule [class.virtual]/8. Based on this assumption, GCC will only emit the implicitly defined constructors, the assignment operator, the destructor and the virtual table of a class in the translation unit that defines its first such non-inline method." [Emphasis mine]

If you just make your virtual destructors non-inline that should fix the problem.

Mark B
I've updated the original post.
knight666