Merge branch 'master' of https://git.mewin.de/mewin/mijin2
This commit is contained in:
@@ -4,6 +4,8 @@
|
||||
#if !defined(MIJIN_UTIL_ALIGN_HPP_INCLUDED)
|
||||
#define MIJIN_UTIL_ALIGN_HPP_INCLUDED 1
|
||||
|
||||
#include <bit>
|
||||
#include <cstdint>
|
||||
#include "../internal/common.hpp"
|
||||
|
||||
namespace mijin
|
||||
@@ -18,7 +20,13 @@ constexpr T alignUp(T value, T alignTo) MIJIN_NOEXCEPT
|
||||
return value;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T* alignUp(T* pointer, std::uintptr_t alignTo) MIJIN_NOEXCEPT
|
||||
{
|
||||
return std::bit_cast<T*>(alignUp(std::bit_cast<std::uintptr_t>(pointer), alignTo));
|
||||
}
|
||||
|
||||
#define MIJIN_STRIDEOF(T) mijin::alignUp(sizeof(T), alignof(T))
|
||||
} // namespace mijin
|
||||
|
||||
#endif // !defined(MIJIN_UTIL_ALIGN_HPP_INCLUDED)
|
||||
#endif // !defined(MIJIN_UTIL_ALIGN_HPP_INCLUDED)
|
||||
|
||||
179
source/mijin/util/annot.hpp
Normal file
179
source/mijin/util/annot.hpp
Normal file
@@ -0,0 +1,179 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(MIJIN_UTIL_ANNOT_HPP_INCLUDED)
|
||||
#define MIJIN_UTIL_ANNOT_HPP_INCLUDED 1
|
||||
|
||||
#include <utility>
|
||||
#include "../internal/common.hpp"
|
||||
#include "../debug/assert.hpp"
|
||||
|
||||
#if !defined(__has_include)
|
||||
#define __has_include(x) (false)
|
||||
#endif
|
||||
|
||||
#if !defined(MIJIN_USE_GSL)
|
||||
#if __has_include(<gsl/gsl>)
|
||||
#define MIJIN_USE_GSL 1
|
||||
#else
|
||||
#define MIJIN_USE_GSL 0
|
||||
#endif
|
||||
#endif // !defined(MIJIN_USE_GSL)
|
||||
|
||||
#include <concepts>
|
||||
#include "./concepts.hpp"
|
||||
|
||||
#if MIJIN_USE_GSL
|
||||
#include <gsl/gsl>
|
||||
#endif
|
||||
|
||||
namespace mijin
|
||||
{
|
||||
template<typename T>
|
||||
concept nullable_type = !std::is_same_v<T, std::nullptr_t> && requires(T t) { t == nullptr; };
|
||||
|
||||
template<nullable_type T>
|
||||
class NotNullable
|
||||
{
|
||||
private:
|
||||
T base_;
|
||||
public:
|
||||
template<typename U> requires(std::is_same_v<T, U> && std::is_copy_constructible_v<T>)
|
||||
constexpr NotNullable(U base) MIJIN_NOEXCEPT_IF(std::is_nothrow_copy_constructible_v<T>)
|
||||
: base_(base)
|
||||
{
|
||||
MIJIN_ASSERT(base_ != nullptr, "Constructed non-nullable type with nullptr.");
|
||||
}
|
||||
|
||||
// some compilers apparently need this since they are unable to do proper pattern matching ...
|
||||
NotNullable(const NotNullable&) MIJIN_NOEXCEPT_IF(std::is_nothrow_copy_constructible_v<T>) = default;
|
||||
NotNullable(NotNullable&&) MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v<T>) = default;
|
||||
|
||||
template<typename TOther> requires(std::is_constructible_v<T, const TOther&>)
|
||||
constexpr NotNullable(const NotNullable<TOther>& other) MIJIN_NOEXCEPT_IF((std::is_nothrow_constructible_v<T, const TOther&>))
|
||||
: base_(other.base_)
|
||||
{
|
||||
MIJIN_ASSERT(base_ != nullptr, "Constructed non-nullable type with nullptr.");
|
||||
}
|
||||
template<typename TOther> requires(std::is_constructible_v<T, TOther&&>)
|
||||
constexpr NotNullable(NotNullable<TOther>&& other) MIJIN_NOEXCEPT_IF((std::is_nothrow_constructible_v<T, TOther&&>))
|
||||
: base_(std::exchange(other.base_, nullptr))
|
||||
{
|
||||
MIJIN_ASSERT(base_ != nullptr, "Constructed non-nullable type with nullptr.");
|
||||
}
|
||||
template<typename TArg, typename... TArgs> requires(!std::is_same_v<TArg, std::nullptr_t>
|
||||
&& (!std::is_same_v<TArg, T> && sizeof...(TArgs) == 0)
|
||||
&& std::is_constructible_v<T, TArg&&, TArgs&&...>)
|
||||
constexpr NotNullable(TArg&& arg, TArgs&&... args) MIJIN_NOEXCEPT_IF((std::is_nothrow_constructible_v<T, TArg&&, TArgs&&...>))
|
||||
: base_(std::forward<TArg>(arg), std::forward<TArgs>(args)...)
|
||||
{
|
||||
MIJIN_ASSERT(base_ != nullptr, "Constructed non-nullable type with nullptr.");
|
||||
}
|
||||
constexpr NotNullable(T&& base) MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v<T>)
|
||||
requires(std::is_move_constructible_v<T>)
|
||||
: base_(std::move(base))
|
||||
{
|
||||
MIJIN_ASSERT(base_ != nullptr, "Constructed non-nullable type with nullptr.");
|
||||
}
|
||||
constexpr NotNullable(NotNullable&& other) MIJIN_NOEXCEPT_IF(std::is_nothrow_copy_constructible_v<T>)
|
||||
requires(std::is_copy_constructible_v<T>)
|
||||
: base_(other.base_)
|
||||
{
|
||||
MIJIN_ASSERT(base_ != nullptr, "Constructed non-nullable type with nullptr.");
|
||||
}
|
||||
constexpr NotNullable(NotNullable&& other) MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v<T>)
|
||||
requires(std::is_move_constructible_v<T>)
|
||||
: base_(std::exchange(other.base_, nullptr))
|
||||
{
|
||||
MIJIN_ASSERT(base_ != nullptr, "Constructed non-nullable type with nullptr.");
|
||||
}
|
||||
constexpr NotNullable(std::nullptr_t) MIJIN_DELETE("Type is not nullable.");
|
||||
|
||||
// some compilers apparently need this since they are unable to do proper pattern matching ...
|
||||
NotNullable& operator=(const NotNullable&) MIJIN_NOEXCEPT_IF(std::is_nothrow_copy_constructible_v<T>) = default;
|
||||
NotNullable& operator=(NotNullable&&) MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v<T>) = default;
|
||||
|
||||
constexpr NotNullable& operator=(const NotNullable& other) MIJIN_NOEXCEPT_IF(std::is_nothrow_copy_assignable_v<T>)
|
||||
requires(std::is_copy_assignable_v<T>)
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
this->base_ = other.base_;
|
||||
}
|
||||
MIJIN_ASSERT(base_ != nullptr, "Assigned nullptr to non-nullable type."); // might still happen if the other type was moved from
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr NotNullable& operator=(NotNullable&& other) MIJIN_NOEXCEPT_IF(std::is_nothrow_move_assignable_v<T>)
|
||||
requires(std::is_move_assignable_v<T>)
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
this->base_ = std::exchange(other.base_, nullptr);
|
||||
}
|
||||
MIJIN_ASSERT(base_ != nullptr, "Assigned nullptr to non-nullable type."); // might still happen if the other type was moved from
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr NotNullable& operator=(std::nullptr_t) MIJIN_DELETE("Type is not nullable.");
|
||||
|
||||
template<std::equality_comparable_with<T> TOther>
|
||||
bool operator==(const NotNullable<TOther>& other) MIJIN_NOEXCEPT_IF(noexcept(std::declval<T>() == std::declval<TOther>()))
|
||||
{
|
||||
return base_ == other.base_;
|
||||
}
|
||||
|
||||
template<std::equality_comparable_with<T> TOther>
|
||||
bool operator!=(const NotNullable<TOther>& other) MIJIN_NOEXCEPT_IF(noexcept(std::declval<T>() != std::declval<TOther>()))
|
||||
{
|
||||
return base_ != other.base_;
|
||||
}
|
||||
|
||||
template<nullable_type TOther> requires(std::equality_comparable_with<T, TOther> && !std::is_same_v<TOther, std::nullptr_t>)
|
||||
bool operator==(const TOther& other) MIJIN_NOEXCEPT_IF(noexcept(std::declval<T>() == std::declval<TOther>()))
|
||||
{
|
||||
return base_ == other;
|
||||
}
|
||||
|
||||
template<nullable_type TOther> requires(std::equality_comparable_with<T, TOther> && !std::is_same_v<TOther, std::nullptr_t>)
|
||||
bool operator!=(const TOther& other) MIJIN_NOEXCEPT_IF(noexcept(std::declval<T>() != std::declval<TOther>()))
|
||||
{
|
||||
return base_ != other;
|
||||
}
|
||||
|
||||
bool operator==(std::nullptr_t) MIJIN_DELETE("Type is not nullable.");
|
||||
bool operator!=(std::nullptr_t) MIJIN_DELETE("Type is not nullable.");
|
||||
|
||||
constexpr operator const T&() const MIJIN_NOEXCEPT { return get(); }
|
||||
constexpr operator std::nullptr_t() const MIJIN_DELETE("Type is not nullable.");
|
||||
constexpr const T& operator->() const MIJIN_NOEXCEPT { return get(); }
|
||||
constexpr decltype(auto) operator*() const MIJIN_NOEXCEPT_IF(noexcept(*get())) { return *get(); }
|
||||
|
||||
NotNullable& operator++() MIJIN_DELETE("Operator disabled for non-nullable types.");
|
||||
NotNullable& operator--() MIJIN_DELETE("Operator disabled for non-nullable types.");
|
||||
NotNullable operator++(int) MIJIN_DELETE("Operator disabled for non-nullable types.");
|
||||
NotNullable operator--(int) MIJIN_DELETE("Operator disabled for non-nullable types.");
|
||||
NotNullable& operator+=(std::ptrdiff_t) MIJIN_DELETE("Operator disabled for non-nullable types.");
|
||||
NotNullable& operator-=(std::ptrdiff_t) MIJIN_DELETE("Operator disabled for non-nullable types.");
|
||||
void operator[](std::ptrdiff_t) const MIJIN_DELETE("Operator disabled for non-nullable types.");
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr const T& get() const MIJIN_NOEXCEPT { return base_; }
|
||||
|
||||
template<nullable_type TOther>
|
||||
friend class NotNullable;
|
||||
};
|
||||
|
||||
#if MIJIN_USE_GSL
|
||||
template<mijin::pointer_type T>
|
||||
using owner_t = gsl::owner<T>;
|
||||
#else
|
||||
template<mijin::pointer_type T>
|
||||
using owner_t = T;
|
||||
#endif
|
||||
|
||||
template<typename T>
|
||||
using not_null_t = NotNullable<T>;
|
||||
}
|
||||
|
||||
#endif // !defined(MIJIN_UTIL_ANNOT_HPP_INCLUDED)
|
||||
55
source/mijin/util/ansi_colors.hpp
Normal file
55
source/mijin/util/ansi_colors.hpp
Normal file
@@ -0,0 +1,55 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(MIJIN_UTIL_ANSI_COLORS_HPP_INCLUDED)
|
||||
#define MIJIN_UTIL_ANSI_COLORS_HPP_INCLUDED 1
|
||||
|
||||
#include "../internal/common.hpp"
|
||||
|
||||
namespace mijin
|
||||
{
|
||||
template<typename TChar = MIJIN_DEFAULT_CHAR_TYPE>
|
||||
struct BaseAnsiFontEffects
|
||||
{
|
||||
using char_t = TChar;
|
||||
|
||||
static constexpr const char_t* RESET = MIJIN_SMART_QUOTE(char_t, "0");
|
||||
|
||||
static constexpr const char_t* FG_BLACK = MIJIN_SMART_QUOTE(char_t, "30");
|
||||
static constexpr const char_t* FG_RED = MIJIN_SMART_QUOTE(char_t, "31");
|
||||
static constexpr const char_t* FG_GREEN = MIJIN_SMART_QUOTE(char_t, "32");
|
||||
static constexpr const char_t* FG_YELLOW = MIJIN_SMART_QUOTE(char_t, "33");
|
||||
static constexpr const char_t* FG_BLUE = MIJIN_SMART_QUOTE(char_t, "34");
|
||||
static constexpr const char_t* FG_MAGENTA = MIJIN_SMART_QUOTE(char_t, "35");
|
||||
static constexpr const char_t* FG_CYAN = MIJIN_SMART_QUOTE(char_t, "36");
|
||||
static constexpr const char_t* FG_WHITE = MIJIN_SMART_QUOTE(char_t, "37");
|
||||
static constexpr const char_t* FG_BRIGHT_BLACK = MIJIN_SMART_QUOTE(char_t, "90");
|
||||
static constexpr const char_t* FG_BRIGHT_RED = MIJIN_SMART_QUOTE(char_t, "91");
|
||||
static constexpr const char_t* FG_BRIGHT_GREEN = MIJIN_SMART_QUOTE(char_t, "92");
|
||||
static constexpr const char_t* FG_BRIGHT_YELLOW = MIJIN_SMART_QUOTE(char_t, "93");
|
||||
static constexpr const char_t* FG_BRIGHT_BLUE = MIJIN_SMART_QUOTE(char_t, "94");
|
||||
static constexpr const char_t* FG_BRIGHT_MAGENTA = MIJIN_SMART_QUOTE(char_t, "95");
|
||||
static constexpr const char_t* FG_BRIGHT_CYAN = MIJIN_SMART_QUOTE(char_t, "96");
|
||||
static constexpr const char_t* FG_BRIGHT_WHITE = MIJIN_SMART_QUOTE(char_t, "97");
|
||||
|
||||
static constexpr const char_t* BG_BLACK = MIJIN_SMART_QUOTE(char_t, "40");
|
||||
static constexpr const char_t* BG_RED = MIJIN_SMART_QUOTE(char_t, "41");
|
||||
static constexpr const char_t* BG_GREEN = MIJIN_SMART_QUOTE(char_t, "42");
|
||||
static constexpr const char_t* BG_YELLOW = MIJIN_SMART_QUOTE(char_t, "43");
|
||||
static constexpr const char_t* BG_BLUE = MIJIN_SMART_QUOTE(char_t, "44");
|
||||
static constexpr const char_t* BG_MAGENTA = MIJIN_SMART_QUOTE(char_t, "45");
|
||||
static constexpr const char_t* BG_CYAN = MIJIN_SMART_QUOTE(char_t, "46");
|
||||
static constexpr const char_t* BG_WHITE = MIJIN_SMART_QUOTE(char_t, "47");
|
||||
static constexpr const char_t* BG_BRIGHT_BLACK = MIJIN_SMART_QUOTE(char_t, "100");
|
||||
static constexpr const char_t* BG_BRIGHT_RED = MIJIN_SMART_QUOTE(char_t, "101");
|
||||
static constexpr const char_t* BG_BRIGHT_GREEN = MIJIN_SMART_QUOTE(char_t, "102");
|
||||
static constexpr const char_t* BG_BRIGHT_YELLOW = MIJIN_SMART_QUOTE(char_t, "103");
|
||||
static constexpr const char_t* BG_BRIGHT_BLUE = MIJIN_SMART_QUOTE(char_t, "104");
|
||||
static constexpr const char_t* BG_BRIGHT_MAGENTA = MIJIN_SMART_QUOTE(char_t, "105");
|
||||
static constexpr const char_t* BG_BRIGHT_CYAN = MIJIN_SMART_QUOTE(char_t, "106");
|
||||
static constexpr const char_t* BG_BRIGHT_WHITE = MIJIN_SMART_QUOTE(char_t, "107");
|
||||
};
|
||||
MIJIN_DEFINE_CHAR_VERSIONS(AnsiFontEffects)
|
||||
}
|
||||
|
||||
#endif // !defined(MIJIN_UTIL_ANSI_COLORS_HPP_INCLUDED)
|
||||
@@ -5,6 +5,7 @@
|
||||
#define MIJIN_UTIL_CONCEPTS_HPP_INCLUDED 1
|
||||
|
||||
#include <type_traits>
|
||||
#include "./traits.hpp"
|
||||
|
||||
namespace mijin
|
||||
{
|
||||
@@ -39,6 +40,32 @@ concept pointer_type = std::is_pointer_v<T>;
|
||||
template<typename T>
|
||||
concept reference_type = std::is_reference_v<T>;
|
||||
|
||||
namespace impl
|
||||
{
|
||||
template<typename T>
|
||||
using pointer_t = typename T::pointer;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
concept allocator_type = requires(T alloc, typename T::value_type value, detect_or_t<typename T::value_type*, impl::pointer_t, T> pointer, int count)
|
||||
{
|
||||
typename T::value_type;
|
||||
{ alloc.allocate(count) } -> std::same_as<decltype(pointer)>;
|
||||
{ alloc.deallocate(pointer, count) } -> std::same_as<void>;
|
||||
} && !std::is_const_v<typename T::value_type> && !std::is_volatile_v<typename T::value_type>;
|
||||
|
||||
template<typename T, typename TOther>
|
||||
concept allocator_type_for = allocator_type<T> && std::is_same_v<typename T::value_type, TOther>;
|
||||
|
||||
template<template<typename> typename T>
|
||||
concept allocator_tmpl = allocator_type<T<int>>;
|
||||
|
||||
template<typename T, typename TData>
|
||||
concept deleter_type = requires(T deleter, TData* ptr)
|
||||
{
|
||||
deleter(ptr);
|
||||
};
|
||||
|
||||
//
|
||||
// public functions
|
||||
//
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#define MIJIN_UTIL_HASH_HPP_INCLUDED 1
|
||||
|
||||
#include <functional>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
namespace mijin
|
||||
{
|
||||
@@ -16,4 +18,21 @@ inline void hashCombine(std::size_t& seed, const T& value, const THasher& hasher
|
||||
}
|
||||
}
|
||||
|
||||
template<typename... T>
|
||||
struct std::hash<std::tuple<T...>>
|
||||
{
|
||||
std::size_t operator()(const std::tuple<T...>& tuple) const noexcept
|
||||
{
|
||||
return hashImpl(tuple, std::index_sequence_for<T...>());
|
||||
}
|
||||
|
||||
template<std::size_t... indices>
|
||||
std::size_t hashImpl(const std::tuple<T...>& tuple, std::index_sequence<indices...>) const noexcept
|
||||
{
|
||||
std::size_t result = 0;
|
||||
(mijin::hashCombine(result, std::get<indices>(tuple)), ...);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // MIJIN_UTIL_HASH_HPP_INCLUDED
|
||||
|
||||
42
source/mijin/util/misc.hpp
Normal file
42
source/mijin/util/misc.hpp
Normal file
@@ -0,0 +1,42 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(MIJIN_UTIL_MISC_HPP_INCLUDED)
|
||||
#define MIJIN_UTIL_MISC_HPP_INCLUDED 1
|
||||
|
||||
#include <array>
|
||||
#include <utility>
|
||||
|
||||
namespace mijin
|
||||
{
|
||||
|
||||
//
|
||||
// public functions
|
||||
//
|
||||
|
||||
template<auto V, typename T>
|
||||
constexpr decltype(auto) idValue(T&& value)
|
||||
{
|
||||
return std::forward<T>(value);
|
||||
}
|
||||
|
||||
namespace impl
|
||||
{
|
||||
template<typename T, typename... TArgs>
|
||||
struct ConstructArrayHelper
|
||||
{
|
||||
template<std::size_t... I>
|
||||
static constexpr std::array<T, sizeof...(I)> construct(const TArgs&... args, std::index_sequence<I...>)
|
||||
{
|
||||
return {idValue<I>(T(args...))...};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
template<typename T, std::size_t count, typename... TArgs>
|
||||
constexpr std::array<T, count> constructArray(const TArgs&... args)
|
||||
{
|
||||
return impl::ConstructArrayHelper<T, TArgs...>::construct(args..., std::make_index_sequence<count>());
|
||||
}
|
||||
}
|
||||
#endif // !defined(MIJIN_UTIL_MISC_HPP_INCLUDED)
|
||||
@@ -5,11 +5,14 @@
|
||||
#include "../debug/assert.hpp"
|
||||
|
||||
#if MIJIN_TARGET_OS == MIJIN_OS_LINUX
|
||||
#include <bit>
|
||||
#include <cstring>
|
||||
#include <mutex>
|
||||
#include <dlfcn.h>
|
||||
#include <pthread.h>
|
||||
#elif MIJIN_TARGET_OS == MIJIN_OS_WINDOWS
|
||||
#include <array>
|
||||
#include <malloc.h>
|
||||
#include <windows.h>
|
||||
#include "../util/winundef.hpp"
|
||||
#endif
|
||||
@@ -139,4 +142,40 @@ std::string getExecutablePath() MIJIN_NOEXCEPT
|
||||
#endif
|
||||
}
|
||||
|
||||
void* alignedAlloc(std::size_t alignment, std::size_t size) MIJIN_NOEXCEPT
|
||||
{
|
||||
#if MIJIN_TARGET_OS == MIJIN_OS_WINDOWS
|
||||
return _aligned_malloc(size, alignment);
|
||||
#else
|
||||
return std::aligned_alloc(alignment, size);
|
||||
#endif
|
||||
}
|
||||
|
||||
void* alignedRealloc(void* ptr, std::size_t alignment, std::size_t size) MIJIN_NOEXCEPT
|
||||
{
|
||||
#if MIJIN_TARGET_OS == MIJIN_OS_WINDOWS
|
||||
return _aligned_realloc(ptr, size, alignment);
|
||||
#else
|
||||
void* newPtr = std::realloc(ptr, size);
|
||||
if (newPtr == ptr || (std::bit_cast<std::uintptr_t>(newPtr) % alignment) == 0)
|
||||
{
|
||||
return newPtr;
|
||||
}
|
||||
// bad luck, have to copy a second time
|
||||
void* newPtr2 = std::aligned_alloc(alignment, size);
|
||||
std::memcpy(newPtr2, newPtr, size);
|
||||
std::free(newPtr);
|
||||
return newPtr2;
|
||||
#endif
|
||||
}
|
||||
|
||||
void alignedFree(void* ptr)
|
||||
{
|
||||
#if MIJIN_TARGET_OS == MIJIN_OS_WINDOWS
|
||||
_aligned_free(ptr);
|
||||
#else
|
||||
std::free(ptr);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace mijin
|
||||
|
||||
@@ -81,6 +81,10 @@ void setCurrentThreadName(const char* threadName) MIJIN_NOEXCEPT;
|
||||
|
||||
[[nodiscard]] std::string makeLibraryFilename(std::string_view libraryName) MIJIN_NOEXCEPT;
|
||||
|
||||
[[nodiscard]] void* alignedAlloc(std::size_t alignment, std::size_t size) MIJIN_NOEXCEPT;
|
||||
[[nodiscard]] void* alignedRealloc(void* ptr, std::size_t alignment, std::size_t size) MIJIN_NOEXCEPT;
|
||||
void alignedFree(void* ptr);
|
||||
|
||||
SharedLibrary::~SharedLibrary() MIJIN_NOEXCEPT
|
||||
{
|
||||
close();
|
||||
|
||||
@@ -7,6 +7,9 @@
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <charconv>
|
||||
#include <climits>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <locale>
|
||||
@@ -17,6 +20,7 @@
|
||||
|
||||
#include "./iterators.hpp"
|
||||
#include "../internal/common.hpp"
|
||||
#include "../util/traits.hpp"
|
||||
|
||||
namespace mijin
|
||||
{
|
||||
@@ -33,8 +37,56 @@ namespace mijin
|
||||
// public traits
|
||||
//
|
||||
|
||||
template<typename TString>
|
||||
using char_type_t = decltype(std::string_view(std::declval<TString>()))::value_type;
|
||||
template<typename T>
|
||||
inline constexpr bool is_string_v = is_template_instance_v<std::basic_string, std::remove_cvref_t<T>>;
|
||||
|
||||
template<typename T>
|
||||
concept std_string_type = is_string_v<T>;
|
||||
|
||||
template<typename T>
|
||||
inline constexpr bool is_string_view_v = is_template_instance_v<std::basic_string_view, std::remove_cvref_t<T>>;
|
||||
|
||||
template<typename T>
|
||||
concept std_string_view_type = is_string_view_v<T>;
|
||||
|
||||
template<typename T>
|
||||
inline constexpr bool is_char_v = is_any_type_v<std::remove_cvref_t<T>, char, wchar_t, char8_t, char16_t, char32_t>;
|
||||
|
||||
template<typename T>
|
||||
concept char_type = is_char_v<T>;
|
||||
|
||||
template<typename T>
|
||||
inline constexpr bool is_cstring_v = std::is_pointer_v<T> && is_char_v<std::remove_pointer_t<T>>;
|
||||
|
||||
template<typename T>
|
||||
concept cstring_type = is_cstring_v<T>;
|
||||
|
||||
template<typename T>
|
||||
struct str_char_type
|
||||
{
|
||||
using type = void;
|
||||
};
|
||||
|
||||
template<char_type T>
|
||||
struct str_char_type<T*>
|
||||
{
|
||||
using type = std::remove_cvref_t<T>;
|
||||
};
|
||||
|
||||
template<std_string_view_type T>
|
||||
struct str_char_type<T>
|
||||
{
|
||||
using type = typename std::remove_cvref_t<T>::value_type;
|
||||
};
|
||||
|
||||
template<std_string_type T>
|
||||
struct str_char_type<T>
|
||||
{
|
||||
using type = typename std::remove_cvref_t<T>::value_type;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
using str_char_type_t = str_char_type<T>::type;
|
||||
|
||||
//
|
||||
// public types
|
||||
@@ -255,7 +307,7 @@ template<typename TString>
|
||||
[[nodiscard]]
|
||||
auto trimPrefix(TString&& string)
|
||||
{
|
||||
return trimPrefix(string, detail::DEFAULT_TRIM_CHARS<char_type_t<TString>>);
|
||||
return trimPrefix(string, detail::DEFAULT_TRIM_CHARS<str_char_type_t<TString>>);
|
||||
}
|
||||
|
||||
template<typename TString, typename TChars>
|
||||
@@ -269,7 +321,7 @@ template<typename TString>
|
||||
[[nodiscard]]
|
||||
auto trimSuffix(TString&& string)
|
||||
{
|
||||
return trimSuffix(string, detail::DEFAULT_TRIM_CHARS<char_type_t<TString>>);
|
||||
return trimSuffix(string, detail::DEFAULT_TRIM_CHARS<str_char_type_t<TString>>);
|
||||
}
|
||||
|
||||
template<typename TString, typename TChars>
|
||||
@@ -283,7 +335,7 @@ template<typename TString>
|
||||
[[nodiscard]]
|
||||
auto trim(TString&& string)
|
||||
{
|
||||
return trim(string, detail::DEFAULT_TRIM_CHARS<char_type_t<TString>>);
|
||||
return trim(string, detail::DEFAULT_TRIM_CHARS<str_char_type_t<TString>>);
|
||||
}
|
||||
|
||||
template<typename TLeft, typename TRight>
|
||||
@@ -425,7 +477,7 @@ struct Join
|
||||
{
|
||||
const char* delimiter;
|
||||
|
||||
explicit Join(const char* delimiter_) MIJIN_NOEXCEPT : delimiter(delimiter_) {}
|
||||
explicit Join(const char* delimiter_) MIJIN_NOEXCEPT: delimiter(delimiter_) {}
|
||||
};
|
||||
|
||||
template<typename TIterable>
|
||||
@@ -433,6 +485,142 @@ auto operator|(TIterable&& iterable, const Join& joiner)
|
||||
{
|
||||
return join(std::forward<TIterable>(iterable), joiner.delimiter);
|
||||
}
|
||||
} // namespace pipe
|
||||
|
||||
struct [[nodiscard]] ConvertCharTypeResult
|
||||
{
|
||||
unsigned numRead = 0;
|
||||
unsigned numWritten = 0;
|
||||
|
||||
constexpr operator bool() const MIJIN_NOEXCEPT
|
||||
{
|
||||
return numRead != 0 || numWritten != 0;
|
||||
}
|
||||
constexpr bool operator !() const MIJIN_NOEXCEPT
|
||||
{
|
||||
return !static_cast<bool>(*this);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename TFrom, typename TTo>
|
||||
ConvertCharTypeResult convertCharType(const TFrom* chrFrom, std::size_t numFrom, TTo* outTo, std::size_t numTo, std::mbstate_t& mbstate) MIJIN_NOEXCEPT
|
||||
{
|
||||
if constexpr (std::is_same_v<TFrom, char>)
|
||||
{
|
||||
if constexpr (std::is_same_v<TTo, wchar_t>)
|
||||
{
|
||||
const std::size_t result = std::mbrtowc(outTo, chrFrom, numFrom, &mbstate);
|
||||
if (result == static_cast<std::size_t>(-1))
|
||||
{
|
||||
return {};
|
||||
}
|
||||
return {
|
||||
.numRead = static_cast<unsigned>(result),
|
||||
.numWritten = 1
|
||||
};
|
||||
}
|
||||
if constexpr (std::is_same_v<TTo, char8_t>)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
if constexpr (std::is_same_v<TFrom, wchar_t>)
|
||||
{
|
||||
if constexpr (std::is_same_v<TTo, char>)
|
||||
{
|
||||
if (numTo < MB_CUR_MAX)
|
||||
{
|
||||
char tmpBuf[MB_LEN_MAX];
|
||||
const ConvertCharTypeResult result = convertCharType(chrFrom, numFrom, tmpBuf, MB_LEN_MAX, mbstate);
|
||||
if (result && result.numWritten <= numTo)
|
||||
{
|
||||
std::memcpy(outTo, tmpBuf, result.numWritten);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
const std::size_t result = std::wcrtomb(outTo, *chrFrom, &mbstate);
|
||||
if (result == static_cast<std::size_t>(-1))
|
||||
{
|
||||
return {};
|
||||
}
|
||||
return {
|
||||
.numRead = 1,
|
||||
.numWritten = static_cast<unsigned>(result)
|
||||
};
|
||||
}
|
||||
if constexpr (std::is_same_v<TTo, char8_t>)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
if constexpr (std::is_same_v<TFrom, char8_t>)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
template<typename TFrom, typename TTo>
|
||||
ConvertCharTypeResult convertCharType(const TFrom* chrFrom, std::size_t numFrom, TTo* outTo, std::size_t numTo) MIJIN_NOEXCEPT
|
||||
{
|
||||
std::mbstate_t mbstate;
|
||||
return convertCharType(chrFrom, numFrom, outTo, numTo, mbstate);
|
||||
}
|
||||
|
||||
template<typename TIterator>
|
||||
struct [[nodiscard]] ConvertStringTypeResult
|
||||
{
|
||||
TIterator iterator;
|
||||
bool success;
|
||||
};
|
||||
|
||||
template<typename TTo, typename TFrom, typename TFromTraits, std::output_iterator<TTo> TIterator>
|
||||
ConvertStringTypeResult<TIterator> convertStringType(std::basic_string_view<TFrom, TFromTraits> strFrom, TIterator outIterator)
|
||||
{
|
||||
TTo outBuffer[MB_LEN_MAX];
|
||||
|
||||
std::mbstate_t mbstate = {};
|
||||
for (auto it = strFrom.begin(); it != strFrom.end();)
|
||||
{
|
||||
const std::size_t remaining = std::distance(it, strFrom.end());
|
||||
const ConvertCharTypeResult result = convertCharType(&*it, remaining, outBuffer, MB_LEN_MAX, mbstate);
|
||||
if (!result)
|
||||
{
|
||||
return {
|
||||
.iterator = outIterator,
|
||||
.success = false
|
||||
};
|
||||
}
|
||||
for (unsigned pos = 0; pos < result.numWritten; ++pos)
|
||||
{
|
||||
*outIterator = outBuffer[pos];
|
||||
++outIterator;
|
||||
}
|
||||
it = std::next(it, result.numRead);
|
||||
}
|
||||
|
||||
return {
|
||||
.iterator = outIterator,
|
||||
.success = true
|
||||
};
|
||||
}
|
||||
|
||||
template<typename TFrom, typename TFromTraits, typename TTo, typename TToTraits, typename TToAllocator>
|
||||
bool convertStringType(std::basic_string_view<TFrom, TFromTraits> strFrom, std::basic_string<TTo, TToTraits, TToAllocator>& outString)
|
||||
{
|
||||
if constexpr (std::is_same_v<TTo, TFrom>)
|
||||
{
|
||||
outString += strFrom;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return convertStringType<TTo>(strFrom, std::back_inserter(outString)).success;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename TFrom, typename TTo, typename TToTraits, typename TToAllocator>
|
||||
bool convertStringType(const TFrom* strFrom, std::basic_string<TTo, TToTraits, TToAllocator>& outString)
|
||||
{
|
||||
return convertStringType(std::basic_string_view<TFrom>(strFrom), outString);
|
||||
}
|
||||
} // namespace mijin
|
||||
|
||||
|
||||
@@ -21,6 +21,9 @@ namespace mijin
|
||||
// public types
|
||||
//
|
||||
|
||||
struct Type_; // use as typevar
|
||||
struct Any_; // use as placeholder in templates
|
||||
|
||||
template<typename T>
|
||||
struct always_false
|
||||
{
|
||||
@@ -95,11 +98,88 @@ struct map_template {
|
||||
using type_t = TTemplate<TPredicate<T>>;
|
||||
};
|
||||
|
||||
template<typename T, typename... Types>
|
||||
struct is_any_type : std::disjunction<std::is_same<T, Types>...> {};
|
||||
template<template<typename...> typename TTemplate, typename TType>
|
||||
struct is_template_instance : std::false_type {};
|
||||
|
||||
template<template<typename...> typename TTemplate, typename... TArgs>
|
||||
struct is_template_instance<TTemplate, TTemplate<TArgs...>> : std::true_type {};
|
||||
|
||||
template<template<typename...> typename TTemplate, typename TType>
|
||||
constexpr bool is_template_instance_v = is_template_instance<TTemplate, TType>::value;
|
||||
|
||||
namespace impl
|
||||
{
|
||||
template<template<typename...> typename TTemplate, typename... TArgsTTmpl>
|
||||
struct tmpl_param_comparator
|
||||
{
|
||||
template<typename TTmpl, typename TInstance>
|
||||
static constexpr bool compare_single = std::is_same_v<TTmpl, TInstance> or std::is_same_v<TTmpl, Any_>;
|
||||
|
||||
template<typename T>
|
||||
struct matches : std::false_type {};
|
||||
|
||||
// original (which MSVC didn't like for some reason :/)
|
||||
// template<typename... TArgsInstance>
|
||||
// struct matches<TTemplate<TArgsInstance...>> : std::bool_constant<(compare_single<TArgsTTmpl, TArgsInstance> && ...)> {};
|
||||
|
||||
template<template<typename...> typename TOtherTemplate, typename... TArgsInstance> requires(is_template_instance_v<TTemplate, TOtherTemplate<TArgsInstance...>>)
|
||||
struct matches<TOtherTemplate<TArgsInstance...>> : std::bool_constant<(compare_single<TArgsTTmpl, TArgsInstance> && ...)> {};
|
||||
|
||||
template<typename T>
|
||||
using matches_t = matches<T>;
|
||||
};
|
||||
}
|
||||
|
||||
template<typename TTemplate, typename TType>
|
||||
struct match_template_type : std::false_type {};
|
||||
|
||||
template<template<typename...> typename TTemplate, typename TType, typename... TArgs>
|
||||
struct match_template_type<TTemplate<TArgs...>, TType> : impl::tmpl_param_comparator<TTemplate, TArgs...>::template matches_t<TType> {};
|
||||
|
||||
template<typename TTemplate, typename TType>
|
||||
constexpr bool match_template_type_v = match_template_type<TTemplate, TType>::value;
|
||||
|
||||
// similar to std::is_same, but allows placeholders
|
||||
// - is_type_or_impl<some_tmpl<Type_>, some_type> resolves to some_tmpl<some_type>
|
||||
// - is_type_or_impl<some_tmpl<Any_, int>, some_type> checks if some_type is an instance of some_tmpl with int as 2nd parameter
|
||||
template<typename TMatch, typename TConcrete>
|
||||
struct type_matches
|
||||
{
|
||||
using type = std::is_same<TMatch, TConcrete>;
|
||||
};
|
||||
|
||||
template<template<typename> typename TTmplMatch, typename TConcrete>
|
||||
struct type_matches<TTmplMatch<Type_>, TConcrete>
|
||||
{
|
||||
using type = TTmplMatch<TConcrete>;
|
||||
};
|
||||
|
||||
template<template<typename...> typename TTmplMatch, typename TConcrete, typename... TArgs>
|
||||
struct type_matches<TTmplMatch<TArgs...>, TConcrete>
|
||||
{
|
||||
using type = match_template_type<TTmplMatch<TArgs...>, TConcrete>;
|
||||
};
|
||||
|
||||
template<typename TMatch, typename TConcrete>
|
||||
using type_matches_t = type_matches<TMatch, TConcrete>::type;
|
||||
|
||||
template<typename TMatch, typename TConcrete>
|
||||
inline constexpr bool type_matches_v = type_matches_t<TMatch, TConcrete>::value;
|
||||
|
||||
template<typename TConcrete, typename... TMatches>
|
||||
struct is_any_type : std::disjunction<std::is_same<TConcrete, TMatches>...> {};
|
||||
|
||||
template<typename TConcrete, typename... TMatches>
|
||||
static constexpr bool is_any_type_v = is_any_type<TConcrete, TMatches...>::value;
|
||||
|
||||
template<typename TConcrete, typename... TMatches>
|
||||
struct match_any_type : std::disjunction<type_matches_t<TMatches, TConcrete>...> {};
|
||||
|
||||
template<typename TConcrete, typename... TMatches>
|
||||
static constexpr bool match_any_type_v = match_any_type<TConcrete, TMatches...>::value;
|
||||
|
||||
template<typename T, typename... Types>
|
||||
static constexpr bool is_any_type_v = is_any_type<T, Types...>::value;
|
||||
concept union_type = match_any_type_v<T, Types...>;
|
||||
|
||||
template<typename TElement, typename TCollection>
|
||||
struct is_type_member;
|
||||
@@ -139,14 +219,39 @@ struct TypeAtHelper<0, TArg, TArgs...>
|
||||
template<std::size_t I, typename... TArgs>
|
||||
using type_at_t = TypeAtHelper<I, TArgs...>::type_t;
|
||||
|
||||
template<template<typename...> typename TTemplate, typename TType>
|
||||
struct is_template_instance : std::false_type {};
|
||||
template<typename TDefault, template<typename...> typename TOper, typename... TArgs>
|
||||
struct detect_or
|
||||
{
|
||||
using type = TDefault;
|
||||
static constexpr bool detected = false;
|
||||
};
|
||||
|
||||
template<template<typename...> typename TTemplate, typename... TArgs>
|
||||
struct is_template_instance<TTemplate, TTemplate<TArgs...>> : std::true_type {};
|
||||
template<typename TDefault, template<typename...> typename TOper, typename... TArgs>
|
||||
requires requires { typename TOper<TArgs...>; }
|
||||
struct detect_or<TDefault, TOper, TArgs...>
|
||||
{
|
||||
using type = TOper<TArgs...>;
|
||||
static constexpr bool detected = true;
|
||||
};
|
||||
template<typename TDefault, template<typename...> typename TOper, typename... TArgs>
|
||||
using detect_or_t = detect_or<TDefault, TOper, TArgs...>::type;
|
||||
|
||||
template<template<typename...> typename TTemplate, typename TType>
|
||||
constexpr bool is_template_instance_v = is_template_instance<TTemplate, TType>::value;
|
||||
struct empty_type {};
|
||||
|
||||
template<typename T, bool enable>
|
||||
struct optional_base
|
||||
{
|
||||
using type = T;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct optional_base<T, false>
|
||||
{
|
||||
using type = empty_type;
|
||||
};
|
||||
|
||||
template<typename T, bool enable>
|
||||
using optional_base_t = optional_base<T, enable>::type;
|
||||
|
||||
//
|
||||
// public functions
|
||||
@@ -158,6 +263,34 @@ decltype(auto) delayEvaluation(TType&& value)
|
||||
return static_cast<TType&&>(value);
|
||||
}
|
||||
|
||||
#define MIJIN_STATIC_TESTS 1
|
||||
#if MIJIN_STATIC_TESTS
|
||||
namespace test
|
||||
{
|
||||
template<typename T, typename U>
|
||||
struct MyTemplate {};
|
||||
|
||||
static_assert(match_template_type_v<MyTemplate<Any_, int>, MyTemplate<int, int>>);
|
||||
static_assert(match_template_type_v<MyTemplate<Any_, Any_>, MyTemplate<int, int>>);
|
||||
static_assert(type_matches_v<MyTemplate<Any_, int>, MyTemplate<int, int>>);
|
||||
static_assert(type_matches_v<MyTemplate<Any_, Any_>, MyTemplate<int, int>>);
|
||||
static_assert(!type_matches_v<MyTemplate<double, int>, MyTemplate<int, int>>);
|
||||
static_assert(type_matches_v<MyTemplate<Any_, Any_>, MyTemplate<int, double>>);
|
||||
static_assert(type_matches_v<std::is_pointer<Type_>, void*>);
|
||||
|
||||
static_assert(union_type<int, int>);
|
||||
static_assert(!union_type<int>);
|
||||
static_assert(!union_type<int, double>);
|
||||
static_assert(union_type<MyTemplate<int, int>, MyTemplate<int, int>>);
|
||||
static_assert(union_type<MyTemplate<int, int>, MyTemplate<Any_, int>>);
|
||||
static_assert(union_type<MyTemplate<int, int>, MyTemplate<Any_, Any_>>);
|
||||
static_assert(union_type<MyTemplate<int, int>, MyTemplate<double, double>, MyTemplate<Any_, Any_>>);
|
||||
static_assert(!union_type<int*, int>);
|
||||
static_assert(union_type<int*, std::is_pointer<Type_>>);
|
||||
static_assert(!union_type<int, std::is_pointer<Type_>>);
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace mijin
|
||||
|
||||
#endif // !defined(MIJIN_UTIL_TRAITS_HPP_INCLUDED)
|
||||
|
||||
@@ -22,3 +22,7 @@
|
||||
#if defined(RELATIVE)
|
||||
#undef RELATIVE
|
||||
#endif
|
||||
|
||||
#if defined(DEBUG)
|
||||
#undef DEBUG
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user