#ifndef _INVOKE_CALLABLE #define _INVOKE_CALLABLE #include #include #include // Only for std::size() /* * This file defines the function: * enumerate( * Container container, * Callable callable * ) * The enumerate function will iterate over all elements in the container * calling the callable object with as many arguments as possible: * callable(*it, index, sized) * callable(*it, index) * callable(*it) */ /* * Concepts filtering Callable objects by the most arguments * their operator() takes. */ template concept TriArg = requires(Callable c, T t, std::size_t n) { c(t, n, n); }; template concept DuoArg = !TriArg && requires(Callable c, T t, std::size_t n) { c(t, n); }; template concept UniArg = !TriArg && !DuoArg && requires(Callable c, T t) { c(t); }; /* * Functions invoking Callable with actual arguments. * Constrained by the n-argument Concepts above. */ template typename std::enable_if< TriArg, void>::type invoke_on_callable(Callable c, T elem, std::size_t index, std::size_t size) { c(elem, index, size); } template typename std::enable_if< DuoArg, void >::type invoke_on_callable(Callable c, T elem, std::size_t index, std::size_t) { c(elem, index); } template typename std::enable_if< UniArg, void >::type invoke_on_callable(Callable c, T elem, std::size_t, std::size_t) { c(elem); } /* * enumerate() */ template void enumerate(Container const& container, Callable callable) { std::size_t i{0}; std::size_t size{ std::size(container) }; for (auto item : container) { invoke_on_callable(callable, item, i++, size); } } #endif