Fixed Xcode link errors by including AppConfig.h


#1

[color=#FF0000]THIS IS FIXED NOW[/color]
I’m at my wits end, I cannot fix the link errors in my SimpleDJ application, can someone give it a whirl on Xcode 4.x?

https://github.com/vinniefalco/AppletJUCE

The errors are:

Undefined symbols for architecture i386: "non-virtual thunk to juce::Button::getTooltip()", referenced from: vtable for CParamToggleButton in CParamToggleButton.o "non-virtual thunk to juce::Button::setTooltip(juce::String const&)", referenced from: vtable for CParamToggleButton in CParamToggleButton.o "non-virtual thunk to juce::Button::applicationCommandInvoked(juce::ApplicationCommandTarget::InvocationInfo const&)", referenced from: vtable for CParamToggleButton in CParamToggleButton.o "non-virtual thunk to juce::Button::applicationCommandListChanged()", referenced from: vtable for CParamToggleButton in CParamToggleButton.o "non-virtual thunk to juce::Button::valueChanged(juce::Value&)", referenced from: vtable for CParamToggleButton in CParamToggleButton.o "non-virtual thunk to juce::Button::keyPressed(juce::KeyPress const&, juce::Component*)", referenced from: vtable for CParamToggleButton in CParamToggleButton.o "non-virtual thunk to juce::Button::keyStateChanged(bool, juce::Component*)", referenced from: vtable for CParamToggleButton in CParamToggleButton.o ld: symbol(s) not found for architecture i386 clang: error: linker command failed with exit code 1 (use -v to see invocation)

People in #macdev tried to help me but this is the best they could come up with:

[attachment=0]IRC.png[/attachment]

Jules take note, the first thing people bitched about was that .cpp was including other .cpp which was included by .mm files. I’m not saying they are right but by far and large 100% of people who are exposed to this source code organization immediately assume it is the source of problems, and an evil that should be “fixed” before any reasonable discussion can take place.


#2

The only time I’ve seen link errors like that has been when some of your cpp files are including different versions of the same headers (or include the same headers, but with different option flags set, so that the classes have different structures)

Bear in mind that if the .cpp files had ended in .h instead, then the overall result would have been identical, but nobody would have noticed it.


#3

The Xcode project was generated by IntroJucer. And I used the latest sources to build it.


#4

Try to reduce the project, as simple as possible, that you can reproduce the problem.
If you think its a bug , post it on the mac developer forum. There are XCode and Clang/LLVM developers in there.
Do you use your static-libs?


#5

[quote=“chkn”]Try to reduce the project, as simple as possible, that you can reproduce the problem.
If you think its a bug , post it on the mac developer forum. There are XCode and Clang/LLVM developers in there.
Do you use your static-libs?[/quote]

I’m not using any static libs. I haven’t changed any code all I did was update to the latest JUCE and re-export the Xcode project from IntroJucer.

The problem goes away if I change CParamToggleButton to no longer derive from juce::TextButton.


#6

FWIW, a “non virtual thunk” is an internal mechanism for dealing with C++ multiple inheritance.

The most common cause is, as chkn notes, linking to a lib that was either built with a different compiler version, or different optimization levels (there are some long standing Apple GCC/LLVM bugs in that area). You can get a similar problem if the compilation options, particularly optimization, is different for specific source files - though that’s not common in XCode.

Jule’s example also would lead to these errors. If headers don’t match C++ code, then functions won’t get resolved. Similarly, if defines differ at different includes and the representation of one of the classes involves changes, name mangling, etc. will differ and the linker won’t be able to resolve things.

It’s important to remember that it doesn’t have to be the topmost classes, just a variant in any common ancestor.

If it is still whacked tomorrow, I can spend a few minutes and try to download and compile it. Usually what I do in a case like this is use the command line tools and dump the individual object files to find the discrepancy.


#7

I didn’t touch the xcode project, IntroJucer generated it, and I’m not using static libraries.

Well, there’s only one JUCE tree in the repo and its from the latest tip.

It’s the juce::Button class, because removing the use of that class makes the problem disappear.

nm shows that the thunk is in the .o file but yet, there’s still a link error.


#8

FWIW, I just pulled the .zip and took a quick look. It seems like there is a vtable difference in a common ancestor. I should have a little more time to look later today.


#9

Hmm…how did you figure this out? What do you mean common ancestor, are you talking about the class hierarchy?


#10

Patrick figured out how to make the problem go away. It is to move the Component base class to the end of the list of bases in the class declaration for Button:

juce_Button.h Fixed:

class JUCE_API  Button :public SettableTooltipClient,
                          public ApplicationCommandManagerListener,
                          public ValueListener,
                          public KeyListener,
                          public Component

Somehow this causes the Xcode link errors to disappear. But now there is a crash at run time which I cannot explain.

Help!!!


#11

For the umpteenth time: this is because you’re linking cpps that have included the same headers using different settings. Every new clue you’ve posted confirms that!

E.g. you probably have one cpp that includes the headers with debug, and are linking it to another cpp which included them without debug. When you do that, the memory layout of the same classes is treated differently in different areas of the code. The main reason it’ll be different is because of the leak-detector member being added in the debug version, so things like Component will be different sizes. Moving your Component class to the end of the inheritance list just means that it’ll be at the end of the structure, so any differences (e.g. whether or not there’s a leak detector member at the end of it) will be sticking off the end into unused memory, so are less likely to overwrite something and crash (if you’re lucky).


#12

Yes I agree that moving the Component derivation shows that this is likely the problem.

I looked around through the code and finally figured it out, it is because AppConfig.h was not being included before including the JUCE headers.

I don’t understand why the project worked before. Isn’t there some way to detect this condition in the headers, i.e.

#ifndef JUCE_APPCONFIG_INCLUDED
#error You must include AppConfig.h before any JUCE headers!
#endif

#13

Good idea about adding a check for that… I wish there was a more elegant way to find out when you link together a bunch of incompatible code, but can’t think how it’d be possible.


#14

@#$%! I should have checked here before getting back to this this morning. I was just going to post an answer… Oh well! Glad you got the solution sooner - I got pulled into another project.

As far as ‘determining’ above, you can still install the command line tools, then dump the individual object files. When getting object memory sizes you can also get the size of each ancestor. If the objects use a different amount of member variable space, it’s a pretty good clue the inheritance tree is splintered.

As far as ‘included test’, it would be nice, though I’m starting to think that my many decades relationship with the pre-processor is coming to a stormy end. Way back when we were tight. Then things cooled, but we still would hang out together pretty regularly. Now I find myself taking pretty elaborate steps to avoid it altogether.

In firmware projects, where I really do still need to use defines and conditional compilation, I often put the configuration defines on the command line via the makefile, instead of a shared header that everyone includes, specifically to avoid this type of problem.