Skip to content

Commit

Permalink
Implement simple passthrough type that can enable custom destructor i…
Browse files Browse the repository at this point in the history
…mplementations executed before proxied object.

This proxy could also forward operator(), but frustratingly, it isn't sufficient to forward for types like `LocalObject`, because it would imply passing `const char*` which the InvocableMap can't see through in a constexpr way.

I had hoped this coudl be used for a "non-deleting" LocalObject.

See #207 for context.

PiperOrigin-RevId: 574202123
  • Loading branch information
jwhpryor authored and copybara-github committed Oct 17, 2023
1 parent a2e21c6 commit 06fd124
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 0 deletions.
14 changes: 14 additions & 0 deletions metaprogramming/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -1034,6 +1034,20 @@ cc_library(
],
)

cc_library(
name = "passthrough",
hdrs = ["passthrough.h"],
)

cc_test(
name = "passthrough_test",
srcs = ["passthrough_test.cc"],
deps = [
":passthrough",
"@googletest//:gtest_main",
],
)

cc_test(
name = "zip_invoke_test",
srcs = ["zip_invoke_test.cc"],
Expand Down
58 changes: 58 additions & 0 deletions metaprogramming/passthrough.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#ifndef JNI_BIND_METAPROGRAMMING_PASSTHROUGH_H_
#define JNI_BIND_METAPROGRAMMING_PASSTHROUGH_H_

#include <utility>

namespace jni::metaprogramming {

template <typename T>
struct DefaultDeleter {
static void Do(T&) {}
};

template <typename T, typename CustomDeleter = DefaultDeleter<T>>
struct Passthrough {
template <typename... Ts>
Passthrough(Ts... ts) : t_(std::forward<Ts>(ts)...) {}

~Passthrough() { CustomDeleter::Do(t_); }

T& operator*() { return t_; }
T* operator->() { return &t_; }

template <typename... Us>
auto operator()(Us&&... us) {
return t_(us...);
}

template <typename U>
explicit operator U() const {
return static_cast<U>(t_);
}

T t_;
};

template <typename T, typename U, typename CustomDeleter>
bool operator==(const T& lhs, const Passthrough<U, CustomDeleter>& rhs) {
return lhs == static_cast<T>(rhs.t_);
}

template <typename T, typename U, typename CustomDeleter>
bool operator==(const Passthrough<U, CustomDeleter>& lhs, const T& rhs) {
return static_cast<T>(lhs.t_) == rhs;
}

template <typename T, typename U, typename CustomDeleter>
bool operator!=(const T& lhs, const Passthrough<U, CustomDeleter>& rhs) {
return !(lhs == rhs);
}

template <typename T, typename U, typename CustomDeleter>
bool operator!=(const Passthrough<U, CustomDeleter>& lhs, const T& rhs) {
return !(lhs == rhs);
}

} // namespace jni::metaprogramming

#endif // JNI_BIND_METAPROGRAMMING_PASSTHROUGH_H_
80 changes: 80 additions & 0 deletions metaprogramming/passthrough_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#include "metaprogramming/passthrough.h"

#include <gtest/gtest.h>

using ::jni::metaprogramming::Passthrough;

template <typename T>
struct ReleaseObjectRef {
static void Do(T& t) { t.val_0 = 0xabababab; }
};

static bool fail_happened = false;

struct A {
A() {}
A(int val_0) : val_0(val_0) {}
A(int val_0, int val_1) : val_0(val_0), val_1(val_1) {}
A(int val_0, int val_1, int val_2)
: val_0(val_0), val_1(val_1), val_2(val_2) {}

int Sum() { return val_0 + val_1 + val_2; }

void Foo() {}

~A() {
if (val_0 == 0xdeadbeef) {
// never happens because custom dtor.
fail_happened = true;
}
}

int val_0 = 123;
int val_1 = 456;
int val_2 = 789;
};

bool operator==(const A& lhs, const A& rhs) {
return lhs.val_0 == rhs.val_0 && lhs.val_1 == rhs.val_1 &&
lhs.val_2 == rhs.val_2;
}

bool operator!=(A& lhs, A& rhs) { return !(lhs == rhs); }

namespace {

TEST(Passthrough, ConstructsI) {
Passthrough<A> val{999};
EXPECT_EQ(val, (A{999}));
}

TEST(Passthrough, ConstructsII) {
Passthrough<A> val{999, 888};
EXPECT_EQ(val, (A{999, 888}));
}

TEST(Passthrough, ConstructsIII) {
Passthrough<A> val{999, 888, 777};
EXPECT_EQ(val, (A{999, 888, 777}));
}

TEST(Passthrough, PeersThroughPointer) {
Passthrough<A> val{};
val->Foo();
}

TEST(Passthrough, PeersThroughDereference) {
Passthrough<A> val{};
(*val).Foo();
}

TEST(Passthrough, CustomDtorIsInvoked) {
{ Passthrough<A, ReleaseObjectRef<A>> val{0xdeadbeef}; }

EXPECT_FALSE(fail_happened);

{ Passthrough<A> val{0xdeadbeef}; }
EXPECT_TRUE(fail_happened);
}

} // namespace

0 comments on commit 06fd124

Please sign in to comment.