aboutsummaryrefslogtreecommitdiffstats
path: root/bindings/cpp/include/serd/detail
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2018-06-16 10:26:47 -0400
committerDavid Robillard <d@drobilla.net>2021-03-08 23:36:35 -0500
commitdc5ccf0913fe716059bb633d8a183cbddc634a7a (patch)
treee2bd280213c648e6a4ff838bd344981f6a60bf4c /bindings/cpp/include/serd/detail
parent0c347c9701af4595a68bb37eb7c69b5db2d452f8 (diff)
downloadserd-dc5ccf0913fe716059bb633d8a183cbddc634a7a.tar.gz
serd-dc5ccf0913fe716059bb633d8a183cbddc634a7a.tar.bz2
serd-dc5ccf0913fe716059bb633d8a183cbddc634a7a.zip
WIP: Add C++ bindings
Diffstat (limited to 'bindings/cpp/include/serd/detail')
-rw-r--r--bindings/cpp/include/serd/detail/Copyable.hpp153
-rw-r--r--bindings/cpp/include/serd/detail/Flags.hpp80
-rw-r--r--bindings/cpp/include/serd/detail/Optional.hpp126
-rw-r--r--bindings/cpp/include/serd/detail/StringView.hpp236
-rw-r--r--bindings/cpp/include/serd/detail/Wrapper.hpp141
5 files changed, 736 insertions, 0 deletions
diff --git a/bindings/cpp/include/serd/detail/Copyable.hpp b/bindings/cpp/include/serd/detail/Copyable.hpp
new file mode 100644
index 00000000..36eb5042
--- /dev/null
+++ b/bindings/cpp/include/serd/detail/Copyable.hpp
@@ -0,0 +1,153 @@
+/*
+ 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>
+#include <utility>
+
+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/bindings/cpp/include/serd/detail/Flags.hpp b/bindings/cpp/include/serd/detail/Flags.hpp
new file mode 100644
index 00000000..115aa543
--- /dev/null
+++ b/bindings/cpp/include/serd/detail/Flags.hpp
@@ -0,0 +1,80 @@
+/*
+ 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(google-explicit-constructor, 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(google-explicit-constructor, 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/bindings/cpp/include/serd/detail/Optional.hpp b/bindings/cpp/include/serd/detail/Optional.hpp
new file mode 100644
index 00000000..0f5b316b
--- /dev/null
+++ b/bindings/cpp/include/serd/detail/Optional.hpp
@@ -0,0 +1,126 @@
+/*
+ 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 <type_traits>
+#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(google-explicit-constructor, 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(google-explicit-constructor, 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/bindings/cpp/include/serd/detail/StringView.hpp b/bindings/cpp/include/serd/detail/StringView.hpp
new file mode 100644
index 00000000..54d8fb84
--- /dev/null
+++ b/bindings/cpp/include/serd/detail/StringView.hpp
@@ -0,0 +1,236 @@
+/*
+ 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(google-explicit-constructor, hicpp-explicit-conversions)
+ StringView(const char* const str) noexcept
+ : _str{str}
+ , _len{str ? strlen(str) : 0}
+ {}
+
+ // NOLINTNEXTLINE(google-explicit-constructor, 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[](const size_t pos) const { return _str[pos]; }
+
+ const char& at(const size_t pos) const
+ {
+ if (pos >= size()) {
+ throw std::out_of_range("serd::StringView::at pos");
+ }
+
+ return _str[pos];
+ }
+
+ StringView substr(const size_t pos) const
+ {
+ if (pos > size()) {
+ throw std::out_of_range("serd::StringView::substr pos");
+ }
+
+ return StringView{data() + pos, size() - pos};
+ }
+
+ StringView substr(const size_t pos, const size_t n) 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
+ {
+ if (!data() && !rhs.data()) {
+ return 0;
+ }
+
+ if (!data()) {
+ return -1;
+ }
+
+ if (!rhs.data()) {
+ return 1;
+ }
+
+ const size_type len = std::min(size(), rhs.size());
+ const int cmp = strncmp(data(), rhs.data(), len);
+
+ if (cmp) {
+ return cmp;
+ }
+
+ if (size() == rhs.size()) {
+ return 0;
+ }
+
+ if (size() < rhs.size()) {
+ return -1;
+ }
+
+ return 1;
+ }
+
+ template<class Alloc = std::allocator<char>>
+ std::basic_string<char, traits_type, Alloc> str() const
+ {
+ return std::basic_string<char, traits_type, Alloc>(data(), size(), Alloc{});
+ }
+
+ 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);
+ }
+
+ explicit operator std::string() const { return str(); }
+
+ explicit operator const char*() const { return _str; }
+
+ // NOLINTNEXTLINE(hicpp-explicit-conversions)
+ operator const SerdStringView() const { return SerdStringView{_str, _len}; }
+
+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/bindings/cpp/include/serd/detail/Wrapper.hpp b/bindings/cpp/include/serd/detail/Wrapper.hpp
new file mode 100644
index 00000000..10395ec4
--- /dev/null
+++ b/bindings/cpp/include/serd/detail/Wrapper.hpp
@@ -0,0 +1,141 @@
+/*
+ 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>
+#include <type_traits>
+#include <utility>
+
+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> {
+ // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
+ 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))
+ {}
+
+ explicit 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