IOKit: add io_object

Signed-off-by: Simon Rozman <simon@rozman.si>
This commit is contained in:
Simon Rozman 2024-09-30 09:42:49 +02:00
parent 03527f563d
commit eac816c487
2 changed files with 469 additions and 0 deletions

52
include/MacStd/IOKit.hpp Normal file
View File

@ -0,0 +1,52 @@
/*
SPDX-License-Identifier: MIT
Copyright © 2024 Amebis
*/
#pragma once
#include "common.hpp"
#import <IOKit/IOKitLib.h>
namespace macstd {
///
/// IOKit handle wrapper class
///
template <typename T>
class io_object : public handle<T, 0>
{
MACSTD_HANDLE_IMPL(io_object, T, 0)
public:
///
/// Releases an object handle
///
/// \sa [IOObjectRelease function](https://developer.apple.com/documentation/iokit/1514627-ioobjectrelease)
///
virtual ~io_object()
{
if (this->m_h != 0)
free_internal();
}
protected:
///
/// Releases an object handle
///
/// \sa [IOObjectRelease function](https://developer.apple.com/documentation/iokit/1514627-ioobjectrelease)
///
void free_internal() noexcept override
{
IOObjectRelease(this->m_h);
}
};
using io_connect = io_object<io_connect_t>;
using io_enumerator = io_object<io_enumerator_t>;
using io_ident = io_object<io_ident_t>;
using io_iterator = io_object<io_iterator_t>;
using io_registry_entry = io_object<io_registry_entry_t>;
using io_service = io_object<io_service_t>;
using uext_object = io_object<uext_object_t>;
}

View File

@ -7,6 +7,22 @@
#include <stddef.h>
///
/// Declares a class as non-copyable
///
#define MACSTD_NONCOPYABLE(C) \
private: \
C (_In_ const C &h) noexcept; \
C& operator=(_In_ const C &h) noexcept;
///
/// Declares a class as non-movable
///
#define MACSTD_NONMOVABLE(C) \
private: \
C (_Inout_ C &&h) noexcept; \
C& operator=(_Inout_ C &&h) noexcept;
///
/// Size of the stack buffer in bytes used for initial system function call
///
@ -15,3 +31,404 @@
/// \note Decrease this value in case of stack overflow.
///
#define MACSTD_STACK_BUFFER_BYTES 1024
///
/// Implements default constructors and operators to prevent their auto-generation by compiler.
///
#define MACSTD_HANDLE_IMPL(C, T, INVAL) \
public: \
C ( ) noexcept {} \
C (_In_opt_ T h) noexcept : handle<T, INVAL>( h ) {} \
C (_Inout_ C &&h) noexcept : handle<T, INVAL>(std::move(h)) {} \
C& operator=(_In_opt_ T h) noexcept { handle<T, INVAL>::operator=( h ); return *this; } \
C& operator=(_Inout_ C &&h) noexcept { handle<T, INVAL>::operator=(std::move(h)); return *this; } \
MACSTD_NONCOPYABLE(C)
///
/// Implements default constructors and operators to prevent their auto-generation by compiler.
///
#define MACSTD_DPLHANDLE_IMPL(C, T, INVAL) \
public: \
C ( ) noexcept {} \
C (_In_opt_ T h) noexcept : dplhandle<T, INVAL>( h ) {} \
C (_In_ const C &h) noexcept : dplhandle<T, INVAL>(duplicate_internal(h.m_h)) {} \
C (_Inout_ C &&h) noexcept : dplhandle<T, INVAL>(std::move (h )) {} \
C& operator=(_In_opt_ T h) noexcept { dplhandle<T, INVAL>::operator=( h ); return *this; } \
C& operator=(_In_ const C &h) noexcept { dplhandle<T, INVAL>::operator=( h ); return *this; } \
C& operator=(_Inout_ C &&h) noexcept { dplhandle<T, INVAL>::operator=(std::move(h)); return *this; } \
private:
namespace macstd
{
///
/// Base abstract template class to support generic object handle keeping
///
/// It provides basic operators and methods common to all descendands of this class establishing a base to ease the replacement of native object handle type with classes in object-oriented approach.
///
template <class T, const T INVAL>
class handle
{
public:
///
/// Initializes a new class instance with the object handle set to INVAL.
///
handle() noexcept : m_h(INVAL)
{}
///
/// Initializes a new class instance with an already available object handle.
///
/// \param[in] h Initial object handle value
///
handle(_In_opt_ T h) noexcept : m_h(h)
{}
///
/// Move constructor
///
/// \param[in,out] h A rvalue reference of another object
///
handle(_Inout_ handle<T, INVAL> &&h) noexcept
{
// Transfer handle.
m_h = h.m_h;
h.m_h = INVAL;
}
private:
// This class is noncopyable.
handle(_In_ const handle<T, INVAL> &h) noexcept {};
handle<T, INVAL>& operator=(_In_ const handle<T, INVAL> &h) noexcept {};
public:
///
/// Attaches already available object handle.
///
/// \param[in] h Object handle value
///
handle<T, INVAL>& operator=(_In_opt_ T h) noexcept
{
attach(h);
return *this;
}
///
/// Move assignment
///
/// \param[inout] h A rvalue reference of another object
///
#pragma warning(suppress: 26432) // Move constructor is also present, but not detected by code analysis somehow.
handle<T, INVAL>& operator=(_Inout_ handle<T, INVAL> &&h) noexcept
{
if (this != std::addressof(h)) {
// Transfer handle.
if (m_h != INVAL)
free_internal();
m_h = h.m_h;
h.m_h = INVAL;
}
return *this;
}
///
/// Auto-typecasting operator
///
/// \return Object handle
///
operator T() const
{
return m_h;
}
///
/// Returns the object handle value when the object handle is a pointer to a value (class, struct, etc.).
///
/// \return Object handle value
///
T*& operator*() const
{
assert(m_h != INVAL);
return *m_h;
}
///
/// Returns the object handle reference.
/// \return Object handle reference
///
T* operator&()
{
assert(m_h == INVAL);
return &m_h;
}
///
/// Provides object handle member access when the object handle is a pointer to a class or struct.
///
/// \return Object handle
///
T operator->() const
{
assert(m_h != INVAL);
return m_h;
}
///
/// Tests if the object handle is invalid.
///
/// \return
/// - Non zero when object handle is invalid;
/// - Zero otherwise.
///
/// \note Implementing `operator bool() const` would be correct C++ approach here. Unfortunately, it would produce ambiguities where `bool` and `T` are polymorphed.
/// Use `!!` to test if the object handle is valid.
///
bool operator!() const
{
return m_h == INVAL;
}
///
/// Is handle less than?
///
/// \param[in] h Object handle to compare against
/// \return
/// - Non zero when object handle is less than h;
/// - Zero otherwise.
///
bool operator<(_In_opt_ T h) const
{
return m_h < h;
}
///
/// Is handle less than or equal to?
///
/// \param[in] h Object handle to compare against
/// \return
/// - Non zero when object handle is less than or equal to h;
/// - Zero otherwise.
///
bool operator<=(_In_opt_ T h) const
{
return !operator>(h);
}
///
/// Is handle greater than or equal to?
///
/// \param[in] h Object handle to compare against
/// \return
/// - Non zero when object handle is greater than or equal to h;
/// - Zero otherwise.
///
bool operator>=(_In_opt_ T h) const
{
return !operator<(h);
}
///
/// Is handle greater than?
///
/// \param[in] h Object handle to compare against
/// \return
/// - Non zero when object handle is greater than h;
/// - Zero otherwise.
///
bool operator>(_In_opt_ T h) const
{
return h < m_h;
}
///
/// Is handle not equal to?
///
/// \param[in] h Object handle to compare against
/// \return
/// - Non zero when object handle is not equal to h;
/// - Zero otherwise.
///
bool operator!=(_In_opt_ T h) const
{
return !operator==(h);
}
///
/// Is handle equal to?
///
/// \param[in] h Object handle to compare against
/// \return
/// - Non zero when object handle is equal to h;
/// - Zero otherwise.
///
bool operator==(_In_opt_ T h) const
{
return m_h == h;
}
///
/// Sets a new object handle for the class
///
/// When the current object handle of the class is non-INVAL, the object is destroyed first.
///
/// \param[in] h New object handle
///
void attach(_In_opt_ T h) noexcept
{
if (m_h != INVAL)
free_internal();
m_h = h;
}
///
/// Dismisses the object handle from this class
///
/// \return Object handle
///
T detach()
{
T h = m_h;
m_h = INVAL;
return h;
}
///
/// Destroys the object
///
void free()
{
if (m_h != INVAL) {
free_internal();
m_h = INVAL;
}
}
protected:
///
/// Abstract member function that must be implemented by child classes to do the actual object destruction.
///
virtual void free_internal() noexcept = 0;
protected:
T m_h; ///< Object handle
};
///
/// Base abstract template class to support object handle keeping for objects that support trivial handle duplication
///
template <class T, T INVAL>
class dplhandle : public handle<T, INVAL>
{
public:
///
/// Initializes a new class instance with the object handle set to INVAL.
///
dplhandle() noexcept
{}
///
/// Initializes a new class instance with an already available object handle.
///
/// \param[in] h Initial object handle value
///
dplhandle(_In_opt_ T h) noexcept : handle<T, INVAL>(h)
{}
///
/// Copy constructor
///
/// \param[in,out] h A reference of another object
///
dplhandle<T, INVAL>(_In_ const dplhandle<T, INVAL> &h) : handle<T, INVAL>(duplicate_internal(h.m_h))
{}
///
/// Move constructor
///
/// \param[in,out] h A rvalue reference of another object
///
dplhandle<T, INVAL>(_Inout_ dplhandle<T, INVAL> &&h) noexcept : handle<T, INVAL>(std::move(h))
{}
///
/// Attaches already available object handle.
///
/// \param[in] h Object handle value
///
dplhandle<T, INVAL>& operator=(_In_opt_ T h) noexcept
{
handle<T, INVAL>::operator=(h);
return *this;
}
///
/// Duplicates the object.
///
/// \param[in] h Object
///
dplhandle<T, INVAL>& operator=(_In_ const dplhandle<T, INVAL> &h) noexcept
{
if (this != std::addressof(h)) {
if (h.m_h != INVAL) {
T h_new = duplicate_internal(h.m_h);
if (this->m_h != INVAL)
this->free_internal();
this->m_h = h_new;
} else {
if (this->m_h != INVAL)
this->free_internal();
this->m_h = INVAL;
}
}
return *this;
}
///
/// Moves the object.
///
/// \param[inout] h A rvalue reference of another object
///
#pragma warning(disable: 26432) // Move constructor is also present, but not detected by code analysis somehow.
dplhandle<T, INVAL>& operator=(_Inout_ dplhandle<T, INVAL> &&h) noexcept
{
handle<T, INVAL>::operator=(std::move(h));
return *this;
}
///
/// Duplicates and returns a new object handle.
///
/// \return Duplicated object handle
///
T duplicate() const
{
return this->m_h != INVAL ? duplicate_internal(this->m_h) : INVAL;
}
///
/// Duplicates an object handle and sets a new object handle.
///
/// \param[in] h Object handle of existing object
///
void attach_duplicated(_In_opt_ T h)
{
if (this->m_h != INVAL)
this->free_internal();
this->m_h = h != INVAL ? duplicate_internal(h) : INVAL;
}
protected:
///
/// Abstract member function that must be implemented by child classes to do the actual object handle duplication.
/// On failure, it should throw appropriate exception describing the cause, rather than return an invalid handle.
///
/// \param[in] h Object handle of existing object
///
/// \return Duplicated object handle
///
virtual T duplicate_internal(_In_ T h) const = 0;
};
}