Throwing exception while constructing the main window in JuceApplication::initialise()


#1

struct MainComponent : public Component
{
MainComponent() // default ctor that throws
{
throw std::exception(); // <<<<<<< NOTE HERE!!! [A]
}
}

struct MainWindow : public DocumentWindow
{
MainWindow() // this ctor will invoke MainComponent’s throwing ctor
{
// … and will also throw on its own:
throw std::exception(); // <<<<<<< NOTE HERE!!! [B]
}

// ......

MainComponent mainComponent; // MainComponent default ctor is used

}

void initialise (const juce::String& /commandLine/) override
{
// …
try
{
mainWindow = std::make_unique(getApplicationName());
}
catch (…)
{
// if execution gets here (= an exception has been thrown while trying
to construct MainWindow), then juce will crash shortly after while
trying to start the message loop, but only with exception [A].
Commenting out exception [A] and leaving exception [B], won’t cause
any problem.

        juce crashes in MessageManager::getInstance()->runDispatchLoop();
}
// ......

}

Why? I have hundreds of subcomponents of MainComponent, each potentially throwing in its constructor. If this happens, even if I catch the
exception in initialise(), there is no why to shut down cleanly since juce will crash as soon as I exit the initialise() function.


#2

Can you format your code so that it’s readable, please… Indent by 4 spaces or surround with triple-backticks.

It’d also be helpful to see some real code (as a PIP?) that we could compile and try ourselves.

Though I’ve got to say that I’m not surprised you’d hit problems. Throwing in a constructor is generally something you only do if there’s really no other option. In the constructor of a polymorphic object is even less recommended. And throwing in the constructor of a polymorphic member variable is getting into the small-print of the C++ spec says should happen to unwind all the semi-constructed parents and other members… And when the polymorphic base class of both the child and parent objects are complex and possibly interdependent 3rd party base classes whose constructors and destructors do a bunch of messing about with OS windowing functions… hmm.


#3

The code was perfectly indented before pushing the “post” button. It is the forum web application that messed up everything.
How sould I preserve the indentation while posting?


#4

Indent it so that everything has at least 4 spaces. Or use triple backticks. (It’s standard markup format I think)


#5

Spaces (no matter how many) are stripped away at the beginning of the line (looking at the preview on the right pane). What are “backticks”?

May I attach a txt file with the code?


#6

The four spaces only work, if there was a newline before. These ``` are the backticks you are looking for (have a newline before as well, just in case)


#7

Could you please tell me how can I insert a newline character at the beginning of the line in front of the spaces, using a windows PC with a std keyboard (which only have the Enter key)? I do not even see the backticks on my keyboard …


#8
struct MainComponent : public juce::Component
{
    MainComponent() // default ctor that throws
    {
		throw std::exception(); // <<<<<<< NOTE HERE!!! [A]
	}
}

struct MainWindow : public juce::DocumentWindow
{
	MainWindow() // this ctor will invoke MainComponent’s throwing ctor
	{
		// … and will also throw on its own:
		throw std::exception(); // <<<<<<< NOTE HERE!!! [B]
	}

	// ......

	MainComponent mainComponent; // MainComponent default ctor is used
}

void MyApplication::initialise(const juce::String&) override
{
	// ......

	try
	{
		// std::unique_ptr<MainWindow> mainWindow; is declared at class scope whose initialise() is a member

		mainWindow = std::make_unique<MainWindow>(getApplicationName());
	}
	catch (std::exception&)
	{
		// if execution gets here (= an exception has been thrown while trying
		// to construct MainWindow), then juce will crash shortly after while
		// trying to start the message loop, but only with exception[A].
		// Commenting out exception[A] and leaving exception[B], won’t cause
		// any problem.

		// juce crashes in MessageManager::getInstance()->runDispatchLoop();
	}

	// ......
}

Calling an exception within a constructor (as I do in MainComponent) to signal an error condition seems to me best practice. Returning error codes is not even possible in constructors.
I throw an exception in the most derived object ctor, and hence base object(s) are already fully constructed and their destructor is called automatically.
In my opinion the Component class has side effects that are not fully reversed by its destructor (that gets called when I throw excpetion [A]).
And hence later, juce crashes trying dereferencing a null ptr (a container’s iterator or something similar: “read access violation” in c++ std library code as reported by Visual Studio) within the runDispatchLoop() function.

DocumentWindow’s destructor in contrast is bug-free and does not leak resources or have unreversed side effects.


P.S.: For those who might be interested. To properly format code you have to:

  1. Start with a line with three bacticks (alt + 096) and nothing else
  2. Type/paste your code
  3. Close with a line with three bacticks and nothing else

#9

DocumentWindow is a subclass of Component, so that’s clearly not the problem.

If you give us some real code rather than pseudo-code that we can actually run, we’ll give it a try and see what’s going on.


#10

Good point.

I cannot give you the full application (hundreds of files). I have to extrapolate something compilable on its own and get back. Let me see …


#11

OK. I attach a compilable barebone app that shows that … I was wrong. Both exceptions [A] and [B] are handled correctly and the application exits cleanly.

The problem was that deep below MainComponent there was a spawned background thread that assumed MainComponent constructor would always come to completion. This was usually the case, even when exception [B] was thrown. But not when exception [A] was thrown. In this latter case the thread was spawned but the constructor never completed, a lot of intermediate objects were destroyed prematurely, and the spawned thread was left pointing to zombie objects.

My fault. Thanks to all for the prompt reply.

	struct MainComponent : public juce::Component
	{
		MainComponent() // default ctor that throws
		{
			throw std::exception(); // <<<<<<< NOTE HERE!!! [A]
		}
	};

	struct MainWindow : public juce::DocumentWindow
	{
		MainWindow() // this ctor will invoke MainComponent’s throwing ctor
			: DocumentWindow("", juce::Colours::lightgrey, DocumentWindow::allButtons)
		{
			// … and will also throw on its own:
			throw std::exception(); // <<<<<<< NOTE HERE!!! [B]
		}

		// ......

		MainComponent mainComponent; // MainComponent default ctor is used
	};


	class MyApplication
		: public juce::JUCEApplication
	{
		std::unique_ptr<MainWindow> mainWindow;

	public:

		
		MyApplication() {}

		void MyApplication::initialise(const juce::String&) override
		{
			// ......

			try
			{
				mainWindow = std::make_unique<MainWindow>();
			}
			catch (std::exception&)
			{
				// if execution gets here an exception has been thrown while trying
				// to construct MainWindow.

				quit();
			}

			// ......
		}


		void shutdown() override
		{
			mainWindow.reset();
		}


		const juce::String getApplicationName() override { return ""; }
		const juce::String getApplicationVersion() override { return ""; }
	};