Cpp Tricks Defer

One of my favorite uses for defer in Go is when a function has multiple return paths, but you still need to run the same handful of instructions before exiting. Instead of writing complicated logic to handle this, a simple defer goes a long way.

I have missed this functionality a lot in C++, but I’ve found a way to use destructors to accomplish the same. It’s not exactly the same as defer, but it’s a good workaround that brings some of the simplicity I appreciate from Go into my C++ code.

#include <functional>
#include <iostream>

class Defer
{
	public:
	explicit Defer(std::function<void()> func)
			: func_(std::move(func))
			, active_(true)
	{}

	~Defer() noexcept
	{
		if(active_)
			func_();
	}

	void cancel()
	{
		active_ = false;
	} // Optionally cancel the deferred action

	private:
	std::function<void()> func_;
	bool active_;
};

// Helper function to simplify usage
template <typename F>
Defer defer(F&& func)
{
	return Defer(std::forward<F>(func));
}

// Example 1: Simple deferred action
void example1()
{
	std::cout << "Start of function" << std::endl;

	auto cleanup = defer([] { std::cout << "Deferred action executed" << std::endl; });

	std::cout << "End of function" << std::endl;
}

// Example 2: Updating the return value in the defer 
int example2()
{
	std::cout << "Allocating heap memory" << std::endl;
	int myVal = 10;
	defer([&myVal] {
		myVal = 11;
		std::cout << "Updated myVal on defer" << std::endl;
	});

	return myVal;
}

// Example 3: Demonstrating the cancel functionality
void example3()
{
	std::cout << "Start of function with cancel" << std::endl;

	auto cleanup = defer([] { std::cout << "Deferred action executed (should not run if canceled)" << std::endl; });

	// Simulate a condition where the deferred action is not needed
	bool condition = true;
	if(condition)
	{
		std::cout << "Condition met, canceling deferred action" << std::endl;
		cleanup.cancel(); // Cancel the deferred action
	}

	std::cout << "End of function with cancel" << std::endl;
}

int main()
{
	std::cout << "Example 1\n-----------------------" << std::endl;
	example1();

	std::cout << "\nExample 2\n-----------------------" << std::endl;
	int val = example2();
	std::cout << "Example 2 returned " << val << std::endl;

	std::cout << "\nExample 3\n-----------------------" << std::endl;
	example3();

	return 0;
}