Skip to content

Deduce the template parameter of a variant based on every return path

License

Notifications You must be signed in to change notification settings

Tradias/variate

Repository files navigation

variate

Quality Gate Status

Deduce the template parameter of a variant based on every return path. Ideally, the C++ language would provide something like:

std::variant auto func(bool ok)
{
    if (ok)
        return 1.5f;
    return "example";
}

static_assert(std::is_same_v<decltype(func()), std::variant<float, const char*>>);

This library attempts to mimic such functionality with minimal boilerplate and (zero) overhead:

auto func(bool ok)
{
    static constexpr dehe::Variate var;
    if (ok)
        return var(1.5f);
    return var("example");
}

static_assert(std::is_same_v<decltype(dehe::make_variant(func())), std::variant<float, const char*>>);

Play with it on godbolt.

Motivation

With the advent of std::execution, C++ developers are faced with more and more unnamable types. All sender types are considered implementation details and can only be identified using something like decltype(std::execution::just(42)). Additionally, we often must write functional-style code when dealing with std::execution, meaning we must return the same sender type from all return paths of our functions. This makes it difficult to express conditional branches. Libunifex provides a variant_sender class template that we can hook into this library to ease the process:

// Code to bridge unifex to this library
static constexpr auto variant_sender_factory = []<std::size_t, class... T, class Arg>(Arg && arg)
{
    return unifex::variant_sender<T...>{std::forward<Arg>(arg)};
};
auto make_variant_sender(auto&& erased)
{
    return dehe::make(std::forward<decltype(erased)>(erased), variant_sender_factory);
}

// Our actual business logic containing branches. Without this library we would need to spell out the return type:
// `unifex::variant_sender<decltype(unifex::just(5)), decltype(unifex::just() | unifex::then(lambda))>`
// and move the lambda into a separate place.
auto business_logic(bool ok)
{
    static constexpr dehe::Variate var;
    if (ok)
    {
        return var(unifex::just(5));
    }
    return var(unifex::just() | unifex::then(
                                    []
                                    {
                                        return 42;
                                    }));
}

// Imagine some chain of senders:
    | unifex::let_value([](bool ok)
                        {
                            return make_variant_sender(business_logic(ok));
                        });

Installation

Copy the headers from src/variate into your project.

Alternatively, use CMake to install the project. From the root of the repository:

cmake -B build -S .
cmake --install build --prefix build/out

And in your CMake project's CMakeLists.txt:

# Add build/out to the CMAKE_PREFIX_PATH
find_package(variate)
target_link_libraries(my_app PRIVATE variate::variate)

Usage

Include the single header:

#include <variate/variate.hpp>

Requirements

The only requirement is a small subset of C++20.

The following compilers are continuously tested by Github Actions:

  • GCC 10
  • Clang 12
  • MSVC 19.34
  • AppleClang 14

About

Deduce the template parameter of a variant based on every return path

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published