[vlc-commits] include: add C++ wrapper for C shared resources
Romain Vimont
git at videolan.org
Thu Nov 15 14:35:56 CET 2018
vlc | branch: master | Romain Vimont <rom1v at videolabs.io> | Tue Sep 11 12:48:29 2018 +0200| [40c6d71fd9f29c7007a5b1fa8c562bdff32f8f63] | committer: Thomas Guillem
include: add C++ wrapper for C shared resources
Add a helper to create RAII wrappers for C shared resources, which
automatically call the Hold() and Release() functions associated to the
raw pointer.
Just declare a new shared resource wrapper type:
using InputItemPtr = vlc_shared_data_ptr_type(input_item_t,
input_item_Hold,
input_item_Release);
Then use this new type directly:
input_item_t *ptr = /* ... */;
InputItemPtr item(ptr);
QString name = item->psz_name;
InputItemPtr other = item; /* hold automatically */
> http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=40c6d71fd9f29c7007a5b1fa8c562bdff32f8f63
---
include/vlc_cxx_helpers.hpp | 137 +++++++++++++++++++++++++++++++++++++++
src/Makefile.am | 4 +-
src/test/shared_data_ptr.cpp | 149 +++++++++++++++++++++++++++++++++++++++++++
3 files changed, 289 insertions(+), 1 deletion(-)
diff --git a/include/vlc_cxx_helpers.hpp b/include/vlc_cxx_helpers.hpp
index 832a964e53..ed807977b1 100644
--- a/include/vlc_cxx_helpers.hpp
+++ b/include/vlc_cxx_helpers.hpp
@@ -122,6 +122,143 @@ inline std::unique_ptr<T[], void (*)(void*)> wrap_carray( T* ptr ) noexcept
} // anonymous namespace
+///
+/// Wraps a C shared resource having associated Hold() and Release() functions
+//
+/// This is a RAII wrapper for C shared resources (which are manually managed by
+/// calling explicitly their Hold() and Release() functions).
+///
+/// The Hold() and Release() functions must accept exactly one parameter having
+/// type T* (the raw pointer type). Their return type is irrelevant.
+///
+/// To create a new shared resource wrapper type for my_type_t, simply declare:
+///
+/// using MyTypePtr =
+/// vlc_shared_data_ptr_type(my_type_t, my_type_Hold, my_type_Release);
+///
+/// Then use it to wrap a raw C pointer:
+///
+/// my_type_t *raw_ptr = /* ... */;
+/// MyTypePtr ptr(raw_ptr);
+
+// In C++17, the template declaration could be replaced by:
+// template<typename T, auto HOLD, auto RELEASE>
+template <typename T, typename H, typename R, H HOLD, R RELEASE>
+class vlc_shared_data_ptr {
+ T *ptr = nullptr;
+
+public:
+ /* default implicit constructor */
+ vlc_shared_data_ptr() = default;
+
+ /**
+ * Wrap a shared resource.
+ *
+ * If the pointer is not nullptr, and hold is true, then the resource is
+ * hold (the caller shared ownership is preserved).
+ * If hold is false, then the caller transfers the ownership to this
+ * wrapper.
+ *
+ * \param ptr the raw pointer (can be nullptr)
+ * \param hold whether the resource must be hold
+ */
+ explicit vlc_shared_data_ptr(T *ptr, bool hold = true)
+ : ptr(ptr)
+ {
+ if (ptr && hold)
+ HOLD(ptr);
+ }
+
+ vlc_shared_data_ptr(const vlc_shared_data_ptr &other)
+ : vlc_shared_data_ptr(other.ptr) {}
+
+ vlc_shared_data_ptr(vlc_shared_data_ptr &&other) noexcept
+ : ptr(other.ptr)
+ {
+ other.ptr = nullptr;
+ }
+
+ ~vlc_shared_data_ptr()
+ {
+ if (ptr)
+ RELEASE(ptr);
+ }
+
+ vlc_shared_data_ptr &operator=(const vlc_shared_data_ptr &other)
+ {
+ reset(other.ptr, true);
+ return *this;
+ }
+
+ vlc_shared_data_ptr &operator=(vlc_shared_data_ptr &&other) noexcept
+ {
+ reset(other.ptr, false);
+ other.ptr = nullptr;
+ return *this;
+ }
+
+ bool operator==(const vlc_shared_data_ptr &other) const
+ {
+ return ptr == other.ptr;
+ }
+
+ bool operator!=(const vlc_shared_data_ptr &other) const
+ {
+ return !(*this == other);
+ }
+
+ operator bool() const
+ {
+ return ptr;
+ }
+
+ T &operator*() const
+ {
+ return *ptr;
+ }
+
+ T *operator->() const
+ {
+ return ptr;
+ }
+
+ T *get() const
+ {
+ return ptr;
+ }
+
+ /**
+ * Reset the shared resource.
+ *
+ * ptr.reset(rawptr, hold);
+ *
+ * is semantically equivalent to:
+ *
+ * ptr = vlc_shared_data_ptr<...>(rawptr, hold);
+ *
+ * If the pointer is not nullptr, and hold is true, then the resource is
+ * hold (the caller shared ownership is preserved).
+ * If hold is false, then the caller transfers the ownership to this
+ * wrapper.
+ *
+ * \param ptr the raw pointer (can be nullptr)
+ * \param hold whether the resource must be hold
+ */
+ void reset(T *newptr = nullptr, bool hold = true)
+ {
+ if (newptr && hold)
+ HOLD(newptr);
+ if (ptr)
+ RELEASE(ptr);
+ ptr = newptr;
+ }
+};
+
+// useful due to the unnecessarily complex template declaration before C++17
+#define vlc_shared_data_ptr_type(type, hold, release) \
+ ::vlc::vlc_shared_data_ptr<type, decltype(&hold), decltype(&release), \
+ &hold, &release>
+
#ifdef VLC_THREADS_H_
namespace threads
diff --git a/src/Makefile.am b/src/Makefile.am
index 4c7f7a6f98..42a60bef8f 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -535,7 +535,8 @@ check_PROGRAMS = \
test_headers \
test_mrl_helpers \
test_arrays \
- test_vector
+ test_vector \
+ test_shared_data_ptr
TESTS = $(check_PROGRAMS) check_symbols
@@ -559,6 +560,7 @@ test_headers_SOURCES = test/headers.c
test_mrl_helpers_SOURCES = test/mrl_helpers.c
test_arrays_SOURCES = test/arrays.c
test_vector_SOURCES = test/vector.c
+test_shared_data_ptr_SOURCES = test/shared_data_ptr.cpp
AM_LDFLAGS = -no-install
LDADD = libvlccore.la \
diff --git a/src/test/shared_data_ptr.cpp b/src/test/shared_data_ptr.cpp
new file mode 100644
index 0000000000..d261fff390
--- /dev/null
+++ b/src/test/shared_data_ptr.cpp
@@ -0,0 +1,149 @@
+#include <assert.h>
+#include <algorithm>
+#include <vector>
+#include <vlc_cxx_helpers.hpp>
+
+struct mockrc
+{
+ int count = 1;
+};
+
+static void mockrc_Hold(mockrc *p)
+{
+ assert(p->count > 0);
+ p->count++;
+}
+
+static void mockrc_Release(mockrc *p)
+{
+ assert(p->count > 0);
+ p->count--;
+}
+
+using MockRcPtr = vlc_shared_data_ptr_type(mockrc, mockrc_Hold, mockrc_Release);
+
+static void test_raii()
+{
+ mockrc mock;
+
+ {
+ MockRcPtr ptr(&mock, true);
+ assert(ptr);
+ assert(mock.count == 2);
+ assert(ptr->count == 2);
+
+ {
+ mockrc_Hold(&mock);
+ /* transfer the ownership */
+ MockRcPtr ptr2(&mock, false);
+ assert(ptr->count == 3);
+ }
+
+ assert(ptr->count == 2);
+ }
+
+ assert(mock.count == 1);
+}
+
+static void test_assignment()
+{
+ mockrc mock;
+
+ MockRcPtr ptr(&mock, false);
+ assert(mock.count == 1);
+
+ {
+ MockRcPtr ptr2 = ptr;
+ assert(mock.count == 2);
+
+ {
+ MockRcPtr ptr3 = std::move(ptr2);
+ assert(mock.count == 2);
+
+ /* assign a nullptr, this releases the previous one */
+ ptr3 = {};
+ assert(mock.count == 1);
+
+ ptr3 = MockRcPtr(&mock);
+ assert(mock.count == 2);
+
+ MockRcPtr ptr4(std::move(ptr3));
+ assert(mock.count == 2);
+ }
+ /* ptr4 is the only wrapper alive (ptr3 had been moved), count is
+ * decremented by 1 */
+ assert(mock.count == 1);
+ }
+
+ /* ptr2 had been moved, no decrement */
+ assert(mock.count == 1);
+
+ ptr = ptr; /* self-assignement should have no effect */
+ assert(mock.count == 1);
+}
+
+static void test_reset()
+{
+ mockrc mock;
+
+ {
+ MockRcPtr ptr(&mock, false);
+ assert(mock.count == 1);
+
+ /* hold once, release once, count is not changed */
+ ptr.reset(&mock, true);
+ assert(mock.count == 1);
+
+ mockrc_Hold(&mock);
+ assert(mock.count == 2);
+
+ ptr.reset();
+ assert(mock.count == 1);
+
+ ptr.reset(&mock, false);
+ assert(mock.count == 1);
+ }
+
+ /* ownership had transferred to ptr, which has been destroyed */
+ assert(mock.count == 0);
+}
+
+static void test_vector()
+{
+ mockrc mock;
+
+ {
+ std::vector<MockRcPtr> vec(10, MockRcPtr(&mock, false));
+ assert(mock.count == 10);
+
+ std::random_shuffle(vec.begin(), vec.end());
+ assert(mock.count == 10);
+
+ {
+ auto vec2 = vec;
+ assert(mock.count == 20);
+
+ vec2.push_back(MockRcPtr(&mock, true));
+ assert(mock.count == 21);
+ }
+
+ assert(mock.count == 10);
+
+ vec.erase(vec.begin() + 2, vec.begin() + 6);
+ assert(mock.count == 6);
+
+ vec.emplace_back(&mock, true);
+ assert(mock.count == 7);
+ }
+
+ /* ownership was transferred to vec constructor */
+ assert(mock.count == 0);
+}
+
+int main() {
+ test_raii();
+ test_assignment();
+ test_reset();
+ test_vector();
+ return 0;
+}
More information about the vlc-commits
mailing list