rtc::Bind: Capture scoped_refptr reference arguments by value
R=tommi@webrtc.org
Review URL: https://codereview.webrtc.org/1308563004 .
Cr-Original-Commit-Position: refs/heads/master@{#9780}
Cr-Mirrored-From: https://chromium.googlesource.com/external/webrtc
Cr-Mirrored-Commit: b274547ebdefd0db470e21cfe113457cabd99dff
diff --git a/base/bind.h b/base/bind.h
index 2d81140..923fda2 100644
--- a/base/bind.h
+++ b/base/bind.h
@@ -128,6 +128,18 @@
T*>::type type;
};
+// RemoveScopedPtrRef will capture scoped_refptr by-value instead of
+// by-reference.
+template <class T> struct RemoveScopedPtrRef { typedef T type; };
+template <class T>
+struct RemoveScopedPtrRef<const scoped_refptr<T>&> {
+ typedef scoped_refptr<T> type;
+};
+template <class T>
+struct RemoveScopedPtrRef<scoped_refptr<T>&> {
+ typedef scoped_refptr<T> type;
+};
+
} // namespace detail
template <class ObjectT, class MethodT, class R>
@@ -208,7 +220,7 @@
private:
MethodT method_;
typename detail::PointerType<ObjectT>::type object_;
- P1 p1_;
+ typename detail::RemoveScopedPtrRef<P1>::type p1_;
};
template <class FunctorT, class R,
@@ -222,7 +234,7 @@
return functor_(p1_); }
private:
FunctorT functor_;
- P1 p1_;
+ typename detail::RemoveScopedPtrRef<P1>::type p1_;
};
@@ -291,8 +303,8 @@
private:
MethodT method_;
typename detail::PointerType<ObjectT>::type object_;
- P1 p1_;
- P2 p2_;
+ typename detail::RemoveScopedPtrRef<P1>::type p1_;
+ typename detail::RemoveScopedPtrRef<P2>::type p2_;
};
template <class FunctorT, class R,
@@ -308,8 +320,8 @@
return functor_(p1_, p2_); }
private:
FunctorT functor_;
- P1 p1_;
- P2 p2_;
+ typename detail::RemoveScopedPtrRef<P1>::type p1_;
+ typename detail::RemoveScopedPtrRef<P2>::type p2_;
};
@@ -389,9 +401,9 @@
private:
MethodT method_;
typename detail::PointerType<ObjectT>::type object_;
- P1 p1_;
- P2 p2_;
- P3 p3_;
+ typename detail::RemoveScopedPtrRef<P1>::type p1_;
+ typename detail::RemoveScopedPtrRef<P2>::type p2_;
+ typename detail::RemoveScopedPtrRef<P3>::type p3_;
};
template <class FunctorT, class R,
@@ -409,9 +421,9 @@
return functor_(p1_, p2_, p3_); }
private:
FunctorT functor_;
- P1 p1_;
- P2 p2_;
- P3 p3_;
+ typename detail::RemoveScopedPtrRef<P1>::type p1_;
+ typename detail::RemoveScopedPtrRef<P2>::type p2_;
+ typename detail::RemoveScopedPtrRef<P3>::type p3_;
};
@@ -502,10 +514,10 @@
private:
MethodT method_;
typename detail::PointerType<ObjectT>::type object_;
- P1 p1_;
- P2 p2_;
- P3 p3_;
- P4 p4_;
+ typename detail::RemoveScopedPtrRef<P1>::type p1_;
+ typename detail::RemoveScopedPtrRef<P2>::type p2_;
+ typename detail::RemoveScopedPtrRef<P3>::type p3_;
+ typename detail::RemoveScopedPtrRef<P4>::type p4_;
};
template <class FunctorT, class R,
@@ -525,10 +537,10 @@
return functor_(p1_, p2_, p3_, p4_); }
private:
FunctorT functor_;
- P1 p1_;
- P2 p2_;
- P3 p3_;
- P4 p4_;
+ typename detail::RemoveScopedPtrRef<P1>::type p1_;
+ typename detail::RemoveScopedPtrRef<P2>::type p2_;
+ typename detail::RemoveScopedPtrRef<P3>::type p3_;
+ typename detail::RemoveScopedPtrRef<P4>::type p4_;
};
@@ -630,11 +642,11 @@
private:
MethodT method_;
typename detail::PointerType<ObjectT>::type object_;
- P1 p1_;
- P2 p2_;
- P3 p3_;
- P4 p4_;
- P5 p5_;
+ typename detail::RemoveScopedPtrRef<P1>::type p1_;
+ typename detail::RemoveScopedPtrRef<P2>::type p2_;
+ typename detail::RemoveScopedPtrRef<P3>::type p3_;
+ typename detail::RemoveScopedPtrRef<P4>::type p4_;
+ typename detail::RemoveScopedPtrRef<P5>::type p5_;
};
template <class FunctorT, class R,
@@ -656,11 +668,11 @@
return functor_(p1_, p2_, p3_, p4_, p5_); }
private:
FunctorT functor_;
- P1 p1_;
- P2 p2_;
- P3 p3_;
- P4 p4_;
- P5 p5_;
+ typename detail::RemoveScopedPtrRef<P1>::type p1_;
+ typename detail::RemoveScopedPtrRef<P2>::type p2_;
+ typename detail::RemoveScopedPtrRef<P3>::type p3_;
+ typename detail::RemoveScopedPtrRef<P4>::type p4_;
+ typename detail::RemoveScopedPtrRef<P5>::type p5_;
};
@@ -773,12 +785,12 @@
private:
MethodT method_;
typename detail::PointerType<ObjectT>::type object_;
- P1 p1_;
- P2 p2_;
- P3 p3_;
- P4 p4_;
- P5 p5_;
- P6 p6_;
+ typename detail::RemoveScopedPtrRef<P1>::type p1_;
+ typename detail::RemoveScopedPtrRef<P2>::type p2_;
+ typename detail::RemoveScopedPtrRef<P3>::type p3_;
+ typename detail::RemoveScopedPtrRef<P4>::type p4_;
+ typename detail::RemoveScopedPtrRef<P5>::type p5_;
+ typename detail::RemoveScopedPtrRef<P6>::type p6_;
};
template <class FunctorT, class R,
@@ -802,12 +814,12 @@
return functor_(p1_, p2_, p3_, p4_, p5_, p6_); }
private:
FunctorT functor_;
- P1 p1_;
- P2 p2_;
- P3 p3_;
- P4 p4_;
- P5 p5_;
- P6 p6_;
+ typename detail::RemoveScopedPtrRef<P1>::type p1_;
+ typename detail::RemoveScopedPtrRef<P2>::type p2_;
+ typename detail::RemoveScopedPtrRef<P3>::type p3_;
+ typename detail::RemoveScopedPtrRef<P4>::type p4_;
+ typename detail::RemoveScopedPtrRef<P5>::type p5_;
+ typename detail::RemoveScopedPtrRef<P6>::type p6_;
};
diff --git a/base/bind.h.pump b/base/bind.h.pump
index 9a4bc66..e1cea61 100644
--- a/base/bind.h.pump
+++ b/base/bind.h.pump
@@ -124,6 +124,18 @@
T*>::type type;
};
+// RemoveScopedPtrRef will capture scoped_refptr by-value instead of
+// by-reference.
+template <class T> struct RemoveScopedPtrRef { typedef T type; };
+template <class T>
+struct RemoveScopedPtrRef<const scoped_refptr<T>&> {
+ typedef scoped_refptr<T> type;
+};
+template <class T>
+struct RemoveScopedPtrRef<scoped_refptr<T>&> {
+ typedef scoped_refptr<T> type;
+};
+
} // namespace detail
$var n = 6
@@ -145,7 +157,7 @@
MethodT method_;
typename detail::PointerType<ObjectT>::type object_;$for j [[
- P$j p$(j)_;]]
+ typename detail::RemoveScopedPtrRef<P$j>::type p$(j)_;]]
};
@@ -162,7 +174,7 @@
private:
FunctorT functor_;$for j [[
- P$j p$(j)_;]]
+ typename detail::RemoveScopedPtrRef<P$j>::type p$(j)_;]]
};
diff --git a/base/bind_unittest.cc b/base/bind_unittest.cc
index 7a621dc..d38729d 100644
--- a/base/bind_unittest.cc
+++ b/base/bind_unittest.cc
@@ -17,6 +17,8 @@
namespace {
+struct LifeTimeCheck;
+
struct MethodBindTester {
void NullaryVoid() { ++call_count; }
int NullaryInt() { ++call_count; return 1; }
@@ -25,6 +27,10 @@
template <class T> T Identity(T value) { ++call_count; return value; }
int UnaryByRef(int& value) const { ++call_count; return ++value; } // NOLINT
int Multiply(int a, int b) const { ++call_count; return a * b; }
+ void RefArgument(const scoped_refptr<LifeTimeCheck>& object) {
+ EXPECT_TRUE(object.get() != nullptr);
+ }
+
mutable int call_count;
};
@@ -42,19 +48,12 @@
void Release();
};
-class LifeTimeCheck : public RefCountInterface {
- public:
- LifeTimeCheck(bool* has_died) : has_died_(has_died), is_ok_to_die_(false) {}
- ~LifeTimeCheck() {
- EXPECT_TRUE(is_ok_to_die_);
- *has_died_ = true;
- }
- void PrepareToDie() { is_ok_to_die_ = true; }
+struct LifeTimeCheck {
+ LifeTimeCheck() : ref_count_(0) {}
+ void AddRef() { ++ref_count_; }
+ void Release() { --ref_count_; }
void NullaryVoid() {}
-
- private:
- bool* const has_died_;
- bool is_ok_to_die_;
+ int ref_count_;
};
int Return42() { return 42; }
@@ -65,6 +64,27 @@
// Try to catch any problem with scoped_refptr type deduction in rtc::Bind at
// compile time.
+static_assert(is_same<detail::RemoveScopedPtrRef<
+ const scoped_refptr<RefCountInterface>&>::type,
+ scoped_refptr<RefCountInterface>>::value,
+ "const scoped_refptr& should be captured by value");
+
+static_assert(is_same<detail::RemoveScopedPtrRef<const scoped_refptr<F>&>::type,
+ scoped_refptr<F>>::value,
+ "const scoped_refptr& should be captured by value");
+
+static_assert(
+ is_same<detail::RemoveScopedPtrRef<const int&>::type, const int&>::value,
+ "const int& should be captured as const int&");
+
+static_assert(
+ is_same<detail::RemoveScopedPtrRef<const F&>::type, const F&>::value,
+ "const F& should be captured as const F&");
+
+static_assert(
+ is_same<detail::RemoveScopedPtrRef<F&>::type, F&>::value,
+ "F& should be captured as F&");
+
#define EXPECT_IS_CAPTURED_AS_PTR(T) \
static_assert(is_same<detail::PointerType<T>::type, T*>::value, \
"PointerType")
@@ -124,46 +144,78 @@
// Test Bind where method object implements RefCountInterface and is passed as a
// pointer.
TEST(BindTest, CapturePointerAsScopedRefPtr) {
- bool object_has_died = false;
- scoped_refptr<LifeTimeCheck> object =
- new RefCountedObject<LifeTimeCheck>(&object_has_died);
+ LifeTimeCheck object;
+ EXPECT_EQ(object.ref_count_, 0);
+ scoped_refptr<LifeTimeCheck> scoped_object(&object);
+ EXPECT_EQ(object.ref_count_, 1);
{
- auto functor = Bind(&LifeTimeCheck::PrepareToDie, object.get());
- object = nullptr;
- EXPECT_FALSE(object_has_died);
- // Run prepare to die via functor.
- functor();
+ auto functor = Bind(&LifeTimeCheck::NullaryVoid, &object);
+ EXPECT_EQ(object.ref_count_, 2);
+ scoped_object = nullptr;
+ EXPECT_EQ(object.ref_count_, 1);
}
- EXPECT_TRUE(object_has_died);
+ EXPECT_EQ(object.ref_count_, 0);
}
// Test Bind where method object implements RefCountInterface and is passed as a
// scoped_refptr<>.
TEST(BindTest, CaptureScopedRefPtrAsScopedRefPtr) {
- bool object_has_died = false;
- scoped_refptr<LifeTimeCheck> object =
- new RefCountedObject<LifeTimeCheck>(&object_has_died);
+ LifeTimeCheck object;
+ EXPECT_EQ(object.ref_count_, 0);
+ scoped_refptr<LifeTimeCheck> scoped_object(&object);
+ EXPECT_EQ(object.ref_count_, 1);
{
- auto functor = Bind(&LifeTimeCheck::PrepareToDie, object);
- object = nullptr;
- EXPECT_FALSE(object_has_died);
- // Run prepare to die via functor.
- functor();
+ auto functor = Bind(&LifeTimeCheck::NullaryVoid, scoped_object);
+ EXPECT_EQ(object.ref_count_, 2);
+ scoped_object = nullptr;
+ EXPECT_EQ(object.ref_count_, 1);
}
- EXPECT_TRUE(object_has_died);
+ EXPECT_EQ(object.ref_count_, 0);
}
// Test Bind where method object is captured as scoped_refptr<> and the functor
// dies while there are references left.
TEST(BindTest, FunctorReleasesObjectOnDestruction) {
- bool object_has_died = false;
- scoped_refptr<LifeTimeCheck> object =
- new RefCountedObject<LifeTimeCheck>(&object_has_died);
- Bind(&LifeTimeCheck::NullaryVoid, object.get())();
- EXPECT_FALSE(object_has_died);
- object->PrepareToDie();
- object = nullptr;
- EXPECT_TRUE(object_has_died);
+ LifeTimeCheck object;
+ EXPECT_EQ(object.ref_count_, 0);
+ scoped_refptr<LifeTimeCheck> scoped_object(&object);
+ EXPECT_EQ(object.ref_count_, 1);
+ Bind(&LifeTimeCheck::NullaryVoid, &object)();
+ EXPECT_EQ(object.ref_count_, 1);
+ scoped_object = nullptr;
+ EXPECT_EQ(object.ref_count_, 0);
+}
+
+// Test Bind with scoped_refptr<> argument.
+TEST(BindTest, ScopedRefPointerArgument) {
+ LifeTimeCheck object;
+ EXPECT_EQ(object.ref_count_, 0);
+ scoped_refptr<LifeTimeCheck> scoped_object(&object);
+ EXPECT_EQ(object.ref_count_, 1);
+ {
+ MethodBindTester bind_tester;
+ auto functor =
+ Bind(&MethodBindTester::RefArgument, &bind_tester, scoped_object);
+ EXPECT_EQ(object.ref_count_, 2);
+ }
+ EXPECT_EQ(object.ref_count_, 1);
+ scoped_object = nullptr;
+ EXPECT_EQ(object.ref_count_, 0);
+}
+
+namespace {
+
+const int* Ref(const int& a) { return &a; }
+
+} // anonymous namespace
+
+// Test Bind with non-scoped_refptr<> reference argument.
+TEST(BindTest, RefArgument) {
+ const int x = 42;
+ EXPECT_TRUE(Ref(x) == &x);
+ // Bind() should not make a copy of |x|, i.e. the pointers should be the same.
+ auto functor = Bind(&Ref, x);
+ EXPECT_TRUE(functor() == &x);
}
} // namespace rtc