Skip to content

Commit

Permalink
Improve implementation quality and documentation (#77)
Browse files Browse the repository at this point in the history
  • Loading branch information
mingxwa authored Apr 1, 2024
1 parent 5345b26 commit 0693279
Show file tree
Hide file tree
Showing 9 changed files with 155 additions and 160 deletions.
36 changes: 18 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,17 @@ The "proxy" is a single-header, cross-platform C++ library that Microsoft uses t

The "proxy" is a header-only C++20 library. Once you set the language level of your compiler not earlier than C++20 and get the header file ([proxy.h](proxy.h)), you are all set. You can also install the library via [vcpkg](https://github.com/microsoft/vcpkg/), which is a C++ library manager invented by Microsoft, by searching for "proxy" (see [vcpkg.info](https://vcpkg.info/port/proxy)).

The majority of the library is defined in namespace `pro`. Some macros are provided (currently not included in the proposal of standardization) to simplify the definiton of `proxy` prior to C++26. Here is a demo showing how to use this library to implement runtime polymorphism in a different way from the traditional inheritance-based approach:
The majority of the library is defined in namespace `pro`. Some macros are provided (currently not included in the proposal for standardization) to simplify the definition of `proxy` prior to C++26. Here is a demo showing how to use this library to implement runtime polymorphism in a different way from the traditional inheritance-based approach:

```cpp
// Abstraction (poly is short for polymorphism)
namespace poly {
// Specifications of abstraction
namespace spec {

PRO_DEF_MEMBER_DISPATCH(Draw, void(std::ostream& out));
PRO_DEF_MEMBER_DISPATCH(Area, double() noexcept);
PRO_DEF_FACADE(Drawable, PRO_MAKE_DISPATCH_PACK(Draw, Area));

} // namespace poly
} // namespace spec

// Implementation
class Rectangle {
Expand All @@ -45,36 +45,36 @@ class Rectangle {
};

// Client - Consumer
std::string PrintDrawableToString(pro::proxy<poly::Drawable> p) {
std::string PrintDrawableToString(pro::proxy<spec::Drawable> p) {
std::stringstream result;
result << "shape = ";
p.invoke<poly::Draw>(result);
result << ", area = " << p.invoke<poly::Area>();
p.invoke<spec::Draw>(result);
result << ", area = " << p.invoke<spec::Area>();
return std::move(result).str();
}

// Client - Producer
pro::proxy<poly::Drawable> CreateRectangleAsDrawable(int width, int height) {
pro::proxy<spec::Drawable> CreateRectangleAsDrawable(int width, int height) {
Rectangle rect;
rect.SetWidth(width);
rect.SetHeight(height);
return pro::make_proxy<poly::Drawable>(rect);
return pro::make_proxy<spec::Drawable>(rect);
}
```
Here is another demo showing how to define overloads in a dispatch. Note that `.invoke<>` can be ommitted when only 1 dispatch is defined in a facade:
```cpp
// Abstraction (poly is short for polymorphism)
namespace poly {
// Specifications of abstraction
namespace spec {
PRO_DEF_MEMBER_DISPATCH(Log, void(const char*), void(const char*, const std::exception&));
PRO_DEF_FACADE(Logger, Log);
} // namespace poly
} // namespace spec
// Client - Consumer
void MyVerboseFunction(pro::proxy<poly::Logger> logger) {
void MyVerboseFunction(pro::proxy<spec::Logger> logger) {
logger("hello");
try {
throw std::runtime_error{"runtime error!"};
Expand Down Expand Up @@ -104,8 +104,8 @@ int main() {
By design, the body of a dispatch could be any code. While member function is one useful pattern supported by macro `PRO_DEF_MEMBER_DISPATCH`, free function is also supported with another macro `PRO_DEF_FREE_DISPATCH`. The following example uses `PRO_DEF_FREE_DISPATCH` and `std::invoke` to implement similar function wrapper as `std::function` and `std::move_only_function` and supports multiple overloads.

```cpp
// Abstraction (poly is short for polymorphism)
namespace poly {
// Specifications of abstraction
namespace spec {

template <class... Overloads>
PRO_DEF_FREE_DISPATCH(Call, std::invoke, Overloads...);
Expand All @@ -114,14 +114,14 @@ PRO_DEF_FACADE(MovableCallable, Call<Overloads...>);
template <class... Overloads>
PRO_DEF_FACADE(CopyableCallable, Call<Overloads...>, pro::copyable_ptr_constraints);

} // namespace poly
} // namespace spec

// MyFunction has similar functionality as std::function but supports multiple overloads
// MyMoveOnlyFunction has similar functionality as std::move_only_function but supports multiple overloads
template <class... Overloads>
using MyFunction = pro::proxy<poly::MovableCallable<Overloads...>>;
using MyFunction = pro::proxy<spec::CopyableCallable<Overloads...>>;
template <class... Overloads>
using MyMoveOnlyFunction = pro::proxy<poly::CopyableCallable<Overloads...>>;
using MyMoveOnlyFunction = pro::proxy<spec::MovableCallable<Overloads...>>;

int main() {
auto f = [](auto&&... v) {
Expand Down
123 changes: 59 additions & 64 deletions proxy.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,15 @@ consteval bool is_tuple_like_well_formed() {
return false;
}

template <template <class...> class T, class TL, class Is> struct instantiated;
template <template <class...> class T, class TL, std::size_t... Is>
struct instantiated<T, TL, std::index_sequence<Is...>>
{ using type = T<std::tuple_element_t<Is, TL>...>; };
template <template <class...> class T, class TL>
using instantiated_t = typename instantiated<
T, TL, std::make_index_sequence<std::tuple_size_v<TL>>>::type;
template <template <class...> class T, class TL, class Is, class... Args>
struct instantiated_traits;
template <template <class...> class T, class TL, std::size_t... Is,
class... Args>
struct instantiated_traits<T, TL, std::index_sequence<Is...>, Args...>
{ using type = T<Args..., std::tuple_element_t<Is, TL>...>; };
template <template <class...> class T, class TL, class... Args>
using instantiated_t = typename instantiated_traits<
T, TL, std::make_index_sequence<std::tuple_size_v<TL>>, Args...>::type;

template <class T>
consteval bool has_copyability(constraint_level level) {
Expand Down Expand Up @@ -221,35 +223,33 @@ struct composite_meta : Ms... {
{ first_applicable_t<nullable_traits, Ms...>::reset(); }
};

template <class D>
struct dispatch_helper {
template <class... Os> struct traits : inapplicable_traits {};
template <class... Os>
requires(sizeof...(Os) > 0u && (overload_traits<Os>::applicable && ...))
struct traits<Os...> : applicable_traits {
private:
struct overload_resolver : overload_traits<Os>::resolver...
{ using overload_traits<Os>::resolver::operator()...; };

public:
using meta = composite_meta<dispatcher_meta<
typename overload_traits<Os>::template meta_provider<D>>...>;
template <class... Args>
using matched_overload =
std::remove_pointer_t<std::invoke_result_t<overload_resolver, Args...>>;
template <class D, class... Os>
struct dispatch_traits_impl : inapplicable_traits {};
template <class D, class... Os>
requires(sizeof...(Os) > 0u && (overload_traits<Os>::applicable && ...))
struct dispatch_traits_impl<D, Os...> : applicable_traits {
private:
struct overload_resolver : overload_traits<Os>::resolver...
{ using overload_traits<Os>::resolver::operator()...; };

template <class P>
static constexpr bool applicable_ptr =
(overload_traits<Os>::template applicable_ptr<D, P> && ...);
};
public:
using meta = composite_meta<dispatcher_meta<
typename overload_traits<Os>::template meta_provider<D>>...>;
template <class... Args>
using matched_overload =
std::remove_pointer_t<std::invoke_result_t<overload_resolver, Args...>>;

template <class P>
static constexpr bool applicable_ptr =
(overload_traits<Os>::template applicable_ptr<D, P> && ...);
};
template <class D> struct dispatch_traits : inapplicable_traits {};
template <class D>
requires(requires { typename D::overload_types; } &&
is_tuple_like_well_formed<typename D::overload_types>() &&
std::is_trivially_default_constructible_v<D>)
struct dispatch_traits<D> : instantiated_t<
dispatch_helper<D>::template traits, typename D::overload_types> {};
dispatch_traits_impl, typename D::overload_types, D> {};

template <constraint_level C> struct copyability_meta_provider;
template <>
Expand Down Expand Up @@ -331,34 +331,33 @@ template <class... Ds>
struct default_dispatch_traits { using default_dispatch = void; };
template <class D>
struct default_dispatch_traits<D> { using default_dispatch = D; };
template <class F>
struct facade_helper {
template <class... Ds> struct traits : inapplicable_traits {};
template <class... Ds> requires(dispatch_traits<Ds>::applicable && ...)
struct traits<Ds...> : applicable_traits, default_dispatch_traits<Ds...> {
using copyability_meta = lifetime_meta<
copyability_meta_provider, F::constraints.copyability>;
using relocatability_meta = lifetime_meta<
relocatability_meta_provider, F::constraints.relocatability>;
using destructibility_meta = lifetime_meta<
destructibility_meta_provider, F::constraints.destructibility>;
using meta = recursive_reduction_t<facade_meta_reduction,
composite_meta<>, copyability_meta, relocatability_meta,
destructibility_meta, typename dispatch_traits<Ds>::meta...,
typename F::reflection_type>;

template <class D>
static constexpr bool has_dispatch = (std::is_same_v<D, Ds> || ...);
template <class P>
static constexpr bool applicable_ptr =
sizeof(P) <= F::constraints.max_size &&
alignof(P) <= F::constraints.max_align &&
has_copyability<P>(F::constraints.copyability) &&
has_relocatability<P>(F::constraints.relocatability) &&
has_destructibility<P>(F::constraints.destructibility) &&
(dispatch_traits<Ds>::template applicable_ptr<P> && ...) &&
is_reflection_type_well_formed<typename F::reflection_type, P>();
};
template <class F, class... Ds>
struct facade_traits_impl : inapplicable_traits {};
template <class F, class... Ds> requires(dispatch_traits<Ds>::applicable && ...)
struct facade_traits_impl<F, Ds...>
: applicable_traits, default_dispatch_traits<Ds...> {
using copyability_meta = lifetime_meta<
copyability_meta_provider, F::constraints.copyability>;
using relocatability_meta = lifetime_meta<
relocatability_meta_provider, F::constraints.relocatability>;
using destructibility_meta = lifetime_meta<
destructibility_meta_provider, F::constraints.destructibility>;
using meta = recursive_reduction_t<facade_meta_reduction,
composite_meta<>, copyability_meta, relocatability_meta,
destructibility_meta, typename dispatch_traits<Ds>::meta...,
typename F::reflection_type>;

template <class D>
static constexpr bool has_dispatch = (std::is_same_v<D, Ds> || ...);
template <class P>
static constexpr bool applicable_ptr =
sizeof(P) <= F::constraints.max_size &&
alignof(P) <= F::constraints.max_align &&
has_copyability<P>(F::constraints.copyability) &&
has_relocatability<P>(F::constraints.relocatability) &&
has_destructibility<P>(F::constraints.destructibility) &&
(dispatch_traits<Ds>::template applicable_ptr<P> && ...) &&
is_reflection_type_well_formed<typename F::reflection_type, P>();
};
template <class F> struct facade_traits : inapplicable_traits {};
template <class F>
Expand All @@ -370,7 +369,7 @@ template <class F>
} && is_tuple_like_well_formed<typename F::dispatch_types>() &&
is_facade_constraints_well_formed<F>())
struct facade_traits<F> : instantiated_t<
facade_helper<F>::template traits, typename F::dispatch_types> {};
facade_traits_impl, typename F::dispatch_types, F> {};

using ptr_prototype = void*[2];

Expand Down Expand Up @@ -818,18 +817,14 @@ struct dispatch_prototype_helper {
};

template <class O, class I> struct flat_reduction : std::type_identity<O> {};
template <class O>
struct flat_reduction_helper {
template <class... Is>
using type = recursive_reduction<flat_reduction, O, Is...>;
};
template <class O, class... Is>
struct flat_reduction_impl : recursive_reduction<flat_reduction, O, Is...> {};
template <class... Os, class I>
requires(!is_tuple_like_well_formed<I>() && (!std::is_same_v<I, Os> && ...))
struct flat_reduction<std::tuple<Os...>, I>
: std::type_identity<std::tuple<Os..., I>> {};
template <class O, class I> requires(is_tuple_like_well_formed<I>())
struct flat_reduction<O, I>
: instantiated_t<flat_reduction_helper<O>::template type, I> {};
struct flat_reduction<O, I> : instantiated_t<flat_reduction_impl, I, O> {};
template <class O, class I>
struct overloads_reduction : std::type_identity<O> {};
template <class O, class I> requires(requires { typename I::overload_types; })
Expand Down
6 changes: 3 additions & 3 deletions samples/resource_dictionary/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@

#include <proxy/proxy.h>

namespace poly {
namespace spec {

PRO_DEF_MEMBER_DISPATCH(at, std::string(int));
PRO_DEF_FACADE(Dictionary, at);

} // namespace poly
} // namespace spec

void demo_print(pro::proxy<poly::Dictionary> dictionary) {
void demo_print(pro::proxy<spec::Dictionary> dictionary) {
std::cout << dictionary(1) << std::endl;
}

Expand Down
Loading

0 comments on commit 0693279

Please sign in to comment.