Chain of Responsibility - C++

Yesterday, I somewhat implemented the Chain of Responsibility design pattern in C++. This was because I grew tired of writing code that constantly called into some C library, checked an error code, and continued on to call another function IF the error was not present. This lent itself naturally (in my opinion) to a Chain of Responsibility pattern, though I may not have implemented it perfectly.

Chain of Responsibility

This design pattern can be boiled down to one idea: a chain of handlers working together to form a “super” handler of sorts. Each link in the chain has the ability to break the execution and return with an error. The output of one handler is passed as the input to the next handler in the chain until all functions have executed.

A detailed, deep dive into Chain of Responsibility is a topic for a different blog post. For now, let’s see some code :)

template<typename T> // 1.
class ChainOfResponsibility {

    public:
    using Functors = std::vector<std::function<int(T)>>; // 2.

    explicit ChainOfResponsibility(Functors functions, T &input): 
        functions_ {std::move(functions)}, input_ {input} {};

    int execute() { // 3.
        for (auto &function : functions_)
        {
            auto error = function(input_); // 4.
            if ( error != ERROR_NO_ERROR )
            {
                printf("error happened: %s\n", error_message((error_t) error));
                return error;
            }
        }
    }

    private:
    Functors functions_;
    T input_;
};

So let’s explain this code sample by each number.

1. is declaring this ChainOfResponsibility class to be a template. I do this because I want to ensure the class is testable. Before, T was a specific type that baked in knowledge of the library that I was trying to create this design pattern abstraction for. Now, with the template declaration, I can test the ChainOfResponsibility class with a different type for input_

Example:

int myval = 12;
auto chain = ChainOfResponsibility{{func1, func2, func3}, myval};
chain.execute();

In my current setup, as long as func* are functions that return integers, and take an integer (myval) as input, then this chain will work as expected!

2. is a type alias, and it gives me an easier name Functors, for the much more complicated std::vector<std::function<int(T)>>. Breaking down my original type specification:

  1. std::vector is an STL collection that stores things.
  2. std::function is a polymorphic function type, used to represent functions as first class citizens.
  3. int(T) is my function type, saying that the function takes a T (from the template), and returns an int.

3. is where I execute the chain. The key thing to notice is how I check the error code after each function is executed, and if the error code is not my nice constant that declares nothing bad happened, break the chain. I return the error code from the chain as it happens so that I can check it from calling functions outside of the chain object.

4. is the chain moving along as planned. I don’t pass the input of one handler to another though. I instead opt to pass an object along and utilize (sometimes mutate) that object in each handler. The object should be able to tell me what state of the computation I ended in, and what happened along the way. In this code snippet, that object had some shared data required by all of the different handlers.

Example

Alright you made it this far, here’s an example of how to use this class.

auto handler1 = [](int myint) { printf("handler1: %d\n") };
auto handler2 = [](int myint) { printf("handler2: %d\n") };
auto chain = ChainOfResponsibility{{handler1, handler2}, 10};

Instantiate the chain with {} instantiation, passing a vector of handlers (C++ lambdas) and a value to pass to each. In our case an int.

 Share!

 
I run WindleWare! Feel free to reach out!

Subscribe for Exclusive Updates

* indicates required