-1
\$\begingroup\$

I was reading about the C++ proposal to drop continuation tasks (which supposedly should come since c++14) in favor of execution pool, executor, and sender/receiver semantics. Was wondering how hard could it be to implement basic functionality of a continuation task. So I wrote a little something:

struct _StorageBase
{
};

template<typename R, typename Arg>
struct _Storage : _StorageBase
{
    explicit _Storage(std::function<R(Arg)> __fn) : _fn(std::move(__fn)) {}
    std::function<R(Arg)> _fn;
};

template<typename..._FollowupType>
class Continuity
{
    template<std::size_t N>
    using nth_type = typename std::tuple_element_t<N, std::tuple<_FollowupType...>>;
public:
    explicit Continuity(std::function<nth_type<1>(nth_type<0>)> __callable)
    {
        _chain = std::make_shared<std::vector<std::unique_ptr<_StorageBase>>>();
        _chain->push_back(std::make_unique<_Storage<nth_type<1>, nth_type<0>>>(std::move(__callable)));
    }
    explicit Continuity(std::shared_ptr<std::vector<std::unique_ptr<_StorageBase>>> __ptr)
            : _chain(std::move(__ptr))
    {
    }

    template<typename R>
    auto then(std::function<R(nth_type<0>)> __callable)
    {
        _chain->push_back(std::make_unique<_Storage<R, nth_type<0>>>(std::move(__callable)));
        return Continuity<_FollowupType..., R>(_chain);
    }

    void invoke_chain(nth_type<0> val)
    {
        invoke_chain_helper<0>(val);
    }
private:
    template<std::size_t I>
    std::enable_if_t<I < sizeof...(_FollowupType) - 1> invoke_chain_helper(auto... val)
    {
        /* Begin followups */
        if constexpr (std::is_void_v<nth_type<I + 1>>)
        {
            reinterpret_cast<_Storage<nth_type<I + 1>, nth_type<I>>*>(_chain->at(I).get())->_fn(val...);
        }
        else
        {
            auto rv = reinterpret_cast<_Storage<nth_type<I + 1>, nth_type<I>>*>(_chain->at(I).get())->_fn(val...);
            invoke_chain_helper<I + 1>(rv);
        }
    }

    template<std::size_t I>
    std::enable_if_t<I == sizeof...(_FollowupType) - 1> invoke_chain_helper(auto... val)
    {
        reinterpret_cast<_Storage<nth_type<I + 1>, nth_type<I>>*>(_chain->at(I).get())->_fn(val...);
    }

    std::shared_ptr<std::vector<std::unique_ptr<_StorageBase>>> _chain;
};

It does not do anything helpful atm. The main idea was to track return types of callables somehow. In the future, I'd like to expand it with proper task managing, signaling when the current task has finished its execution so the next one can start, etc.

Did I get the part about types tracking right, or is there a better alternative?

\$\endgroup\$
5
  • 4
    \$\begingroup\$ Can you please add some test cases so we can see how this is used. \$\endgroup\$
    – pacmaninbw
    Sep 21 at 13:35
  • \$\begingroup\$ (Down-voters please comment.) \$\endgroup\$
    – greybeard
    Sep 25 at 12:20
  • \$\begingroup\$ If you show us how it's used, we could help with getting that done better. Whether the tracking was done right depends on how you define tracking and whether the rest of the usage will break it anyway or not. \$\endgroup\$
    – Mast
    Sep 26 at 16:57
  • \$\begingroup\$ @greybeard I commented, 24 hours later I down voted. \$\endgroup\$
    – pacmaninbw
    Sep 27 at 14:44
  • \$\begingroup\$ (@pacmaninbw. Fine, that's about my grace period for downvoting recent posts. (Once I think a question should not be answered as is, I'm pretty quick to vote to close it.)) \$\endgroup\$
    – greybeard
    Sep 27 at 14:51

0

Browse other questions tagged or ask your own question.