Added more stuff like Logging, DynamicPointer, utilities for using STL allocators and NotNullable.

This commit is contained in:
Patrick 2025-06-21 08:07:57 +02:00
parent 17bd408d3c
commit 061c58ef41
16 changed files with 970 additions and 1 deletions

View File

@ -10,6 +10,7 @@ mijin_sources = Split("""
source/mijin/debug/symbol_info.cpp
source/mijin/io/process.cpp
source/mijin/io/stream.cpp
source/mijin/logging/logger.cpp
source/mijin/net/http.cpp
source/mijin/net/ip.cpp
source/mijin/net/socket.cpp

View File

@ -2,3 +2,4 @@
#pragma once
#include "./exception.hpp"
#include "./version_support.hpp"

View File

@ -23,10 +23,12 @@
#else
#if defined(MIJIN_TEST_NO_NOEXCEPT) // only use for testing
#define MIJIN_NOEXCEPT
#define MIJIN_NOEXCEPT_IF(x)
#define MIJIN_THROWS
#define MIJIN_CONDITIONAL_NOEXCEPT(...)
#else
#define MIJIN_NOEXCEPT noexcept
#define MIJIN_NOEXCEPT_IF(x) noexcept(x)
#define MIJIN_THROWS noexcept
#define MIJIN_CONDITIONAL_NOEXCEPT(...) noexcept(__VA_ARGS__)
#endif

View File

@ -0,0 +1,13 @@
#pragma once
#if !defined(MIJIN_INTERNAL_VERSION_SUPPORT_HPP_INCLUDED)
#define MIJIN_INTERNAL_VERSION_SUPPORT_HPP_INCLUDED 1
#if defined(__cpp_deleted_function)
#define MIJIN_DELETE(reason) = delete(reason)
#else
#define MIJIN_DELETE(reason) = delete
#endif
#endif // !defined(MIJIN_INTERNAL_VERSION_SUPPORT_HPP_INCLUDED)

View File

@ -0,0 +1,176 @@
#pragma once
#if !defined(MIJIN_LOGGING_FORMATTING_HPP_INCLUDED)
#define MIJIN_LOGGING_FORMATTING_HPP_INCLUDED 1
#include <flat_map>
#include <format>
#include <variant>
#include "./logger.hpp"
#include "../memory/dynamic_pointer.hpp"
#include "../memory/memutil.hpp"
#include "../memory/virtual_allocator.hpp"
#include "../util/annot.hpp"
#include "../util/concepts.hpp"
namespace mijin
{
template<allocator_type_for<char> TAllocator = std::allocator<char>>
class LogFormatter
{
public:
using string_t = std::basic_string<char, std::char_traits<char>, TAllocator>;
virtual ~LogFormatter() noexcept = default;
virtual void format(const LogMessage& message, string_t& outFormatted) noexcept = 0;
};
template<allocator_type_for<char> TAllocator = std::allocator<char>>
class SimpleLogFormatter : public LogFormatter<TAllocator>
{
public:
using typename LogFormatter<TAllocator>::string_t;
private:
string_t mFormat;
public:
explicit SimpleLogFormatter(string_t format) MIJIN_NOEXCEPT : mFormat(std::move(format)) {}
void format(const LogMessage& message, string_t& outFormatted) noexcept override;
};
template<template<typename> typename TAllocator = std::allocator,
deleter_type<LogFormatter<TAllocator<char>>> TDeleter = AllocatorDeleter<TAllocator<LogFormatter<TAllocator<char>>>>>
requires(allocator_type<TAllocator<char>>)
class FormattingLogSink : public LogSink
{
public:
using allocator_t = TAllocator<char>;
using formatter_t = LogFormatter<allocator_t>;
using formatter_deleter_t = TDeleter;
using formatter_ptr_t = DynamicPointer<formatter_t, formatter_deleter_t>;
using string_t = formatter_t::string_t;
private:
not_null_t<formatter_ptr_t> mFormatter;
string_t mBuffer;
public:
explicit FormattingLogSink(not_null_t<formatter_ptr_t> formatter, TAllocator<char> allocator = {})
MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v<TAllocator<char>>)
: mFormatter(std::move(formatter)), mBuffer(std::move(allocator))
{}
virtual void handleMessageFormatted(const LogMessage& message, const char* formatted) MIJIN_NOEXCEPT = 0;
void handleMessage(const LogMessage& message) noexcept override
{
mBuffer.clear();
mFormatter->format(message, mBuffer);
handleMessageFormatted(message, mBuffer.c_str());
}
};
template<allocator_type_for<char> TAllocator>
void SimpleLogFormatter<TAllocator>::format(const LogMessage& message, string_t& outFormatted) noexcept
{
for (auto pos = mFormat.begin(); pos != mFormat.end(); ++pos)
{
if (*pos == '{')
{
++pos;
if (*pos == '{')
{
// double {
outFormatted += '{';
continue;
}
const auto argStart = pos;
static const std::string_view endChars = ":}";
pos = std::find_first_of(pos, mFormat.end(), endChars.begin(), endChars.end());
MIJIN_ASSERT(pos != mFormat.end(), "Invalid format.");
const std::string_view argName(argStart, pos);
std::string argFormat;
if (*pos == ':')
{
const auto formatStart = pos;
pos = std::find(pos, mFormat.end(), '}');
MIJIN_ASSERT(pos != mFormat.end(), "Invalid format.");
argFormat = std::string_view(formatStart, pos);
}
// small utility that uses the provided string buffer for storing the format string
auto formatInline = [&](const auto& value)
{
using type_t = std::decay_t<decltype(value)>;
// if there is no format, just directly print the value
if (argFormat.empty())
{
if constexpr (std::is_arithmetic_v<type_t>)
{
std::format_to(std::back_inserter(outFormatted), "{}", value);
}
else
{
outFormatted += value;
}
return;
}
// first copy the format string + braces into the buffer
const auto formatStart = outFormatted.size();
outFormatted += '{';
outFormatted += argFormat;
outFormatted += '}';
const auto formatEnd = outFormatted.size();
auto format = std::string_view(outFormatted).substr(formatStart, formatEnd - formatStart);
// then append the formatted text
std::vformat_to(std::back_inserter(outFormatted), format, std::make_format_args(value));
// and then remove the format from the buffer again
outFormatted.erase(formatStart, formatEnd - formatStart);
};
if (argName == "text")
{
formatInline(message.text);
}
else if (argName == "channel")
{
formatInline(message.channel->name);
}
else if (argName == "file")
{
formatInline(message.sourceLocation.file_name());
}
else if (argName == "function")
{
formatInline(message.sourceLocation.function_name());
}
else if (argName == "line")
{
formatInline(message.sourceLocation.line());
}
else if (argName == "column")
{
formatInline(message.sourceLocation.column());
}
else if (argName == "level")
{
formatInline(message.level->name);
}
else
{
MIJIN_ERROR("Invalid format argument name.");
}
}
else
{
outFormatted += *pos;
}
}
}
} // namespace mijin
#endif // !defined(MIJIN_LOGGING_FORMATTING_HPP_INCLUDED)

View File

@ -0,0 +1,13 @@
#include "logger.hpp"
namespace mijin
{
//
// public constants
//
MIJIN_DEFINE_LOG_CHANNEL(GENERAL)
} // namespace mijin

View File

@ -0,0 +1,173 @@
#pragma once
#if !defined(MIJIN_LOGGING_LOGGER_HPP_INCLUDED)
#define MIJIN_LOGGING_LOGGER_HPP_INCLUDED 1
#include <cstdint>
#include <format>
#include <source_location>
#include <string>
#include <vector>
#include "../internal/common.hpp"
#include "../util/annot.hpp"
namespace mijin
{
struct LogLevel
{
const char* name;
int value;
explicit operator int() const MIJIN_NOEXCEPT { return value; }
auto operator<=>(const LogLevel& other) const MIJIN_NOEXCEPT { return value <=> other.value; }
};
struct LogChannel
{
const char* name;
};
struct LogMessage
{
const char* text;
const LogChannel* channel;
const LogLevel* level;
std::source_location sourceLocation;
};
class LogSink
{
public:
virtual ~LogSink() noexcept = default;
virtual void handleMessage(const LogMessage& message) MIJIN_NOEXCEPT = 0;
};
template<template<typename T> typename TAllocator = std::allocator>
class Logger
{
private:
std::vector<LogSink*, TAllocator<LogSink*>> mSinks;
public:
explicit Logger(TAllocator<LogSink*> allocator = {}) MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v<TAllocator<LogSink*>>)
: mSinks(std::move(allocator))
{}
Logger(const Logger&) = default;
Logger(Logger&&) = default;
Logger& operator=(const Logger&) = default;
Logger& operator=(Logger&&) = default;
void addSink(LogSink& sink)
{
mSinks.push_back(&sink);
}
void postMessage(const LogMessage& message) const MIJIN_NOEXCEPT
{
for (LogSink* sink: mSinks)
{
sink->handleMessage(message);
}
}
void log(const LogLevel& level, const LogChannel& channel, std::source_location sourceLocation, const char* msg) const MIJIN_NOEXCEPT
{
postMessage({
.text = msg,
.channel = &channel,
.level = &level,
.sourceLocation = std::move(sourceLocation)
});
}
template<typename... TArgs>
void log(const LogLevel& level, const LogChannel& channel, std::source_location sourceLocation,
std::format_string<TArgs...> fmt, TArgs&& ... args) const
MIJIN_NOEXCEPT_IF(noexcept(std::declval<TAllocator<char>>().allocate(1)))
{
std::basic_string<char, std::char_traits<char>, TAllocator<char>> buffer(TAllocator<char>(mSinks.get_allocator()));
std::format_to(std::back_inserter(buffer), fmt, std::forward<TArgs>(args)...);
log(level, channel, std::move(sourceLocation), buffer.c_str());
}
};
#define MIJIN_DECLARE_LOG_CHANNEL(cnlName) \
namespace mijin_log_channel \
{ \
extern const ::mijin::LogChannel cnlName; \
}
#define MIJIN_DEFINE_LOG_CHANNEL(cnlName) \
namespace mijin_log_channel \
{ \
const ::mijin::LogChannel cnlName { \
.name = #cnlName \
}; \
}
#define MIJIN_DEFINE_LOG_LEVEL(lvlName, lvlValue) \
namespace mijin_log_level \
{ \
inline constexpr ::mijin::LogLevel lvlName{ \
.name = #lvlName, \
.value = lvlValue \
}; \
}
MIJIN_DECLARE_LOG_CHANNEL(GENERAL)
MIJIN_DEFINE_LOG_LEVEL(DEBUG, -1000)
MIJIN_DEFINE_LOG_LEVEL(VERBOSE, -500)
MIJIN_DEFINE_LOG_LEVEL(INFO, 0)
MIJIN_DEFINE_LOG_LEVEL(WARNING, 500)
MIJIN_DEFINE_LOG_LEVEL(ERROR, 1000)
#if defined(MIJIN_MIN_LOGLEVEL)
inline constexpr int MIN_LOG_LEVEL = static_cast<int>(MIJIN_MIN_LOGLEVEL);
#elif defined(MIJIN_DEBUG)
inline constexpr int MIN_LOG_LEVEL = mijin_log_level::DEBUG.value;
#else
inline constexpr int MIN_LOG_LEVEL = mijin_log_level::VERBOSE.value;
#endif
#define MIJIN_IMPORT_LOG_DEFAULTS \
namespace mijin_log_channel \
{ \
using ::mijin::mijin_log_channel::GENERAL; \
} \
\
namespace mijin_log_level \
{ \
using ::mijin::mijin_log_level::DEBUG; \
using ::mijin::mijin_log_level::VERBOSE; \
using ::mijin::mijin_log_level::INFO; \
using ::mijin::mijin_log_level::WARNING; \
using ::mijin::mijin_log_level::ERROR; \
}
#define MIJIN_LOG_ALWAYS(level, channel, ...) mijin__getLogger__().log( \
mijin_log_level::level, mijin_log_channel::channel, std::source_location::current(), __VA_ARGS__ \
)
#define MIJIN_LOG(level, channel, ...) \
if constexpr (mijin_log_level::level.value < mijin::MIN_LOG_LEVEL) {} \
else MIJIN_LOG_ALWAYS(level, channel, __VA_ARGS__)
#define MIJIN_SET_CLASS_LOGGER(loggerExpr) \
const auto& mijin__getLogger__() const noexcept \
{ \
return loggerExpr; \
}
#define MIJIN_SET_SCOPE_LOGGER(loggerExpr) \
auto mijin__getLogger__ = [&]() -> const auto& \
{ \
return loggerExpr; \
};
} // namespace mijin
#endif // !defined(MIJIN_LOGGING_LOGGER_HPP_INCLUDED)

View File

@ -0,0 +1,40 @@
#pragma once
#if !defined(MIJIN_LOGGING_STDIO_SINK_HPP_INCLUDED)
#define MIJIN_LOGGING_STDIO_SINK_HPP_INCLUDED 1
#include "./formatting.hpp"
namespace mijin
{
template<template<typename> typename TAllocator = std::allocator,
deleter_type<LogFormatter<TAllocator<char>>> TDeleter = AllocatorDeleter<TAllocator<LogFormatter<TAllocator<char>>>>>
requires(allocator_type<TAllocator<char>>)
class StdioSink : public FormattingLogSink<TAllocator, TDeleter>
{
public:
using base_t = FormattingLogSink<TAllocator, TDeleter>;
using typename base_t::formatter_ptr_t;
explicit StdioSink(not_null_t<formatter_ptr_t> formatter, TAllocator<char> allocator = {})
MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v<TAllocator<char>>)
: base_t(std::move(formatter), std::move(allocator)) {}
void handleMessageFormatted(const LogMessage& message, const char* formatted) MIJIN_NOEXCEPT override
{
if (*message.level >= mijin_log_level::WARNING)
{
std::fputs(formatted, stderr);
std::fputc('\n', stderr);
}
else
{
std::puts(formatted);
}
}
};
} // namespace mijin
#endif // !defined(MIJIN_LOGGING_STDIO_SINK_HPP_INCLUDED)

View File

@ -0,0 +1,176 @@
#pragma once
#if !defined(MIJIN_MEMORY_DYNAMIC_POINTER_HPP_INCLUDED)
#define MIJIN_MEMORY_DYNAMIC_POINTER_HPP_INCLUDED 1
#include <bit>
#include <cstdint>
#include <utility>
#include "../internal/common.hpp"
#include "../memory/memutil.hpp"
#include "../util/concepts.hpp"
#include "../util/flag.hpp"
namespace mijin
{
MIJIN_DEFINE_FLAG(Owning);
template<typename T, deleter_type<T> TDeleter = std::default_delete<T>>
class DynamicPointer
{
public:
using pointer = T*;
using element_type = T;
using deleter_t = TDeleter;
private:
std::uintptr_t mData = 0;
[[no_unique_address]] TDeleter mDeleter;
public:
constexpr DynamicPointer(std::nullptr_t = nullptr) MIJIN_NOEXCEPT {}
DynamicPointer(const DynamicPointer&) = delete;
constexpr DynamicPointer(pointer ptr, Owning owning, TDeleter deleter = {}) MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v<TDeleter>)
: mData(std::bit_cast<std::uintptr_t>(ptr) | (owning ? 1 : 0)), mDeleter(std::move(deleter))
{
MIJIN_ASSERT((std::bit_cast<std::uintptr_t>(ptr) & 1) == 0, "Invalid address, DynamicPointer requires addresses to be divisible by two.");
}
template<typename TOther, typename TOtherDeleter> requires (std::is_constructible_v<TDeleter, TOtherDeleter&&>)
constexpr DynamicPointer(DynamicPointer<TOther, TOtherDeleter>&& other) MIJIN_NOEXCEPT_IF((std::is_nothrow_convertible_v<TOtherDeleter, TDeleter>))
: mData(std::exchange(other.mData, 0)), mDeleter(std::move(other.mDeleter)) {
MIJIN_ASSERT(other.mData == 0, "");
}
constexpr ~DynamicPointer() noexcept
{
reset();
}
DynamicPointer& operator=(const DynamicPointer&) = delete;
DynamicPointer& operator=(std::nullptr_t) MIJIN_NOEXCEPT
{
reset();
return *this;
}
template<typename TOther, typename TOtherDeleter> requires(std::is_assignable_v<TDeleter, TOtherDeleter>)
DynamicPointer& operator=(DynamicPointer<TOther, TOtherDeleter>&& other) MIJIN_NOEXCEPT_IF((std::is_nothrow_assignable_v<TDeleter, TOtherDeleter>))
{
if (this != &other)
{
reset();
mData = std::exchange(other.mData, 0);
mDeleter = std::move(other.mDeleter);
}
return *this;
}
template<typename TOther, typename TOtherDeleter> requires(std::equality_comparable_with<T, TOther>)
auto operator<=>(const DynamicPointer<TOther, TOtherDeleter>& other) MIJIN_NOEXCEPT
{
return mData <=> other.mData;
}
constexpr bool operator==(std::nullptr_t) const MIJIN_NOEXCEPT
{
return empty();
}
constexpr bool operator!=(std::nullptr_t) const MIJIN_NOEXCEPT
{
return !empty();
}
constexpr operator bool() const MIJIN_NOEXCEPT
{
return !empty();
}
constexpr bool operator!() const MIJIN_NOEXCEPT
{
return empty();
}
constexpr pointer operator->() const MIJIN_NOEXCEPT
{
return get();
}
constexpr element_type& operator*() const MIJIN_NOEXCEPT
{
return *get();
}
[[nodiscard]]
constexpr bool isOwning() const MIJIN_NOEXCEPT
{
return (mData & 1) == 1;
}
[[nodiscard]]
constexpr bool empty() const MIJIN_NOEXCEPT
{
return mData == 0;
}
[[nodiscard]]
constexpr pointer get() const MIJIN_NOEXCEPT
{
return std::bit_cast<pointer>(mData & ~1);
}
constexpr void reset(pointer ptr, Owning owning) MIJIN_NOEXCEPT
{
if (isOwning())
{
mDeleter(get());
}
mData = std::bit_cast<std::uintptr_t>(ptr) | (owning ? 1 : 0);
}
constexpr void reset() MIJIN_NOEXCEPT
{
reset(nullptr, Owning::NO);
}
[[nodiscard]]
pointer release() MIJIN_NOEXCEPT
{
return std::bit_cast<pointer>(std::exchange(mData, 0) & ~1);
}
template<typename TOther, deleter_type<TOther> TOtherDeleter>
friend class DynamicPointer;
};
template<typename T, typename TDeleter>
bool operator==(std::nullptr_t, const DynamicPointer<T, TDeleter>& pointer) MIJIN_NOEXCEPT
{
return pointer == nullptr;
}
template<typename T, typename TDeleter>
bool operator!=(std::nullptr_t, const DynamicPointer<T, TDeleter>& pointer) MIJIN_NOEXCEPT
{
return pointer != nullptr;
}
template<typename T, typename... TArgs>
DynamicPointer<T, std::default_delete<T>> makeDynamic(TArgs&&... args) MIJIN_NOEXCEPT_IF((std::is_nothrow_constructible_v<T, TArgs...>))
{
return DynamicPointer<T, std::default_delete<T>>(new T(std::forward<TArgs>(args)...), Owning::YES);
}
template<typename T, allocator_type_for<T> TAllocator, typename... TArgs>
DynamicPointer<T, AllocatorDeleter<TAllocator>> makeDynamicWithAllocator(TAllocator allocator, TArgs&&... args)
MIJIN_NOEXCEPT_IF((std::is_nothrow_constructible_v<T, TArgs...> && std::is_nothrow_move_constructible_v<TAllocator>))
{
T* obj = allocator.allocate(1);
if (obj != nullptr)
{
::new(obj) T(std::forward<TArgs>(args)...);
}
return DynamicPointer<T, AllocatorDeleter<TAllocator>>(obj, Owning::YES, AllocatorDeleter<TAllocator>(std::move(allocator)));
}
} // namespace mijin
#endif // !defined(MIJIN_MEMORY_DYNAMIC_POINTER_HPP_INCLUDED)

View File

@ -0,0 +1,85 @@
#pragma once
#if !defined(MIJIN_MEMORY_MEMUTIL_HPP_INCLUDED)
#define MIJIN_MEMORY_MEMUTIL_HPP_INCLUDED 1
#include <memory>
#include "../internal/common.hpp"
namespace mijin
{
template<typename TAllocator>
class AllocatorDeleter
{
public:
using value_type = std::allocator_traits<TAllocator>::value_type;
using pointer = std::allocator_traits<TAllocator>::pointer;
private:
[[no_unique_address]] TAllocator allocator_;
public:
explicit AllocatorDeleter(TAllocator allocator = {}) MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v<TAllocator>)
: allocator_(std::move(allocator)) {}
template<typename TOtherAllocator> requires (std::is_constructible_v<TAllocator, const TOtherAllocator&>)
AllocatorDeleter(const AllocatorDeleter<TOtherAllocator>& other)
MIJIN_NOEXCEPT_IF((std::is_nothrow_constructible_v<TAllocator, TOtherAllocator>))
: allocator_(other.allocator_) {}
template<typename TOtherAllocator> requires (std::is_constructible_v<TAllocator, TOtherAllocator&&>)
AllocatorDeleter(AllocatorDeleter<TOtherAllocator>&& other)
MIJIN_NOEXCEPT_IF((std::is_nothrow_constructible_v<TAllocator, TOtherAllocator&&>))
: allocator_(std::move(other.allocator_)) {}
template<typename TOtherAllocator> requires (std::is_assignable_v<TAllocator&, const TOtherAllocator&>)
AllocatorDeleter& operator=(const AllocatorDeleter<TOtherAllocator>& other)
MIJIN_NOEXCEPT_IF((std::is_nothrow_assignable_v<TAllocator&, const TOtherAllocator&>))
{
if (this != static_cast<const void*>(&other))
{
allocator_ = other.allocator_;
}
return *this;
}
template<typename TOtherAllocator> requires (std::is_assignable_v<TAllocator&, TOtherAllocator&&>)
AllocatorDeleter& operator=(AllocatorDeleter<TOtherAllocator>&& other)
MIJIN_NOEXCEPT_IF((std::is_nothrow_assignable_v<TAllocator&, TOtherAllocator&&>))
{
if (this != static_cast<const void*>(&other))
{
allocator_ = std::move(other.allocator_);
}
return *this;
}
void operator()(pointer ptr) MIJIN_NOEXCEPT_IF(noexcept(allocator_.deallocate(ptr, sizeof(value_type))))
{
allocator_.deallocate(ptr, sizeof(value_type));
}
template<typename TOtherAllocator>
friend class AllocatorDeleter;
};
template<typename T>
class AllocatorDeleter<std::allocator<T>>
{
public:
AllocatorDeleter() noexcept = default;
template<typename TOther>
AllocatorDeleter(const AllocatorDeleter<std::allocator<TOther>>&) noexcept {}
template<typename TOther>
AllocatorDeleter& operator=(const AllocatorDeleter<std::allocator<TOther>>&) noexcept { return *this; }
void operator()(T* ptr) const MIJIN_NOEXCEPT
{
delete ptr;
}
};
} // namespace mijin
#endif // !defined(MIJIN_MEMORY_MEMUTIL_HPP_INCLUDED)

View File

@ -0,0 +1,56 @@
#pragma once
#if !defined(MIJIN_MEMORY_VIRTUAL_ALLOCATOR_HPP_INCLUDED)
#define MIJIN_MEMORY_VIRTUAL_ALLOCATOR_HPP_INCLUDED 1
#include "../internal/common.hpp"
#include "../util/annot.hpp"
namespace mijin
{
template<typename T>
class VirtualAllocator
{
public:
virtual ~VirtualAllocator() noexcept = default;
[[nodiscard]]
virtual owner_t<T*> allocate(std::size_t count) noexcept;
virtual void deallocate(owner_t<T*> ptr, std::size_t count) noexcept;
};
template<typename T, typename TImpl>
class WrappedVirtualAllocator : public VirtualAllocator<T>
{
private:
[[no_unique_address]] TImpl mImpl;
public:
explicit constexpr WrappedVirtualAllocator(TImpl impl = {}) MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v<TImpl>)
: mImpl(std::move(impl)) {}
constexpr WrappedVirtualAllocator(const WrappedVirtualAllocator&) = default;
constexpr WrappedVirtualAllocator(WrappedVirtualAllocator&&) = default;
WrappedVirtualAllocator& operator=(const WrappedVirtualAllocator&) = default;
WrappedVirtualAllocator& operator=(WrappedVirtualAllocator&&) = default;
[[nodiscard]]
owner_t<T*> allocate(std::size_t count) noexcept override
{
return mImpl.allocate(count);
}
void deallocate(owner_t<T*> ptr, std::size_t count) noexcept override
{
mImpl.deallocate(ptr, count);
}
};
template<typename T>
WrappedVirtualAllocator<typename T::value_type, T> makeVirtualAllocator(T allocator) MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v<T>)
{
return WrappedVirtualAllocator<typename T::value_type, T>(std::move(allocator));
}
} // namespace mijin
#endif // !defined(MIJIN_MEMORY_VIRTUAL_ALLOCATOR_HPP_INCLUDED)

146
source/mijin/util/annot.hpp Normal file
View File

@ -0,0 +1,146 @@
#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> requires(!std::is_same_v<T, std::nullptr_t>) && requires(T t) { t == nullptr; }
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.");
}
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_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.");
constexpr NotNullable& operator=(const NotNullable& other) MIJIN_NOEXCEPT_IF(std::is_nothrow_copy_constructible_v<T>)
requires(std::is_copy_constructible_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<std::equality_comparable_with<T> TOther> requires(!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<std::equality_comparable_with<T> TOther> requires(!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_; }
};
#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)

View File

@ -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
//

View File

@ -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_alloc(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

View File

@ -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();

View File

@ -148,6 +148,23 @@ 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;
template<typename TDefault, template<typename...> typename TOper, typename... TArgs>
struct detect_or
{
using type = TDefault;
static constexpr bool detected = false;
};
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;
//
// public functions
//