Hi,
Weird things happen. While playing around with the ‘audio plugin host’ demo solution under VS2017 (Win32) I noticed sth really weird. In Debug mode everything’s working fine. When building in Release mode, I am not able to connect pins anymore. My guess it’s the optimizer doing sth wrong (as it’s turned on in Relase mode). Of course I’m using everything “out of the box” - unmodified latest JUCE framework and solutions generated by the ProJucer.
I managed to identify the code responsible for this bug (GraphEditorPanel.cpp, line 830):
PinComponent* GraphEditorPanel::findPinAt (Point<float> pos) const
{
for (auto* child : getChildren())
if (auto* fc = dynamic_cast<FilterComponent*> (child))
if (auto* pin = dynamic_cast<PinComponent*> (fc->getComponentAt (pos.toInt() - fc->getPosition())))
return pin;
return nullptr;
}
In Release mode the “return pin” statement never gets executed (when I inserted a break point on this statement, it’s automatically removed by VS upon running it). It means the optimizer assumes the condition (second if) is always 0/nullptr (false). There is an assigment, so it means it assumes a result of the dynamic_cast is always 0. I did some test and changed the code a little bit into:
PinComponent* GraphEditorPanel::findPinAt (Point<float> pos) const
{
for (auto* child : getChildren())
if (auto* fc = dynamic_cast<FilterComponent*> (child))
{
auto* pin = (fc->getComponentAt(pos.toInt() - fc->getPosition()));
auto* pinC = dynamic_cast<PinComponent*>(pin);
if (pinC)
return pinC;
}
return nullptr;
}
Now (in Release mode) it’s working fine.
But if I change it into
PinComponent* GraphEditorPanel::findPinAt (Point<float> pos) const
{
for (auto* child : getChildren())
if (auto* fc = dynamic_cast<FilterComponent*> (child))
{
auto* pin= dynamic_cast<PinComponent*>(fc->getComponentAt(pos.toInt() - fc->getPosition()));
if (pin)
return pin;
}
return nullptr;
}
then the optimizer assumes “pin” is always nullptr again.
Can anyone explain this behavior? What makes such difference for the optimizer, so that
auto* pin = (fc->getComponentAt(pos.toInt() - fc->getPosition()));
auto* pinC = dynamic_cast<PinComponent*>(pin);
// optimizer doesn't make any assumptions regarding pin/pinC and everything is working fine
is treated so much different than
auto* pin = dynamic_cast<PinComponent*>(fc->getComponentAt(pos.toInt() - fc->getPosition()));
// optimizer assumes "pin" will be always nullptr
I have a feeling that the optimizer should be blamed, as in the Debug mode when the optimizer is turned off, “pin” is not always nullptr! So the optimizer fails assuming it’s alwyas nullptr. It’s not really a JUCE question (and definitely not JUCE framework’s fault!), as the same behaviour might occur in any other C++ code containing similar operations.
(reminder: getComponentAt returns Component*, PinComponent is a subclass (though defined as a struct) of Component class).