kelbon / AnyAny

C++17 library for comfortable and efficient dynamic polymorphism
Apache License 2.0
436 stars 13 forks source link

How i can use plugins without vtable calls? #17

Closed uselessgoddess closed 2 years ago

uselessgoddess commented 2 years ago

I write simple trait/interface Fizz:

template<typename T>
struct Fizz {
    static auto do_invoke(const T &self) -> bool;

    template <typename CRTP>
    struct plugin {
        [[nodiscard]] auto is_fizz() const {
            return aa::invoke<Fizz>(*static_cast<const CRTP*>(this));
        }
    };
};

I can write function that use it:

// I didn't write `Buzz`. This is obvious
auto fizz_buzz(const auto& self) -> bool {
    return aa::invoke<Fizz>(self) && aa::invoke<Buzz>(self);
}

I know: aa::invoke call do_invoke as static if self isn't any-like type But i cannot call plugin methods functions without const_poly_ref or etc.

Is there something like poly_or_static_ref?

kelbon commented 2 years ago

Heh, its interesting... You can use smth like this:

// do not works for fundamental types
template <typename T, TTA... Methods>
struct AA_MSVC_EBO static_ref : T, aa::plugin_t<Methods, static_ref<T, Methods...>>... {
  static_ref() = delete;
  static_ref(const static_ref&) = delete;
  void operator=(const static_ref&) = delete;
};
template<TTA... Methods, typename T>
static_ref<T, Methods...>& make_static_ref(T& value) noexcept {
  return static_cast<static_ref<T, Methods...>&>(value);
}

https://godbolt.org/z/Tq3ab4K6P

uselessgoddess commented 2 years ago

I try to make the smartest static or poly ref sop_ref:

struct infallible{};

template <TTA... Methods>
constexpr inline bool satisfies_v<infallible, Methods...> = true;

template <typename T, TTA... Methods>
using static_or_poly = std::variant<static_ref<T, Methods...>*, poly_ref<Methods...>>;

// static or poly
template <typename T, TTA... Methods>
struct sop_ref : plugin_t<Methods, sop_ref<T, Methods...>>... {
    static_or_poly<T, Methods...> sop;

    sop_ref(poly_ref<Methods...> ref) : sop(std::move(ref)) {}
    sop_ref(static_ref<T, Methods...>& ref) : sop(ref) {}
    sop_ref(T& ref) : sop(&make_static_ref<Methods...>(ref)) {}
};

template <TTA... Methods>
sop_ref(poly_ref<Methods...>) -> sop_ref<infallible, Methods...>;

// in `invoke_fn`
template <typename T, TTA... Methods>
result_t<Method> operator()(sop_ref<T, Methods...> p, Args... args) const {
    return std::visit(/* impl */, p.sop);
}

But it doesn't make sense. C++ not have basic type infer :(

template <typename T>
auto smart_foo(aa::sop_ref<T, Foo> ref) {
  return ref.foo();
}
. . . 
auto val = xd{10};
// not works 
smart_foo(val);
// works :(
smart_foo(aa::sop_ref<xd, Foo>(val));
uselessgoddess commented 2 years ago

Right now, I can only hope for compiler optimizations for poly_ref.

kelbon commented 2 years ago

Why not use overload in this case? smart_foo(poly_ref) smart_foo(auto&&) / smart_foo(static_ref) if you want to use plugin methods

uselessgoddess commented 2 years ago

For the same reason that I want to use plugins. self.foo() looks shorter than aa::invoke<Foo>(self)

kelbon commented 2 years ago

I try to make the smartest static or poly ref sop_ref:

struct infallible{};

template <TTA... Methods>
constexpr inline bool satisfies_v<infallible, Methods...> = true;

template <typename T, TTA... Methods>
using static_or_poly = std::variant<static_ref<T, Methods...>*, poly_ref<Methods...>>;

// static or poly
template <typename T, TTA... Methods>
struct sop_ref : plugin_t<Methods, sop_ref<T, Methods...>>... {
    static_or_poly<T, Methods...> sop;

    sop_ref(poly_ref<Methods...> ref) : sop(std::move(ref)) {}
    sop_ref(static_ref<T, Methods...>& ref) : sop(ref) {}
    sop_ref(T& ref) : sop(&make_static_ref<Methods...>(ref)) {}
};

template <TTA... Methods>
sop_ref(poly_ref<Methods...>) -> sop_ref<infallible, Methods...>;

// in `invoke_fn`
template <typename T, TTA... Methods>
result_t<Method> operator()(sop_ref<T, Methods...> p, Args... args) const {
    return std::visit(/* impl */, p.sop);
}

But it doesn't make sense. C++ not have basic type infer :(

template <typename T>
auto smart_foo(aa::sop_ref<T, Foo> ref) {
  return ref.foo();
}
. . . 
auto val = xd{10};
// not works 
smart_foo(val);
// works :(
smart_foo(aa::sop_ref<xd, Foo>(val));

hmm, may be i will add specialization for aa::interface_t (satisfy)

kelbon commented 2 years ago

I try to make the smartest static or poly ref sop_ref:

struct infallible{};

template <TTA... Methods>
constexpr inline bool satisfies_v<infallible, Methods...> = true;

template <typename T, TTA... Methods>
using static_or_poly = std::variant<static_ref<T, Methods...>*, poly_ref<Methods...>>;

// static or poly
template <typename T, TTA... Methods>
struct sop_ref : plugin_t<Methods, sop_ref<T, Methods...>>... {
    static_or_poly<T, Methods...> sop;

    sop_ref(poly_ref<Methods...> ref) : sop(std::move(ref)) {}
    sop_ref(static_ref<T, Methods...>& ref) : sop(ref) {}
    sop_ref(T& ref) : sop(&make_static_ref<Methods...>(ref)) {}
};

template <TTA... Methods>
sop_ref(poly_ref<Methods...>) -> sop_ref<infallible, Methods...>;

// in `invoke_fn`
template <typename T, TTA... Methods>
result_t<Method> operator()(sop_ref<T, Methods...> p, Args... args) const {
    return std::visit(/* impl */, p.sop);
}

But it doesn't make sense. C++ not have basic type infer :(

template <typename T>
auto smart_foo(aa::sop_ref<T, Foo> ref) {
  return ref.foo();
}
. . . 
auto val = xd{10};
// not works 
smart_foo(val);
// works :(
smart_foo(aa::sop_ref<xd, Foo>(val));

hm, why you cant change realization invoke for !any type such that it will invoke through static_ref, so you can use plugins?..

uselessgoddess commented 2 years ago

hm, why you cant change realization invoke for !any type such that it will invoke through static_ref, so you can use plugins?..

Implementation, bro))

I would like to use static and dynamic refs in one function. I try to use static_ref with poly_ref – not works. (double inherit of plugin_t)

auto val = xd{123};
std::cout << smart_foo(aa::make_static_ref<Foo>(val)) << std::endl;
aa::poly_ref<Foo> ref = val;
std::cout << smart_foo(aa::make_static_ref<Foo>(ref)) << std::endl;

And it's too long))