• HOME
  • ABOUT US
  • SERVICES
  • PORTFOLIO
  • BLOG
Connection Quest Connection Quest
  • HOME
  • ABOUT US
  • SERVICES
  • PORTFOLIO
  • BLOG
Connection Quest

Prepare your defense

Home / Clean C++ / Prepare your defense

Prepare your defense

By Marko Jovic inClean C++

Here’s what a typical programming workflow might look like:

  1. learn about the new feature you need to implement
  2. stress out about it
  3. think about the design, make a sketch or two
  4. code the solution
  5. give it a few test runs
  6. proclaim victory

Everything is working, you’re happy, your boss is happy, the universe is in harmony and you hop off to a new feature. A few months later, however, there is a phone call from an angry customer: the app just crashed during an important business presentation. It was a boundary condition, an impossible situation that just couldn’t have happened (at least that’s what you thought).

So, where did it go wrong? Of course, you didn’t just blindly develop this feature without thinking about potential problems down the road. Or did you? Whatever the answer, you need to make sure this doesn’t happen again. You need to start coding defensively.

The most common approach to error handling is to check for a condition and return an error code, as seen in the following contrived example:

int divide(int dividend, int divisor) {
	if (divisor == 0)
		return -1;
	...
}

int main() {
	int result = divide(5, 0);
	if (result == -1) {
		std::cout << “Oops. You shouldn’t divide by zero.”
	}
}

What we’re doing here is testing our boundary condition and returning a common error code (-1) if that condition isn’t satisfied. While this concept works, it has a flaw: the caller of the function has to explicitly check the returned value and handle the error. In other words, the immediate caller accepts the error handling responsibility, thereby obscuring its logic. The result is often a cluttered mess of intertwined if statements and “normal” processing code. And that mess is even more emphasized by returning NULL or nullptr from your functions, which effectively forces you to guard against possible crashes all the time.

Let’s say you want to handle the error somewhere else in the call chain, not in the most recent caller. Well, you would have to terminate all intermediate functions and return that same error code. And check for it in each of the callers. Luckily, there is a better way: exceptions.

void secondFun() {
	throw -1;
}
 
void firstFun() {  
	secondFun();
}
 
int main() {   
	try {
         firstFun();
    }
    catch (int) {
         std::cout << "Just caught an int exception!";
	}
}

As you can see in the example above, you can deal with the error from the secondFun anywhere in the call stack by declaring a corresponding try / catch block. This illustrates an important point: exception mechanisms separate logic from error handling, thereby making your code easier to understand.

However, exceptions also mandate careful usage. It’s easy to fall into a trap of trying to catch every exception in every function which leads to code oversaturated with try / catch blocks. Therefore, when using exceptions, try to stick to the following:

  • always provide context with exceptions
  • throw an exception to indicate that you cannot perform an assigned task
  • write tests that force exceptions, then add behavior to the logic part
  • wrap similar exceptions in a common exception type
  • if your function will not throw, declare it noexcept
  • let a constructor establish an invariant, and throw if it cannot (see the following example)
class Container
{
public: 
	Container(int length) {
        if (length <= 0)
			throw InvalidLengthException(); 
        ...
    }

	int& operator[](int index) {
		...
    }
 
    ...
 
	~Array() {
        delete[] data;
    }

private:
    int* data{};
	int length{};	
}

int main() {   
	try {
        Container container{-1};
    }
    catch (const InvalidLengthException &exception) {
        std::cout << "Invalid length supplied.";
	}
}

So, what can we conclude from this post? Both of the aforementioned concepts ultimately work and its up to you to decide which one is appropriate for your context. If you need to handle a simple error condition only a few times, then it’s probably easier to stick with error codes. On the other hand, more complex error handling warrants exception mechanisms. Remember, error handling is important, but it shouldn’t obscure logic.

Stay tuned and happy coding.

AdviceC++Clean CodeConventionsDefensive ProgrammingError HandlingSoftwareSoftware Development
15 Posts
Marko Jovic
  • Concurrency control
    Previous PostConcurrency control
  • Next PostTest!
    Concurrency control

Leave a Reply (Cancel reply)

Your email address will not be published. Required fields are marked *

*
*

© 2025 Connection Quest j.d.o.o. All rights reserved.

Copy