Skip to content

Commit

Permalink
Implement make_proxy_inplace and inplace_proxiable_target with freest…
Browse files Browse the repository at this point in the history
…anding (#92)

* Implement make_proxy_inplace and inplace_proxiable_target with freestanding

* Add freestanding tests

* Merge

* Fix regression in code generation for `copying_default_dispatcher`

* Fix build

* Add noexcept

* Remove empty line

* Resolve comments
  • Loading branch information
mingxwa authored Apr 22, 2024
1 parent 60ecacd commit aa384eb
Show file tree
Hide file tree
Showing 6 changed files with 154 additions and 44 deletions.
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ if (BUILD_TESTING)
googletest
URL https://github.com/google/googletest/archive/e2239ee6043f73722e7aa812a459f54a28552929.zip
)

set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) # For Windows: Prevent overriding the parent project's compiler/linker settings
set(BUILD_GMOCK OFF CACHE BOOL "" FORCE) # Disable GMock
FetchContent_MakeAvailable(googletest)
Expand Down
72 changes: 50 additions & 22 deletions proxy.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
#ifndef _MSFT_PROXY_
#define _MSFT_PROXY_

#include <cstring>
#include <bit>
#include <concepts>
#include <initializer_list>
Expand Down Expand Up @@ -197,8 +196,8 @@ void copying_dispatcher(char* self, const char* rhs)
}
template <std::size_t Len, std::size_t Align>
void copying_default_dispatcher(char* self, const char* rhs) noexcept {
std::memcpy(std::assume_aligned<Align>(self),
std::assume_aligned<Align>(rhs), Len);
std::uninitialized_copy_n(
std::assume_aligned<Align>(rhs), Len, std::assume_aligned<Align>(self));
}
template <class P>
void relocation_dispatcher(char* self, const char* rhs)
Expand Down Expand Up @@ -525,7 +524,7 @@ class proxy {
if (rhs.meta_.has_value()) {
if constexpr (F::constraints.relocatability ==
constraint_level::trivial) {
memcpy(ptr_, rhs.ptr_, F::constraints.max_size);
std::ranges::uninitialized_copy(rhs.ptr_, ptr_);
} else {
rhs.meta_->Traits::relocatability_meta::dispatcher(ptr_, rhs.ptr_);
}
Expand Down Expand Up @@ -698,23 +697,25 @@ constexpr proxiable_ptr_constraints trivial_ptr_constraints{
namespace details {

template <class T>
class sbo_ptr {
class inplace_ptr {
public:
template <class... Args>
sbo_ptr(Args&&... args) noexcept(std::is_nothrow_constructible_v<T, Args...>)
inplace_ptr(Args&&... args)
noexcept(std::is_nothrow_constructible_v<T, Args...>)
requires(std::is_constructible_v<T, Args...>)
: value_(std::forward<Args>(args)...) {}
sbo_ptr(const sbo_ptr&) noexcept(std::is_nothrow_copy_constructible_v<T>)
= default;
sbo_ptr(sbo_ptr&&) noexcept(std::is_nothrow_move_constructible_v<T>)
= default;
inplace_ptr(const inplace_ptr&)
noexcept(std::is_nothrow_copy_constructible_v<T>) = default;
inplace_ptr(inplace_ptr&&)
noexcept(std::is_nothrow_move_constructible_v<T>) = default;

T* operator->() const noexcept { return &value_; }

private:
mutable T value_;
};

#if __STDC_HOSTED__
template <class T, class Alloc>
static auto rebind_allocator(const Alloc& alloc) {
return typename std::allocator_traits<Alloc>::template rebind_alloc<T>(alloc);
Expand Down Expand Up @@ -761,7 +762,6 @@ class allocated_ptr {
Alloc alloc_;
T* ptr_;
};

template <class T, class Alloc>
class compact_ptr {
public:
Expand All @@ -782,36 +782,63 @@ class compact_ptr {
struct storage {
template <class... Args>
explicit storage(const Alloc& alloc, Args&&... args)
: alloc(alloc), value(std::forward<Args>(args)...) {}
: value(std::forward<Args>(args)...), alloc(alloc) {}

Alloc alloc;
T value;
Alloc alloc;
};

storage* ptr_;
};

template <class F, class T, class Alloc, class... Args>
proxy<F> allocate_proxy_impl(const Alloc& alloc, Args&&... args) {
if constexpr (proxiable<details::sbo_ptr<T>, F>) {
return proxy<F>{std::in_place_type<details::sbo_ptr<T>>,
std::forward<Args>(args)...};
} else if constexpr (proxiable<details::allocated_ptr<T, Alloc>, F>) {
return proxy<F>{std::in_place_type<details::allocated_ptr<T, Alloc>>,
if constexpr (proxiable<allocated_ptr<T, Alloc>, F>) {
return proxy<F>{std::in_place_type<allocated_ptr<T, Alloc>>,
alloc, std::forward<Args>(args)...};
} else {
return proxy<F>{std::in_place_type<details::compact_ptr<T, Alloc>>,
return proxy<F>{std::in_place_type<compact_ptr<T, Alloc>>,
alloc, std::forward<Args>(args)...};
}
}
template <class F, class T, class... Args>
proxy<F> make_proxy_impl(Args&&... args) {
return allocate_proxy_impl<F, T>(
std::allocator<T>{}, std::forward<Args>(args)...);
if constexpr (proxiable<inplace_ptr<T>, F>) {
return proxy<F>{std::in_place_type<inplace_ptr<T>>,
std::forward<Args>(args)...};
} else {
return allocate_proxy_impl<F, T>(
std::allocator<T>{}, std::forward<Args>(args)...);
}
}
#endif // __STDC_HOSTED__

} // namespace details

template <class T, class F>
concept inplace_proxiable_target = proxiable<details::inplace_ptr<T>, F>;

template <facade F, inplace_proxiable_target<F> T, class... Args>
proxy<F> make_proxy_inplace(Args&&... args)
noexcept(std::is_nothrow_constructible_v<T, Args...>) {
return proxy<F>{std::in_place_type<details::inplace_ptr<T>>,
std::forward<Args>(args)...};
}
template <facade F, inplace_proxiable_target<F> T, class U, class... Args>
proxy<F> make_proxy_inplace(std::initializer_list<U> il, Args&&... args)
noexcept(std::is_nothrow_constructible_v<
T, std::initializer_list<U>&, Args...>) {
return proxy<F>{std::in_place_type<details::inplace_ptr<T>>,
il, std::forward<Args>(args)...};
}
template <facade F, class T>
proxy<F> make_proxy_inplace(T&& value)
noexcept(std::is_nothrow_constructible_v<std::decay_t<T>, T>)
requires(inplace_proxiable_target<std::decay_t<T>, F>) {
return proxy<F>{std::in_place_type<details::inplace_ptr<std::decay_t<T>>>,
std::forward<T>(value)};
}

#if __STDC_HOSTED__
template <facade F, class T, class Alloc, class... Args>
proxy<F> allocate_proxy(const Alloc& alloc, Args&&... args) {
return details::allocate_proxy_impl<F, T>(alloc, std::forward<Args>(args)...);
Expand All @@ -837,6 +864,7 @@ template <facade F, class T>
proxy<F> make_proxy(T&& value) {
return details::make_proxy_impl<F, std::decay_t<T>>(std::forward<T>(value));
}
#endif // __STDC_HOSTED__

// The following types and macros aim to simplify definition of dispatch and
// facade types prior to C++26
Expand Down
10 changes: 10 additions & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,13 @@ endif()

include(GoogleTest)
gtest_discover_tests(msft_proxy_tests)

if(NOT MSVC)
add_executable(msft_proxy_freestanding_tests freestanding/proxy_freestanding_tests.cpp)
target_include_directories(msft_proxy_freestanding_tests PRIVATE .)
target_compile_features(msft_proxy_freestanding_tests PRIVATE cxx_std_20)
target_compile_options(msft_proxy_freestanding_tests PRIVATE -ffreestanding -fno-exceptions -fno-rtti -Wall -Wextra -Wpedantic -Werror)
target_link_options(msft_proxy_freestanding_tests PRIVATE -nodefaultlibs -lc)
target_link_libraries(msft_proxy_freestanding_tests PRIVATE msft_proxy)
add_test(NAME ProxyFreestandingTests COMMAND msft_proxy_freestanding_tests)
endif()
48 changes: 48 additions & 0 deletions tests/freestanding/proxy_freestanding_tests.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#if __STDC_HOSTED__
#error "This file shall be compiled targeting a freestanding environment."
#endif // __STDC_HOSTED__

#include "proxy.h"

unsigned GetHash(int v) { return static_cast<unsigned>(v + 3) * 31; }
unsigned GetHash(double v) { return static_cast<unsigned>(v * v + 5) * 87; }
unsigned GetHash(const char* v) {
unsigned result = 91u;
for (int i = 0; v[i]; ++i) {
result = result * 47u + v[i];
}
return result;
}
unsigned GetDefaultHash() { return -1; }

namespace spec {

PRO_DEF_FREE_DISPATCH_WITH_DEFAULT(GetHash, ::GetHash, ::GetDefaultHash, unsigned());
PRO_DEF_FACADE(Hashable, GetHash);

} // namespace spec

extern "C" int main() {
int i = 123;
double d = 3.14159;
const char* s = "lalala";
std::tuple<int, double> t{11, 22};
pro::proxy<spec::Hashable> p;
p = &i;
if (p() != GetHash(i)) {
return 1;
}
p = &d;
if (p() != GetHash(d)) {
return 1;
}
p = pro::make_proxy_inplace<spec::Hashable>(s);
if (p() != GetHash(s)) {
return 1;
}
p = &t;
if (p() != GetDefaultHash()) {
return 1;
}
return 0;
}
Loading

0 comments on commit aa384eb

Please sign in to comment.