Skip to content

Commit

Permalink
Implement indirection syntax (#121)
Browse files Browse the repository at this point in the history
* Implement indirection

* Strengthen constraints for accessors

* Fix semantics of facade_traits::has_indirection

* Fix typo

* Remove qualifier_of_v which is hard to use correctly
  • Loading branch information
mingxwa authored Jun 24, 2024
1 parent c9c1d1b commit ab17914
Show file tree
Hide file tree
Showing 11 changed files with 1,118 additions and 605 deletions.
16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ class Rectangle {
std::string PrintDrawableToString(pro::proxy<Drawable> p) {
std::stringstream result;
result << "shape = ";
p.Draw(result); // Polymorphic call
result << ", area = " << p.Area(); // Polymorphic call
p->Draw(result); // Polymorphic call
result << ", area = " << p->Area(); // Polymorphic call
return std::move(result).str();
}

Expand All @@ -76,11 +76,11 @@ struct Logger : pro::facade_builder
// Client - Consumer
void MyVerboseFunction(pro::proxy<Logger> logger) {
logger.Log("hello");
logger->Log("hello");
try {
throw std::runtime_error{"runtime error!"};
} catch (const std::exception& e) {
logger.Log("world", e);
logger->Log("world", e);
}
}
Expand Down Expand Up @@ -133,11 +133,11 @@ int main() {
puts("");
};
MyFunction<void(int)> p0{&f};
p0(123); // Prints "f() called. Args: 123:i," (assuming GCC)
(*p0)(123); // Prints "f() called. Args: 123:i," (assuming GCC)
MyMoveOnlyFunction<void(), void(int), void(double)> p1{&f};
p1(); // Prints "f() called. Args:"
p1(456); // Prints "f() called. Args: 456:i,"
p1(1.2); // Prints "f() called. Args: 1.2:d,"
(*p1)(); // Prints "f() called. Args:"
(*p1)(456); // Prints "f() called. Args: 456:i,"
(*p1)(1.2); // Prints "f() called. Args: 1.2:d,"
return 0;
}
```
Expand Down
1,228 changes: 864 additions & 364 deletions proxy.h

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion samples/resource_dictionary/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ struct Dictionary : pro::facade_builder
} // namespace spec

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

int main() {
Expand Down
8 changes: 4 additions & 4 deletions tests/freestanding/proxy_freestanding_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,19 +31,19 @@ extern "C" int main() {
std::tuple<int, double> t{11, 22};
pro::proxy<spec::Hashable> p;
p = &i;
if (GetHash(p) != GetHash(i)) {
if (GetHash(*p) != GetHash(i)) {
return 1;
}
p = &d;
if (GetHash(p) != GetHash(d)) {
if (GetHash(*p) != GetHash(d)) {
return 1;
}
p = pro::make_proxy_inplace<spec::Hashable>(s);
if (GetHash(p) != GetHash(s)) {
if (GetHash(*p) != GetHash(s)) {
return 1;
}
p = &t;
if (GetHash(p) != GetDefaultHash()) {
if (GetHash(*p) != GetDefaultHash()) {
return 1;
}
return 0;
Expand Down
61 changes: 31 additions & 30 deletions tests/proxy_creation_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ namespace {

struct SboObserver {
public:
static constexpr bool is_direct = true;
template <class T>
constexpr explicit SboObserver(std::in_place_type_t<pro::details::inplace_ptr<T>>)
: SboEnabled(true), AllocatorAllocatesForItself(false) {}
Expand Down Expand Up @@ -86,7 +87,7 @@ TEST(ProxyCreationTests, TestMakeProxyInplace_FromValue) {
{
auto p = pro::make_proxy_inplace<spec::TestLargeStringable>(session);
ASSERT_TRUE(p.has_value());
ASSERT_EQ(ToString(p), "Session 2");
ASSERT_EQ(ToString(*p), "Session 2");
ASSERT_TRUE(pro::proxy_reflect<SboObserver>(p).SboEnabled);
expected_ops.emplace_back(2, utils::LifetimeOperationType::kCopyConstruction);
ASSERT_TRUE(tracker.GetOperations() == expected_ops);
Expand All @@ -101,7 +102,7 @@ TEST(ProxyCreationTests, TestMakeProxyInplace_InPlace) {
{
auto p = pro::make_proxy_inplace<spec::TestLargeStringable, utils::LifetimeTracker::Session>(&tracker);
ASSERT_TRUE(p.has_value());
ASSERT_EQ(ToString(p), "Session 1");
ASSERT_EQ(ToString(*p), "Session 1");
ASSERT_TRUE(pro::proxy_reflect<SboObserver>(p).SboEnabled);
expected_ops.emplace_back(1, utils::LifetimeOperationType::kValueConstruction);
ASSERT_TRUE(tracker.GetOperations() == expected_ops);
Expand All @@ -116,7 +117,7 @@ TEST(ProxyCreationTests, TestMakeProxyInplace_InPlaceInitializerList) {
{
auto p = pro::make_proxy_inplace<spec::TestLargeStringable, utils::LifetimeTracker::Session>({ 1, 2, 3 }, &tracker);
ASSERT_TRUE(p.has_value());
ASSERT_EQ(ToString(p), "Session 1");
ASSERT_EQ(ToString(*p), "Session 1");
ASSERT_TRUE(pro::proxy_reflect<SboObserver>(p).SboEnabled);
expected_ops.emplace_back(1, utils::LifetimeOperationType::kInitializerListConstruction);
ASSERT_TRUE(tracker.GetOperations() == expected_ops);
Expand All @@ -133,10 +134,10 @@ TEST(ProxyCreationTests, TestMakeProxyInplace_Lifetime_Copy) {
expected_ops.emplace_back(1, utils::LifetimeOperationType::kValueConstruction);
auto p2 = p1;
ASSERT_TRUE(p1.has_value());
ASSERT_EQ(ToString(p1), "Session 1");
ASSERT_EQ(ToString(*p1), "Session 1");
ASSERT_TRUE(pro::proxy_reflect<SboObserver>(p1).SboEnabled);
ASSERT_TRUE(p2.has_value());
ASSERT_EQ(ToString(p2), "Session 2");
ASSERT_EQ(ToString(*p2), "Session 2");
ASSERT_TRUE(pro::proxy_reflect<SboObserver>(p2).SboEnabled);
expected_ops.emplace_back(2, utils::LifetimeOperationType::kCopyConstruction);
ASSERT_TRUE(tracker.GetOperations() == expected_ops);
Expand All @@ -155,7 +156,7 @@ TEST(ProxyCreationTests, TestMakeProxyInplace_Lifetime_Move) {
auto p2 = std::move(p1);
ASSERT_FALSE(p1.has_value());
ASSERT_TRUE(p2.has_value());
ASSERT_EQ(ToString(p2), "Session 2");
ASSERT_EQ(ToString(*p2), "Session 2");
ASSERT_TRUE(pro::proxy_reflect<SboObserver>(p2).SboEnabled);
expected_ops.emplace_back(2, utils::LifetimeOperationType::kMoveConstruction);
expected_ops.emplace_back(1, utils::LifetimeOperationType::kDestruction);
Expand All @@ -173,7 +174,7 @@ TEST(ProxyCreationTests, TestAllocateProxy_DirectAllocator_FromValue) {
{
auto p = pro::allocate_proxy<spec::TestSmallStringable>(std::allocator<void>{}, session);
ASSERT_TRUE(p.has_value());
ASSERT_EQ(ToString(p), "Session 2");
ASSERT_EQ(ToString(*p), "Session 2");
ASSERT_FALSE(pro::proxy_reflect<SboObserver>(p).SboEnabled);
ASSERT_FALSE(pro::proxy_reflect<SboObserver>(p).AllocatorAllocatesForItself);
expected_ops.emplace_back(2, utils::LifetimeOperationType::kCopyConstruction);
Expand All @@ -189,7 +190,7 @@ TEST(ProxyCreationTests, TestAllocateProxy_DirectAllocator_InPlace) {
{
auto p = pro::allocate_proxy<spec::TestSmallStringable, utils::LifetimeTracker::Session>(std::allocator<void>{}, & tracker);
ASSERT_TRUE(p.has_value());
ASSERT_EQ(ToString(p), "Session 1");
ASSERT_EQ(ToString(*p), "Session 1");
ASSERT_FALSE(pro::proxy_reflect<SboObserver>(p).SboEnabled);
ASSERT_FALSE(pro::proxy_reflect<SboObserver>(p).AllocatorAllocatesForItself);
expected_ops.emplace_back(1, utils::LifetimeOperationType::kValueConstruction);
Expand All @@ -205,7 +206,7 @@ TEST(ProxyCreationTests, TestAllocateProxy_DirectAllocator_InPlaceInitializerLis
{
auto p = pro::allocate_proxy<spec::TestSmallStringable, utils::LifetimeTracker::Session>(std::allocator<void>{}, { 1, 2, 3 }, & tracker);
ASSERT_TRUE(p.has_value());
ASSERT_EQ(ToString(p), "Session 1");
ASSERT_EQ(ToString(*p), "Session 1");
ASSERT_FALSE(pro::proxy_reflect<SboObserver>(p).SboEnabled);
ASSERT_FALSE(pro::proxy_reflect<SboObserver>(p).AllocatorAllocatesForItself);
expected_ops.emplace_back(1, utils::LifetimeOperationType::kInitializerListConstruction);
Expand All @@ -223,11 +224,11 @@ TEST(ProxyCreationTests, TestAllocateProxy_DirectAllocator_Lifetime_Copy) {
expected_ops.emplace_back(1, utils::LifetimeOperationType::kValueConstruction);
auto p2 = p1;
ASSERT_TRUE(p1.has_value());
ASSERT_EQ(ToString(p1), "Session 1");
ASSERT_EQ(ToString(*p1), "Session 1");
ASSERT_FALSE(pro::proxy_reflect<SboObserver>(p1).SboEnabled);
ASSERT_FALSE(pro::proxy_reflect<SboObserver>(p1).AllocatorAllocatesForItself);
ASSERT_TRUE(p2.has_value());
ASSERT_EQ(ToString(p2), "Session 2");
ASSERT_EQ(ToString(*p2), "Session 2");
ASSERT_FALSE(pro::proxy_reflect<SboObserver>(p2).SboEnabled);
ASSERT_FALSE(pro::proxy_reflect<SboObserver>(p2).AllocatorAllocatesForItself);
expected_ops.emplace_back(2, utils::LifetimeOperationType::kCopyConstruction);
Expand All @@ -247,7 +248,7 @@ TEST(ProxyCreationTests, TestAllocateProxy_DirectAllocator_Lifetime_Move) {
auto p2 = std::move(p1);
ASSERT_FALSE(p1.has_value());
ASSERT_TRUE(p2.has_value());
ASSERT_EQ(ToString(p2), "Session 1");
ASSERT_EQ(ToString(*p2), "Session 1");
ASSERT_FALSE(pro::proxy_reflect<SboObserver>(p2).SboEnabled);
ASSERT_FALSE(pro::proxy_reflect<SboObserver>(p2).AllocatorAllocatesForItself);
ASSERT_TRUE(tracker.GetOperations() == expected_ops);
Expand All @@ -265,7 +266,7 @@ TEST(ProxyCreationTests, TestAllocateProxy_IndirectAllocator_FromValue) {
std::pmr::unsynchronized_pool_resource memory_pool;
auto p = pro::allocate_proxy<spec::TestSmallStringable>(std::pmr::polymorphic_allocator<>{&memory_pool}, session);
ASSERT_TRUE(p.has_value());
ASSERT_EQ(ToString(p), "Session 2");
ASSERT_EQ(ToString(*p), "Session 2");
ASSERT_FALSE(pro::proxy_reflect<SboObserver>(p).SboEnabled);
ASSERT_TRUE(pro::proxy_reflect<SboObserver>(p).AllocatorAllocatesForItself);
expected_ops.emplace_back(2, utils::LifetimeOperationType::kCopyConstruction);
Expand All @@ -282,7 +283,7 @@ TEST(ProxyCreationTests, TestAllocateProxy_IndirectAllocator_InPlace) {
std::pmr::unsynchronized_pool_resource memory_pool;
auto p = pro::allocate_proxy<spec::TestSmallStringable, utils::LifetimeTracker::Session>(std::pmr::polymorphic_allocator<>{&memory_pool}, & tracker);
ASSERT_TRUE(p.has_value());
ASSERT_EQ(ToString(p), "Session 1");
ASSERT_EQ(ToString(*p), "Session 1");
ASSERT_FALSE(pro::proxy_reflect<SboObserver>(p).SboEnabled);
ASSERT_TRUE(pro::proxy_reflect<SboObserver>(p).AllocatorAllocatesForItself);
expected_ops.emplace_back(1, utils::LifetimeOperationType::kValueConstruction);
Expand All @@ -299,7 +300,7 @@ TEST(ProxyCreationTests, TestAllocateProxy_IndirectAllocator_InPlaceInitializerL
std::pmr::unsynchronized_pool_resource memory_pool;
auto p = pro::allocate_proxy<spec::TestSmallStringable, utils::LifetimeTracker::Session>(std::pmr::polymorphic_allocator<>{&memory_pool}, { 1, 2, 3 }, & tracker);
ASSERT_TRUE(p.has_value());
ASSERT_EQ(ToString(p), "Session 1");
ASSERT_EQ(ToString(*p), "Session 1");
ASSERT_FALSE(pro::proxy_reflect<SboObserver>(p).SboEnabled);
ASSERT_TRUE(pro::proxy_reflect<SboObserver>(p).AllocatorAllocatesForItself);
expected_ops.emplace_back(1, utils::LifetimeOperationType::kInitializerListConstruction);
Expand All @@ -318,11 +319,11 @@ TEST(ProxyCreationTests, TestAllocateProxy_IndirectAllocator_Lifetime_Copy) {
expected_ops.emplace_back(1, utils::LifetimeOperationType::kValueConstruction);
auto p2 = p1;
ASSERT_TRUE(p1.has_value());
ASSERT_EQ(ToString(p1), "Session 1");
ASSERT_EQ(ToString(*p1), "Session 1");
ASSERT_FALSE(pro::proxy_reflect<SboObserver>(p1).SboEnabled);
ASSERT_TRUE(pro::proxy_reflect<SboObserver>(p1).AllocatorAllocatesForItself);
ASSERT_TRUE(p2.has_value());
ASSERT_EQ(ToString(p2), "Session 2");
ASSERT_EQ(ToString(*p2), "Session 2");
ASSERT_FALSE(pro::proxy_reflect<SboObserver>(p2).SboEnabled);
ASSERT_TRUE(pro::proxy_reflect<SboObserver>(p2).AllocatorAllocatesForItself);
expected_ops.emplace_back(2, utils::LifetimeOperationType::kCopyConstruction);
Expand All @@ -343,7 +344,7 @@ TEST(ProxyCreationTests, TestAllocateProxy_IndirectAllocator_Lifetime_Move) {
auto p2 = std::move(p1);
ASSERT_FALSE(p1.has_value());
ASSERT_TRUE(p2.has_value());
ASSERT_EQ(ToString(p2), "Session 1");
ASSERT_EQ(ToString(*p2), "Session 1");
ASSERT_FALSE(pro::proxy_reflect<SboObserver>(p2).SboEnabled);
ASSERT_TRUE(pro::proxy_reflect<SboObserver>(p2).AllocatorAllocatesForItself);
ASSERT_TRUE(tracker.GetOperations() == expected_ops);
Expand All @@ -360,7 +361,7 @@ TEST(ProxyCreationTests, TestMakeProxy_WithSBO_FromValue) {
{
auto p = pro::make_proxy<spec::TestLargeStringable>(session);
ASSERT_TRUE(p.has_value());
ASSERT_EQ(ToString(p), "Session 2");
ASSERT_EQ(ToString(*p), "Session 2");
ASSERT_TRUE(pro::proxy_reflect<SboObserver>(p).SboEnabled);
expected_ops.emplace_back(2, utils::LifetimeOperationType::kCopyConstruction);
ASSERT_TRUE(tracker.GetOperations() == expected_ops);
Expand All @@ -375,7 +376,7 @@ TEST(ProxyCreationTests, TestMakeProxy_WithSBO_InPlace) {
{
auto p = pro::make_proxy<spec::TestLargeStringable, utils::LifetimeTracker::Session>(&tracker);
ASSERT_TRUE(p.has_value());
ASSERT_EQ(ToString(p), "Session 1");
ASSERT_EQ(ToString(*p), "Session 1");
ASSERT_TRUE(pro::proxy_reflect<SboObserver>(p).SboEnabled);
expected_ops.emplace_back(1, utils::LifetimeOperationType::kValueConstruction);
ASSERT_TRUE(tracker.GetOperations() == expected_ops);
Expand All @@ -390,7 +391,7 @@ TEST(ProxyCreationTests, TestMakeProxy_WithSBO_InPlaceInitializerList) {
{
auto p = pro::make_proxy<spec::TestLargeStringable, utils::LifetimeTracker::Session>({ 1, 2, 3 }, &tracker);
ASSERT_TRUE(p.has_value());
ASSERT_EQ(ToString(p), "Session 1");
ASSERT_EQ(ToString(*p), "Session 1");
ASSERT_TRUE(pro::proxy_reflect<SboObserver>(p).SboEnabled);
expected_ops.emplace_back(1, utils::LifetimeOperationType::kInitializerListConstruction);
ASSERT_TRUE(tracker.GetOperations() == expected_ops);
Expand All @@ -407,10 +408,10 @@ TEST(ProxyCreationTests, TestMakeProxy_WithSBO_Lifetime_Copy) {
expected_ops.emplace_back(1, utils::LifetimeOperationType::kValueConstruction);
auto p2 = p1;
ASSERT_TRUE(p1.has_value());
ASSERT_EQ(ToString(p1), "Session 1");
ASSERT_EQ(ToString(*p1), "Session 1");
ASSERT_TRUE(pro::proxy_reflect<SboObserver>(p1).SboEnabled);
ASSERT_TRUE(p2.has_value());
ASSERT_EQ(ToString(p2), "Session 2");
ASSERT_EQ(ToString(*p2), "Session 2");
ASSERT_TRUE(pro::proxy_reflect<SboObserver>(p2).SboEnabled);
expected_ops.emplace_back(2, utils::LifetimeOperationType::kCopyConstruction);
ASSERT_TRUE(tracker.GetOperations() == expected_ops);
Expand All @@ -429,7 +430,7 @@ TEST(ProxyCreationTests, TestMakeProxy_WithSBO_Lifetime_Move) {
auto p2 = std::move(p1);
ASSERT_FALSE(p1.has_value());
ASSERT_TRUE(p2.has_value());
ASSERT_EQ(ToString(p2), "Session 2");
ASSERT_EQ(ToString(*p2), "Session 2");
ASSERT_TRUE(pro::proxy_reflect<SboObserver>(p2).SboEnabled);
expected_ops.emplace_back(2, utils::LifetimeOperationType::kMoveConstruction);
expected_ops.emplace_back(1, utils::LifetimeOperationType::kDestruction);
Expand All @@ -447,7 +448,7 @@ TEST(ProxyCreationTests, TestMakeProxy_WithoutSBO_FromValue) {
{
auto p = pro::make_proxy<spec::TestSmallStringable>(session);
ASSERT_TRUE(p.has_value());
ASSERT_EQ(ToString(p), "Session 2");
ASSERT_EQ(ToString(*p), "Session 2");
ASSERT_FALSE(pro::proxy_reflect<SboObserver>(p).SboEnabled);
ASSERT_FALSE(pro::proxy_reflect<SboObserver>(p).AllocatorAllocatesForItself);
expected_ops.emplace_back(2, utils::LifetimeOperationType::kCopyConstruction);
Expand All @@ -463,7 +464,7 @@ TEST(ProxyCreationTests, TestMakeProxy_WithoutSBO_InPlace) {
{
auto p = pro::make_proxy<spec::TestSmallStringable, utils::LifetimeTracker::Session>(&tracker);
ASSERT_TRUE(p.has_value());
ASSERT_EQ(ToString(p), "Session 1");
ASSERT_EQ(ToString(*p), "Session 1");
ASSERT_FALSE(pro::proxy_reflect<SboObserver>(p).SboEnabled);
ASSERT_FALSE(pro::proxy_reflect<SboObserver>(p).AllocatorAllocatesForItself);
expected_ops.emplace_back(1, utils::LifetimeOperationType::kValueConstruction);
Expand All @@ -479,7 +480,7 @@ TEST(ProxyCreationTests, TestMakeProxy_WithoutSBO_InPlaceInitializerList) {
{
auto p = pro::make_proxy<spec::TestSmallStringable, utils::LifetimeTracker::Session>({ 1, 2, 3 }, &tracker);
ASSERT_TRUE(p.has_value());
ASSERT_EQ(ToString(p), "Session 1");
ASSERT_EQ(ToString(*p), "Session 1");
ASSERT_FALSE(pro::proxy_reflect<SboObserver>(p).SboEnabled);
ASSERT_FALSE(pro::proxy_reflect<SboObserver>(p).AllocatorAllocatesForItself);
expected_ops.emplace_back(1, utils::LifetimeOperationType::kInitializerListConstruction);
Expand All @@ -497,11 +498,11 @@ TEST(ProxyCreationTests, TestMakeProxy_WithoutSBO_Lifetime_Copy) {
expected_ops.emplace_back(1, utils::LifetimeOperationType::kValueConstruction);
auto p2 = p1;
ASSERT_TRUE(p1.has_value());
ASSERT_EQ(ToString(p1), "Session 1");
ASSERT_EQ(ToString(*p1), "Session 1");
ASSERT_FALSE(pro::proxy_reflect<SboObserver>(p1).SboEnabled);
ASSERT_FALSE(pro::proxy_reflect<SboObserver>(p1).AllocatorAllocatesForItself);
ASSERT_TRUE(p2.has_value());
ASSERT_EQ(ToString(p2), "Session 2");
ASSERT_EQ(ToString(*p2), "Session 2");
ASSERT_FALSE(pro::proxy_reflect<SboObserver>(p2).SboEnabled);
ASSERT_FALSE(pro::proxy_reflect<SboObserver>(p2).AllocatorAllocatesForItself);
expected_ops.emplace_back(2, utils::LifetimeOperationType::kCopyConstruction);
Expand All @@ -521,7 +522,7 @@ TEST(ProxyCreationTests, TestMakeProxy_WithoutSBO_Lifetime_Move) {
auto p2 = std::move(p1);
ASSERT_FALSE(p1.has_value());
ASSERT_TRUE(p2.has_value());
ASSERT_EQ(ToString(p2), "Session 1");
ASSERT_EQ(ToString(*p2), "Session 1");
ASSERT_FALSE(pro::proxy_reflect<SboObserver>(p2).SboEnabled);
ASSERT_FALSE(pro::proxy_reflect<SboObserver>(p2).AllocatorAllocatesForItself);
ASSERT_TRUE(tracker.GetOperations() == expected_ops);
Expand Down
Loading

0 comments on commit ab17914

Please sign in to comment.