diff --git a/rtc_base/BUILD.gn b/rtc_base/BUILD.gn index 7ffd9919bc..df718f78a4 100644 --- a/rtc_base/BUILD.gn +++ b/rtc_base/BUILD.gn @@ -198,7 +198,10 @@ rtc_source_set("refcount") { "ref_counted_object.h", "ref_counter.h", ] - deps = [ ":macromagic" ] + deps = [ + ":macromagic", + "../api:scoped_refptr", + ] } rtc_library("criticalsection") { diff --git a/rtc_base/ref_counted_object.h b/rtc_base/ref_counted_object.h index 873eaccd47..c9faefe068 100644 --- a/rtc_base/ref_counted_object.h +++ b/rtc_base/ref_counted_object.h @@ -13,6 +13,7 @@ #include #include +#include "api/scoped_refptr.h" #include "rtc_base/constructor_magic.h" #include "rtc_base/ref_count.h" #include "rtc_base/ref_counter.h" @@ -69,6 +70,15 @@ class FinalRefCountedObject final : public T { FinalRefCountedObject(const FinalRefCountedObject&) = delete; FinalRefCountedObject& operator=(const FinalRefCountedObject&) = delete; + template + explicit FinalRefCountedObject(P0&& p0) : T(std::forward(p0)) {} + + template + FinalRefCountedObject(P0&& p0, P1&& p1, Args&&... args) + : T(std::forward(p0), + std::forward(p1), + std::forward(args)...) {} + void AddRef() const { ref_count_.IncRef(); } void Release() const { if (ref_count_.DecRef() == RefCountReleaseStatus::kDroppedLastRef) { @@ -83,6 +93,113 @@ class FinalRefCountedObject final : public T { mutable webrtc::webrtc_impl::RefCounter ref_count_{0}; }; +// General utilities for constructing a reference counted class and the +// appropriate reference count implementation for that class. +// +// These utilities select either the `RefCountedObject` implementation or +// `FinalRefCountedObject` depending on whether the to-be-shared class is +// derived from the RefCountInterface interface or not (respectively). + +// `make_ref_counted`: +// +// Use this when you want to construct a reference counted object of type T and +// get a `scoped_refptr<>` back. Example: +// +// auto p = make_ref_counted("bar", 123); +// +// For a class that inherits from RefCountInterface, this is equivalent to: +// +// auto p = scoped_refptr(new RefCountedObject("bar", 123)); +// +// If the class does not inherit from RefCountInterface, the example is +// equivalent to: +// +// auto p = scoped_refptr>( +// new FinalRefCountedObject("bar", 123)); +// +// In these cases, `make_ref_counted` reduces the amount of boilerplate code but +// also helps with the most commonly intended usage of RefCountedObject whereby +// methods for reference counting, are virtual and designed to satisfy the need +// of an interface. When such a need does not exist, it is more efficient to use +// the `FinalRefCountedObject` template, which does not add the vtable overhead. +// +// Note that in some cases, using RefCountedObject directly may still be what's +// needed. + +// `make_ref_counted` for classes that are convertible to RefCountInterface. +template < + typename T, + typename... Args, + typename std::enable_if::value, + T>::type* = nullptr> +scoped_refptr make_ref_counted(Args&&... args) { + return new RefCountedObject(std::forward(args)...); +} + +// `make_ref_counted` for complete classes that are not convertible to +// RefCountInterface. +template < + typename T, + typename... Args, + typename std::enable_if::value, + T>::type* = nullptr> +scoped_refptr> make_ref_counted(Args&&... args) { + return new FinalRefCountedObject(std::forward(args)...); +} + +// `Ref<>`, `Ref<>::Type` and `Ref<>::Ptr`: +// +// `Ref` is a type declaring utility that is compatible with `make_ref_counted` +// and can be used in classes and methods where it's more convenient (or +// readable) to have the compiler figure out the fully fleshed out type for a +// class rather than spell it out verbatim in all places the type occurs (which +// can mean maintenance work if the class layout changes). +// +// Usage examples: +// +// If you want to declare the parameter type that's always compatible with +// this code: +// +// Bar(make_ref_counted()); +// +// You can use `Ref<>::Ptr` to declare a compatible scoped_refptr type: +// +// void Bar(Ref::Ptr p); +// +// This might be more practically useful in templates though. +// +// In rare cases you might need to be able to declare a parameter that's fully +// compatible with the reference counted T type - and just using T* is not +// enough. To give a code example, we can declare a function, `Foo` that is +// compatible with this code: +// auto p = make_ref_counted(); +// Foo(p.get()); +// +// void Foo(Ref::Type* foo_ptr); +// +// Alternatively this would be: +// void Foo(Foo* foo_ptr); +// or +// void Foo(FinalRefCountedObject* foo_ptr); + +// Declares the approprate reference counted type for T depending on whether +// T is convertible to RefCountInterface or not. +// For classes that are convertible, the type will simply be T. +// For classes that cannot be converted to RefCountInterface, the type will be +// FinalRefCountedObject. +// This is most useful for declaring a scoped_refptr instance for a class +// that may or may not implement a virtual reference counted interface: +// * scoped_refptr::Type> my_ptr; +template +struct Ref { + typedef typename std::conditional< + std::is_convertible::value, + T, + FinalRefCountedObject>::type Type; + + typedef scoped_refptr Ptr; +}; + } // namespace rtc #endif // RTC_BASE_REF_COUNTED_OBJECT_H_ diff --git a/rtc_base/ref_counted_object_unittest.cc b/rtc_base/ref_counted_object_unittest.cc index 05380b7ca9..eeb93bea98 100644 --- a/rtc_base/ref_counted_object_unittest.cc +++ b/rtc_base/ref_counted_object_unittest.cc @@ -64,6 +64,20 @@ class RefClassWithMixedValues : public RefCountInterface { std::string c_; }; +class Foo { + public: + Foo() {} + Foo(int i, int j) : foo_(i + j) {} + int foo_ = 0; +}; + +class FooItf : public RefCountInterface { + public: + FooItf() {} + FooItf(int i, int j) : foo_(i + j) {} + int foo_ = 0; +}; + } // namespace TEST(RefCountedObject, HasOneRef) { @@ -111,4 +125,42 @@ TEST(FinalRefCountedObject, CanWrapIntoScopedRefptr) { EXPECT_TRUE(ref2->HasOneRef()); } +// This test is mostly a compile-time test for scoped_refptr compatibility. +TEST(RefCounted, SmartPointers) { + // Sanity compile-time tests. FooItf is virtual, Foo is not, FooItf inherits + // from RefCountInterface, Foo does not. + static_assert(std::is_base_of::value, ""); + static_assert(!std::is_base_of::value, ""); + static_assert(std::is_polymorphic::value, ""); + static_assert(!std::is_polymorphic::value, ""); + + // Check if Ref generates the expected types for Foo and FooItf. + static_assert(std::is_base_of::Type>::value && + !std::is_same::Type>::value, + ""); + static_assert(std::is_same::Type>::value, ""); + + { + // Test with FooItf, a class that inherits from RefCountInterface. + // Check that we get a valid FooItf reference counted object. + auto p = make_ref_counted(2, 3); + EXPECT_NE(p.get(), nullptr); + EXPECT_EQ(p->foo_, 5); // the FooItf ctor just stores 2+3 in foo_. + + // Use a couple of different ways of declaring what should result in the + // same type as `p` is of. + scoped_refptr::Type> p2 = p; + Ref::Ptr p3 = p; + } + + { + // Same for `Foo` + auto p = make_ref_counted(2, 3); + EXPECT_NE(p.get(), nullptr); + EXPECT_EQ(p->foo_, 5); + scoped_refptr::Type> p2 = p; + Ref::Ptr p3 = p; + } +} + } // namespace rtc