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:
parent
f4772ee436
commit
b274547ebd
@ -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_;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@ -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)_;]]
|
||||
|
||||
};
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user