C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Draft Paper - Increase the versatility of operator->

From: Frederick Virchanza Gotham <cauldwell.thomas_at_[hidden]>
Date: Thu, 22 Sep 2022 16:45:26 +0100
On Wed, Sep 21, 2022 at 7:13 PM Breno GuimarĂ£es via Std-Proposals
<std-proposals_at_[hidden]> wrote:

> Such that:
>
> v->Init();
>
> would be transformed by the compiler into
>
> v.operator->( [&] (auto& arg) { arg.Init(); } );
>
> By doing so, all you need is the ability to pass more arguments to the operator-> (today it's forbidden).
> See how that would work for variant: https://godbolt.org/z/xxKnE44dr


Breno I found your code up on GodBolt very helpful, thank you. I'm
made some changes to it:

            https://godbolt.org/z/KaMdv1fqn

And also here it is copy-pasted:

#include <variant> // We're adding functionality to std::variant

#include <cstddef> // size_t
#include <tuple> // tuple, tuple_size_v, std::get<>()
#include <functional> // invoke
#include <utility> // forward, index_sequence, make_index_sequence
#include <type_traits> // remove_reference_t

namespace detail {

// The standard library has a function called 'apply'
// but I need to make a new function called 'apply_with_pointer'
// which has an extra parameter. So instead of invoking as follows:
// std::apply(func, my_tuple);
// , we instead invoke like this:
// apply_with_pointer(func, some_pointer, my_tuple);

template <class F, class Tuple, std::size_t... I>
constexpr decltype(auto) apply_with_pointer_impl(F &&f, void *const p,
Tuple &&t, std::index_sequence<I...>)
{
    return std::invoke(std::forward<F>(f), p,
std::get<I>(std::forward<Tuple>(t))...);
}

template <class F, class Tuple>
constexpr decltype(auto) apply_with_pointer(F &&f, void *const p, Tuple &&t)
{
    return apply_with_pointer_impl(
        std::forward<F>(f), p, std::forward<Tuple>(t),
        std::make_index_sequence<std::tuple_size_v<std::remove_reference_t<Tuple>>>{});
}

} // close namespace 'detail'

struct A {
    int init(int const arg) { return arg + 7; }
};

struct B {
    int init(int const arg) { return arg - 3; }
};

template<typename... Types>
struct NewStdVariant : public std::variant<Types...> {

    typedef std::variant<Types...> V;

    using V::V;

    template<class Visitor>
    auto operator_arrow(Visitor &&visitor)
    {
        return std::visit(visitor, *static_cast<V*>(this) );
    }
};

template<typename R, typename... Args>
struct HelperForArrowOperator {

    std::tuple<Args...> stored_args;

    HelperForArrowOperator(Args... args) : stored_args(args...) {}

    template<class T>
    static R invoke(void *const arg_p, Args... args)
    {
        return static_cast<T*>(arg_p)->init( args... );
    }

    template<class T>
    R operator()(T &obj)
    {
        return detail::apply_with_pointer(invoke<T>, &obj, stored_args);
    }
};

#include <iostream>

template<typename... Types>
void foo(NewStdVariant<Types...> &v)
{
    // The following line:
    //
    // std::cout << v->init(5) << std::endl;
    //
    // gets translated into:

    HelperForArrowOperator<
        /* return type */ int,
        /* 1st parameter type */ int> monkey( /* 1st argument */ 5);

    std::cout << v.operator_arrow(monkey) << std::endl;
}

int main(void)
{
    NewStdVariant<A,B> obj;

    foo(obj);

    obj.emplace<B>();

    foo(obj);
}

Received on 2022-09-22 15:45:38