vlc/include/vlc_cxx_helpers.hpp

476 lines
12 KiB
C++

/*****************************************************************************
* vlc_cxx_helpers.hpp: C++ helpers
*****************************************************************************
* Copyright (C) 1998-2018 VLC authors and VideoLAN
*
* Authors: Hugo Beauzée-Luyssen <hugo@beauzee.fr>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifndef VLC_CXX_HELPERS_HPP
#define VLC_CXX_HELPERS_HPP
/******************************************************************************
* C++ memory management helpers
******************************************************************************/
#if defined(__cplusplus) || defined(DOC)
#include <memory>
#include <utility>
#include <type_traits>
#include <string>
#include <stdexcept>
#ifdef VLC_THREADS_H_
// Ensure we can use vlc_sem_wait_i11e. We can't declare different versions
// of the semaphore helper based on vlc_interrupt inclusion, as it would
// violate ODR
# include <vlc_interrupt.h>
#endif
namespace vlc
{
namespace
{
// This helpers need static linkage to avoid their signature to change when
// building as C++17 (noexcept becomes part of the function signature stating there)
// Wraps a pointer with a custom releaser
// ex: auto ptr = vlc_wrap_cptr( input_item, &input_item_Release );
///
/// Wraps a C pointer into a std::unique_ptr
///
/// This will convert a C pointer of type T to a std::unique_ptr<T, R> where
/// T is the pointee type, and R is an arbitrary releaser type.
///
/// ptr will be automatically released by calling r( ptr ) when falling out of
/// scope (whether by returning of by throwing an exception
///
/// @param ptr a C pointer
/// @param r An instance of a Callable type, that will be invoked with ptr
/// as its first and only parameter.
template <typename T, typename Releaser>
inline auto wrap_cptr( T* ptr, Releaser&& r ) noexcept
-> std::unique_ptr<T, typename std::decay<decltype( r )>::type>
{
return std::unique_ptr<T, typename std::decay<decltype( r )>::type>{
ptr, std::forward<Releaser>( r )
};
}
///
/// Wraps a C pointer into a std::unique_ptr
///
/// This will convert a C pointer to an array of type T to a
/// std::unique_ptr<T[], R> where T is the pointee type, and R is an arbitrary
/// releaser type.
///
/// ptr will be automatically released by calling r( ptr ) when falling out of
/// scope (whether by returning of by throwing an exception
///
/// This function is equivalent to wrap_cptr, except that the returned
/// unique_ptr provides an operator[] for array access instead of operator* and
/// operator->
///
/// @param ptr a C pointer
/// @param r An instance of a Callable type, that will be invoked with ptr
/// as its first and only parameter.
template <typename T, typename Releaser>
inline auto wrap_carray( T* ptr, Releaser&& r ) noexcept
-> std::unique_ptr<T[], typename std::decay<decltype( r )>::type>
{
return std::unique_ptr<T[], typename std::decay<decltype( r )>::type>{
ptr, std::forward<Releaser>( r )
};
}
///
/// Wraps a C pointer into a std::unique_ptr
///
/// This is a convenience wrapper that will use free() as its releaser
///
template <typename T>
inline std::unique_ptr<T, void (*)(void*)> wrap_cptr( T* ptr ) noexcept
{
return wrap_cptr( ptr, &free );
}
///
/// Wraps a C pointer into a std::unique_ptr
///
/// This is a convenience wrapper that will use free() as its releaser
///
template <typename T>
inline std::unique_ptr<T[], void (*)(void*)> wrap_carray( T* ptr ) noexcept
{
return wrap_carray( ptr, &free );
}
} // 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(std::nullptr_t)
: ptr(nullptr)
{
}
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==(std::nullptr_t) const noexcept
{
return ptr == nullptr;
}
bool operator!=(const vlc_shared_data_ptr &other) const
{
return !(*this == other);
}
bool operator!=(std::nullptr_t) const noexcept
{
return ptr != nullptr;
}
explicit 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 newptr 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>
#if defined(VLC_THREADS_H_) || defined(DOC)
namespace threads
{
class mutex
{
public:
mutex() noexcept
{
vlc_mutex_init( &m_mutex );
}
mutex( const mutex& ) = delete;
mutex& operator=( const mutex& ) = delete;
mutex( mutex&& ) = delete;
mutex& operator=( mutex&& ) = delete;
void lock() noexcept
{
vlc_mutex_lock( &m_mutex );
}
void unlock() noexcept
{
vlc_mutex_unlock( &m_mutex );
}
private:
vlc_mutex_t m_mutex;
friend class condition_variable;
friend class mutex_locker;
};
class condition_variable
{
public:
condition_variable() noexcept
{
vlc_cond_init( &m_cond );
}
void signal() noexcept
{
vlc_cond_signal( &m_cond );
}
void broadcast() noexcept
{
vlc_cond_broadcast( &m_cond );
}
void wait( mutex& mutex ) noexcept
{
vlc_cond_wait( &m_cond, &mutex.m_mutex );
}
int timedwait( mutex& mutex, vlc_tick_t deadline ) noexcept
{
return vlc_cond_timedwait( &m_cond, &mutex.m_mutex, deadline );
}
private:
vlc_cond_t m_cond;
};
class mutex_locker
{
public:
mutex_locker( vlc_mutex_t* m ) noexcept
: m_mutex( m )
{
vlc_mutex_lock( m_mutex );
}
mutex_locker( mutex& m ) noexcept
: mutex_locker( &m.m_mutex )
{
}
~mutex_locker()
{
vlc_mutex_unlock( m_mutex );
}
mutex_locker( const mutex_locker& ) = delete;
mutex_locker& operator=( const mutex_locker& ) = delete;
mutex_locker( mutex_locker&& ) = delete;
mutex_locker& operator=( mutex_locker&& ) = delete;
private:
vlc_mutex_t* m_mutex;
};
class semaphore
{
public:
semaphore() noexcept
{
vlc_sem_init( &m_sem, 0 );
}
semaphore( unsigned int count ) noexcept
{
vlc_sem_init( &m_sem, count );
}
~semaphore()
{
}
semaphore( const semaphore& ) = delete;
semaphore& operator=( const semaphore& ) = delete;
semaphore( semaphore&& ) = delete;
semaphore& operator=( semaphore&& ) = delete;
int post() noexcept
{
return vlc_sem_post( &m_sem );
}
void wait() noexcept
{
vlc_sem_wait( &m_sem );
}
int wait_i11e() noexcept
{
return vlc_sem_wait_i11e( &m_sem );
}
private:
vlc_sem_t m_sem;
};
}
#endif // VLC_THREADS_H_
#ifdef VLC_URL_H
class url : public vlc_url_t
{
public:
class invalid : public std::runtime_error
{
public:
explicit invalid( const char* url )
: std::runtime_error( std::string{ "Invalid url: " } + url )
{
}
};
url()
{
psz_buffer = nullptr;
psz_pathbuffer = nullptr;
psz_host = nullptr;
}
explicit url( const char* str )
{
if ( vlc_UrlParse( this, str ) )
{
vlc_UrlClean( this );
throw invalid( str );
}
}
explicit url( const std::string& str )
: url( str.c_str() )
{
}
~url()
{
vlc_UrlClean(this);
}
url( const url& ) = delete;
url& operator=( const url& ) = delete;
url( url&& u ) noexcept
: vlc_url_t( u )
{
u.psz_buffer = nullptr;
u.psz_pathbuffer = nullptr;
u.psz_host = nullptr;
}
url& operator=( url&& u ) noexcept
{
vlc_UrlClean( this );
*(static_cast<vlc_url_t*>( this )) = u;
u.psz_buffer = nullptr;
u.psz_pathbuffer = nullptr;
u.psz_host = nullptr;
return *this;
}
};
#endif
} // namespace vlc
#endif
#endif // VLC_CXX_HELPERS_HPP