diff options
Diffstat (limited to 'serd/detail')
-rw-r--r-- | serd/detail/Copyable.hpp | 150 | ||||
-rw-r--r-- | serd/detail/Flags.hpp | 76 | ||||
-rw-r--r-- | serd/detail/Optional.hpp | 127 | ||||
-rw-r--r-- | serd/detail/StringView.hpp | 204 | ||||
-rw-r--r-- | serd/detail/Wrapper.hpp | 127 |
5 files changed, 684 insertions, 0 deletions
diff --git a/serd/detail/Copyable.hpp b/serd/detail/Copyable.hpp new file mode 100644 index 00000000..df5305d4 --- /dev/null +++ b/serd/detail/Copyable.hpp @@ -0,0 +1,150 @@ +/* + Copyright 2019-2020 David Robillard <http://drobilla.net> + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#ifndef SERD_DETAIL_COPYABLE_HPP +#define SERD_DETAIL_COPYABLE_HPP + +#include "serd/detail/Wrapper.hpp" + +#include <cstddef> +#include <memory> +#include <type_traits> + +namespace serd { +namespace detail { + +/// Copy function for a C object +template <class T> +using CopyFunc = T* (*)(const T*); + +template <class T, Mutable<T>* Copy(const T*)> +typename std::enable_if<std::is_const<T>::value, T>::type* +copy(const T* ptr) +{ + return ptr; // Making a view (const reference), do not copy +} + +template <class T, Mutable<T>* Copy(const T*)> +typename std::enable_if<!std::is_const<T>::value, T>::type* +copy(const T* ptr) +{ + return Copy(ptr); // Making a mutable wrapper, copy +} + +template <class T, + Mutable<T>* Copy(const T*), + bool Equals(const T*, const T*), + void Free(Mutable<T>*)> +class BasicCopyable : public Wrapper<T, BasicDeleter<T, Free>> +{ +public: + using Deleter = BasicDeleter<T, Free>; + using Base = Wrapper<T, Deleter>; + + explicit BasicCopyable(T* ptr) : Base{ptr} {} + + BasicCopyable(const BasicCopyable& wrapper) + : Base(copy<T, Copy>(wrapper.cobj())) + { + } + + template <class U, void UFree(Mutable<U>*)> + explicit BasicCopyable(const BasicCopyable<U, Copy, Equals, UFree>& wrapper) + : Base(copy<T, Copy>(wrapper.cobj())) + { + } + + BasicCopyable(BasicCopyable&&) noexcept = default; + ~BasicCopyable() noexcept = default; + + BasicCopyable& operator=(BasicCopyable&&) noexcept = default; + + BasicCopyable& operator=(const BasicCopyable& wrapper) + { + if (&wrapper != this) { + this->_ptr = + std::unique_ptr<T, Deleter>(copy<T, Copy>(wrapper.cobj())); + } + return *this; + } + + template <class U> + bool operator==(const BasicCopyable<U, Copy, Equals, Free>& wrapper) const + { + return Equals(this->cobj(), wrapper.cobj()); + } + + template <class U> + bool operator!=(const BasicCopyable<U, Copy, Equals, Free>& wrapper) const + { + return !operator==(wrapper); + } +}; + +/// Generic C++ wrapper for a copyable C object +template <class T, + Mutable<T>* Copy(const T*), + bool Equals(const T*, const T*), + void Free(Mutable<T>*)> +class DynamicCopyable : public Wrapper<T, DynamicDeleter<T, Free>> +{ +public: + using Deleter = DynamicDeleter<T, Free>; + using Base = Wrapper<T, Deleter>; + using UPtr = typename Base::UPtr; + + explicit DynamicCopyable(UPtr ptr) : Base(std::move(ptr)) {} + + DynamicCopyable(const DynamicCopyable& wrapper) + : Base(Copy(wrapper.cobj()), Ownership::owned) + { + } + + DynamicCopyable(DynamicCopyable&&) noexcept = default; + DynamicCopyable& operator=(DynamicCopyable&&) noexcept = default; + + ~DynamicCopyable() noexcept = default; + + DynamicCopyable& operator=(const DynamicCopyable& wrapper) + { + if (&wrapper != this) { + this->_ptr = std::unique_ptr<T, Deleter>(Copy(wrapper.cobj()), + Ownership::owned); + } + + return *this; + } + + template <class U> + bool operator==(const DynamicCopyable<U, Copy, Equals, Free>& wrapper) const + { + return Equals(this->cobj(), wrapper.cobj()); + } + + template <class U> + bool operator!=(const DynamicCopyable<U, Copy, Equals, Free>& wrapper) const + { + return !operator==(wrapper); + } + +protected: + explicit DynamicCopyable(std::nullptr_t) : Base(nullptr) {} +}; + +} // namespace detail +} // namespace serd + +#endif // SERD_DETAIL_COPYABLE_HPP diff --git a/serd/detail/Flags.hpp b/serd/detail/Flags.hpp new file mode 100644 index 00000000..637b3aec --- /dev/null +++ b/serd/detail/Flags.hpp @@ -0,0 +1,76 @@ +/* + Copyright 2019-2020 David Robillard <http://drobilla.net> + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#ifndef SERD_DETAIL_FLAGS_HPP +#define SERD_DETAIL_FLAGS_HPP + +#include <type_traits> + +namespace serd { +namespace detail { + +/** + Type-safe bit flags + + This is a minimal interface for a type-safe bit flags field, which only + allows values from the given enum to be set. + + @tparam Flag Enum class of flag values. +*/ +template <typename Flag> +class Flags +{ +public: + static_assert(std::is_enum<Flag>::value, ""); + + using FlagUnderlyingType = typename std::underlying_type<Flag>::type; + using Value = typename std::make_unsigned<FlagUnderlyingType>::type; + + constexpr Flags() noexcept : _value(0) {} + constexpr explicit Flags(const Value value) noexcept : _value{value} {} + + // NOLINTNEXTLINE(hicpp-explicit-conversions) + constexpr Flags(const Flag f) noexcept + : _value(static_cast<Value>(f)) + { + } + + constexpr Flags operator|(const Flag rhs) const noexcept + { + return Flags{_value | static_cast<Value>(rhs)}; + } + + constexpr Flags operator|(const Flags rhs) const noexcept + { + return Flags{_value | rhs._value}; + } + + constexpr bool operator==(const Flag rhs) const noexcept + { + return _value == static_cast<Value>(rhs); + } + + // NOLINTNEXTLINE(hicpp-explicit-conversions) + constexpr operator Value() const noexcept { return _value; } + +private: + Value _value{}; +}; + +} // namespace detail +} // namespace serd + +#endif // SERD_DETAIL_FLAGS_HPP diff --git a/serd/detail/Optional.hpp b/serd/detail/Optional.hpp new file mode 100644 index 00000000..24a5acce --- /dev/null +++ b/serd/detail/Optional.hpp @@ -0,0 +1,127 @@ +/* + Copyright 2019-2020 David Robillard <http://drobilla.net> + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#ifndef SERD_DETAIL_OPTIONAL_HPP +#define SERD_DETAIL_OPTIONAL_HPP + +#include <cassert> +#include <cstddef> +#include <utility> + +namespace serd { +namespace detail { + +struct ConstructNullOptional +{ +}; + +/** + A simple optional wrapper around a wrapped type with a pointer-like API + + This works like a typical optional type, but only works with Wrapper types, + and exploits the fact that these are interally just pointers to avoid adding + space overhead for an "is_set" flag, like a generic optional class would. + + Types must explicitly opt-in to being optional by providing a constructor + that takes a single ContructNullOptional argument. This constructor should + only be used by the Optional implementation, which guarantees that such an + object will not be used except by calling its cobj() method. +*/ +template <typename T> +class Optional +{ +public: + using CType = typename T::CType; + + Optional() : _value(nullptr) {} + + // NOLINTNEXTLINE(hicpp-explicit-conversions, modernize-pass-by-value) + Optional(const T& value) : _value(value) {} + + // NOLINTNEXTLINE(hicpp-explicit-conversions) + Optional(T&& value) : _value(std::move(value)) {} + + template <typename U, + typename = typename std::enable_if< + std::is_convertible<U, T>::value>::type> + // NOLINTNEXTLINE(hicpp-explicit-conversions) + Optional(U&& value) : _value(std::forward<U>(value)) + { + } + + void reset() { _value = T{nullptr}; } + + const T& operator*() const + { + assert(_value.cobj()); + return _value; + } + + T& operator*() + { + assert(_value.cobj()); + return _value; + } + + const T* operator->() const + { + assert(_value.cobj()); + return &_value; + } + + T* operator->() + { + assert(_value.cobj()); + return &_value; + } + + bool operator==(const Optional& optional) + { + return (!*this && !optional) || + (*this && optional && _value == optional._value); + } + + bool operator!=(const Optional& optional) { return !operator==(optional); } + + explicit operator bool() const { return _value.cobj(); } + bool operator!() const { return !_value.cobj(); } + + inline CType* cobj() { return _value.cobj(); } + inline const CType* cobj() const { return _value.cobj(); } + +private: + T _value; +}; + +} // namespace detail + +template <class T> +constexpr detail::Optional<T> +make_optional(T&& value) +{ + return detail::Optional<T>{std::forward<T>(value)}; +} + +template <class T, class... Args> +constexpr detail::Optional<T> +make_optional(Args&&... args) +{ + return detail::Optional<T>{std::forward<Args>(args)...}; +} + +} // namespace serd + +#endif // SERD_DETAIL_OPTIONAL_HPP diff --git a/serd/detail/StringView.hpp b/serd/detail/StringView.hpp new file mode 100644 index 00000000..dab3aecd --- /dev/null +++ b/serd/detail/StringView.hpp @@ -0,0 +1,204 @@ +/* + Copyright 2019-2020 David Robillard <http://drobilla.net> + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#ifndef SERD_DETAIL_STRINGVIEW_HPP +#define SERD_DETAIL_STRINGVIEW_HPP + +#include <algorithm> +#include <cstddef> +#include <cstring> +#include <ostream> +#include <stdexcept> +#include <string> + +namespace serd { +namespace detail { + +/** + Immutable slice of a string. + + This is a minimal implementation that is compatible with std::string_view + and std::string for most basic use cases. This could be replaced with + std::string_view once C++17 support can be relied on. +*/ +class StringView +{ +public: + using char_type = char; + using size_type = size_t; + using traits_type = std::char_traits<char>; + using value_type = char; + using pointer = value_type*; + using const_pointer = const value_type*; + using reference = value_type&; + using const_reference = const value_type&; + using iterator = const char*; + using const_iterator = const char*; + + static constexpr size_type npos = size_t(-1); + + constexpr StringView() noexcept = default; + + constexpr StringView(const char* const str, const size_t len) noexcept + : _str{str}, _len{len} + { + } + + // NOLINTNEXTLINE(hicpp-explicit-conversions) + StringView(const char* const str) noexcept + : _str{str}, _len{str ? strlen(str) : 0} + { + } + + // NOLINTNEXTLINE(hicpp-explicit-conversions) + StringView(const std::string& str) noexcept + : _str{str.c_str()}, _len{str.length()} + { + } + + constexpr size_t size() const { return _len; } + constexpr size_t length() const { return _len; } + constexpr bool empty() const { return _len == 0; } + constexpr const char* c_str() const { return _str; } + constexpr const char* data() const { return _str; } + constexpr const char& front() const { return _str[0]; } + constexpr const char& back() const { return _str[_len - 1]; } + + constexpr const_iterator begin() const { return _str; } + constexpr const_iterator end() const { return _str + _len; } + constexpr const_iterator cbegin() const { return begin(); } + constexpr const_iterator cend() const { return end(); } + + constexpr const char& operator[](size_t pos) const { return _str[pos]; } + + const char& at(size_t pos) const + { + if (pos >= size()) { + throw std::out_of_range("serd::StringView::at pos"); + } + + return _str[pos]; + } + + StringView substr(size_t pos, size_t n = npos) const + { + if (pos > size()) { + throw std::out_of_range("serd::StringView::substr pos"); + } + + return StringView{data() + pos, std::min(size() - pos, n)}; + } + + int compare(StringView rhs) const noexcept + { + const size_type len = std::min(size(), rhs.size()); + const int cmp = strncmp(data(), rhs.data(), len); + if (cmp) { + return cmp; + } else if (size() == rhs.size()) { + return 0; + } else if (size() < rhs.size()) { + return -1; + } + return 1; + } + + template <class Alloc = std::allocator<char>> + std::basic_string<char, traits_type, Alloc> + str(const Alloc& alloc = {}) const + { + return std::basic_string<char, traits_type, Alloc>( + data(), size(), alloc); + } + + // NOLINTNEXTLINE(hicpp-explicit-conversions) + explicit operator std::string() const { return str(); } + + // NOLINTNEXTLINE(hicpp-explicit-conversions) + explicit operator const char*() const { return _str; } + +private: + const char* const _str{}; + const size_t _len{}; +}; + +inline bool +operator==(const detail::StringView& lhs, const detail::StringView& rhs) +{ + return !lhs.compare(rhs); +} + +inline bool +operator==(const detail::StringView& lhs, const std::string& rhs) +{ + return lhs.length() == rhs.length() && + !strncmp(lhs.c_str(), rhs.c_str(), lhs.length()); +} + +inline bool +operator==(const detail::StringView& lhs, const char* rhs) +{ + return !strncmp(lhs.c_str(), rhs, lhs.length()); +} + +inline bool +operator!=(const detail::StringView& lhs, const detail::StringView& rhs) +{ + return lhs.compare(rhs); +} + +inline bool +operator!=(const detail::StringView& lhs, const std::string& rhs) +{ + return lhs.length() != rhs.length() || + strncmp(lhs.c_str(), rhs.c_str(), lhs.length()); +} + +inline bool +operator!=(const detail::StringView& lhs, const char* rhs) +{ + return strncmp(lhs.c_str(), rhs, lhs.length()); +} + +inline bool +operator<(const detail::StringView& lhs, const detail::StringView& rhs) +{ + return lhs.compare(rhs) < 0; +} + +inline bool +operator<(const detail::StringView& lhs, const std::string& rhs) +{ + return lhs.c_str() < StringView(rhs); +} + +inline bool +operator<(const detail::StringView& lhs, const char* rhs) +{ + return strncmp(lhs.c_str(), rhs, lhs.length()) < 0; +} + +inline std::ostream& +operator<<(std::ostream& os, const StringView& str) +{ + os.write(str.data(), std::streamsize(str.size())); + return os; +} + +} // namespace detail +} // namespace serd + +#endif // SERD_DETAIL_STRINGVIEW_HPP diff --git a/serd/detail/Wrapper.hpp b/serd/detail/Wrapper.hpp new file mode 100644 index 00000000..ad38b121 --- /dev/null +++ b/serd/detail/Wrapper.hpp @@ -0,0 +1,127 @@ +/* + Copyright 2019-2020 David Robillard <http://drobilla.net> + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#ifndef SERD_DETAIL_WRAPPER_HPP +#define SERD_DETAIL_WRAPPER_HPP + +#include <cstddef> +#include <memory> + +namespace serd { +namespace detail { + +template <typename T> +class Optional; + +/// Free function for a C object +template <typename T> +using FreeFunc = void (*)(T*); + +template <class T> +using Mutable = typename std::remove_const<T>::type; + +/** + Simple overhead-free deleter for a C object. + + Can be used with const or mutable pointers, but only mutable pointers will + be freed. This makes it simple to wrap APIs where constness conveys + ownership, but can not handle unowned mutable pointers. +*/ +template <typename T, void Free(Mutable<T>*)> +struct BasicDeleter +{ + template <typename = std::enable_if<!std::is_const<T>::value>> + void operator()(typename std::remove_const<T>::type* ptr) + { + Free(ptr); + } + + template <typename = std::enable_if<std::is_const<T>::value>> + void operator()(const T*) + { + } +}; + +/// Ownership for `DynamicDeleter` +enum class Ownership { owned, view }; + +/** + Deleter for a C object that can handle dynamic ownership. + + Unlike `BasicDeleter`, this can be used to handle non-owned references to + mutable objects, at the cost of an extra word for tracking the ownership + (since constness in the type can't convey this information). +*/ +template <typename T, void Free(Mutable<T>*)> +struct DynamicDeleter : BasicDeleter<T, Free> +{ + DynamicDeleter(Ownership ownership) : _ownership{ownership} {} + + void operator()(T* ptr) + { + if (_ownership == Ownership::owned) { + BasicDeleter<T, Free>::operator()(ptr); + } + } + +private: + Ownership _ownership; +}; + +/// Generic C++ wrapper for a C object +template <typename T, class Deleter> +class Wrapper +{ +public: + using CType = T; + using UPtr = std::unique_ptr<T, Deleter>; + + explicit Wrapper(T* ptr) : _ptr(ptr, Deleter{}) {} + Wrapper(T* ptr, Deleter deleter) : _ptr(ptr, std::move(deleter)) {} + Wrapper(UPtr ptr) : _ptr(std::move(ptr)) {} + + Wrapper(Wrapper&&) noexcept = default; + Wrapper& operator=(Wrapper&&) noexcept = default; + + Wrapper(const Wrapper&) = delete; + Wrapper& operator=(const Wrapper&) = delete; + + ~Wrapper() = default; + + T* cobj() { return _ptr.get(); } + const T* cobj() const { return _ptr.get(); } + +protected: + friend class detail::Optional<T>; + + explicit Wrapper(std::nullptr_t) : _ptr(nullptr) {} + + void reset() { _ptr.reset(); } + + std::unique_ptr<T, Deleter> _ptr; +}; + +template <typename T, void Free(Mutable<T>*)> +class BasicWrapper : public Wrapper<T, BasicDeleter<T, Free>> +{ +public: + explicit BasicWrapper(T* ptr) : Wrapper<T, BasicDeleter<T, Free>>{ptr} {} +}; + +} // namespace detail +} // namespace serd + +#endif // SERD_DETAIL_WRAPPER_HPP |