VLC  4.0.0-dev
vlc_cxx_helpers.hpp
Go to the documentation of this file.
1 /*****************************************************************************
2  * vlc_cxx_helpers.hpp: C++ helpers
3  *****************************************************************************
4  * Copyright (C) 1998-2018 VLC authors and VideoLAN
5  *
6  * Authors: Hugo BeauzĂ©e-Luyssen <hugo@beauzee.fr>
7  *
8  * This program is free software; you can redistribute it and/or modify it
9  * under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation; either version 2.1 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this program; if not, write to the Free Software Foundation,
20  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21  *****************************************************************************/
22 
23 #ifndef VLC_CXX_HELPERS_HPP
24 #define VLC_CXX_HELPERS_HPP
25 
26 /******************************************************************************
27  * C++ memory management helpers
28  ******************************************************************************/
29 
30 #ifdef __cplusplus
31 
32 #include <memory>
33 #include <utility>
34 #include <type_traits>
35 
36 #ifdef VLC_THREADS_H_
37 // Ensure we can use vlc_sem_wait_i11e. We can't declare different versions
38 // of the semaphore helper based on vlc_interrupt inclusion, as it would
39 // violate ODR
40 # include <vlc_interrupt.h>
41 #endif
42 
43 namespace vlc
44 {
45 
46 namespace
47 {
48 // This helpers need static linkage to avoid their signature to change when
49 // building as C++17 (noexcept becomes part of the function signature stating there)
50 
51 // Wraps a pointer with a custom releaser
52 // ex: auto ptr = vlc_wrap_cptr( input_item, &input_item_Release );
53 
54 ///
55 /// Wraps a C pointer into a std::unique_ptr
56 ///
57 /// This will convert a C pointer of type T to a std::unique_ptr<T, R> where
58 /// T is the pointee type, and R is an arbitrary releaser type.
59 ///
60 /// ptr will be automatically released by calling r( ptr ) when falling out of
61 /// scope (whether by returning of by throwing an exception
62 ///
63 /// @param ptr a C pointer
64 /// @param r An instance of a Callable type, that will be invoked with ptr
65 /// as its first and only parameter.
66 template <typename T, typename Releaser>
67 inline auto wrap_cptr( T* ptr, Releaser&& r ) noexcept
68  -> std::unique_ptr<T, typename std::decay<decltype( r )>::type>
69 {
70  return std::unique_ptr<T, typename std::decay<decltype( r )>::type>{
71  ptr, std::forward<Releaser>( r )
72  };
73 }
74 
75 ///
76 /// Wraps a C pointer into a std::unique_ptr
77 ///
78 /// This will convert a C pointer to an array of type T to a
79 /// std::unique_ptr<T[], R> where T is the pointee type, and R is an arbitrary
80 /// releaser type.
81 ///
82 /// ptr will be automatically released by calling r( ptr ) when falling out of
83 /// scope (whether by returning of by throwing an exception
84 ///
85 /// This function is equivalent to wrap_cptr, except that the returned
86 /// unique_ptr provides an operator[] for array access instead of operator* and
87 /// operator->
88 ///
89 /// @param ptr a C pointer
90 /// @param r An instance of a Callable type, that will be invoked with ptr
91 /// as its first and only parameter.
92 template <typename T, typename Releaser>
93 inline auto wrap_carray( T* ptr, Releaser&& r ) noexcept
94  -> std::unique_ptr<T[], typename std::decay<decltype( r )>::type>
95 {
96  return std::unique_ptr<T[], typename std::decay<decltype( r )>::type>{
97  ptr, std::forward<Releaser>( r )
98  };
99 }
100 
101 ///
102 /// Wraps a C pointer into a std::unique_ptr
103 ///
104 /// This is a convenience wrapper that will use free() as its releaser
105 ///
106 template <typename T>
107 inline std::unique_ptr<T, void (*)(void*)> wrap_cptr( T* ptr ) noexcept
108 {
109  return wrap_cptr( ptr, &free );
110 }
111 
112 ///
113 /// Wraps a C pointer into a std::unique_ptr
114 ///
115 /// This is a convenience wrapper that will use free() as its releaser
116 ///
117 template <typename T>
118 inline std::unique_ptr<T[], void (*)(void*)> wrap_carray( T* ptr ) noexcept
119 {
120  return wrap_carray( ptr, &free );
121 }
122 
123 } // anonymous namespace
124 
125 ///
126 /// Wraps a C shared resource having associated Hold() and Release() functions
127 //
128 /// This is a RAII wrapper for C shared resources (which are manually managed by
129 /// calling explicitly their Hold() and Release() functions).
130 ///
131 /// The Hold() and Release() functions must accept exactly one parameter having
132 /// type T* (the raw pointer type). Their return type is irrelevant.
133 ///
134 /// To create a new shared resource wrapper type for my_type_t, simply declare:
135 ///
136 /// using MyTypePtr =
137 /// vlc_shared_data_ptr_type(my_type_t, my_type_Hold, my_type_Release);
138 ///
139 /// Then use it to wrap a raw C pointer:
140 ///
141 /// my_type_t *raw_ptr = /* ... */;
142 /// MyTypePtr ptr(raw_ptr);
143 
144 // In C++17, the template declaration could be replaced by:
145 // template<typename T, auto HOLD, auto RELEASE>
146 template <typename T, typename H, typename R, H HOLD, R RELEASE>
147 class vlc_shared_data_ptr {
148  T *ptr = nullptr;
149 
150 public:
151  /* default implicit constructor */
152  vlc_shared_data_ptr() = default;
153 
154  /**
155  * Wrap a shared resource.
156  *
157  * If the pointer is not nullptr, and hold is true, then the resource is
158  * hold (the caller shared ownership is preserved).
159  * If hold is false, then the caller transfers the ownership to this
160  * wrapper.
161  *
162  * \param ptr the raw pointer (can be nullptr)
163  * \param hold whether the resource must be hold
164  */
165  explicit vlc_shared_data_ptr(T *ptr, bool hold = true)
166  : ptr(ptr)
167  {
168  if (ptr && hold)
169  HOLD(ptr);
170  }
171 
172  vlc_shared_data_ptr(const vlc_shared_data_ptr &other)
173  : vlc_shared_data_ptr(other.ptr) {}
174 
175  vlc_shared_data_ptr(vlc_shared_data_ptr &&other) noexcept
176  : ptr(other.ptr)
177  {
178  other.ptr = nullptr;
179  }
180 
181  ~vlc_shared_data_ptr()
182  {
183  if (ptr)
184  RELEASE(ptr);
185  }
186 
187  vlc_shared_data_ptr &operator=(const vlc_shared_data_ptr &other)
188  {
189  reset(other.ptr, true);
190  return *this;
191  }
192 
193  vlc_shared_data_ptr &operator=(vlc_shared_data_ptr &&other) noexcept
194  {
195  reset(other.ptr, false);
196  other.ptr = nullptr;
197  return *this;
198  }
199 
200  bool operator==(const vlc_shared_data_ptr &other) const
201  {
202  return ptr == other.ptr;
203  }
204 
205  bool operator==(std::nullptr_t) const noexcept
206  {
207  return ptr == nullptr;
208  }
209 
210  bool operator!=(const vlc_shared_data_ptr &other) const
211  {
212  return !(*this == other);
213  }
214 
215  bool operator!=(std::nullptr_t) const noexcept
216  {
217  return ptr != nullptr;
218  }
219 
220  explicit operator bool() const
221  {
222  return ptr;
223  }
224 
225  T &operator*() const
226  {
227  return *ptr;
228  }
229 
230  T *operator->() const
231  {
232  return ptr;
233  }
234 
235  T *get() const
236  {
237  return ptr;
238  }
239 
240  /**
241  * Reset the shared resource.
242  *
243  * ptr.reset(rawptr, hold);
244  *
245  * is semantically equivalent to:
246  *
247  * ptr = vlc_shared_data_ptr<...>(rawptr, hold);
248  *
249  * If the pointer is not nullptr, and hold is true, then the resource is
250  * hold (the caller shared ownership is preserved).
251  * If hold is false, then the caller transfers the ownership to this
252  * wrapper.
253  *
254  * \param ptr the raw pointer (can be nullptr)
255  * \param hold whether the resource must be hold
256  */
257  void reset(T *newptr = nullptr, bool hold = true)
258  {
259  if (newptr && hold)
260  HOLD(newptr);
261  if (ptr)
262  RELEASE(ptr);
263  ptr = newptr;
264  }
265 };
266 
267 // useful due to the unnecessarily complex template declaration before C++17
268 #define vlc_shared_data_ptr_type(type, hold, release) \
269  ::vlc::vlc_shared_data_ptr<type, decltype(&hold), decltype(&release), \
270  &hold, &release>
271 
272 #ifdef VLC_THREADS_H_
273 
274 namespace threads
275 {
276 
277 class mutex
278 {
279 public:
280  mutex() noexcept
281  {
282  vlc_mutex_init( &m_mutex );
283  }
284 
285  mutex( const mutex& ) = delete;
286  mutex& operator=( const mutex& ) = delete;
287  mutex( mutex&& ) = delete;
288  mutex& operator=( mutex&& ) = delete;
289 
290  void lock() noexcept
291  {
292  vlc_mutex_lock( &m_mutex );
293  }
294  void unlock() noexcept
295  {
296  vlc_mutex_unlock( &m_mutex );
297  }
298 
299 private:
300  vlc_mutex_t m_mutex;
301  friend class condition_variable;
302  friend class mutex_locker;
303 };
304 
305 class condition_variable
306 {
307 public:
308  condition_variable() noexcept
309  {
310  vlc_cond_init( &m_cond );
311  }
312  void signal() noexcept
313  {
314  vlc_cond_signal( &m_cond );
315  }
316  void broadcast() noexcept
317  {
318  vlc_cond_broadcast( &m_cond );
319  }
320  void wait( mutex& mutex ) noexcept
321  {
322  vlc_cond_wait( &m_cond, &mutex.m_mutex );
323  }
324  int timedwait( mutex& mutex, vlc_tick_t deadline ) noexcept
325  {
326  return vlc_cond_timedwait( &m_cond, &mutex.m_mutex, deadline );
327  }
328 
329 private:
330  vlc_cond_t m_cond;
331 };
332 
333 class mutex_locker
334 {
335 public:
336  mutex_locker( vlc_mutex_t* m ) noexcept
337  : m_mutex( m )
338  {
339  vlc_mutex_lock( m_mutex );
340  }
341  mutex_locker( mutex& m ) noexcept
342  : mutex_locker( &m.m_mutex )
343  {
344  }
345  ~mutex_locker()
346  {
347  vlc_mutex_unlock( m_mutex );
348  }
349  mutex_locker( const mutex_locker& ) = delete;
350  mutex_locker& operator=( const mutex_locker& ) = delete;
351  mutex_locker( mutex_locker&& ) = delete;
352  mutex_locker& operator=( mutex_locker&& ) = delete;
353 
354 private:
355  vlc_mutex_t* m_mutex;
356 };
357 
358 class semaphore
359 {
360 public:
361  semaphore() noexcept
362  {
363  vlc_sem_init( &m_sem, 0 );
364  }
365  semaphore( unsigned int count ) noexcept
366  {
367  vlc_sem_init( &m_sem, count );
368  }
369  ~semaphore()
370  {
371  }
372 
373  semaphore( const semaphore& ) = delete;
374  semaphore& operator=( const semaphore& ) = delete;
375  semaphore( semaphore&& ) = delete;
376  semaphore& operator=( semaphore&& ) = delete;
377 
378  int post() noexcept
379  {
380  return vlc_sem_post( &m_sem );
381  }
382  void wait() noexcept
383  {
384  vlc_sem_wait( &m_sem );
385  }
386 
387  int wait_i11e() noexcept
388  {
389  return vlc_sem_wait_i11e( &m_sem );
390  }
391 
392 private:
393  vlc_sem_t m_sem;
394 };
395 
396 }
397 
398 #endif // VLC_THREADS_H_
399 
400 } // namespace vlc
401 
402 #endif
403 
404 #endif // VLC_CXX_HELPERS_HPP
Semaphore.
Definition: vlc_threads.h:498
vlc_mutex_t lock
Definition: rand.c:32
int64_t vlc_tick_t
High precision date or time interval.
Definition: vlc_tick.h:45
void vlc_mutex_unlock(vlc_mutex_t *mtx)
Releases a mutex.
Definition: threads.c:212
size_t count
Definition: core.c:402
int vlc_cond_timedwait(vlc_cond_t *cond, vlc_mutex_t *mutex, vlc_tick_t deadline)
Waits on a condition variable up to a certain date.
Definition: threads.c:367
Mutex.
Definition: vlc_threads.h:266
void vlc_cond_broadcast(vlc_cond_t *cond)
Wakes up all threads waiting on a condition variable.
Definition: threads.c:285
int vlc_sem_post(vlc_sem_t *sem)
Increments the value of a semaphore.
Definition: threads.c:484
Condition variable.
Definition: vlc_threads.h:390
void vlc_sem_wait(vlc_sem_t *sem)
Waits on a semaphore.
Definition: threads.c:500
int vlc_sem_wait_i11e(vlc_sem_t *sem)
Interruptible variant of vlc_sem_wait().
Definition: interrupt.c:197
void vlc_cond_signal(vlc_cond_t *cond)
Wakes up one thread waiting on a condition variable.
Definition: threads.c:258
void vlc_cond_wait(vlc_cond_t *cond, vlc_mutex_t *mutex)
Waits on a condition variable.
Definition: threads.c:356
This file declares interruptible sleep functions.
void vlc_sem_init(vlc_sem_t *sem, unsigned value)
Initializes a semaphore.
Definition: threads.c:479
void vlc_cond_init(vlc_cond_t *cond)
Initializes a condition variable.
Definition: threads.c:237
void vlc_mutex_init(vlc_mutex_t *mtx)
Initializes a fast mutex.
Definition: threads.c:123
void vlc_mutex_lock(vlc_mutex_t *mtx)
Acquires a mutex.
Definition: threads.c:158