diff options
| author | Felix Morgner <felix.morgner@ost.ch> | 2025-12-15 17:13:12 +0100 |
|---|---|---|
| committer | Felix Morgner <felix.morgner@ost.ch> | 2025-12-15 17:13:12 +0100 |
| commit | 7b9482ae637126ac9337876e60f519b493437711 (patch) | |
| tree | 6fc71a253c8b0325d303bd34c95b564ba536ed14 /libs | |
| parent | 116f9332a206767c45095950f09f7c7447b561cf (diff) | |
| parent | a9eeec745e29d89afd48ee43d09432eb6fc35be7 (diff) | |
| download | teachos-7b9482ae637126ac9337876e60f519b493437711.tar.xz teachos-7b9482ae637126ac9337876e60f519b493437711.zip | |
os: rework kernel architecture
Rework the code structure and architecture of the kernel by separating
platform-dependent and platform-independent code more cleanly. As of
this patchset, full feature parity has not been achieved. Nonetheless, a
sufficient subset of functionality has been ported to the new
architecture to demonstrate the feasibility of the new structure.
Diffstat (limited to 'libs')
26 files changed, 2579 insertions, 0 deletions
diff --git a/libs/CMakeLists.txt b/libs/CMakeLists.txt new file mode 100644 index 0000000..58d9796 --- /dev/null +++ b/libs/CMakeLists.txt @@ -0,0 +1,3 @@ +add_subdirectory("elf" EXCLUDE_FROM_ALL SYSTEM) +add_subdirectory("kstd" EXCLUDE_FROM_ALL SYSTEM) +add_subdirectory("multiboot2" EXCLUDE_FROM_ALL SYSTEM)
\ No newline at end of file diff --git a/libs/elf/CMakeLists.txt b/libs/elf/CMakeLists.txt new file mode 100644 index 0000000..66e59ee --- /dev/null +++ b/libs/elf/CMakeLists.txt @@ -0,0 +1,19 @@ +add_library("elf" INTERFACE) +add_library("libs::elf" ALIAS "elf") + +target_sources("elf" INTERFACE + FILE_SET HEADERS + BASE_DIRS "include" + FILES + "include/elf/format.hpp" + "include/elf/section_header.hpp" + +) + +target_include_directories("elf" INTERFACE + "include" +) + +set_target_properties("elf" PROPERTIES + VERIFY_INTERFACE_HEADER_SETS YES +)
\ No newline at end of file diff --git a/libs/elf/include/elf/format.hpp b/libs/elf/include/elf/format.hpp new file mode 100644 index 0000000..b3220f5 --- /dev/null +++ b/libs/elf/include/elf/format.hpp @@ -0,0 +1,15 @@ +#ifndef ELF_FORMAT_HPP +#define ELF_FORMAT_HPP + +namespace elf +{ + + enum struct format + { + elf32, + elf64, + }; + +} // namespace elf + +#endif
\ No newline at end of file diff --git a/libs/elf/include/elf/section_header.hpp b/libs/elf/include/elf/section_header.hpp new file mode 100644 index 0000000..3afe334 --- /dev/null +++ b/libs/elf/include/elf/section_header.hpp @@ -0,0 +1,99 @@ +#ifndef ELF_SECTION_HEADER_HPP +#define ELF_SECTION_HEADER_HPP + +#include "format.hpp" + +#include <cstdint> +#include <limits> +#include <type_traits> +#include <utility> + +namespace elf +{ + + template<format Format> + constexpr auto inline section_header_size = std::numeric_limits<std::size_t>::max(); + + template<> + constexpr auto inline section_header_size<format::elf32> = 40uz; + + template<> + constexpr auto inline section_header_size<format::elf64> = 64uz; + + template<format Format> + struct section_header + { + using format_uint = std::conditional_t<Format == format::elf32, std::uint32_t, std::uint64_t>; + + enum struct header_type : std::uint32_t + { + null = 0, ///< Is inactive + program_data = 1, ///< Contains program data + symbol_table = 2, ///< Contains a symbol table + string_table = 3, ///< Contains a string table + relocation_entries_with_addends = 4, ///< Contains relocation information with addends + hash_table = 5, ///< Contains a symbol hash table + dynamic_linking_entries = 6, ///< Contains dynamic linking information + notes = 7, ///< Contains additional notes about the object file + no_content = 8, ///< Contains no data + relocation_entries_without_addends = 9, ///< Contains relocation information without addends + reserved = 10, ///< Reserved for future use + dynamic_linker_symbol_table = 11, ///< Contains the dynamic linker symbol table + init_array = 14, ///< Contains an array of constructor pointers + fini_array = 15, ///< Contains an array of destructor pointers + preinit_array = 16, ///< Contains an array of pre-constructor pointers + group_table = 17, ///< Defines a section group + extended_section_header_indices = 18, ///< Contains extended section header indices + }; + + enum struct header_flags : format_uint + { + writeable = 0x1, ///< Contains writable data + allocated = 0x2, ///< Occupies memory during execution + executable = 0x4, ///< Contains executable instructions + mergeable = 0x10, ///< Contained data may be merged for deduplication + strings = 0x20, ///< Contains null-terminated strings + info_link = 0x40, ///< Contains the section header index of linked section + link_order = 0x80, ///< Must respect linking location relative to linked section + os_specific = 0x100, ///< Must be handled in an OS specific way + group_member = 0x200, ///< Is a member of a section group + thread_local_storage = 0x400, ///< Contains thread local storage data + compressed = 0x800, ///< Is compressed + }; + + //! Check if the section is allocated + [[nodiscard]] constexpr auto allocated() const noexcept -> bool + { + return std::to_underlying(flags) & std::to_underlying(header_flags::allocated); + } + + //! Check if the section is executable + [[nodiscard]] constexpr auto executable() const noexcept -> bool + { + return std::to_underlying(flags) & std::to_underlying(header_flags::executable); + } + + //! Check if the section is writeable + [[nodiscard]] constexpr auto writable() const noexcept -> bool + { + return std::to_underlying(flags) & std::to_underlying(header_flags::writeable); + } + + std::uint32_t name_offset; ///< Offset into the section header string table, defining the section name + header_type type; ///< Type of this section + header_flags flags; ///< Flags of this section + format_uint virtual_load_address; ///< Virtual address where this section is loaded + format_uint file_offset; ///< Offset of the start of this section's data in the file + format_uint size; ///< Size of this section in memory + std::uint32_t linked_section; ///< Index of a section this section is linked to + std::uint32_t extra_info; ///< Additional information for this section (type and flag dependent) + format_uint alignment; ///< Alignment requirement of this section in memory + format_uint entry_size; ///< Size of the entries inside this section (if any) + }; + + static_assert(sizeof(section_header<format::elf32>) == section_header_size<format::elf32>); + static_assert(sizeof(section_header<format::elf64>) == section_header_size<format::elf64>); + +} // namespace elf + +#endif
\ No newline at end of file diff --git a/libs/kstd/CMakeLists.txt b/libs/kstd/CMakeLists.txt new file mode 100644 index 0000000..d83e704 --- /dev/null +++ b/libs/kstd/CMakeLists.txt @@ -0,0 +1,40 @@ +add_library("kstd" STATIC) +add_library("libs::kstd" ALIAS "kstd") + +set(KSTD_LIBC_SYMBOLS + "abort" + "strlen" + "memcmp" +) + +target_sources("kstd" PRIVATE + "src/bits/os.cpp" + "src/libc/stdlib.cpp" + "src/libc/string.cpp" + "src/mutex.cpp" +) + +target_sources("kstd" PUBLIC + FILE_SET HEADERS + BASE_DIRS "include" + FILES + "include/kstd/bits/os.hpp" + "include/kstd/bits/shared_ptr.hpp" + "include/kstd/bits/unique_ptr.hpp" + + "include/kstd/ext/bitfield_enum" + + "include/kstd/asm_ptr" + "include/kstd/memory" + "include/kstd/mutex" + "include/kstd/stack" + "include/kstd/vector" +) + +target_include_directories("kstd" PUBLIC + "include" +) + +list(TRANSFORM KSTD_LIBC_SYMBOLS PREPEND "-Wl,--undefined=") + +target_link_options("kstd" INTERFACE ${KSTD_LIBC_SYMBOLS})
\ No newline at end of file diff --git a/libs/kstd/include/kstd/asm_ptr b/libs/kstd/include/kstd/asm_ptr new file mode 100644 index 0000000..e8636c3 --- /dev/null +++ b/libs/kstd/include/kstd/asm_ptr @@ -0,0 +1,77 @@ +#ifndef KSTD_ASM_POINTER_HPP +#define KSTD_ASM_POINTER_HPP + +#include <bit> + +namespace kstd +{ + + /** + * @brief A pointer that is defined in some assembly source file. + * + * @tparam Type The type of the pointer + */ + template<typename Type> + struct asm_ptr + { + using value_type = Type; + using pointer = value_type *; + using const_pointer = value_type const *; + using reference = value_type &; + using const_reference = value_type const &; + + asm_ptr() = delete; + asm_ptr(asm_ptr const &) = delete; + asm_ptr(asm_ptr &&) = delete; + ~asm_ptr() = delete; + + constexpr auto operator=(asm_ptr const &) = delete; + constexpr auto operator=(asm_ptr &&) = delete; + + auto get() const noexcept -> pointer + { + return m_ptr; + } + + constexpr auto operator+(std::ptrdiff_t offset) const noexcept -> pointer + { + return std::bit_cast<pointer>(m_ptr) + offset; + } + + constexpr auto operator*() noexcept -> reference + { + return *(std::bit_cast<pointer>(m_ptr)); + } + + constexpr auto operator*() const noexcept -> const_reference + { + return *(std::bit_cast<const_pointer>(m_ptr)); + } + + constexpr auto operator[](std::ptrdiff_t offset) noexcept -> reference + { + return *(*this + offset); + } + + constexpr auto operator[](std::ptrdiff_t offset) const noexcept -> const_reference + { + return *(*this + offset); + } + + constexpr auto operator->() noexcept -> pointer + { + return m_ptr; + } + + constexpr auto operator->() const noexcept -> const_pointer + { + return m_ptr; + } + + private: + pointer m_ptr; + }; + +} // namespace kstd + +#endif
\ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/os.hpp b/libs/kstd/include/kstd/bits/os.hpp new file mode 100644 index 0000000..0474f16 --- /dev/null +++ b/libs/kstd/include/kstd/bits/os.hpp @@ -0,0 +1,34 @@ +#ifndef KSTD_OS_HPP +#define KSTD_OS_HPP + +#include <source_location> +#include <string_view> + +namespace kstd::os +{ + /** + * @brief Handle an unrecoverable library error. + * + * The operating system kernel may choose to implement this function in order to try to perform additional cleanup + * before terminating execution. If the kernel does not implement this function, the default implementation will be + * chosen. This default implementation doest nothing. + */ + [[noreturn]] + auto abort() -> void; + + /** + * @brief Terminate execution of the operating system. + * + * The operating system must implement this function. This function must terminate the execution of the operating + * system kernel as is. It may choose to restart the kernel or to halt execution entirely. The implementation must + * guarantee that execution never return from this function. + * + * @param message A message describing the reason for termination of execution. + * @param where The source code location at which the panic was triggered. In general, no argument shall be provided + * for this parameter, thus implicitly capturing the location at which the call originates. + */ + [[noreturn]] + auto panic(std::string_view message, std::source_location where = std::source_location::current()) -> void; +} // namespace kstd::os + +#endif
\ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/shared_ptr.hpp b/libs/kstd/include/kstd/bits/shared_ptr.hpp new file mode 100644 index 0000000..4bcf499 --- /dev/null +++ b/libs/kstd/include/kstd/bits/shared_ptr.hpp @@ -0,0 +1,269 @@ +#ifndef KSTD_BITS_SHARED_PTR_HPP +#define KSTD_BITS_SHARED_PTR_HPP + +#include <atomic> + +namespace kstd +{ + /** + * @brief Shared_pointer is a smart pointer that retains shared ownership of an object through a pointer. Several + * shared_ptr objects may own the same object. The object is destroyed and its memory deallocated when either of + * the following happens: the last remaining shared_ptr owning the object is destroyed; the last remaining + * shared_ptr owning the object is assigned another pointer via operator= or reset(). A + * shared_ptr can share ownership of an object while storing a pointer to another object. This feature can be used + * to point to member objects while owning the object they belong to. The stored pointer is the one accessed by get(), + * the dereference and the comparison operators. The managed pointer is the one passed to the deleter when use count + * reaches zero. + * + * @tparam T The type of the managed object. + */ + template<typename T> + struct shared_ptr + { + /** + * @brief Constructor. + * + * @param pointer A pointer to an object to manage (default is nullptr). + */ + [[gnu::section(".stl_text")]] + explicit shared_ptr(T * pointer = nullptr) + : pointer(pointer) + , ref_count(new std::atomic<std::size_t>(pointer != nullptr ? 1 : 0)) + { + // Nothing to do. + } + + /** + * @brief Copy constructor. + * + * @param other The shared_ptr to copy from. + */ + [[gnu::section(".stl_text")]] + shared_ptr(shared_ptr const & other) + : pointer(other.pointer) + , ref_count(other.ref_count) + { + if (pointer != nullptr) + { + ++(*ref_count); + } + } + + /** + * @brief Move constructor. + * + * @param other The shared_ptr to move from. + */ + [[gnu::section(".stl_text")]] + shared_ptr(shared_ptr && other) noexcept + : pointer(other.pointer) + , ref_count(other.ref_count) + { + other.pointer = nullptr; + other.ref_count = nullptr; + } + + /** + * @brief Copy assignment operator. Replaces the managed object with the one managed by r. Shares ownership of the + * object managed by r. If r manages no object, *this manages no object too. Equivalent to + * shared_ptr<T>(r).swap(*this). + * + * @param other Another smart pointer to share the ownership with. + * @return Reference to this shared pointer. + */ + [[gnu::section(".stl_text")]] + shared_ptr & operator=(shared_ptr const & other) + { + if (this != &other) + { + cleanup(); + pointer = other.pointer; + ref_count = other.ref_count; + + if (pointer != nullptr) + { + ++(*ref_count); + } + } + + return *this; + } + + /** + * @brief Move assignment operator. Move-assigns a shared_ptr from r. After the assignment, *this contains a copy of + * the previous state of r, and r is empty. Equivalent to shared_ptr<T>(std::move(r)).swap(*this). + * + * @param other Another smart pointer to acquire the ownership from. + * @return Reference to this shared pointer. + */ + [[gnu::section(".stl_text")]] + shared_ptr & operator=(shared_ptr && other) noexcept + { + if (this != &other) + { + cleanup(); + pointer = other.pointer; + ref_count = other.ref_count; + other.pointer = nullptr; + other.ref_count = nullptr; + } + + return *this; + } + + /** + * @brief Destructor. Cleans up resources if necessary. + */ + [[gnu::section(".stl_text")]] + ~shared_ptr() + { + cleanup(); + } + + /** + * @brief Replaces the managed object. + * + * @param ptr Pointer to a new object to manage (default = nullptr). + */ + [[gnu::section(".stl_text")]] + void reset(T * ptr = nullptr) + { + cleanup(); + pointer = ptr; + ref_count = new std::atomic<std::size_t>(ptr != nullptr ? 1 : 0); + } + + /** + * @brief Exchanges the stored pointer values and the ownerships of *this and r. Reference counts, if any, are not + * adjusted. + * + * @param other The shared_ptr to swap with. + */ + [[gnu::section(".stl_text")]] + void swap(shared_ptr & other) + { + std::swap(pointer, other.pointer); + std::swap(ref_count, other.ref_count); + } + + /** + * @brief Dereference operator. If get() is a null pointer, the behavior is undefined. + * + * @return Returns the object owned by *this, equivalent to *get(). + */ + [[gnu::section(".stl_text")]] + auto operator*() const -> T & + { + return *pointer; + } + + /** + * @brief Member access operator. + * + * @return Returns a pointer to the object owned by *this, i.e. get(). + */ + [[gnu::section(".stl_text")]] + auto operator->() const -> T * + { + return pointer; + } + + /** + * @brief Returns a pointer to the managed object or nullptr if no object is owned. + * + * @return Pointer to the managed object or nullptr if no object is owned. + */ + [[gnu::section(".stl_text")]] + auto get() const -> T * + { + return pointer; + } + + /** + * @brief Returns the number of different shared_ptr instances (*this included) managing the current object. If + * there is no managed object, 0 is returned. + * + * @note Common use cases include comparison with 0. If use_count returns zero, the shared pointer is empty + * and manages no objects (whether or not its stored pointer is nullptr). Comparison with 1. If use_count returns 1, + * there are no other owners. + * + * @return The number of Shared_pointer instances managing the current object or 0 if there is no managed + * object. + */ + [[gnu::section(".stl_text")]] + auto use_count() const -> std::size_t + { + if (pointer != nullptr) + { + return *ref_count; + } + + return 0; + } + + /** + * @brief Checks whether *this owns an object, i.e. whether get() != nullptr. + * + * @return true if *this owns an object, false otherwise. + */ + [[gnu::section(".stl_text")]] + explicit operator bool() const + { + return pointer != nullptr; + } + + /** + * @brief Defaulted three-way comparator operator. + */ + [[gnu::section(".stl_text")]] + auto operator<=>(shared_ptr const & other) const = default; + + private: + /** + * @brief Releases ownership and deletes the object if this was the last reference to the owned managed object. + */ + [[gnu::section(".stl_text")]] + auto cleanup() -> void + { + if (pointer != nullptr && ref_count != nullptr && --(*ref_count) == 0) + { + delete pointer; + delete ref_count; + } + } + + T * pointer; ///< The managed object. + std::atomic<std::size_t> * ref_count; ///< Reference count. + }; + + /** + * @brief Specializes the std::swap algorithm for stl::unique_ptr. Swaps the contents of lhs and rhs. Calls + * lhs.swap(rhs). + * + * @tparam T Type of the managed object. + * @param lhs, rhs Smart pointers whose contents to swap. + */ + template<typename T> + auto swap(shared_ptr<T> & lhs, shared_ptr<T> & rhs) -> void + { + lhs.swap(rhs); + } + + /** + * @brief Constructs an object of type T and wraps it in a shared_ptr. Constructs a non-array type T. The + * arguments args are passed to the constructor of T. This overload participates in overload resolution only if T is + * not an array type. The function is equivalent to: shared_ptr<T>(new T(std::forward<Args>(args)...)). + * + * @tparam T Type of the managed object. + * @tparam Args Argument types for T's constructor. + * @param args List of arguments with which an instance of T will be constructed. + * @returns Shared_pointer of an instance of type T. + */ + template<typename T, typename... Args> + auto make_shared(Args &&... args) -> shared_ptr<T> + { + return shared_ptr<T>(new T(std::forward<Args>(args)...)); + } +} // namespace kstd + +#endif
\ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/unique_ptr.hpp b/libs/kstd/include/kstd/bits/unique_ptr.hpp new file mode 100644 index 0000000..5f54848 --- /dev/null +++ b/libs/kstd/include/kstd/bits/unique_ptr.hpp @@ -0,0 +1,206 @@ +#ifndef KSTD_BITS_UNIQUE_POINTER_HPP +#define KSTD_BITS_UNIQUE_POINTER_HPP + +#include <utility> + +namespace kstd +{ + /** + * @brief Unique_pointer is a smart pointer that owns (is responsible for) and manages another object via a pointer + * and subsequently disposes of that object when the unique_ptr goes out of scope. + * + * @tparam T Type of the managed object. + */ + template<typename T> + struct unique_ptr + { + /** + * @brief Constructor. + * + * @param ptr A pointer to an object to manage (default is nullptr). + */ + [[gnu::section(".stl_text")]] + explicit unique_ptr(T * ptr = nullptr) + : pointer(ptr) + { + // Nothing to do. + } + + /** + * @brief Destructor that deletes the managed object. + */ + [[gnu::section(".stl_text")]] + ~unique_ptr() + { + delete pointer; + } + + /** + * @brief Deleted copy constructor to enforce unique ownership. + */ + unique_ptr(unique_ptr const &) = delete; + + /** + * @brief Deleted copy assignment operator to enforce unique ownership. + */ + auto operator=(unique_ptr const &) -> unique_ptr & = delete; + + /** + * @brief Move constructor. + * + * @param other Unique pointer to move from. + */ + [[gnu::section(".stl_text")]] + unique_ptr(unique_ptr && other) noexcept + : pointer(other.pointer) + { + other.pointer = nullptr; + } + + /** + * @brief Move assignment operator. Transfers ownership from other to *this as if by calling reset(r.release()). + * + * @param other Smart pointer from which ownership will be transferred. + * @return Reference to this unique pointer. + */ + [[gnu::section(".stl_text")]] + auto operator=(unique_ptr && other) noexcept -> unique_ptr & + { + if (this != &other) + { + delete pointer; + pointer = other.pointer; + other.pointer = nullptr; + } + return *this; + } + + /** + * @brief Dereference operator. If get() is a null pointer, the behavior is undefined. + * + * @return Returns the object owned by *this, equivalent to *get(). + */ + [[gnu::section(".stl_text")]] + auto operator*() const -> T & + { + return *pointer; + } + + /** + * @brief Member access operator. + * + * @return Returns a pointer to the object owned by *this, i.e. get(). + */ + [[gnu::section(".stl_text")]] + auto operator->() const -> T * + { + return pointer; + } + + /** + * @brief Returns a pointer to the managed object or nullptr if no object is owned. + * + * @return Pointer to the managed object or nullptr if no object is owned. + */ + [[gnu::section(".stl_text")]] + auto get() const -> T * + { + return pointer; + } + + /** + * @brief Checks whether *this owns an object, i.e. whether get() != nullptr. + * + * @return true if *this owns an object, false otherwise. + */ + [[gnu::section(".stl_text")]] + explicit operator bool() const noexcept + { + return pointer != nullptr; + } + + /** + * @brief Releases the ownership of the managed object, if any. + * get() returns nullptr after the call. + * The caller is responsible for cleaning up the object (e.g. by use of get_deleter()). + * + * @return Pointer to the managed object or nullptr if there was no managed object, i.e. the value which would be + * returned by get() before the call. + */ + [[gnu::section(".stl_text")]] + auto release() -> T * + { + T * temp = pointer; + pointer = nullptr; + return temp; + } + + /** + * @brief Replaces the managed object. + * + * @note A test for self-reset, i.e. whether ptr points to an object already managed by *this, is not performed, + * except where provided as a compiler extension or as a debugging assert. Note that code such as + * p.reset(p.release()) does not involve self-reset, only code like p.reset(p.get()) does. + * + * @param ptr Pointer to a new object to manage (default = nullptr). + */ + [[gnu::section(".stl_text")]] + auto reset(T * ptr = nullptr) -> void + { + delete pointer; + pointer = ptr; + } + + /** + * @brief Swaps the managed objects and associated deleters of *this and another unique_ptr object other. + * + * @param other Another unique_ptr object to swap the managed object and the deleter with. + */ + [[gnu::section(".stl_text")]] + auto swap(unique_ptr & other) -> void + { + using std::swap; + swap(pointer, other.pointer); + } + + /** + * @brief Defaulted three-way comparator operator. + */ + [[gnu::section(".stl_text")]] + auto operator<=>(unique_ptr const & other) const = default; + + private: + T * pointer; ///< The managed pointer. + }; + + /** + * @brief Specializes the std::swap algorithm for stl::unique_ptr. Swaps the contents of lhs and rhs. Calls + * lhs.swap(rhs). + * + * @tparam T Type of the managed object. + * @param lhs, rhs Smart pointers whose contents to swap. + */ + template<typename T> + auto swap(unique_ptr<T> & lhs, unique_ptr<T> & rhs) -> void + { + lhs.swap(rhs); + } + + /** + * @brief Constructs an object of type T and wraps it in a unique_ptr. Constructs a non-array type T. The + * arguments args are passed to the constructor of T. This overload participates in overload resolution only if T is + * not an array type. The function is equivalent to: unique_ptr<T>(new T(std::forward<Args>(args)...)). + * + * @tparam T Type of the managed object. + * @tparam Args Argument types for T's constructor. + * @param args List of arguments with which an instance of T will be constructed. + * @returns Unique_pointer of an instance of type T. + */ + template<typename T, typename... Args> + auto make_unique(Args &&... args) -> unique_ptr<T> + { + return unique_ptr<T>(new T(std::forward<Args>(args)...)); + } +} // namespace kstd + +#endif
\ No newline at end of file diff --git a/libs/kstd/include/kstd/ext/bitfield_enum b/libs/kstd/include/kstd/ext/bitfield_enum new file mode 100644 index 0000000..80fe9d2 --- /dev/null +++ b/libs/kstd/include/kstd/ext/bitfield_enum @@ -0,0 +1,65 @@ +#ifndef KSTD_EXT_BITFIELD_ENUM_HPP +#define KSTD_EXT_BITFIELD_ENUM_HPP + +#include <bit> +#include <type_traits> +#include <utility> + +namespace kstd::ext +{ + + template<typename EnumType> + requires std::is_enum_v<EnumType> + struct is_bitfield_e |
