Calling multiple functions for an input

Simply put, when I opt for a data-driven design, I separate the data from the behavior. Given an input as

struct Input {
    int value;
};

I pass it to some components that operate on it. I… call some functions.

void set(Input& in, int value) { input.value = value; }
void reset(Input& in) { input.value = 0; }

Input in{};
set(in, 2);
reset(in);

 

Because life is better with patterns, I’d want to have independent and configurable functions and a clear intent of their role and usage. Short story, a way to do this is a list of functions to be called with an input.

template<typename T, typename... Fs>
void apply(T& in, Fs&&... fs) {
    (fs(in), ...);
}

I’ve used the C++17 fold expression to unpack the template parameters (the list of functions).

 

And I can pass the input and the functions:

apply(in, set, reset);

But set does not have the signature required by the function calls in apply. The apply function requires a list of functions that have only the input parameter (the data that I am operating on). Thus set can return a lambda function with that signature:

auto set(int value) {
    return [value](Input& in) {
        in.value = value;
    };
}

apply(in, set(2), reset);

 

A thing that I don’t like about the apply function is that it can be called with no function as argument, only with the input. And it does not make any sense.

apply(in);

I want to be required to pass at least one function:

template<typename T, typename F, typename... Fs>
void apply(T& in, F&& f, Fs&&... fs) {
    f(in);
    (fs(in), ...);
}

 

Some usage examples that show how to create a business flow based on an input:

#include <cassert>
#include <iostream>

struct Input {
    int value;
};

auto set(int value) {
    return [value](Input& in) {
        in.value = value;
    };
}

auto add(int value) {
    return [value](Input& in) {
        in.value += value;
    };
}

void triple(Input& in) {
    in.value *= 3;
}

void print(const Input& in) {
    std::cout << "value = " << in.value << '\n';
}

class Reset {
    public:
    static void reset(Input& in) {
        in.value = 0;
    }
};

auto assert_eq(int value) {
    return [value](Input& in) {
        assert(in.value == value);
    };
}

template<typename T, typename F, typename... Fs>
void apply(T& in, F&& f, Fs&&... fs) {
    f(in);
    (fs(in), ...);
}

int main() {
    Input in{};

    apply(in,
        print,
        set(1), print,
        add(2), print,
        triple, print,
        assert_eq(9),
        [](const Input& in){
            std::cout << "final is = " << in.value << '\n';
        }
    );

    apply(in, set(5), assert_eq(5));

    apply(in, &Reset::reset, assert_eq(0));
}

Leave a Reply

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.