ranges::view 管道运算符 - 通过将 std::variant 参数隐藏到变量->选项模式来实现 std::visit 的单子链?

问题描述 投票:0回答:1

我的灵感来自 std::ranges 和 std::view 及其 |有助于链接不同算法的运算符,所以我希望将 std::get_if 和 std::visit 模式打包在引擎盖下,以进行就地可构造的非修改(constexpr?) 获得我的 std::variant<...> 的包装器,我可以指定我在 lambda 中工作的类型 想法是将其用作 selftink 模式“可选变体访客视图?”带注入器的链式单子视图方法 << for nullopt value C++17 - C++20 and idea to use template concepts instead of direct c++14 style concepts.

使用示例:

int main() {
   std::optional<std::variant<int, float, bool>> myVariant = 42;
   /* NOTE: u can use any order when building visiting chain
   but don't repeat sequentially or non sequential same types like passing type<int> .... 
   others type<int> since second should generate static assert "int handler proceed 
   before" on first duplication occured */
   variants(myVariant) << no_option([]() { /* what we do when no variant i.e std::nullopt - comment if unneeded*/; })
        | on_type<bool>([](const auto& value) {})
        | on_type<int>([](const auto& value) {}) 
    //  | on_type<string>([](const auto& value) {}) // should produce static assert type handler for type not listed in variant
        | on_type<float>([](const auto& value) {})
    //  | on_type<int>([](const auto& value) {}) // should produce static_assert duplicate in handler chain
        | no_type(); // chain close checker will produce static assert that we forget to add some type(s) if so, that listed in variant, comment if not all types needed and we forced skip handling there

   return 0;
}

这里的代码我无法完成在编译时进行类型检查以生成正确的静态断言,并且没有线索完成我的 unordered_set 以防止用户重复处理程序,例如 apply SFINAE - 需要等。 .

template <typename T>
struct on_type {
    using type_t = T;
    on_type(std::function<void(const T&)> callback) : callback_func(callback) {}

    void if_type(const T& value) const { callback_func(value); }
    std::function<void(const T&)> callback_func;
};

struct no_type {
    // TODO: add decltype decay etc to print type in static assert
    template <bool asserted, typename unhandled_in_chain>
    void if_true() const { static_assert(asserted, "type X not handled in chain"); }
};

struct no_option {
    no_option(std::function<void()> callback) : callback_func(std::move(callback)) {}
    void if_true() const { callback_func(); }
    std::function<void()> callback_func;
};

template <typename... Ts>
struct VariantHandler {

    using variant_t = std::variant<Ts...>;
    using optional_variant_t = std::optional < variant_t >;

    VariantHandler( const variant_t& from ) : variant_holder(from) {}
    VariantHandler( const optional_variant_t &from ) {
        if (from.has_value())
            variant_holder = from;
        else
            variant_holder = std::nullopt;
    }

    optional_variant_t variant_holder;

    mutable std::unordered_set<std::size_t> processedTypes{};

    template <typename T>
    const VariantHandler< Ts... > & operator | (const on_type<T>& type_handler) const {
        // FIXME expression don't evaluate to constant
        //static_assert(processedTypes.contains(typeid(T).hash_code()) == 0, "Duplicate type handler encountered");
        processedTypes.insert(typeid(T).hash_code());

        if (true == variant_holder.has_value()) {
            std::visit([&](const auto& value) {
                if constexpr (std::is_same_v<std::decay_t<decltype(value)>, T>) {
                    type_handler.if_type(value);
                }
            }, variant_holder.value());
        }

        return *this; // Or return a proxy object for further chaining
    }

    const VariantHandler< Ts... > operator << (const no_option &on_true) const {
        if (false == variant_holder.has_value())
            on_true.if_true();
        return *this;
    }

    // called for all remaining types in variant that not handled through on_type chain
    //template <typename T>
    constexpr void operator | (const no_type& when_no_type) const {
        // TODO: how we check that type handler not registered when listed through | operator in chain?
        when_no_type.if_true<true, bool>();
    }

    // TODO: determine which type we forget to add listener if we use no_type static last param it will assert to us
};

// TODO: add 'requires' to valid variant types check and static assert for on_type<T> where T is not part of Ts...
template <typename... Ts>
constexpr auto variants(const std::variant<Ts...>& variant) {
    return VariantHandler<Ts...>{variant};
}
template <typename... Ts>
constexpr auto variants(const std::optional<std::variant<Ts...>>& optional_variant) {
    return VariantHandler<Ts...>{optional_variant};
}
c++ c++20 variadic-templates sfinae c++-concepts
1个回答
0
投票

因此,我制作了基于工作范围视图的方法来构建访问可选变体实用程序类,感谢所有建议! https://github.com/ivitaly/variant_view/tree/main

© www.soinside.com 2019 - 2024. All rights reserved.