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 #include <string>
36 #include <stdexcept>
37 
38 #ifdef VLC_THREADS_H_
39 // Ensure we can use vlc_sem_wait_i11e. We can't declare different versions
40 // of the semaphore helper based on vlc_interrupt inclusion, as it would
41 // violate ODR
42 # include <vlc_interrupt.h>
43 #endif
44 
45 namespace vlc
46 {
47 
48 namespace
49 {
50 // This helpers need static linkage to avoid their signature to change when
51 // building as C++17 (noexcept becomes part of the function signature stating there)
52 
53 // Wraps a pointer with a custom releaser
54 // ex: auto ptr = vlc_wrap_cptr( input_item, &input_item_Release );
55 
56 ///
57 /// Wraps a C pointer into a std::unique_ptr
58 ///
59 /// This will convert a C pointer of type T to a std::unique_ptr<T, R> where
60 /// T is the pointee type, and R is an arbitrary releaser type.
61 ///
62 /// ptr will be automatically released by calling r( ptr ) when falling out of
63 /// scope (whether by returning of by throwing an exception
64 ///
65 /// @param ptr a C pointer
66 /// @param r An instance of a Callable type, that will be invoked with ptr
67 /// as its first and only parameter.
68 template <typename T, typename Releaser>
69 inline auto wrap_cptr( T* ptr, Releaser&& r ) noexcept
70  -> std::unique_ptr<T, typename std::decay<decltype( r )>::type>
71 {
72  return std::unique_ptr<T, typename std::decay<decltype( r )>::type>{
73  ptr, std::forward<Releaser>( r )
74  };
75 }
76 
77 ///
78 /// Wraps a C pointer into a std::unique_ptr
79 ///
80 /// This will convert a C pointer to an array of type T to a
81 /// std::unique_ptr<T[], R> where T is the pointee type, and R is an arbitrary
82 /// releaser type.
83 ///
84 /// ptr will be automatically released by calling r( ptr ) when falling out of
85 /// scope (whether by returning of by throwing an exception
86 ///
87 /// This function is equivalent to wrap_cptr, except that the returned
88 /// unique_ptr provides an operator[] for array access instead of operator* and
89 /// operator->
90 ///
91 /// @param ptr a C pointer
92 /// @param r An instance of a Callable type, that will be invoked with ptr
93 /// as its first and only parameter.
94 template <typename T, typename Releaser>
95 inline auto wrap_carray( T* ptr, Releaser&& r ) noexcept
96  -> std::unique_ptr<T[], typename std::decay<decltype( r )>::type>
97 {
98  return std::unique_ptr<T[], typename std::decay<decltype( r )>::type>{
99  ptr, std::forward<Releaser>( r )
100  };
101 }
102 
103 ///
104 /// Wraps a C pointer into a std::unique_ptr
105 ///
106 /// This is a convenience wrapper that will use free() as its releaser
107 ///
108 template <typename T>
109 inline std::unique_ptr<T, void (*)(void*)> wrap_cptr( T* ptr ) noexcept
110 {
111  return wrap_cptr( ptr, &free );
112 }
113 
114 ///
115 /// Wraps a C pointer into a std::unique_ptr
116 ///
117 /// This is a convenience wrapper that will use free() as its releaser
118 ///
119 template <typename T>
120 inline std::unique_ptr<T[], void (*)(void*)> wrap_carray( T* ptr ) noexcept
121 {
122  return wrap_carray( ptr, &free );
123 }
124 
125 } // anonymous namespace
126 
127 ///
128 /// Wraps a C shared resource having associated Hold() and Release() functions
129 //
130 /// This is a RAII wrapper for C shared resources (which are manually managed by
131 /// calling explicitly their Hold() and Release() functions).
132 ///
133 /// The Hold() and Release() functions must accept exactly one parameter having
134 /// type T* (the raw pointer type). Their return type is irrelevant.
135 ///
136 /// To create a new shared resource wrapper type for my_type_t, simply declare:
137 ///
138 /// using MyTypePtr =
139 /// vlc_shared_data_ptr_type(my_type_t, my_type_Hold, my_type_Release);
140 ///
141 /// Then use it to wrap a raw C pointer:
142 ///
143 /// my_type_t *raw_ptr = /* ... */;
144 /// MyTypePtr ptr(raw_ptr);
145 
146 // In C++17, the template declaration could be replaced by:
147 // template<typename T, auto HOLD, auto RELEASE>
148 template <typename T, typename H, typename R, H HOLD, R RELEASE>
149 class vlc_shared_data_ptr {
150  T *ptr = nullptr;
151 
152 public:
153  /* default implicit constructor */
154  vlc_shared_data_ptr() = default;
155 
156  /**
157  * Wrap a shared resource.
158  *
159  * If the pointer is not nullptr, and hold is true, then the resource is
160  * hold (the caller shared ownership is preserved).
161  * If hold is false, then the caller transfers the ownership to this
162  * wrapper.
163  *
164  * \param ptr the raw pointer (can be nullptr)
165  * \param hold whether the resource must be hold
166  */
167  explicit vlc_shared_data_ptr(T *ptr, bool hold = true)
168  : ptr(ptr)
169  {
170  if (ptr && hold)
171  HOLD(ptr);
172  }
173 
174  vlc_shared_data_ptr(const vlc_shared_data_ptr &other)
175  : vlc_shared_data_ptr(other.ptr) {}
176 
177  vlc_shared_data_ptr(vlc_shared_data_ptr &&other) noexcept
178  : ptr(other.ptr)
179  {
180  other.ptr = nullptr;
181  }
182 
183  ~vlc_shared_data_ptr()
184  {
185  if (ptr)
186  RELEASE(ptr);
187  }
188 
189  vlc_shared_data_ptr &operator=(const vlc_shared_data_ptr &other)
190  {
191  reset(other.ptr, true);
192  return *this;
193  }
194 
195  vlc_shared_data_ptr &operator=(vlc_shared_data_ptr &&other) noexcept
196  {
197  reset(other.ptr, false);
198  other.ptr = nullptr;
199  return *this;
200  }
201 
202  bool operator==(const vlc_shared_data_ptr &other) const
203  {
204  return ptr == other.ptr;
205  }
206 
207  bool operator==(std::nullptr_t) const noexcept
208  {
209  return ptr == nullptr;
210  }
211 
212  bool operator!=(const vlc_shared_data_ptr &other) const
213  {
214  return !(*this == other);
215  }
216 
217  bool operator!=(std::nullptr_t) const noexcept
218  {
219  return ptr != nullptr;
220  }
221 
222  explicit operator bool() const
223  {
224  return ptr;
225  }
226 
227  T &operator*() const
228  {
229  return *ptr;
230  }
231 
232  T *operator->() const
233  {
234  return ptr;
235  }
236 
237  T *get() const
238  {
239  return ptr;
240  }
241 
242  /**
243  * Reset the shared resource.
244  *
245  * ptr.reset(rawptr, hold);
246  *
247  * is semantically equivalent to:
248  *
249  * ptr = vlc_shared_data_ptr<...>(rawptr, hold);
250  *
251  * If the pointer is not nullptr, and hold is true, then the resource is
252  * hold (the caller shared ownership is preserved).
253  * If hold is false, then the caller transfers the ownership to this
254  * wrapper.
255  *
256  * \param ptr the raw pointer (can be nullptr)
257  * \param hold whether the resource must be hold
258  */
259  void reset(T *newptr = nullptr, bool hold = true)
260  {
261  if (newptr && hold)
262  HOLD(newptr);
263  if (ptr)
264  RELEASE(ptr);
265  ptr = newptr;
266  }
267 };
268 
269 // useful due to the unnecessarily complex template declaration before C++17
270 #define vlc_shared_data_ptr_type(type, hold, release) \
271  ::vlc::vlc_shared_data_ptr<type, decltype(&hold), decltype(&release), \
272  &hold, &release>
273 
274 #ifdef VLC_THREADS_H_
275 
276 namespace threads
277 {
278 
279 class mutex
280 {
281 public:
282  mutex() noexcept
283  {
284  vlc_mutex_init( &m_mutex );
285  }
286 
287  mutex( const mutex& ) = delete;
288  mutex& operator=( const mutex& ) = delete;
289  mutex( mutex&& ) = delete;
290  mutex& operator=( mutex&& ) = delete;
291 
292  void lock() noexcept
293  {
294  vlc_mutex_lock( &m_mutex );
295  }
296  void unlock() noexcept
297  {
298  vlc_mutex_unlock( &m_mutex );
299  }
300 
301 private:
302  vlc_mutex_t m_mutex;
303  friend class condition_variable;
304  friend class mutex_locker;
305 };
306 
307 class condition_variable
308 {
309 public:
310  condition_variable() noexcept
311  {
312  vlc_cond_init( &m_cond );
313  }
314  void signal() noexcept
315  {
316  vlc_cond_signal( &m_cond );
317  }
318  void broadcast() noexcept
319  {
320  vlc_cond_broadcast( &m_cond );
321  }
322  void wait( mutex& mutex ) noexcept
323  {
324  vlc_cond_wait( &m_cond, &mutex.m_mutex );
325  }
326  int timedwait( mutex& mutex, vlc_tick_t deadline ) noexcept
327  {
328  return vlc_cond_timedwait( &m_cond, &mutex.m_mutex, deadline );
329  }
330 
331 private:
332  vlc_cond_t m_cond;
333 };
334 
335 class mutex_locker
336 {
337 public:
338  mutex_locker( vlc_mutex_t* m ) noexcept
339  : m_mutex( m )
340  {
341  vlc_mutex_lock( m_mutex );
342  }
343  mutex_locker( mutex& m ) noexcept
344  : mutex_locker( &m.m_mutex )
345  {
346  }
347  ~mutex_locker()
348  {
349  vlc_mutex_unlock( m_mutex );
350  }
351  mutex_locker( const mutex_locker& ) = delete;
352  mutex_locker& operator=( const mutex_locker& ) = delete;
353  mutex_locker( mutex_locker&& ) = delete;
354  mutex_locker& operator=( mutex_locker&& ) = delete;
355 
356 private:
357  vlc_mutex_t* m_mutex;
358 };
359 
360 class semaphore
361 {
362 public:
363  semaphore() noexcept
364  {
365  vlc_sem_init( &m_sem, 0 );
366  }
367  semaphore( unsigned int count ) noexcept
368  {
369  vlc_sem_init( &m_sem, count );
370  }
371  ~semaphore()
372  {
373  }
374 
375  semaphore( const semaphore& ) = delete;
376  semaphore& operator=( const semaphore& ) = delete;
377  semaphore( semaphore&& ) = delete;
378  semaphore& operator=( semaphore&& ) = delete;
379 
380  int post() noexcept
381  {
382  return vlc_sem_post( &m_sem );
383  }
384  void wait() noexcept
385  {
386  vlc_sem_wait( &m_sem );
387  }
388 
389  int wait_i11e() noexcept
390  {
391  return vlc_sem_wait_i11e( &m_sem );
392  }
393 
394 private:
395  vlc_sem_t m_sem;
396 };
397 
398 }
399 
400 #endif // VLC_THREADS_H_
401 
402 #ifdef VLC_URL_H
403 
404 class url : public vlc_url_t
405 {
406 public:
407  class invalid : public std::runtime_error
408  {
409  public:
410  invalid( const char* url )
411  : std::runtime_error( std::string{ "Invalid url: " } + url )
412  {
413  }
414  };
415 
416  url()
417  {
418  psz_buffer = nullptr;
419  psz_pathbuffer = nullptr;
420  psz_host = nullptr;
421  }
422 
423  url( const char* str )
424  {
425  if ( vlc_UrlParse( this, str ) )
426  throw invalid( str );
427  }
428 
429  url( const std::string& str )
430  : url( str.c_str() )
431  {
432  }
433 
434  ~url()
435  {
436  vlc_UrlClean(this);
437  }
438 
439  url( const url& ) = delete;
440  url& operator=( const url& ) = delete;
441 
442  url( url&& u ) noexcept
443  : vlc_url_t( u )
444  {
445  u.psz_buffer = nullptr;
446  u.psz_pathbuffer = nullptr;
447  u.psz_host = nullptr;
448  }
449 
450  url& operator=( url&& u ) noexcept
451  {
452  *(static_cast<vlc_url_t*>( this )) = u;
453  u.psz_buffer = nullptr;
454  u.psz_pathbuffer = nullptr;
455  u.psz_host = nullptr;
456  return *this;
457  }
458 };
459 
460 #endif
461 
462 } // namespace vlc
463 
464 #endif
465 
466 #endif // VLC_CXX_HELPERS_HPP
vlc_cond_broadcast
void vlc_cond_broadcast(vlc_cond_t *cond)
Wakes up all threads waiting on a condition variable.
Definition: threads.c:280
vlc_mutex_init
void vlc_mutex_init(vlc_mutex_t *mtx)
Initializes a fast mutex.
Definition: threads.c:123
count
size_t count
Definition: core.c:401
vlc_sem_wait_i11e
int vlc_sem_wait_i11e(vlc_sem_t *sem)
Interruptible variant of vlc_sem_wait().
Definition: interrupt.c:197
vlc_sem_init
void vlc_sem_init(vlc_sem_t *sem, unsigned value)
Initializes a semaphore.
Definition: threads.c:442
vlc_interrupt.h
vlc_cond_wait
void vlc_cond_wait(vlc_cond_t *cond, vlc_mutex_t *mutex)
Waits on a condition variable.
Definition: threads.c:340
vlc_UrlClean
void vlc_UrlClean(vlc_url_t *restrict url)
Definition: url.c:598
vlc_cond_signal
void vlc_cond_signal(vlc_cond_t *cond)
Wakes up one thread waiting on a condition variable.
Definition: threads.c:253
vlc_mutex_t
Mutex.
Definition: vlc_threads.h:225
vlc_UrlParse
int vlc_UrlParse(vlc_url_t *url, const char *str)
Parses an URI or IRI.
Definition: url.c:558
vlc_tick_t
int64_t vlc_tick_t
High precision date or time interval.
Definition: vlc_tick.h:45
vlc_url_t
struct vlc_url_t vlc_url_t
Definition: vlc_common.h:422
lock
vlc_mutex_t lock
Definition: rand.c:50
vlc_url_t
Definition: vlc_url.h:145
vlc_sem_wait
void vlc_sem_wait(vlc_sem_t *sem)
Waits on a semaphore.
Definition: threads.c:463
vlc_sem_t
Semaphore.
Definition: vlc_threads.h:449
vlc_mutex_lock
void vlc_mutex_lock(vlc_mutex_t *mtx)
Acquires a mutex.
Definition: threads.c:158
vlc_cond_t
Condition variable.
Definition: vlc_threads.h:349
vlc_sem_post
int vlc_sem_post(vlc_sem_t *sem)
Increments the value of a semaphore.
Definition: threads.c:447
vlc_cond_init
void vlc_cond_init(vlc_cond_t *cond)
Initializes a condition variable.
Definition: threads.c:234
vlc_cond_timedwait
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:349
vlc_mutex_unlock
void vlc_mutex_unlock(vlc_mutex_t *mtx)
Releases a mutex.
Definition: threads.c:209
vlc_url_t::psz_buffer
char * psz_buffer
Definition: vlc_url.h:156