rtc::Bind: Capture scoped_refptr reference arguments by value

R=tommi@webrtc.org

Review URL: https://codereview.webrtc.org/1308563004 .

Cr-Commit-Position: refs/heads/master@{#9780}
This commit is contained in:
Magnus Jedvert 2015-08-25 17:56:22 +02:00
parent f4772ee436
commit b274547ebd
3 changed files with 158 additions and 82 deletions

View File

@ -128,6 +128,18 @@ struct PointerType {
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 @@ class MethodFunctor1 {
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 @@ class Functor1 {
return functor_(p1_); }
private:
FunctorT functor_;
P1 p1_;
typename detail::RemoveScopedPtrRef<P1>::type p1_;
};
@ -291,8 +303,8 @@ class MethodFunctor2 {
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 @@ class Functor2 {
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 @@ class MethodFunctor3 {
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 @@ class Functor3 {
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 @@ class MethodFunctor4 {
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 @@ class Functor4 {
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 @@ class MethodFunctor5 {
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 @@ class Functor5 {
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 @@ class MethodFunctor6 {
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 @@ class Functor6 {
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_;
};

View File

@ -124,6 +124,18 @@ struct PointerType {
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 @@ class MethodFunctor$i {
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 @@ Functor$i(const FunctorT& functor$for j [[, P$j p$j]])
private:
FunctorT functor_;$for j [[
P$j p$(j)_;]]
typename detail::RemoveScopedPtrRef<P$j>::type p$(j)_;]]
};

View File

@ -17,6 +17,8 @@ namespace rtc {
namespace {
struct LifeTimeCheck;
struct MethodBindTester {
void NullaryVoid() { ++call_count; }
int NullaryInt() { ++call_count; return 1; }
@ -25,6 +27,10 @@ struct MethodBindTester {
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 @@ struct F {
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 @@ int Multiply(int a, int b) { return a * b; }
// 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(BindTest, BindToFunction) {
// 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