Search code examples
c++callablecallable-object

chaining callables in C++


I come from the python world where I could define a chain of operations and call them in a for loop:

class AddOne:
    def __call__(self, x, **common_kwargs):
        return x+1
class Stringify:
    def __call__(self, x, **common_kwargs):
        return str(x)
class WrapNicely:
    def __call__(self, s, **common_kwargs):
        return "result="+s
data = 42
for operation in [AddOne(), Stringify(), WrapNicely()]:
    data = operation(data)
output = data

(Note: the goal is to have complex operations. Ideally, common kwargs could be given)

What would be the equivalent in C++ if the return type can be different after each call?

I'm not sure I could find anything close but I may have search with wrong keywords…


Solution

  • C++ is statically typed, so options here are limited:

    • Create a chain of functions that can be determined at compile time.
    • Create functions with parameter and return type being the same
    • Return a type that could "store multiple alternative types" such as std::variant

    For the first alternative you could create a class template that executes functions via recursive calls, but it's a bit more complex than your python code:

    template<class...Fs>
    class Functions
    {
        std::tuple<Fs...> m_functions;
    
        template<size_t index, class Arg>
        decltype(auto) CallHelper(Arg&& arg)
        {
            if constexpr (index == 0)
            {
                return std::forward<Arg>(arg);
            }
            else
            {
                return std::get<index - 1>(m_functions)(CallHelper<index - 1>(std::forward<Arg>(arg)));
            }
        }
    
    public:
        Functions(Fs...functions)
            : m_functions(functions...)
        {
        }
    
        template<class Arg>
        decltype(auto) operator()(Arg&& arg)
        {
            return CallHelper<sizeof...(Fs)>(std::forward<Arg>(arg));
        }
    };
    
    int main() {
        Functions f{
            [](int x) { return x + 1; },
            [](int x) { return std::to_string(x); },
            [](std::string const& s) { return "result=" + s; }
        };
    
        std::cout << f(42) << '\n';
    }
    

    Note: This requires the use of a C++ standard of at least C++17.