8 Commits

16 changed files with 82 additions and 1100 deletions

View File

@@ -68,12 +68,12 @@ public:
BoxedObject(const BoxedObject&) = delete;
BoxedObject(BoxedObject&&) = delete;
#if MIJIN_BOXED_OBJECT_DEBUG
constexpr ~BoxedObject() noexcept
{
#if MIJIN_BOXED_OBJECT_DEBUG
MIJIN_ASSERT(!constructed, "BoxedObject::~BoxedObject(): Object has not been destroyed prior to destructor!");
#endif
}
#endif
BoxedObject& operator=(const BoxedObject&) = delete;
BoxedObject& operator=(BoxedObject&&) = delete;

View File

@@ -113,42 +113,6 @@ StreamError Stream::writeBinaryString(std::string_view str)
return writeSpan(str.begin(), str.end());
}
StreamError Stream::readZString(std::string& outString)
{
char chr = '\0';
std::string result;
while (true)
{
if (isAtEnd())
{
return StreamError::IO_ERROR;
}
if (StreamError error = read(chr); error != StreamError::SUCCESS)
{
return error;
}
if (chr == '\0')
{
outString = std::move(result);
return StreamError::SUCCESS;
}
result.push_back(chr);
}
}
StreamError Stream::writeZString(std::string_view str)
{
static const char ZERO = '\0';
if (StreamError error = writeRaw(str.data(), str.size() * sizeof(char)); error != StreamError::SUCCESS)
{
return error;
}
return write(ZERO);
}
mijin::Task<StreamError> Stream::c_readBinaryString(std::string& outString)
{
std::uint32_t length; // NOLINT(cppcoreguidelines-init-variables)

View File

@@ -221,7 +221,7 @@ public:
}
template<typename T>
StreamError write(const T& value)
StreamError write(const T& value) requires(std::is_trivial_v<T>)
{
return writeRaw(&value, sizeof(T));
}
@@ -261,9 +261,6 @@ public:
StreamError readBinaryString(std::string& outString);
StreamError writeBinaryString(std::string_view str);
StreamError readZString(std::string& outString);
StreamError writeZString(std::string_view str);
mijin::Task<StreamError> c_readBinaryString(std::string& outString);
mijin::Task<StreamError> c_writeBinaryString(std::string_view str);

View File

@@ -1,52 +0,0 @@
#pragma once
#if !defined(MIJIN_LOGGING_FILTERS_HPP_INCLUDED)
#define MIJIN_LOGGING_FILTERS_HPP_INCLUDED 1
#include "./logger.hpp"
namespace mijin
{
template<typename TChar = MIJIN_DEFAULT_CHAR_TYPE>
class BaseLevelFilter : public BaseLogFilter<TChar>
{
public:
using base_t = BaseLogFilter<TChar>;
using typename base_t::char_t;
using typename base_t::message_t;
private:
int mMinLevel = 0;
int mMaxLevel = 0;
public:
explicit BaseLevelFilter(int minLevel, int maxLevel = std::numeric_limits<int>::max()) MIJIN_NOEXCEPT
: mMinLevel(minLevel), mMaxLevel(maxLevel) {}
explicit BaseLevelFilter(const BaseLogLevel<char_t>& minLevel, const BaseLogLevel<char_t>& maxLevel = {nullptr, std::numeric_limits<int>::max()}) MIJIN_NOEXCEPT
: mMinLevel(minLevel.value), mMaxLevel(maxLevel.value) {}
[[nodiscard]]
int getMinLevel() const MIJIN_NOEXCEPT { return mMinLevel; }
[[nodiscard]]
int getMaxLevel() const MIJIN_NOEXCEPT { return mMaxLevel; }
void setMinLevel(int level) MIJIN_NOEXCEPT { mMinLevel = level; }
void setMinLevel(const BaseLogLevel<char_t>& level) MIJIN_NOEXCEPT { mMinLevel = level.value; }
void setMaxLevel(int level) MIJIN_NOEXCEPT { mMaxLevel = level; }
void setMaxLevel(const BaseLogLevel<char_t>& level) MIJIN_NOEXCEPT { mMaxLevel = level.value; }
bool shouldShow(const message_t& message) MIJIN_NOEXCEPT override
{
return message.level->value >= mMinLevel && message.level->value <= mMaxLevel;
}
};
MIJIN_DEFINE_CHAR_VERSIONS(LevelFilter)
} // namespace mijin
#endif // !defined(MIJIN_LOGGING_STDIO_SINK_HPP_INCLUDED)

View File

@@ -6,7 +6,6 @@
#include <cstdint>
#include <format>
#include <mutex>
#include <source_location>
#include <string>
#include <vector>
@@ -106,20 +105,6 @@ public:
MIJIN_DEFINE_CHAR_VERSIONS(LogSink)
template<typename TChar = MIJIN_DEFAULT_CHAR_TYPE>
class BaseLogFilter
{
public:
using char_t = TChar;
using message_t = BaseLogMessage<char_t>;
virtual ~BaseLogFilter() noexcept = default;
virtual bool shouldShow(const message_t& message) MIJIN_NOEXCEPT = 0;
};
MIJIN_DEFINE_CHAR_VERSIONS(LogFilter)
#define LOGGER_COMMON_ARGS(chr_type) template<typename T> typename TAllocator = MIJIN_DEFAULT_ALLOCATOR
template<typename TChar = MIJIN_DEFAULT_CHAR_TYPE, typename TTraits = std::char_traits<TChar>, LOGGER_COMMON_ARGS(TChar)>
class BaseLogger
@@ -130,22 +115,15 @@ public:
using allocator_t = TAllocator<char_t>;
using sink_t = BaseLogSink<char_t>;
using filter_t = BaseLogFilter<char_t>;
using level_t = BaseLogLevel<char_t>;
using channel_t = BaseLogChannel<char_t>;
using message_t = BaseLogMessage<char_t>;
using string_t = std::basic_string<char_t, traits_t, allocator_t>;
private:
struct SinkEntry
{
sink_t* sink;
filter_t* filter;
};
std::vector<SinkEntry, TAllocator<SinkEntry>> mSinks;
mutable std::mutex mMutex;
std::vector<sink_t*, TAllocator<sink_t*>> mSinks;
public:
explicit BaseLogger(TAllocator<void> allocator = {}) MIJIN_NOEXCEPT_IF((std::is_nothrow_constructible_v<TAllocator<SinkEntry>, TAllocator<void>&&>))
: mSinks(TAllocator<SinkEntry>(std::move(allocator)))
explicit BaseLogger(TAllocator<sink_t*> allocator = {}) MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v<TAllocator<sink_t*>>)
: mSinks(std::move(allocator))
{}
BaseLogger(const BaseLogger&) = default;
@@ -155,25 +133,14 @@ public:
void addSink(sink_t& sink)
{
std::unique_lock _(mMutex);
mSinks.push_back({&sink, nullptr});
}
void addSink(sink_t& sink, filter_t& filter)
{
std::unique_lock _(mMutex);
mSinks.push_back({&sink, &filter});
mSinks.push_back(&sink);
}
void postMessage(const message_t& message) const MIJIN_NOEXCEPT
{
std::unique_lock _(mMutex);
for (const SinkEntry& entry : mSinks)
for (sink_t* sink: mSinks)
{
if (entry.filter != nullptr && !entry.filter->shouldShow(message)) {
continue;
}
entry.sink->handleMessage(message);
sink->handleMessage(message);
}
}

View File

@@ -1,58 +0,0 @@
#pragma once
#if !defined(MIJIN_LOGGING_STREAM_SINK_HPP_INCLUDED)
#define MIJIN_LOGGING_STREAM_SINK_HPP_INCLUDED 1
#include "./formatting.hpp"
#include "../io/stream.hpp"
#include "../util/traits.hpp"
namespace mijin
{
template<MIJIN_FORMATTING_SINK_TMPL_ARGS_INIT>
requires(allocator_type<TAllocator<TChar>>)
class BaseStreamSink : public BaseFormattingLogSink<MIJIN_FORMATTING_SINK_TMP_ARG_NAMES>
{
public:
using base_t = BaseFormattingLogSink<MIJIN_FORMATTING_SINK_TMP_ARG_NAMES>;
using typename base_t::char_t;
using typename base_t::allocator_t;
using typename base_t::formatter_ptr_t;
using typename base_t::message_t;
using stream_ptr_t = DynamicPointer<Stream>;
private:
stream_ptr_t mStream;
int mMinStderrLevel = MIJIN_LOG_LEVEL_VALUE_WARNING;
public:
explicit BaseStreamSink(not_null_t<formatter_ptr_t> formatter, allocator_t allocator = {})
MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v<allocator_t>)
: base_t(std::move(formatter), std::move(allocator)) {}
explicit BaseStreamSink(not_null_t<stream_ptr_t> stream, not_null_t<formatter_ptr_t> formatter, allocator_t allocator = {})
MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v<allocator_t>)
: base_t(std::move(formatter), std::move(allocator)), mStream(std::move(stream)) {}
void setStream(not_null_t<stream_ptr_t> stream) {
mStream = std::move(stream).release();
}
void handleMessageFormatted(const message_t& /* message */, const char_t* formatted) MIJIN_NOEXCEPT override
{
if (!mStream) {
return;
}
(void) mStream->writeSpan(std::basic_string_view(formatted));
(void) mStream->write('\n');
mStream->flush();
}
};
#define SINK_SET_ARGS(chr_type) chr_type, std::char_traits<chr_type>, TAllocator, TDeleter
MIJIN_DEFINE_CHAR_VERSIONS_TMPL(StreamSink, MIJIN_FORMATTING_SINK_COMMON_ARGS, SINK_SET_ARGS)
#undef SINK_SET_ARGS
} // namespace mijin
#endif // !defined(MIJIN_LOGGING_STREAM_SINK_HPP_INCLUDED)

View File

@@ -17,7 +17,7 @@ namespace mijin
{
MIJIN_DEFINE_FLAG(Owning);
template<typename T, deleter_type<T> TDeleter = AllocatorDeleter<MIJIN_DEFAULT_ALLOCATOR<T>>>
template<typename T, deleter_type<T> TDeleter = std::default_delete<T>>
class DynamicPointer
{
public:

View File

@@ -7,317 +7,56 @@
#include <array>
#include <cstddef>
#include <cstdint>
#include "../internal/common.hpp"
#include <limits>
#include <memory>
#include <vector>
#include "../debug/assert.hpp"
#include "../util/align.hpp"
namespace mijin
{
namespace impl
{
struct ObjectPoolGap
{
std::uint16_t nextGapOffset;
std::uint16_t remainingObjects;
ObjectPoolGap* nextGap() noexcept {
return nextGapOffset == 0 ? nullptr : reinterpret_cast<ObjectPoolGap*>(reinterpret_cast<std::byte*>(this) + nextGapOffset);
}
};
template<typename TObject, std::size_t OBJECTS_PER_PAGE>
struct ObjectPoolPage
{
ObjectPoolGap* firstGap;
ObjectPoolPage* nextPage = nullptr;
std::array<std::byte, OBJECTS_PER_PAGE * MIJIN_STRIDEOF(TObject)> data;
ObjectPoolPage() noexcept
{
firstGap = reinterpret_cast<ObjectPoolGap*>(data.data());
firstGap->nextGapOffset = 0;
firstGap->remainingObjects = OBJECTS_PER_PAGE;
}
};
}
template<typename TObject, std::size_t OBJECTS_PER_PAGE>
class ObjectPoolIterator
{
public:
using value_type = TObject;
using difference_type = std::ptrdiff_t;
private:
using gap_t = impl::ObjectPoolGap;
using page_t = impl::ObjectPoolPage<TObject, OBJECTS_PER_PAGE>;
page_t* currentPage_ = nullptr;
gap_t* currentGap_ = nullptr;
std::uint16_t currentObject_ = 0;
explicit ObjectPoolIterator(page_t& firstPage) noexcept
{
seekNextOnPage(firstPage);
}
public:
ObjectPoolIterator() noexcept = default;
ObjectPoolIterator(const ObjectPoolIterator&) noexcept = default;
ObjectPoolIterator& operator=(const ObjectPoolIterator&) noexcept = default;
auto operator<=>(const ObjectPoolIterator&) const noexcept = default;
TObject& operator*() const noexcept { return getObject(); }
TObject* operator->() const noexcept { return &getObject(); }
ObjectPoolIterator& operator++() noexcept
{
seekNext();
return *this;
}
ObjectPoolIterator operator++(int) const noexcept
{
ObjectPoolIterator copy(*this);
++(*this);
return copy;
}
private:
TObject& getObject() const noexcept
{
MIJIN_ASSERT(currentGap_ != nullptr, "Attempting to dereference an invalid iterator.");
return *(reinterpret_cast<TObject*>(currentGap_) + currentGap_->remainingObjects + currentObject_);
}
bool seekNextOnPage(page_t& firstPage) noexcept
{
for (page_t* page = &firstPage; page != nullptr; page = page->nextPage)
{
if (page->firstGap != nullptr && seekNextInGap(*page->firstGap))
{
currentPage_ = page;
return true;
}
}
return false;
}
bool seekNextInGap(gap_t& firstGap) noexcept
{
for (gap_t* gap = &firstGap; gap != nullptr; gap = gap->nextGap())
{
if (gap->remainingObjects < OBJECTS_PER_PAGE)
{
currentGap_ = gap;
currentObject_ = 0;
return true;
}
}
return false;
}
void seekNext() noexcept
{
if (++currentObject_ < OBJECTS_PER_PAGE - currentGap_->remainingObjects)
{
return;
}
if (currentGap_->nextGapOffset != 0 && seekNextInGap(*currentGap_->nextGap())) {
return;
}
if (currentPage_->nextPage != nullptr && seekNextOnPage(*currentPage_->nextPage)) {
return;
}
currentPage_ = nullptr;
currentGap_ = nullptr;
currentObject_ = 0;
}
template<typename TObject2, std::size_t OBJECTS_PER_PAGE2, template<typename> typename TAllocator>
friend class ObjectPool;
};
template<typename TObject, std::size_t OBJECTS_PER_PAGE = 1024, template<typename> typename TAllocator = MIJIN_DEFAULT_ALLOCATOR>
template<typename TObject, std::size_t OBJECTS_PER_PAGE = 1024>
class ObjectPool
{
public:
using iterator = ObjectPoolIterator<TObject, OBJECTS_PER_PAGE>;
using const_iterator = iterator;
private:
using gap_t = impl::ObjectPoolGap;
using page_t = impl::ObjectPoolPage<TObject, OBJECTS_PER_PAGE>;
struct GapInfo
{
std::uint32_t objectCount;
std::uint32_t nextGap;
};
static constexpr std::size_t ALIGN = std::max(alignof(TObject), alignof(GapInfo));
static_assert(sizeof(gap_t) <= sizeof(TObject));
struct alignas(ALIGN) ObjectData
{
std::array<std::byte, sizeof(TObject)> bytes;
};
struct Page
{
std::uint32_t freeOffset = 0;
std::array<ObjectData, OBJECTS_PER_PAGE> data;
};
[[no_unique_address]] TAllocator<page_t> allocator_;
page_t* firstPage = nullptr;
std::vector<std::unique_ptr<Page>> pages;
public:
explicit ObjectPool(TAllocator<void> allocator = {}) MIJIN_NOEXCEPT_IF((std::is_nothrow_constructible_v<TAllocator<page_t>, TAllocator<void>&&>))
: allocator_(std::move(allocator))
{
}
ObjectPool(const ObjectPool&) = delete;
ObjectPool(ObjectPool&&) = default;
ObjectPool& operator=(const ObjectPool&) = delete;
ObjectPool& operator=(ObjectPool&&) = default;
[[nodiscard]]
TObject* allocate(std::size_t count = 1) noexcept
{
MIJIN_ASSERT(count <= OBJECTS_PER_PAGE, "Cannot allocate that many objects at once.");
// first try to find a free spot in the existing pages
for (page_t* page = firstPage; page != nullptr; page = page->nextPage)
{
if (TObject* result = allocateFromPage(*page, count); result != nullptr) {
return result;
}
}
// nothing found in the existing pages, allocate a new one and return memory from there
page_t* newPage = ::new (allocator_.allocate(1)) page_t;
if (firstPage == nullptr)
{
firstPage = newPage;
}
else
{
page_t* lastPage = firstPage;
for (; lastPage->nextPage != nullptr; lastPage = lastPage->nextPage);
lastPage->nextPage = newPage;
}
return allocateFromPage(*newPage, count);
}
void deallocate(TObject* object, std::size_t count = 1) noexcept
{
std::byte* const objectPtr = reinterpret_cast<std::byte*>(object); // for easier comparison
for (page_t* page = firstPage; page != nullptr; page = page->nextPage)
{
// first find the page it's in
std::byte* pageStart = page->data.data();
std::byte* pageEnd = pageStart + (OBJECTS_PER_PAGE * sizeof(TObject));
if (objectPtr >= pageStart && objectPtr <= pageEnd)
{
// then the corresponding gap
if (page->firstGap == nullptr)
{
// everything is used, create a new gap
gap_t* newGap = reinterpret_cast<gap_t*>(objectPtr);
newGap->remainingObjects = count;
newGap->nextGapOffset = 0;
page->firstGap = newGap;
return;
}
for (gap_t* gap = page->firstGap; gap != nullptr; gap = gap->nextGap())
{
std::byte* gapStart = reinterpret_cast<std::byte*>(gap);
std::byte* gapEnd = gap->nextGapOffset == 0 ? pageEnd : gapStart + gap->nextGapOffset;
if (objectPtr > gapStart && objectPtr < gapEnd)
{
if (objectPtr + (count * sizeof(TObject)) == gapEnd)
{
// deallocating right from the end -> just increase the gap remainingObjects
gap->remainingObjects += count;
mergeGaps(gap);
}
else
{
// deallocating from the middle -> create a new gap
gap_t* newGap = reinterpret_cast<gap_t*>(objectPtr);
newGap->remainingObjects = count;
newGap->nextGapOffset = gap->nextGapOffset == 0 ? 0 : (gapEnd - reinterpret_cast<std::byte*>(newGap));
gap->nextGapOffset -= objectPtr - gapStart;
mergeGaps(newGap);
}
return;
}
}
}
}
}
template<typename... TArgs>
[[nodiscard]]
TObject* create(TArgs&&... args) noexcept
{
TObject* result = allocate();
if (result == nullptr) {
return nullptr;
}
return ::new (result) TObject(std::forward<TArgs>(args)...);
}
[[nodiscard]]
TObject* createMultiple(std::size_t count = 1) noexcept
{
TObject* result = allocate(count);
if (result == nullptr) {
return nullptr;
}
return ::new (result) TObject[count];
}
void destroy(TObject* ptr, std::size_t count = 1) noexcept
{
for (std::size_t idx = 0; idx < count; ++idx) {
ptr[idx].~TObject();
}
deallocate(ptr, count);
}
[[nodiscard]]
iterator begin() const noexcept { return firstPage != nullptr ? iterator(*firstPage) : iterator(); }
[[nodiscard]]
iterator end() const noexcept { return {}; }
private:
TObject* allocateFromPage(page_t& page, std::size_t count)
{
gap_t* previousGap = nullptr;
for (gap_t* gap = page.firstGap; gap != nullptr; previousGap = gap, gap = gap->nextGap())
{
if (gap->remainingObjects == count)
{
// exactly the correct size
if (previousGap != nullptr) {
previousGap->nextGapOffset += gap->nextGapOffset;
}
else {
page.firstGap = gap->nextGap();
}
return reinterpret_cast<TObject*>(gap);
}
else if (gap->remainingObjects > count)
{
// still enough space
TObject* memory = reinterpret_cast<TObject*>(gap) + gap->remainingObjects - count;
gap->remainingObjects -= count;
return reinterpret_cast<TObject*>(memory);
}
}
return nullptr;
}
void mergeGaps(gap_t* start) noexcept
{
while (start->nextGapOffset != 0 && start->remainingObjects == start->nextGapOffset * sizeof(TObject))
{
gap_t& next = *start->nextGap();
start->remainingObjects += start->nextGap()->remainingObjects;
if (next.nextGapOffset == 0) {
start->nextGapOffset = 0;
}
else {
start->nextGapOffset += next.nextGapOffset;
}
}
}
[[nodiscard]] TObject* allocate(std::size_t count = 1) noexcept;
void free(TObject* object, std::size_t count = 1) noexcept;
};
template<typename TObject, std::size_t OBJECTS_PER_PAGE>
TObject* ObjectPool<TObject, OBJECTS_PER_PAGE>::allocate(std::size_t count) noexcept
{
MIJIN_ASSERT(count <= OBJECTS_PER_PAGE, "Cannot allocate more than OBJECTS_PER_PAGE elements at once!");
// first try to find a free spot in the existing pages
for (std::unique_ptr<Page>& page : pages)
{
std::uint32_t offset = page->freeOffset;
while (offset < OBJECTS_PER_PAGE)
{
GapInfo& gapInfo = *std::bit_cast<GapInfo*>(&page->data[offset]);
}
}
}
} // namespace mijin
#endif // !defined(MIJIN_MEMORY_OBJECT_POOL_HPP_INCLUDED)

View File

@@ -170,9 +170,6 @@ public:
[[nodiscard]]
constexpr const T& get() const MIJIN_NOEXCEPT { return base_; }
[[nodiscard]]
constexpr T release() && MIJIN_NOEXCEPT { return std::exchange(base_, nullptr); }
template<nullable_type TOther>
friend class NotNullable;
};

View File

@@ -8,8 +8,6 @@
#include <atomic>
#include <cstddef>
#include "../debug/assert.hpp"
namespace mijin
{

View File

@@ -1,122 +0,0 @@
#pragma once
#ifndef MIJIN_UTIL_FORMATTABLE_HPP_INCLUDED
#define MIJIN_UTIL_FORMATTABLE_HPP_INCLUDED 1
#include <format>
#include "../container/boxed_object.hpp"
namespace mijin
{
//
// public concepts
//
template<typename T>
concept parse_result_type = requires
{
typename T::parse_result_t;
};
template<typename T, typename TParseContext>
concept parseable_by_type = parse_result_type<T> &&
requires(const T& object, TParseContext& parseContext)
{
{ T::parseFormat(parseContext) } -> std::convertible_to<std::pair<typename TParseContext::iterator, typename T::parse_result_t>>;
};
template<typename T, typename TFmtContext>
concept simple_formattable_to_type = requires(const T& object, TFmtContext& formatContext)
{
{ object.format(formatContext) } -> std::convertible_to<typename TFmtContext::iterator>;
};
template<typename T, typename TFmtContext, typename TParseContext>
concept complex_formattable_to_type = parseable_by_type<T, TParseContext> &&
requires(const T& object, TParseContext& parseContext, TFmtContext& formatContext, T::parse_result_t& parseResult)
{
{ object.format(formatContext, parseResult) } -> std::convertible_to<typename TFmtContext::iterator>;
};
template<typename T, typename TFmtContext, typename TParseContext>
concept formattable_to_type = simple_formattable_to_type<T, TFmtContext> || complex_formattable_to_type<T, TFmtContext, TParseContext>;
template<typename T>
concept cformattable_type = formattable_to_type<T, std::format_context, std::format_parse_context>;
template<typename T>
concept wformattable_type = formattable_to_type<T, std::wformat_context, std::wformat_parse_context>;
template<typename T>
concept formattable_type = cformattable_type<T> && wformattable_type<T>;
template<typename T>
concept any_formattable_type = cformattable_type<T> || wformattable_type<T>;
//
// internal types
//
namespace impl
{
template<typename T>
struct ParseResult
{
};
template<parse_result_type T>
struct ParseResult<T>
{
BoxedObject<typename T::parse_result_t> value;
};
}
} // namespace mijin
template<mijin::any_formattable_type T>
struct std::formatter<T>
{
[[no_unique_address]] [[maybe_unused]] mijin::impl::ParseResult<T> parseResult;
template<typename TContext>
constexpr TContext::iterator parse(TContext& ctx)
{
if constexpr (mijin::parse_result_type<T>)
{
static_assert(mijin::parseable_by_type<T, TContext>, "Type does not support parsing by this context.");
auto [it, result] = T::parseFormat(ctx);
parseResult.value.construct(std::move(result));
return it;
}
else
{
auto it = ctx.begin();
auto end = ctx.end();
if (it != end && *it != '}')
{
throw std::format_error("invalid format");
}
return it;
}
}
template<typename TContext>
TContext::iterator format(const T& object, TContext& ctx) const
{
if constexpr (mijin::parse_result_type<T>)
{
auto it = object.format(ctx, std::move(*parseResult.value));
parseResult.destroy();
return it;
}
else
{
static_assert(mijin::simple_formattable_to_type<T, TContext>, "Type does not support formatting to this context.");
return object.format(ctx);
}
}
};
#endif // MIJIN_UTIL_FORMATTABLE_HPP_INCLUDED

View File

@@ -3,7 +3,6 @@
#if !defined(MIJIN_UTIL_ITERATORS_HPP_INCLUDED)
#define MIJIN_UTIL_ITERATORS_HPP_INCLUDED 1
#include <array>
#include <cstddef>
#include <functional>
#include <optional>

View File

@@ -39,9 +39,9 @@ namespace mijin
// internal variables
//
#if MIJIN_TARGET_OS == MIJIN_OS_LINUX
namespace
{
#if MIJIN_TARGET_OS == MIJIN_OS_LINUX
std::mutex gDlErrorMutex; // dlerror may not be thread-safe
const std::uint64_t gCPUClockResolution = []()
@@ -52,16 +52,8 @@ const std::uint64_t gCPUClockResolution = []()
MIJIN_ASSERT(time.tv_sec == 0, "What kind of cpu clock is this?");
return static_cast<std::uint64_t>(time.tv_nsec);
}();
#elif MIJIN_TARGET_OS == MIJIN_OS_WINDOWS
const std::uint64_t gCPUTickFrequency = []()
{
LARGE_INTEGER ticks;
[[maybe_unused]] const BOOL result = QueryPerformanceFrequency(&ticks);
MIJIN_ASSERT(result, "Error getting cpu frequency.");
return static_cast<std::uint64_t>(ticks.QuadPart);
}();
#endif // MIJIN_TARGET_OS == MIJIN_OS_LINUX || MIJIN_TARGET_OS == MIJIN_OS_WINDOWS
}
};
#endif // MIJIN_TARGET_OS == MIJIN_OS_LINUX
//
// internal functions
@@ -204,10 +196,8 @@ std::uint64_t getCPUTicks() MIJIN_NOEXCEPT
MIJIN_ASSERT(result == 0, "Error getting cpu time.");
return (static_cast<std::uint64_t>(time.tv_sec) * 1e9 + time.tv_nsec) / gCPUClockResolution;
#else
LARGE_INTEGER ticks;
[[maybe_unused]] const BOOL result = QueryPerformanceCounter(&ticks);
MIJIN_ASSERT(result, "Error getting cpu time.");
return static_cast<std::uint64_t>(ticks.QuadPart);
MIJIN_ERROR("TODO");
return 0;
#endif
}
@@ -216,7 +206,8 @@ std::uint64_t getCPUTicksPerSecond() MIJIN_NOEXCEPT
#if MIJIN_TARGET_OS == MIJIN_OS_LINUX
return 1e9 / gCPUClockResolution;
#else
return gCPUTickFrequency;
MIJIN_ERROR("TODO");
return 0;
#endif
}
} // namespace mijin

View File

@@ -33,16 +33,6 @@ namespace mijin
// public constants
//
namespace detail
{
template<typename TChar>
static constexpr std::array DEFAULT_TRIM_CHARS_DATA = {TChar(' '), TChar('\t'), TChar('\r'), TChar('\n')};
}
template<typename TChar>
static const std::basic_string_view<TChar, std::char_traits<TChar>> DEFAULT_TRIM_CHARS
= {detail::DEFAULT_TRIM_CHARS_DATA<TChar>.begin(), detail::DEFAULT_TRIM_CHARS_DATA<TChar>.end()};
//
// public traits
//
@@ -108,361 +98,6 @@ struct SplitOptions
bool ignoreEmpty = true;
};
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);
}
};
struct SplitViewOptions
{
bool ignoreEmpty = true;
bool trim = false;
};
template<typename TChar, TChar splitAt, SplitViewOptions options = SplitViewOptions(), typename TCharTraits = std::char_traits<TChar>>
struct SplitStringTraitsCT
{
using char_t = TChar;
using string_view_t = std::basic_string_view<TChar, TCharTraits>;
static constexpr char_t getSplitAt() MIJIN_NOEXCEPT { return splitAt; }
static constexpr bool getIgnoreEmpty() MIJIN_NOEXCEPT { return options.ignoreEmpty; }
static constexpr bool getTrim() MIJIN_NOEXCEPT { return options.trim; }
static constexpr auto getTrimChars() MIJIN_NOEXCEPT { return DEFAULT_TRIM_CHARS<char_t>; }
};
template<typename TChar, typename TCharTraits = std::char_traits<TChar>>
struct SplitStringTraitsRT
{
using char_t = TChar;
using string_view_t = std::basic_string_view<TChar, TCharTraits>;
char_t splitAt;
bool ignoreEmpty;
string_view_t trimChars = {};
constexpr char_t getSplitAt() const MIJIN_NOEXCEPT { return splitAt; }
constexpr bool getIgnoreEmpty() const MIJIN_NOEXCEPT { return ignoreEmpty; }
constexpr bool getTrim() const MIJIN_NOEXCEPT { return !trimChars.empty(); }
constexpr string_view_t getTrimChars() const MIJIN_NOEXCEPT { return trimChars; }
};
template<typename T, typename TChar>
concept SplitStringTraitsType = std::is_copy_constructible_v<T> && requires(const T& object)
{
typename T::char_t;
typename T::string_view_t;
{ object.getSplitAt() } -> std::convertible_to<TChar>;
{ object.getIgnoreEmpty() } -> std::convertible_to<bool>;
{ object.getTrim() } -> std::convertible_to<bool>;
{ object.getTrimChars() } -> std::convertible_to<typename T::string_view_t>;
};
static_assert(SplitStringTraitsType<SplitStringTraitsCT<char, ' '>, char>);
static_assert(SplitStringTraitsType<SplitStringTraitsRT<char>, char>);
template<typename TChar, typename TLine, SplitViewOptions options = SplitViewOptions(), typename TCharTraits = std::char_traits<TChar>>
struct SplitLineTraitsCT : SplitStringTraitsCT<TChar, '\n', options, TCharTraits>
{
using base_t = SplitStringTraitsCT<TChar, '\n', options, TCharTraits>;
using char_t = TChar;
using line_t = TLine;
line_t line = 1;
using base_t::getSplitAt;
using base_t::getIgnoreEmpty;
using base_t::getTrim;
using base_t::getTrimChars;
constexpr void onNext() MIJIN_NOEXCEPT {
++line;
}
constexpr line_t getLine() const MIJIN_NOEXCEPT { return line; }
};
template<typename TChar, typename TLine, typename TCharTraits = std::char_traits<TChar>>
struct SplitLineTraitsRT
{
using char_t = TChar;
using line_t = TLine;
using string_view_t = std::basic_string_view<TChar, TCharTraits>;
line_t line = 1;
bool ignoreEmpty;
string_view_t trimChars = {};
constexpr char_t getSplitAt() const MIJIN_NOEXCEPT { return '\n'; }
constexpr bool getIgnoreEmpty() const MIJIN_NOEXCEPT { return ignoreEmpty; }
constexpr bool getTrim() const MIJIN_NOEXCEPT { return !trimChars.empty(); }
constexpr string_view_t getTrimChars() const MIJIN_NOEXCEPT { return trimChars; }
constexpr line_t getLine() const MIJIN_NOEXCEPT { return line; }
constexpr void onNext() MIJIN_NOEXCEPT {
++line;
}
};
template<typename T, typename TChar, typename TLine>
concept SplitLineTraitsType = SplitStringTraitsType<T, TChar> && requires (const T& object)
{
{ object.getLine() } -> std::convertible_to<TLine>;
};
static_assert(SplitLineTraitsType<SplitLineTraitsCT<char, unsigned>, char, unsigned>);
static_assert(SplitLineTraitsType<SplitLineTraitsRT<char, unsigned>, char, unsigned>);
template<typename TString, typename TChars>
[[nodiscard]]
auto trim(TString&& string, TChars&& chars);
template<typename TChar, SplitStringTraitsType<TChar> TTraits>
class SplitStringIterator
{
public:
using char_t = TChar;
using traits_t = TTraits;
using string_view_t = traits_t::string_view_t;
using base_t = string_view_t::iterator;
using value_type = string_view_t;
private:
[[no_unique_address]] traits_t traits_;
string_view_t full_;
string_view_t::iterator pos_;
string_view_t::iterator next_;
public:
constexpr SplitStringIterator(string_view_t full, base_t pos, traits_t traits = {}) MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v<traits_t>)
: full_(full), pos_(pos), traits_(std::move(traits))
{
findNext();
}
constexpr explicit SplitStringIterator(traits_t traits = {}) MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v<traits_t>)
: traits_(std::move(traits)) {}
constexpr SplitStringIterator(const SplitStringIterator&) noexcept(std::is_nothrow_copy_constructible_v<traits_t>) = default;
constexpr SplitStringIterator(SplitStringIterator&&) noexcept(std::is_nothrow_move_constructible_v<traits_t>) = default;
constexpr SplitStringIterator& operator=(const SplitStringIterator&) noexcept(std::is_nothrow_copy_assignable_v<traits_t>) = default;
constexpr SplitStringIterator& operator=(SplitStringIterator&&) noexcept(std::is_nothrow_move_assignable_v<traits_t>) = default;
constexpr bool operator==(const SplitStringIterator& other) const MIJIN_NOEXCEPT { MIJIN_ASSERT(full_ == other.full_, "Comparing unrelated iterators."); return pos_ == other.pos_; }
constexpr bool operator!=(const SplitStringIterator& other) const MIJIN_NOEXCEPT { MIJIN_ASSERT(full_ == other.full_, "Comparing unrelated iterators."); return pos_ != other.pos_; }
constexpr bool operator<(const SplitStringIterator& other) const MIJIN_NOEXCEPT { MIJIN_ASSERT(full_ == other.full_, "Comparing unrelated iterators."); return pos_ < other.pos_; }
constexpr bool operator<=(const SplitStringIterator& other) const MIJIN_NOEXCEPT { MIJIN_ASSERT(full_ == other.full_, "Comparing unrelated iterators."); return pos_ <= other.pos_; }
constexpr bool operator>(const SplitStringIterator& other) const MIJIN_NOEXCEPT { MIJIN_ASSERT(full_ == other.full_, "Comparing unrelated iterators."); return pos_ > other.pos_; }
constexpr bool operator>=(const SplitStringIterator& other) const MIJIN_NOEXCEPT { MIJIN_ASSERT(full_ == other.full_, "Comparing unrelated iterators."); return pos_ >= other.pos_; }
[[nodiscard]]
traits_t& getTraits() MIJIN_NOEXCEPT
{
return traits_;
}
[[nodiscard]]
const traits_t& getTraits() const MIJIN_NOEXCEPT
{
return traits_;
}
constexpr value_type operator*() const MIJIN_NOEXCEPT
{
MIJIN_ASSERT(pos_ != full_.end(), "Dereferencing an invalid iterator.");
string_view_t result{pos_, next_};
if (traits_.getTrim())
{
result = trim(result, traits_.getTrimChars());
}
return result;
}
constexpr SplitStringIterator& operator++() MIJIN_NOEXCEPT
{
MIJIN_ASSERT(pos_ != full_.end(), "Iterating past end.");
if (next_ == full_.end()) {
pos_ = full_.end();
}
else
{
pos_ = std::next(next_);
findNext();
}
return *this;
}
constexpr SplitStringIterator operator++(int) const MIJIN_NOEXCEPT
{
SplitStringIterator copy(*this);
++copy;
return copy;
}
// TODO
// SplitStringIterator& operator--() MIJIN_NOEXCEPT
// {
// MIJIN_ASSERT(pos_ != full_.begin(), "Iterating past begin.");
// next_ = std::prev(pos_);
// pos_ = std::find(std::reverse_iterator(next_), std::reverse_iterator(full_.begin()), separator).base();
// }
private:
constexpr void findNext()
{
while (true)
{
if constexpr (requires{{ traits_.onNext() };}) {
traits_.onNext();
}
next_ = std::find(pos_, full_.end(), traits_.getSplitAt());
if (!traits_.getIgnoreEmpty() || pos_ == full_.end()) {
break;
}
if (traits_.getTrim())
{
const string_view_t trimChars = traits_.getTrimChars();
typename string_view_t::iterator trimmedPos = std::find_if(pos_, next_, [&](char_t chr)
{
return !trimChars.contains(chr);
});
if (trimmedPos == next_)
{
pos_ = next_; // skip this part
}
}
if (pos_ != next_) {
break;
}
pos_ = std::next(pos_);
}
}
};
template<typename TChar, SplitStringTraitsType<TChar> TTraits>
class SplitStringRange
{
public:
using char_t = TChar;
using traits_t = TTraits;
using string_view_t = traits_t::string_view_t;
using iterator = SplitStringIterator<char_t, traits_t>;
private:
[[no_unique_address]] traits_t traits_;
string_view_t stringView_;
public:
constexpr explicit SplitStringRange(string_view_t stringView, traits_t traits = {}) MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v<traits_t>)
: stringView_(stringView), traits_(std::move(traits)) {}
constexpr SplitStringRange(const SplitStringRange&) noexcept(std::is_nothrow_copy_constructible_v<traits_t>) = default;
constexpr SplitStringRange(SplitStringRange&&) noexcept(std::is_nothrow_move_constructible_v<traits_t>) = default;
constexpr SplitStringRange& operator=(const SplitStringRange&) noexcept(std::is_nothrow_copy_assignable_v<traits_t>) = default;
constexpr SplitStringRange& operator=(SplitStringRange&&) noexcept(std::is_nothrow_move_assignable_v<traits_t>) = default;
constexpr auto operator<=>(const SplitStringRange&) const noexcept = default;
constexpr iterator begin() const MIJIN_NOEXCEPT_IF(std::is_nothrow_copy_constructible_v<traits_t>)
{
return iterator(stringView_, stringView_.begin(), traits_);
}
constexpr iterator end() const MIJIN_NOEXCEPT_IF(std::is_nothrow_copy_constructible_v<traits_t>)
{
return iterator(stringView_, stringView_.end(), traits_);
}
};
template<typename TChar, typename TLine = unsigned, SplitLineTraitsType<TChar, TLine> TTraits = SplitLineTraitsCT<TChar, TLine>>
class LineIterator
{
public:
using char_t = TChar;
using line_t = TLine;
using traits_t = TTraits;
using base_t = SplitStringIterator<TChar, traits_t>;
using string_view_t = base_t::string_view_t;
using value_type = std::pair<string_view_t, line_t>;
private:
base_t base_ = {};
public:
constexpr LineIterator(string_view_t full, string_view_t::iterator pos, traits_t traits = {}) MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v<traits_t>)
: base_(full, pos, std::move(traits)) {}
constexpr explicit LineIterator(traits_t traits = {}) MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v<traits_t>)
: base_(std::move(traits)) {}
LineIterator(const LineIterator&) noexcept = default;
LineIterator(LineIterator&&) noexcept = default;
LineIterator& operator=(const LineIterator&) noexcept = default;
LineIterator& operator=(LineIterator&&) noexcept = default;
bool operator==(const LineIterator& other) const MIJIN_NOEXCEPT { return base_ == other.base_; }
bool operator!=(const LineIterator& other) const MIJIN_NOEXCEPT { return base_ != other.base_; }
bool operator<(const LineIterator& other) const MIJIN_NOEXCEPT { return base_ < other.base_; }
bool operator>(const LineIterator& other) const MIJIN_NOEXCEPT { return base_ > other.base_; }
bool operator<=(const LineIterator& other) const MIJIN_NOEXCEPT { return base_ <= other.base_; }
bool operator>=(const LineIterator& other) const MIJIN_NOEXCEPT { return base_ >= other.base_; }
constexpr value_type operator*() const MIJIN_NOEXCEPT
{
string_view_t stringView = *base_;
if (!base_.getTraits().getTrim())
{
// always split \r, even if not trimming other whitespace
if (stringView.ends_with('\r')) {
stringView = stringView.substr(0, stringView.size() - 1);
}
}
return {stringView, base_.getTraits().line};
}
constexpr LineIterator& operator++() MIJIN_NOEXCEPT
{
++base_;
return *this;
}
constexpr LineIterator operator++(int) const MIJIN_NOEXCEPT
{
SplitStringIterator copy(*this);
++copy;
return copy;
}
};
template<typename TChar, typename TLine = unsigned, SplitLineTraitsType<TChar, TLine> TTraits = SplitLineTraitsCT<TChar, TLine>>
class LineRange
{
public:
using char_t = TChar;
using line_t = TLine;
using traits_t = TTraits;
using iterator = LineIterator<char_t, line_t, traits_t>;
using string_view_t = iterator::string_view_t;
private:
[[no_unique_address]] traits_t traits_;
string_view_t stringView_;
public:
constexpr explicit LineRange(string_view_t stringView, traits_t traits = {}) MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v<traits_t>)
: traits_(std::move(traits)), stringView_(stringView) {}
constexpr LineRange(const LineRange&) noexcept(std::is_nothrow_copy_constructible_v<traits_t>) = default;
constexpr LineRange(LineRange&&) noexcept(std::is_nothrow_move_constructible_v<traits_t>) = default;
constexpr LineRange& operator=(const LineRange&) noexcept(std::is_nothrow_copy_assignable_v<traits_t>) = default;
constexpr LineRange& operator=(LineRange&&) noexcept(std::is_nothrow_move_assignable_v<traits_t>) = default;
constexpr auto operator<=>(const LineRange&) const noexcept = default;
constexpr iterator begin() const MIJIN_NOEXCEPT
{
return iterator(stringView_, stringView_.begin(), traits_);
}
constexpr iterator end() const MIJIN_NOEXCEPT
{
return iterator(stringView_, stringView_.end(), traits_);
}
};
//
// public functions
//
@@ -638,6 +273,13 @@ std::basic_string_view<TChar, TTraits> trimImpl(std::basic_string_view<TChar, TT
{
return trimPrefixImpl(trimSuffixImpl(stringView, charsToTrim), charsToTrim);
}
template<typename TChar>
static const std::array DEFAULT_TRIM_CHARS_DATA = {TChar(' '), TChar('\t'), TChar('\r'), TChar('\n')};
template<typename TChar>
static const std::basic_string_view<TChar, std::char_traits<TChar>> DEFAULT_TRIM_CHARS
= {DEFAULT_TRIM_CHARS_DATA<TChar>.begin(), DEFAULT_TRIM_CHARS_DATA<TChar>.end()};
}
template<typename TLeft, typename TRight>
@@ -654,61 +296,6 @@ template<std::size_t count, typename TLeft, typename TRight>
std::basic_string_view(std::forward<TRight>(separator)), options, outNumResults);
}
template<typename TTraits, typename TChar> requires (SplitStringTraitsType<TTraits, TChar>)
[[nodiscard]] SplitStringRange<TChar, TTraits> splitView(typename TTraits::string_view_t stringView, TTraits traits = {}) MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v<TTraits>)
{
return SplitStringRange<TChar, TTraits>(stringView, std::move(traits));
}
template<auto splitAt, SplitViewOptions options = SplitViewOptions(), typename TStringView, typename TChar = decltype(splitAt), typename TCharTraits = std::char_traits<TChar>>
[[nodiscard]] SplitStringRange<TChar, SplitStringTraitsCT<TChar, splitAt, options, TCharTraits>> splitView(TStringView&& stringView) MIJIN_NOEXCEPT
{
return splitView<SplitStringTraitsCT<TChar, splitAt, options, TCharTraits>>(std::basic_string_view<TChar, TCharTraits>(std::forward<TStringView>(stringView)));
}
template<typename TStringView, typename TChar, typename TCharTraits = std::char_traits<TChar>, typename TTrimChars = std::basic_string_view<TChar, TCharTraits>>
[[nodiscard]] auto splitView(TStringView&& stringView, TChar splitAt, bool ignoreEmpty = true, TTrimChars trimChars = {}) MIJIN_NOEXCEPT
{
return splitView(std::basic_string_view<TChar, TCharTraits>(std::forward<TStringView>(stringView)), SplitStringTraitsRT<TChar, TCharTraits>{
.splitAt = splitAt,
.ignoreEmpty = ignoreEmpty,
.trimChars = std::basic_string_view<TChar, TCharTraits>(std::forward<TTrimChars>(trimChars))
});
}
template<typename TTraits, typename TChar, typename TLine = unsigned> requires(SplitLineTraitsType<TTraits, TChar, TLine>)
[[nodiscard]] LineRange<TChar, TLine, TTraits> splitLines(typename TTraits::string_view_t stringView, TTraits traits = {}) MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v<TTraits>)
{
return LineRange<TChar, TLine, TTraits>(stringView, std::move(traits));
}
template<typename TLine = unsigned, SplitViewOptions options = SplitViewOptions{.ignoreEmpty=false},
typename TParam,
typename TStringView = decltype(std::basic_string_view(std::declval<TParam&&>())),
typename TChar = typename TStringView::value_type,
typename TCharTraits = typename TStringView::traits_type,
typename TTraits = SplitLineTraitsCT<TChar, TLine, options, TCharTraits>>
[[nodiscard]]
auto splitLines(TParam&& stringView) MIJIN_NOEXCEPT -> LineRange<TChar, TLine, TTraits>
{
return LineRange<TChar, TLine, TTraits>(std::basic_string_view(std::forward<TParam>(stringView)));
}
template<typename TParam, typename TLine = unsigned,
typename TStringView = decltype(std::basic_string_view(std::declval<TParam&&>())),
typename TChar = typename TStringView::value_type,
typename TCharTraits = typename TStringView::traits_type,
typename TTraits = SplitLineTraitsRT<TChar, TLine, TCharTraits>,
typename TTrimChars = TStringView>
[[nodiscard]]
auto splitLines(TParam&& stringView, bool ignoreEmpty, TTrimChars&& trimChars = {}) MIJIN_NOEXCEPT -> LineRange<TChar, TLine, TTraits>
{
return LineRange<TChar, TLine, TTraits>(std::basic_string_view(std::forward<TParam>(stringView)), TTraits{
.ignoreEmpty = ignoreEmpty,
.trimChars = std::basic_string_view<TChar, TCharTraits>(std::forward<TTrimChars>(trimChars))
});
}
template<typename TString, typename TChars>
[[nodiscard]]
auto trimPrefix(TString&& string, TChars&& chars)
@@ -720,7 +307,7 @@ template<typename TString>
[[nodiscard]]
auto trimPrefix(TString&& string)
{
return trimPrefix(string, DEFAULT_TRIM_CHARS<str_char_type_t<TString>>);
return trimPrefix(string, detail::DEFAULT_TRIM_CHARS<str_char_type_t<TString>>);
}
template<typename TString, typename TChars>
@@ -734,7 +321,7 @@ template<typename TString>
[[nodiscard]]
auto trimSuffix(TString&& string)
{
return trimSuffix(string, DEFAULT_TRIM_CHARS<str_char_type_t<TString>>);
return trimSuffix(string, detail::DEFAULT_TRIM_CHARS<str_char_type_t<TString>>);
}
template<typename TString, typename TChars>
@@ -748,7 +335,7 @@ template<typename TString>
[[nodiscard]]
auto trim(TString&& string)
{
return trim(string, DEFAULT_TRIM_CHARS<str_char_type_t<TString>>);
return trim(string, detail::DEFAULT_TRIM_CHARS<str_char_type_t<TString>>);
}
template<typename TLeft, typename TRight>
@@ -900,6 +487,21 @@ auto operator|(TIterable&& iterable, const Join& joiner)
}
} // 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
{

View File

@@ -253,42 +253,6 @@ struct optional_base<T, false>
template<typename T, bool enable>
using optional_base_t = optional_base<T, enable>::type;
namespace impl
{
template<typename TFunc>
struct function_traits_base {};
template<typename TResult, typename... TParams>
struct function_traits_base<TResult (*)(TParams...)>
{
using result_t = TResult;
using params_t = std::tuple<TParams...>;
};
template<typename TResult, typename TType, typename... TParams>
struct function_traits_base<TResult (TType::*)(TParams...)>
{
using result_t = TResult;
using params_t = std::tuple<TType*, TParams...>;
};
template<typename TResult, typename TType, typename... TParams>
struct function_traits_base<TResult (TType::*)(TParams...) const>
{
using result_t = TResult;
using params_t = std::tuple<const TType*, TParams...>;
};
}
template<typename TFunc>
struct function_traits : impl::function_traits_base<TFunc>
{
static constexpr std::size_t NUM_PARAMS = std::tuple_size_v<typename impl::function_traits_base<TFunc>::params_t>;
template<std::size_t pos>
using param_t = std::tuple_element_t<pos, typename impl::function_traits_base<TFunc>::params_t>;
};
//
// public functions
//

View File

@@ -26,7 +26,3 @@
#if defined(DEBUG)
#undef DEBUG
#endif
#if defined(VOID)
#undef VOID
#endif