devxlogo

Use Local Classes for Proper Cleanup in Exception-enabled Apps

Use Local Classes for Proper Cleanup in Exception-enabled Apps

Exceptions enable you to detect and handle anomalous runtime conditions promptly. However, they also complicate the design of your program as they increase the number of possible execution paths. In most cases, implementing the “resource acquisition is initialization” idiom guarantees that data isn’t destroyed and that resources are always released. However, this idiom isn’t always an ideal solution. The following sections present a more refined technique for ensuring proper cleanup in an exception-enabled environment.


How do you ensure that critical cleanup code always executes regardless of whether an exception has occurred?


Use a local class’s destructor to ensure the unconditional execution of cleanup code.

Demonstrating the Problem
An exception-enabled program has at least two execution paths. Consider:

void func(){ try  {  if (something_bad) //#a1    throw X();  // #b1 otherwise continue normally, then jump to #b2 } catch (X& x)//#a2 {  exit(1); }//#b2 we get here only if no exception was thrown}

When an exception occurs, control is transferred to the catch block marked #a2, after which the exit() call terminates the program. If however no exception occurs, the section marked #b1 executes and then control is transferred to the section #b2. Thus, when certain sections of func() execute, others don’t, and vice versa. In real world code, there are more execution paths because the program may throw various types of exceptions from different locations. Additionally, there may be multiple catch blocks. The challenge is to ensure that certain operations (logging, notifying the sysadmin that the process is about to die, etc.) always take place, regardless of whether an exception has occurred.

If you’re familiar with Java you will recognize that this is exactly what the finally keyword does. However, since C++ doesn’t have a similar keyword, you need another mechanism to ensure the unconditional execution of cleanup code.

Introducing Local Classes
C++ has a little known feature called local classes. A local class looks like an ordinary class except that it’s declared inside a function (normally, you declare classes in a namespace scope). For example:

void f(){ class A{ public:   A() {cout

A local class has no linkage. Therefore, you can't refer to it from an outer scope or from other functions, blocks, and translation units:

void f(){ class C {}; C c; //OK, in the scope of C's declaration C * p = new C; // also OK}int main(){ f(); C another_c; //error: undefined symbol 'C'}

Local classes are instrumental in solving this problem.

A Synergic Combination
C++ ensures that destructors of auto objects execute when an exception occurs as part of the stack unwinding process. Take advantage of this behavior and move unconditional cleanup code into a destructor. This technique is hardly new, of course. However, the properties of local classes enable you to refine it.

You want to add cleanup code to func() which will execute unconditionally. More importantly, you want this code to execute upon leaving func(), but not sooner. To achieve these goals, you will combine the deterministic execution of auto objects' destructors and the properties of local classes. This synergic combination imitates the functionality of Java's finally.

First, define a local class before the first throw statement and add cleanup code to its destructor. This class should have only one member: a public destructor. Then, instantiate an object of this class immediately after the class's definition:

void func(){ try  {   struct Finally  {   ~Finally() { cleanup(); SMS_sysadmin("terminating");}  } finalizer;   if (something_bad) //#a1    throw X(); //#b1 otherwise continue normally } catch (X& x) // #a2 {   exit(1); }//#b2 we get here only if no exception was thrown}

That's all! When func() exits, either normally or due to an exception, the destructor of finalizer will execute unconditionally and perform the necessarily process termination procedures.

The Grand Finale
You might argue that there's no need to use a local class to perform this feat: any auto object's destructor will do. However, because Finally is a local class, other programmers won't be able to create instances thereof elsewhere. More importantly, local classes have no linkage. Therefore, you can define classes with the same name in different scopes without risking name clashes:

void func(){ class Local{} loc;//#1}int main(){ class Local {} loc;//#2 OK, doesn't clash with #1}

This property enables you to use a uniform name for such "disposable classes" in all your projects. As a security bonus, neither the local class' name nor the names of its member functions will be stored in an executable/DLL file. Consequently, crackers attempting to decompile your binaries will have a hard time indeed.

devxblackblue

About Our Editorial Process

At DevX, we’re dedicated to tech entrepreneurship. Our team closely follows industry shifts, new products, AI breakthroughs, technology trends, and funding announcements. Articles undergo thorough editing to ensure accuracy and clarity, reflecting DevX’s style and supporting entrepreneurs in the tech sphere.

See our full editorial policy.

About Our Journalist