/*********************************************************************** THIS FILE IS AUTOMATICALLY GENERATED. DO NOT MODIFY DEVELOPER: Zihan Chen(vczh) ***********************************************************************/ /*********************************************************************** .\BASIC.H ***********************************************************************/ /*********************************************************************** Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ #ifndef VCZH_BASIC #define VCZH_BASIC #include #include #ifdef VCZH_CHECK_MEMORY_LEAKS #define _CRTDBG_MAP_ALLOC #include #define VCZH_CHECK_MEMORY_LEAKS_NEW new(_NORMAL_BLOCK, __FILE__, __LINE__) #define new VCZH_CHECK_MEMORY_LEAKS_NEW #endif #if defined _WIN64 || defined __x86_64 || defined __LP64__ || defined __aarch64__ || defined _M_ARM64 #define VCZH_64 #endif #if defined _MSC_VER #define VCZH_MSVC #else #define VCZH_GCC #if defined(__APPLE__) #define VCZH_APPLE #endif #endif #if defined __arm__ || defined __aarch64__ || defined _M_ARM || defined _M_ARM64 #define VCZH_ARM #endif #if defined VCZH_MSVC #define VCZH_WCHAR_UTF16 #elif defined VCZH_GCC #define VCZH_WCHAR_UTF32 #endif #if defined VCZH_WCHAR_UTF16 static_assert(sizeof(wchar_t) == sizeof(char16_t), "wchar_t is not UTF-16."); #elif defined VCZH_WCHAR_UTF32 static_assert(sizeof(wchar_t) == sizeof(char32_t), "wchar_t is not UTF-32."); #else static_assert(false, "wchar_t configuration is not right."); #endif #if defined VCZH_GCC #include #include #include #define abstract #define __thiscall #define __forceinline inline #define _I8_MIN ((vint8_t)0x80) #define _I8_MAX ((vint8_t)0x7F) #define _UI8_MAX ((vuint8_t)0xFF) #define _I16_MIN ((vint16_t)0x8000) #define _I16_MAX ((vint16_t)0x7FFF) #define _UI16_MAX ((vuint16_t)0xFFFF) #define _I32_MIN ((vint32_t)0x80000000) #define _I32_MAX ((vint32_t)0x7FFFFFFF) #define _UI32_MAX ((vuint32_t)0xFFFFFFFF) #define _I64_MIN ((vint64_t)0x8000000000000000L) #define _I64_MAX ((vint64_t)0x7FFFFFFFFFFFFFFFL) #define _UI64_MAX ((vuint64_t)0xFFFFFFFFFFFFFFFFL) #endif #include #include #include #include #include namespace vl { /*********************************************************************** x86 and x64 Compatbility ***********************************************************************/ #if defined VCZH_MSVC /// 1-byte signed integer. typedef signed __int8 vint8_t; /// 1-byte unsigned integer. typedef unsigned __int8 vuint8_t; /// 2-bytes signed integer. typedef signed __int16 vint16_t; /// 2-bytes unsigned integer. typedef unsigned __int16 vuint16_t; /// 4-bytes signed integer. typedef signed __int32 vint32_t; /// 4-bytes unsigned integer. typedef unsigned __int32 vuint32_t; /// 8-bytes signed integer. typedef signed __int64 vint64_t; /// 8-bytes unsigned integer. typedef unsigned __int64 vuint64_t; #elif defined VCZH_GCC typedef int8_t vint8_t; typedef uint8_t vuint8_t; typedef int16_t vint16_t; typedef uint16_t vuint16_t; typedef int32_t vint32_t; typedef uint32_t vuint32_t; typedef int64_t vint64_t; typedef uint64_t vuint64_t; #endif #ifdef VCZH_64 /// Signed interface whose size equals to sizeof(void*). typedef vint64_t vint; /// Signed interface whose size equals to sizeof(void*). typedef vint64_t vsint; /// Unsigned interface whose size equals to sizeof(void*). typedef vuint64_t vuint; #else /// Signed interface whose size equals to sizeof(void*). typedef vint32_t vint; /// Signed interface whose size equals to sizeof(void*). typedef vint32_t vsint; /// Unsigned interface whose size equals to sizeof(void*). typedef vuint32_t vuint; #endif /// Signed interger representing position. typedef vint64_t pos_t; /// Signed atomic integer. typedef std::atomic atomic_vint; #define INCRC(ATOMIC) ((ATOMIC)->fetch_add(1) + 1) #define DECRC(ATOMIC) ((ATOMIC)->fetch_sub(1) - 1) #ifdef VCZH_64 #define ITOA_S _i64toa_s #define ITOW_S _i64tow_s #define I64TOA_S _i64toa_s #define I64TOW_S _i64tow_s #define UITOA_S _ui64toa_s #define UITOW_S _ui64tow_s #define UI64TOA_S _ui64toa_s #define UI64TOW_S _ui64tow_s #else #define ITOA_S _itoa_s #define ITOW_S _itow_s #define I64TOA_S _i64toa_s #define I64TOW_S _i64tow_s #define UITOA_S _ui64toa_s #define UITOW_S _ui64tow_s #define UI64TOA_S _ui64toa_s #define UI64TOW_S _ui64tow_s #endif /*********************************************************************** Basic Types ***********************************************************************/ /// Base type of all errors. An error is an exception that is not recommended to catch. Raising it means there is a mistake in the code. class Error { private: const wchar_t* description; public: Error(const wchar_t* _description); const wchar_t* Description()const; }; #if defined VCZH_MSVC || defined VCZH_GCC || defined _DEBUG #define CHECK_ERROR(CONDITION,DESCRIPTION) do{if(!(CONDITION))throw Error(DESCRIPTION);}while(0) #elif defined NDEBUG #define CHECK_ERROR(CONDITION,DESCRIPTION) #endif #define CHECK_FAIL(DESCRIPTION) do{throw Error(DESCRIPTION);}while(0) #define SCOPE_VARIABLE(TYPE, VARIABLE, VALUE)\ if(bool __scope_variable_flag__=true)\ for(TYPE VARIABLE = VALUE;__scope_variable_flag__;__scope_variable_flag__=false) /// /// Base type of all classes. /// This type has a virtual destructor, making all derived classes destructors virtual. /// In this way an object is allowed to be deleted using a pointer of a qualified base type pointing to this object. /// class Object { public: virtual ~Object() = default; }; /*********************************************************************** Type Traits ***********************************************************************/ template struct TypeTuple { }; template struct TypeTupleItemRetriver; template struct TypeTupleItemRetriver> { using Type = typename TypeTupleItemRetriver>::Type; }; template struct TypeTupleItemRetriver<0, TypeTuple> { using Type = T; }; template using TypeTupleElement = typename TypeTupleItemRetriver::Type; template struct RemoveCVRefArrayCtad { using Type = std::remove_cvref_t; }; template struct RemoveCVRefArrayCtad { using Type = T*; }; /// Type for specify and create a representative value for comparing another value of a specific type for containers. /// The element type for containers. template struct KeyType { public: /// The type of the representative value. typedef T Type; /// Convert a value in a container to its representative value. /// The representative value. /// The value in a container. static const T& GetKeyValue(const T& value) { return value; } }; namespace ordering_decision { template struct OrderingSelection { using Type = std::partial_ordering; }; template struct OrderingSelection { using Type = std::weak_ordering; }; template struct OrderingSelection { using Type = std::partial_ordering; }; template struct OrderingDecision { static constexpr const vint POs = (0 + ... + (std::is_same_v, std::partial_ordering>)); static constexpr const vint WOs = (0 + ... + (std::is_same_v, std::weak_ordering>)); static constexpr const vint SOs = (0 + ... + (std::is_same_v, std::strong_ordering>)); static_assert(POs + WOs + SOs == sizeof...(TOrderings), "vl::OrderingDecision can only be used on std::(strong|weak|partial)_ordering."); using Type = typename OrderingSelection<(POs > 0), (WOs > 0), (SOs > 0)>::Type; }; } template using OrderingOf = typename ordering_decision::OrderingDecision::Type; /*********************************************************************** Interface ***********************************************************************/ #define NOT_COPYABLE(TYPE)\ TYPE(const TYPE&) = delete;\ TYPE(TYPE&&) = delete;\ TYPE& operator=(const TYPE&) = delete;\ TYPE& operator=(TYPE&&) = delete /// Base type of all interfaces. All interface types are encouraged to be virtual inherited. class Interface { public: NOT_COPYABLE(Interface); Interface() = default; virtual ~Interface() = default; }; } #endif /*********************************************************************** .\FEATUREINJECTION.H ***********************************************************************/ /*********************************************************************** Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ #ifndef VCZH_FEATUREINJECTION #define VCZH_FEATUREINJECTION namespace vl { namespace feature_injection { /// /// Base interface for all feature injection implementations. /// Provides lifecycle management and delegation support through linked list structure. /// class IFeatureImpl : public virtual Interface { public: /// Get the previous implementation in the injection chain. /// The previous implementation, or nullptr if this is the first in chain. virtual IFeatureImpl* GetPreviousImpl() = 0; /// Begin injection lifecycle, called when this implementation becomes active. /// The previously active implementation to delegate to. virtual void BeginInjection(IFeatureImpl* previousImpl) = 0; /// End injection lifecycle, called when this implementation is being removed. virtual void EndInjection() = 0; }; /// /// Default implementation template for IFeatureImpl providing type-safe delegation. /// TImpl should be the actual feature interface that inherits from IFeatureImpl. /// template requires std::is_base_of_v class FeatureImpl : public Object, public virtual TImpl { private: TImpl* _previousImpl = nullptr; public: IFeatureImpl* GetPreviousImpl() override { return _previousImpl; } void BeginInjection(IFeatureImpl* _previousImpl) override { TImpl* typedPrevious = nullptr; if (_previousImpl != nullptr) { typedPrevious = dynamic_cast(_previousImpl); CHECK_ERROR(typedPrevious != nullptr, L"vl::feature_injection::FeatureImpl::BeginInjection(IFeatureImpl*): Invalid previous implementation type."); } this->_previousImpl = typedPrevious; BeginInjection(typedPrevious); } void EndInjection() override { _previousImpl = nullptr; } /// Type-safe injection hook for derived classes to override. /// The type-safe previous implementation. virtual void BeginInjection(TImpl* _previousImpl) { } protected: /// Get the previous implementation with type safety. /// The previous implementation, or nullptr if this is the first in chain. TImpl* Previous() { return _previousImpl; // Already verified and cast during BeginInjection } }; /// /// Template class for managing feature injection with type-safe operations. /// Maintains a linked list of implementations and provides inject/eject functionality. /// template requires std::is_base_of_v class FeatureInjection { private: IFeatureImpl* currentImpl; public: /// Initialize with a default implementation. /// The default implementation, must not be nullptr. FeatureInjection(TImpl* defaultImpl) : currentImpl(defaultImpl) { CHECK_ERROR(defaultImpl != nullptr, L"vl::feature_injection::FeatureInjection::FeatureInjection(TImpl*): Default implementation cannot be nullptr."); } /// Get the current active implementation. /// The current implementation, never nullptr after proper initialization. TImpl* Get() { return dynamic_cast(currentImpl); } /// Inject a new implementation, making it the current active one. /// The implementation to inject, must not be nullptr. void Inject(TImpl* impl) { CHECK_ERROR(impl != nullptr, L"vl::feature_injection::FeatureInjection::Inject(TImpl*): Implementation cannot be nullptr."); dynamic_cast(impl)->BeginInjection(currentImpl); currentImpl = impl; } /// Eject an implementation from the chain, restoring previous implementations. /// The implementation to eject. void Eject(TImpl* impl) { CHECK_ERROR(impl != nullptr, L"vl::feature_injection::FeatureInjection::Eject(TImpl*): Implementation cannot be nullptr."); // Eject all implementations from currentImpl down to impl auto toEject = currentImpl; while (true) { CHECK_ERROR(toEject != nullptr, L"vl::feature_injection::FeatureInjection::Eject(TImpl*): Implementation not found in chain."); auto next = toEject->GetPreviousImpl(); toEject->EndInjection(); if (toEject == impl) { currentImpl = next; break; } toEject = next; } } /// Eject all implementations, restoring the default implementation. void EjectAll() { while (currentImpl->GetPreviousImpl() != nullptr) { auto toEject = currentImpl; currentImpl = currentImpl->GetPreviousImpl(); toEject->EndInjection(); } } }; } } #endif /*********************************************************************** .\COLLECTIONS\PAIR.H ***********************************************************************/ /*********************************************************************** Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ #ifndef VCZH_COLLECTIONS_PAIR #define VCZH_COLLECTIONS_PAIR namespace vl { namespace collections { template class Pair; namespace pair_internal { template struct AddConstInsideReference { using Type = const T; }; template struct AddConstInsideReference { using Type = const T&; }; template struct TypePairElementRetriver; template struct TypePairElementRetriver> { using Type = TypeTupleElement>; }; template struct TypePairElementRetriver> { using Type = typename AddConstInsideReference>>::Type; }; } // G++ workaround template struct PairGet; /// A type representing a pair of key and value. /// Type of the key. /// Type of the value. template class Pair { public: /// The key. K key; /// The value. V value; Pair() = default; Pair(const Pair&) = default; Pair(Pair&&) = default; template Pair(TKey&& _key, TValue&& _value) requires(std::is_constructible_v && std::is_constructible_v) : key(std::forward(_key)) , value(std::forward(_value)) { } template Pair(const Pair& p) requires(std::is_constructible_v && std::is_constructible_v) : key(p.key) , value(p.value) { } template Pair(Pair&& p) requires(std::is_constructible_v && std::is_constructible_v) : key(std::move(p.key)) , value(std::move(p.value)) { } Pair& operator=(const Pair&) = default; Pair& operator=(Pair&&) = default; template auto operator<=>(const Pair& p) const requires(std::three_way_comparable_with && std::three_way_comparable_with) { using TOrdering = OrderingOf p.key), decltype(value <=> p.value)>; { auto result = key <=> p.key; if (result != 0) return (TOrdering)result; } { auto result = value <=> p.value; if (result != 0) return (TOrdering)result; } return (TOrdering)std::strong_ordering::equal; } template bool operator==(const Pair& p) const requires(std::equality_comparable_with&& std::equality_comparable_with) { return key == p.key && value == p.value; } template decltype(auto) get() { return PairGet::get(*this); } template decltype(auto) get() const { return PairGet::get(*this); } }; template struct PairGet { static typename pair_internal::TypePairElementRetriver<0, Pair>::Type& get(Pair& p) { return p.key; } static typename pair_internal::TypePairElementRetriver<0, const Pair>::Type& get(const Pair& p) { return p.key; } }; template struct PairGet { static typename pair_internal::TypePairElementRetriver<1, Pair>::Type& get(Pair& p) { return p.value; } static typename pair_internal::TypePairElementRetriver<1, const Pair>::Type& get(const Pair& p) { return p.value; } }; template Pair(K&&, V&&) -> Pair::Type, typename RemoveCVRefArrayCtad::Type>; } } namespace std { template struct tuple_size> : integral_constant {}; template struct tuple_element> { using type = decltype(std::declval>().template get()); }; template struct tuple_element> { using type = decltype(std::declval>().template get()); }; } #endif /*********************************************************************** .\PRIMITIVES\DATETIME.H ***********************************************************************/ /*********************************************************************** Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ #ifndef VCZH_DATETIME #define VCZH_DATETIME namespace vl { /*********************************************************************** Date and Time ***********************************************************************/ /// A type representing the combination of date and time. struct DateTime { /// The year. vint year = 0; /// The month, from 1 to 12. vint month = 0; /// The day, from 1 to 31. vint day = 0; /// The hour, from 0 to 23. vint hour = 0; /// The minute, from 0 to 59. vint minute = 0; /// The second, from 0 to 60. vint second = 0; /// The milliseconds, from 0 to 999. vint milliseconds = 0; /// /// It is from 0 to 6, representing Sunday to Saturday. /// vint dayOfWeek = 0; /// /// The calculated total milliseconds. It is OS dependent because the start time is different. /// You should not rely on the fact about how this value is created. /// The only invariant thing is that, when an date time is earlier than another, the totalMilliseconds is lesser. /// vuint64_t osMilliseconds = 0; /// /// The OS dependent internal representation for the date and time. /// You should not rely on the fact about how this value is created. /// It only ensure that, lesser osInternal means eariler date and time. /// vuint64_t osInternal = 0; /// Get the current local time. /// The current local time. static DateTime LocalTime(); /// Get the current UTC time. /// The current UTC time. static DateTime UtcTime(); /// Create a date time value. /// The created date time value. /// The year. /// The month. /// The day. /// The hour. /// The minute. /// The second. /// The millisecond. static DateTime FromDateTime(vint _year, vint _month, vint _day, vint _hour = 0, vint _minute = 0, vint _second = 0, vint _milliseconds = 0); /// Create a date time value from a file time. /// The created date time value. /// The file time. static DateTime FromOSInternal(vuint64_t _osInternal); /// Convert the UTC time to the local time. /// The UTC time. DateTime ToLocalTime(); /// Convert the local time to the UTC time. /// The local time. DateTime ToUtcTime(); /// Move forward. /// The moved time. /// The delta in milliseconds. DateTime Forward(vuint64_t milliseconds); /// Move Backward. /// The moved time. /// The delta in milliseconds. DateTime Backward(vuint64_t milliseconds); std::strong_ordering operator<=>(const DateTime& value) const { return osInternal <=> value.osInternal; } bool operator==(const DateTime& value) const { return osInternal == value.osInternal; } }; class IDateTimeImpl : public virtual feature_injection::IFeatureImpl { public: virtual DateTime FromDateTime(vint _year, vint _month, vint _day, vint _hour, vint _minute, vint _second, vint _milliseconds) = 0; virtual DateTime FromOSInternal(vuint64_t osInternal) = 0; virtual vuint64_t LocalTime() = 0; virtual vuint64_t UtcTime() = 0; virtual vuint64_t LocalToUtcTime(vuint64_t osInternal) = 0; virtual vuint64_t UtcToLocalTime(vuint64_t osInternal) = 0; virtual vuint64_t Forward(vuint64_t osInternal, vuint64_t milliseconds) = 0; virtual vuint64_t Backward(vuint64_t osInternal, vuint64_t milliseconds) = 0; }; extern void InjectDateTimeImpl(IDateTimeImpl* impl); extern void EjectDateTimeImpl(IDateTimeImpl* impl); } #endif /*********************************************************************** .\PRIMITIVES\NULLABLE.H ***********************************************************************/ /*********************************************************************** Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ #ifndef VCZH_NULLABLE #define VCZH_NULLABLE #ifdef VCZH_CHECK_MEMORY_LEAKS_NEW #undef new #endif namespace vl { /// Type for representing nullable data. /// Type of the data, typically it is a value type, or [T:vl.Ptr`1] could be used here. template class Nullable { private: union { char buffer[sizeof(T)]; T object; }; bool initialized = false; public: static const Nullable Empty; /// Create a null value. Nullable() { } /// Create a non-null value by copying data. /// The data to copy. Nullable(const T& value) : initialized(true) { new (&object)T(value); } /// Create a non-null value by moving data. /// The data to move. Nullable(T&& value) : initialized(true) { new (&object)T(std::move(value)); } /// Create a nullable value by copying from another nullable value. /// The nullable value to copy. Nullable(const Nullable& nullable) : initialized(nullable.initialized) { if (nullable.initialized) { new (&object)T(nullable.object); } } /// Create a nullable value by moving from another nullable value. /// The nullable value to move. Nullable(Nullable&& nullable) : initialized(nullable.initialized) { if (nullable.initialized) { new (&object)T(std::move(nullable.object)); nullable.Reset(); } } ~Nullable() { Reset(); } /// /// Remove the contained data. /// void Reset() { if (initialized) { object.~T(); initialized = false; } } /// Replace the data inside this nullable value by copying from data. /// The nullable value itself. /// The data to copy. Nullable& operator=(const T& value) { if constexpr (std::is_copy_assignable_v) { if (initialized) { object = value; return *this; } } Reset(); new (&object)T(value); initialized = true; return *this; } /// Replace the data inside this nullable value by moving from data. /// The nullable value itself. /// The data to copy. Nullable& operator=(T&& value) { if constexpr (std::is_move_assignable_v) { if (initialized) { object = std::move(value); return *this; } } Reset(); new (&object)T(std::move(value)); initialized = true; return *this; } /// Replace the data inside this nullable value by copying from another nullable value. /// The nullable value itself. /// The nullable value to copy. Nullable& operator=(const Nullable& nullable) { if (!nullable.initialized) { Reset(); } else if (this != &nullable) { if constexpr (std::is_copy_assignable_v) { if (initialized) { object = nullable.object; return *this; } } Reset(); new (&object)T(nullable.object); initialized = true; } return *this; } /// Replace the data inside this nullable value by moving from another nullable value. /// The nullable value itself. /// The nullable value to move. Nullable& operator=(Nullable&& nullable) { if (!nullable.initialized) { Reset(); } else if (this != &nullable) { if constexpr (std::is_move_assignable_v) { if (initialized) { object = std::move(nullable.object); nullable.Reset(); return *this; } } Reset(); new (&object)T(std::move(nullable.object)); nullable.Reset(); initialized = true; } return *this; } /// Comparing two nullable values. /// /// Returns a value indicating the order of the two values, the type is decided by T. /// When one is null and another one is not, the non-null one is greater. /// /// The first nullable value to compare. /// The second nullable value to compare. auto operator<=>(const Nullable& b) const requires(std::three_way_comparable) { using TOrdering = decltype(object <=> b.object); if (initialized && b.initialized) return object <=> b.object; if (initialized) return (TOrdering)std::strong_ordering::greater; if (b.initialized) return (TOrdering)std::strong_ordering::less; return (TOrdering)std::strong_ordering::equal; } bool operator==(const Nullable& b)const requires(std::equality_comparable) { if (initialized && b.initialized) return object == b.object; return initialized == b.initialized; } /// Test if this nullable value is non-null. /// Returns true if it is non-null. operator bool()const { return initialized; } /// Return the data inside this nullable value /// The data inside this nullable value. It crashes when it is null. const T& Value()const { CHECK_ERROR(initialized, L"vl::Nullable::Value()#Cannot unbox from an empty nullable value."); return object; } }; template const Nullable Nullable::Empty; } #ifdef VCZH_CHECK_MEMORY_LEAKS_NEW #define new VCZH_CHECK_MEMORY_LEAKS_NEW #endif #endif /*********************************************************************** .\PRIMITIVES\POINTER.H ***********************************************************************/ /*********************************************************************** Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ #ifndef VCZH_POINTER #define VCZH_POINTER namespace vl { /*********************************************************************** ReferenceCounterOperator ***********************************************************************/ /// /// The strategy class to create and delete the reference counter of an object. /// For any object inherits from [T:vl.reflection.DescriptableObject], the reference counter is stored inside the object. /// For any other object, the reference counter is allocated separately. /// You can create your own strategy by adding a new partial specialization to this class. /// /// /// The type of the object. /// /// /// [T:vl.Ptr`1] will always use [T:vl.YesType] as the second type parameter. /// This parameter is useful when you want to do partial specialization in the SFINAE way. /// template struct ReferenceCounterOperator { /// Create the reference counter of an object. /// The pointer to the reference counter. /// The object. static __forceinline atomic_vint* CreateCounter(T* reference) { return new atomic_vint(0); } /// Delete the reference counter from an object. /// The pointer to the reference counter. /// The object. static __forceinline void DeleteReference(atomic_vint* counter, void* reference) { delete counter; delete (T*)reference; } }; /*********************************************************************** Ptr ***********************************************************************/ /// /// A shared pointer. /// It maintains a reference counter to the object. /// When no [T:vl.Ptr`1] is referencing the object, the object will be deleted automatically. /// /// /// It is safe to convert the same pointer to an object to a shared pointer once. /// If you do it multiple times, it depends on [T:vl.ReferenceCounterOperator`2]. /// For built-in strategies, only pointer to [T:vl.reflection.DescriptableObject] or its derived classes can be safely converted to a shared pointer for multiple times. /// For any other object, it will crash on the destructor of [T:vl.Ptr`1]. /// /// The type of the object. template class Ptr { template friend class Ptr; protected: typedef void(*Destructor)(atomic_vint*, void*); atomic_vint* counter = nullptr; T* reference = nullptr; void* originalReference = nullptr; Destructor originalDestructor = nullptr; void SetEmptyNoIncDec() { counter = nullptr; reference = nullptr; originalReference = nullptr; originalDestructor = nullptr; } void Inc() { if (counter) { INCRC(counter); } } void Dec(bool deleteIfZero = true) { if (counter) { if (DECRC(counter) == 0) { if (deleteIfZero) { originalDestructor(counter, originalReference); } SetEmptyNoIncDec(); } } } atomic_vint* Counter()const { return counter; } Ptr(atomic_vint* _counter, T* _reference, void* _originalReference, Destructor _originalDestructor) :counter(_counter) , reference(_reference) , originalReference(_originalReference) , originalDestructor(_originalDestructor) { Inc(); } public: /// Create a null pointer. Ptr() = default; /// Create a null pointer. Ptr(std::nullptr_t) {}; /// Convert a pointer to an object to a shared pointer. /// The pointer to the object. explicit Ptr(T* pointer) { if (pointer) { counter = ReferenceCounterOperator::CreateCounter(pointer); reference = pointer; originalReference = pointer; originalDestructor = &ReferenceCounterOperator::DeleteReference; Inc(); } } /// Copy a shared pointer. /// The shared pointer to copy. Ptr(const Ptr& pointer) :counter(pointer.counter) , reference(pointer.reference) , originalReference(pointer.originalReference) , originalDestructor(pointer.originalDestructor) { Inc(); } /// Move a shared pointer. /// The shared pointer to Move. Ptr(Ptr&& pointer) :counter(pointer.counter) , reference(pointer.reference) , originalReference(pointer.originalReference) , originalDestructor(pointer.originalDestructor) { pointer.SetEmptyNoIncDec(); } /// Cast a shared pointer implicitly by copying another shared pointer. /// The type of the object before casting. /// The shared pointer to cast. template Ptr(const Ptr& pointer) requires (std::is_convertible_v) { if (auto converted = pointer.Obj()) { counter = pointer.Counter(); reference = converted; originalReference = pointer.originalReference; originalDestructor = pointer.originalDestructor; Inc(); } } /// Cast a shared pointer implicitly by moving another shared pointer. /// The type of the object before casting. /// The shared pointer to cast. template Ptr(Ptr&& pointer) requires (std::is_convertible_v) { if (auto converted = pointer.Obj()) { counter = pointer.Counter(); reference = converted; originalReference = pointer.originalReference; originalDestructor = pointer.originalDestructor; pointer.SetEmptyNoIncDec(); } } ~Ptr() { Dec(); } /// /// Detach the contained object from this shared pointer. /// When no [T:vl.Ptr`1] is referencing to the object because of a call to Detach, the object will not be deleted. /// /// The detached object. Returns null if this shared pointer is empty. T* Detach() { auto detached = reference; Dec(false); return detached; } /// Cast a shared pointer explicitly. /// The type of the object after casting. /// The casted shared pointer. Returns null for empty shared pointer or a failed cast. template Ptr Cast()const { C* converted = dynamic_cast(reference); return Ptr((converted ? counter : 0), converted, originalReference, originalDestructor); } /// Replace by nullptr. /// The shared pointer itself. /// The nullptr. Ptr& operator=(std::nullptr_t) { Dec(); SetEmptyNoIncDec(); return *this; } /// Replace by copying another shared pointer. /// The shared pointer itself. /// The shared pointer to copy. Ptr& operator=(const Ptr& pointer) { if (this != &pointer) { Dec(); counter = pointer.counter; reference = pointer.reference; originalReference = pointer.originalReference; originalDestructor = pointer.originalDestructor; Inc(); } return *this; } /// Replace by moving another shared pointer. /// The shared pointer itself. /// The shared pointer to copy. Ptr& operator=(Ptr&& pointer) { if (this != &pointer) { Dec(); counter = pointer.counter; reference = pointer.reference; originalReference = pointer.originalReference; originalDestructor = pointer.originalDestructor; pointer.SetEmptyNoIncDec(); } return *this; } std::strong_ordering operator<=>(const T* pointer)const { return reference <=> pointer; } std::strong_ordering operator<=>(const Ptr& pointer)const { return reference <=> pointer.reference; } bool operator==(const T* pointer) const { return reference == pointer; } bool operator==(const Ptr& pointer) const { return reference == pointer.reference; } /// Test if it is an empty shared pointer. /// Returns true if it is non-null. operator bool()const { return reference != nullptr; } /// Get the pointer to the contained object. /// The pointer to the contained object. Returns null for an empty shared pointer. T* Obj()const { return reference; } /// Get the pointer to the contained object. /// The pointer to the contained object. Returns null for an empty shared pointer. T* operator->()const { return reference; } }; template Ptr(C*) -> Ptr; /*********************************************************************** ComPtr ***********************************************************************/ template class ComPtr { protected: atomic_vint* counter = nullptr; T* reference = nullptr; void SetEmpty() { counter = nullptr; reference = nullptr; } void Inc() { if (counter) { INCRC(counter); } } void Dec() { if (counter) { if (DECRC(counter) == 0) { delete counter; reference->Release(); SetEmpty(); } } } atomic_vint* Counter()const { return counter; } ComPtr(atomic_vint* _counter, T* _reference) :counter(_counter) , reference(_reference) { Inc(); } public: ComPtr() = default; ComPtr(std::nullptr_t) {} ComPtr(T* pointer) { if (pointer) { counter = new atomic_vint(1); reference = pointer; } else { SetEmpty(); } } ComPtr(const ComPtr& pointer) { counter = pointer.counter; reference = pointer.reference; Inc(); } ComPtr(ComPtr&& pointer) { counter = pointer.counter; reference = pointer.reference; pointer.SetEmpty(); } ~ComPtr() { Dec(); } ComPtr& operator=(std::nullptr_t) { Dec(); SetEmpty(); return *this; } ComPtr& operator=(T* pointer) { Dec(); if (pointer) { counter = new atomic_vint(1); reference = pointer; } else { SetEmpty(); } return *this; } ComPtr& operator=(const ComPtr& pointer) { if (this != &pointer) { Dec(); counter = pointer.counter; reference = pointer.reference; Inc(); } return *this; } ComPtr& operator=(ComPtr&& pointer) { if (this != &pointer) { Dec(); counter = pointer.counter; reference = pointer.reference; pointer.SetEmpty(); } return *this; } std::strong_ordering operator<=>(const T* pointer)const { return reference <=> pointer; } std::strong_ordering operator<=>(const ComPtr& pointer)const { return reference <=> pointer.reference; } bool operator==(const T* value) const { return operator<=>(value) == 0; } bool operator==(const ComPtr& value) const { return operator<=>(value) == 0; } operator bool()const { return reference != nullptr; } T* Obj()const { return reference; } T* operator->()const { return reference; } }; template ComPtr(C*) ->ComPtr; /*********************************************************************** Traits ***********************************************************************/ template struct KeyType> { typedef T* Type; static T* GetKeyValue(const Ptr& key) { return key.Obj(); } }; template struct KeyType> { typedef T* Type; static T* GetKeyValue(const ComPtr& key) { return key.Obj(); } }; } #endif /*********************************************************************** .\COLLECTIONS\INTERFACES.H ***********************************************************************/ /*********************************************************************** Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ #ifndef VCZH_COLLECTIONS_INTERFACES #define VCZH_COLLECTIONS_INTERFACES namespace vl { namespace collections { /*********************************************************************** Reflection ***********************************************************************/ class ICollectionReference : public virtual Interface { public: virtual void OnDisposed() = 0; }; /*********************************************************************** Interfaces ***********************************************************************/ /// An enumerator interface for receiving values without going back. /// Type of the values returned from the enumerator. template class IEnumerator : public virtual Interface { public: typedef T ElementType; /// Copy the enumerator with the current state. /// The copied enumerator. virtual IEnumerator* Clone()const=0; /// Get the reference to the current value in the enumerator. /// The reference to the current value. /// /// After calling , need to be called to make the first value available. /// Return value of will be changed after each time is called. /// If returns false, the behavior of is undefined. /// virtual const T& Current()const=0; /// Get the position of the current value in the enumerator. /// The position of the current value. /// /// After calling , need to be called to make the first value available. /// Index will be increased after each time is called with true returned. /// virtual vint Index()const=0; /// Prepare for the next value. /// Returns false if there is no more value. virtual bool Next()=0; /// Reset the enumerator. virtual void Reset()=0; /// Test if all values of this enumerator have been evaluated. /// Returns true if all values have been evaluated. /// An evaluated enumerator typically means, there will be no more calculation happens in regardless if all values have been read or not. virtual bool Evaluated()const{return false;} }; /// /// An enumerable interface representing all types that provide multiple values in order. /// range-based for-loop is available on enumerable yet. /// by applying the indexed function on the collection, a tuple of value and index is returned, structured binding could apply. /// functions work for all enumerable implementation. /// provides high-level operations for enumerables, you can create a lazy list by calling on any enumerables. /// /// xs; /// for (vint i = 0; i < 10; i++) /// xs.Add(i); /// List ys; /// /// // replace values in ys using xs, it could also be appending instead of replacing, which is controlled by the third argument /// CopyFrom(ys, xs); /// /// // print ys /// for (auto y : ys) /// Console::Write(itow(y) + L" "); /// Console::WriteLine(L""); /// /// // print ys, added by the position /// for (auto [y, i] : indexed(ys)) /// Console::Write(itow(y + i) + L" "); /// Console::WriteLine(L""); /// /// // print all odd numbers in ys /// for (auto y : From(ys).Where([](int a){return a % 2 == 1;})) /// Console::Write(itow(y) + L" "); /// Console::WriteLine(L""); /// } /// ]]> /// Type of the values in the enumerable. template class IEnumerable : public virtual Interface { public: typedef T ElementType; /// /// Create an enumerator. [M:vl.collections.IEnumerator`1.Next] should be called before reading the first value. /// /// /// In most of the cases, you do not need to call this function. /// "for (auto x : xs);", "for (auto [x, i] : indexed(xs));", and do all the jobs for you. /// /// The enumerator. virtual IEnumerator* CreateEnumerator() const = 0; /// Get the underlying collection object. /// The underlying collection object, could be nullptr. virtual const Object* GetCollectionObject() const = 0; /// Get the associated collection reference. /// The associated collection reference. virtual Ptr GetCollectionReference() const = 0; /// /// Associate a collection reference to this collection. /// It will crash if one has been associated. /// will be called when this collection is no longer available. /// /// The associated collection reference. virtual void SetCollectionReference(Ptr ref) const = 0; /// /// Get the strong-typed associated collection reference. /// It returns nullptr when none has been associated. /// It throws when one has been associated but the type is unexpected. /// /// The expected type of the associated collection reference. /// The strong-typed associated collection reference. template Ptr TryGetCollectionReference() { auto ref = GetCollectionReference(); if (!ref) return nullptr; auto sref = ref.template Cast(); CHECK_ERROR(sref, L"IEnumerable::TryGetCollectionReference()#The associated collection reference has an unexpected type."); return sref; } }; template class EnumerableBase : public Object, public virtual IEnumerable { private: mutable Ptr colref; public: ~EnumerableBase() { if (colref) colref->OnDisposed(); } const Object* GetCollectionObject() const override { return this; } Ptr GetCollectionReference() const override { return colref; } void SetCollectionReference(Ptr ref) const override { CHECK_ERROR(!colref, L"EnumerableBase::SetCollectionReference(Ptr)#Cannot associate another collection reference to this collection."); colref = ref; } }; /*********************************************************************** Random Access ***********************************************************************/ namespace randomaccess_internal { template struct RandomAccessable { static const bool CanRead = false; static const bool CanResize = false; }; template struct RandomAccess { static vint GetCount(const T& t) { return t.Count(); } static const typename T::ElementType& GetValue(const T& t, vint index) { return t.Get(index); } static void SetCount(T& t, vint count) { t.Resize(count); } static void SetValue(T& t, vint index, const typename T::ElementType& value) { t.Set(index, value); } template static void AppendValue(T& t, V&& value) { t.Add(value); } }; } } } #endif /*********************************************************************** .\COLLECTIONS\LIST.H ***********************************************************************/ /*********************************************************************** Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ #ifndef VCZH_COLLECTIONS_LIST #define VCZH_COLLECTIONS_LIST #include #ifdef VCZH_CHECK_MEMORY_LEAKS_NEW #undef new #endif namespace vl { namespace collections { /*********************************************************************** Memory Management ***********************************************************************/ namespace memory_management { template void CallDefaultCtors(T* items, vint count) { if constexpr (!std::is_trivially_constructible_v) { for (vint i = 0; i < count; i++) { new(&items[i])T(); } } } template void CallCopyCtors(T* items, const T* source, vint count) { if constexpr (!std::is_trivially_copy_constructible_v) { for (vint i = 0; i < count; i++) { new(&items[i])T(source[i]); } } else { memcpy(items, source, sizeof(T) * count); } } template void CallMoveCtors(T* items, T* source, vint count) { if constexpr (!std::is_trivially_move_constructible_v) { for (vint i = 0; i < count; i++) { new(&items[i])T(std::move(source[i])); } } else { memcpy(items, source, sizeof(T) * count); } } template void CallMoveAssignmentsOverlapped(T* items, T* source, vint count) { if constexpr (!std::is_trivially_move_assignable_v) { if (items < source) { for (vint i = 0; i < count; i++) { items[i] = std::move(source[i]); } } else if (items > source) { for (vint i = count - 1; i >= 0; i--) { items[i] = std::move(source[i]); } } } else { memmove(items, source, sizeof(T) * count); } } template void CallDtors(T* items, vint count) { if constexpr (!std::is_trivially_destructible_v) { for (vint i = 0; i < count; i++) { items[i].~T(); } } } template T* AllocateBuffer(vint size) { if (size <= 0) return nullptr; return (T*)malloc(sizeof(T) * size); } template void DeallocateBuffer(T* buffer) { if (buffer == nullptr) return; free(buffer); } template void ReleaseUnnecessaryBuffer(T*& items, vint& capacity, vint oldCount, vint newCount) { if (!items) return; if(newCount < oldCount) { CallDtors(&items[newCount], oldCount - newCount); } if (newCount <= capacity / 2 && newCount <= 8) { vint newCapacity = capacity * 5 / 8; if (newCount < newCapacity) { T* newBuffer = AllocateBuffer(newCapacity); CallMoveCtors(newBuffer, items, newCount); CallDtors(items, newCount); DeallocateBuffer(items); capacity = newCapacity; items = newBuffer; } } } template void InsertUninitializedItems(T*& items, vint& capacity, vint& count, vint index, vint insertCount) { vint newCount = count + insertCount; if (newCount > capacity) { vint newCapacity = newCount < capacity ? capacity : (newCount * 5 / 4 + 1); T* newBuffer = AllocateBuffer(newCapacity); CallMoveCtors(newBuffer, items, index); CallMoveCtors(&newBuffer[index + insertCount], &items[index], count - index); CallDtors(items, count); DeallocateBuffer(items); capacity = newCapacity; items = newBuffer; } else if (index < count) { if (insertCount >= (count - index)) { CallMoveCtors(&items[index + insertCount], &items[index], count - index); CallDtors(&items[index], count - index); } else { CallMoveCtors(&items[count], &items[count - insertCount], insertCount); CallMoveAssignmentsOverlapped(&items[index + insertCount], &items[index], count - index - insertCount); CallDtors(&items[index], insertCount); } } count = newCount; } template, std::remove_cvref_t>> struct Accept_; template struct Accept_ { using TAccept = TArg; using TForward = TArg; }; template struct Accept_ { using TAccept = TItem; using TForward = TItem&&; }; template using AcceptType = typename Accept_::TAccept; template using ForwardType = typename Accept_::TForward; template AcceptType RefOrConvert(TArg&& arg) { return std::forward(arg); } } /*********************************************************************** ArrayBase ***********************************************************************/ /// Base type of all linear container. /// Type of elements. template class ArrayBase abstract : public EnumerableBase { protected: class Enumerator : public Object, public virtual IEnumerator { private: const ArrayBase* container; vint index; public: Enumerator(const ArrayBase* _container, vint _index = -1) { container = _container; index = _index; } IEnumerator* Clone()const override { return new Enumerator(container, index); } const T& Current()const override { return container->Get(index); } vint Index()const override { return index; } bool Next() override { index++; return index >= 0 && index < container->Count(); } void Reset() override { index = -1; } bool Evaluated()const override { return true; } }; T* buffer = nullptr; vint count = 0; ArrayBase() = default; public: IEnumerator* CreateEnumerator()const { return new Enumerator(this); } /// Get the number of elements in the container. /// The number of elements. vint Count()const { return count; } /// Get the reference to the specified element. /// The reference to the specified element. It will crash when the index is out of range. /// The index of the element. const T& Get(vint index)const { CHECK_ERROR(index >= 0 && index < this->count, L"ArrayBase::Get(vint)#Argument index not in range."); return this->buffer[index]; } /// Get the reference to the specified element. /// The reference to the specified element. It will crash when the index is out of range. /// The index of the element. const T& operator[](vint index)const { CHECK_ERROR(index >= 0 && index < this->count, L"ArrayBase::operator[](vint)#Argument index not in range."); return this->buffer[index]; } }; /*********************************************************************** Array ***********************************************************************/ /// Array: linear container with fixed size in runtime. All elements are contiguous in memory. /// Type of elements. template class Array : public ArrayBase { using K = typename KeyType::Type; public: /// Create an array. /// The size of the array. /// /// The default value is zero. can be called to determine the size later. /// It will crash when the size is a negative number. /// Array(vint size = 0) { CHECK_ERROR(size >= 0, L"Array::Array(vint)#Size should not be negative."); this->buffer = memory_management::AllocateBuffer(size); memory_management::CallDefaultCtors(this->buffer, size); this->count = size; } /// Create an array with elements provided. /// Pointer to values to copy. /// The number of values to copy. /// It will crash when the size is a negative number. Array(const T* _buffer, vint size) { CHECK_ERROR(size >= 0, L"Array::Array(const T*, vint)#Size should not be negative."); this->buffer = memory_management::AllocateBuffer(size); memory_management::CallCopyCtors(this->buffer, _buffer, size); this->count = size; } ~Array() { if (this->buffer) { memory_management::CallDtors(this->buffer, this->count); memory_management::DeallocateBuffer(this->buffer); } } Array(const Array&) = delete; Array(Array&& _move) { this->buffer = _move.buffer; this->count = _move.count; _move.buffer = nullptr; _move.count = 0; } Array& operator=(const Array&) = delete; Array& operator=(Array&& _move) { if (this->buffer) { memory_management::CallDtors(this->buffer, this->count); memory_management::DeallocateBuffer(this->buffer); } this->buffer = _move.buffer; this->count = _move.count; _move.buffer = nullptr; _move.count = 0; return *this; } /// Test does the array contain a value or not. /// Returns true if the array contains the specified value. /// The value to test. bool Contains(const K& item)const { return IndexOf(item) != -1; } /// Get the position of a value in this array. /// Returns the position of first element that equals to the specified value. Returns -1 if failed to find. /// The value to find. vint IndexOf(const K& item)const { for (vint i = 0; i < this->count; i++) { if (this->buffer[i] == item) { return i; } } return -1; } /// Replace an element in the specified position. /// Returns true. It will crash when the index is out of range /// The position of the element to replace. /// The new value to replace. bool Set(vint index, const T& item) { CHECK_ERROR(index >= 0 && index < this->count, L"Array::Set(vint)#Argument index not in range."); this->buffer[index] = item; return true; } /// Replace an element in the specified position. /// Returns true. It will crash when the index is out of range /// The position of the element to replace. /// The new value to replace. bool Set(vint index, T&& item) { CHECK_ERROR(index >= 0 && index < this->count, L"Array::Set(vint)#Argument index not in range."); this->buffer[index] = std::move(item); return true; } using ArrayBase::operator[]; /// Get the reference to the specified element. /// The reference to the specified element. It will crash when the index is out of range. /// The index of the element. T& operator[](vint index) { CHECK_ERROR(index >= 0 && index < this->count, L"Array::operator[](vint)#Argument index not in range."); return this->buffer[index]; } /// Change the size of the array. This function can be called multiple times to change the size. /// The new size of the array. /// It will crash when the size is a negative number. void Resize(vint size) { CHECK_ERROR(size >= 0, L"Array::Resize(vint)#Size should not be negative."); T* newBuffer = memory_management::AllocateBuffer(size); if (size < this->count) { memory_management::CallMoveCtors(newBuffer, this->buffer, size); } else { memory_management::CallMoveCtors(newBuffer, this->buffer, this->count); memory_management::CallDefaultCtors(&newBuffer[this->count], size - this->count); } memory_management::CallDtors(this->buffer, this->count); memory_management::DeallocateBuffer(this->buffer); this->buffer = newBuffer; this->count = size; } }; /*********************************************************************** ListBase ***********************************************************************/ /// Base type for all list containers. /// Type of elements. template class ListBase abstract : public ArrayBase { using K = typename KeyType::Type; protected: vint capacity = 0; public: ListBase() = default; ~ListBase() { if (this->buffer) { memory_management::CallDtors(this->buffer, this->count); memory_management::DeallocateBuffer(this->buffer); } } ListBase(const ListBase&) = delete; ListBase(ListBase&& _move) { this->buffer = _move.buffer; this->count = _move.count; this->capacity = _move.capacity; _move.buffer = nullptr; _move.count = 0; _move.capacity = 0; } ListBase& operator=(const ListBase&) = delete; ListBase& operator=(ListBase&& _move) { if (this->buffer) { memory_management::CallDtors(this->buffer, this->count); memory_management::DeallocateBuffer(this->buffer); } this->buffer = _move.buffer; this->count = _move.count; this->capacity = _move.capacity; _move.buffer = nullptr; _move.count = 0; _move.capacity = 0; return *this; } /// Remove an element at a specified position. /// Returns true if the element is removed. It will crash when the index is out of range. /// The index of the element to remove. bool RemoveAt(vint index) { vint previousCount = this->count; CHECK_ERROR(index >= 0 && index < this->count, L"ListBase::RemoveAt(vint)#Argument index not in range."); memory_management::CallMoveAssignmentsOverlapped(&this->buffer[index], &this->buffer[index + 1], this->count - index - 1); this->count--; memory_management::ReleaseUnnecessaryBuffer(this->buffer, this->capacity, previousCount, this->count); return true; } /// Remove contiguous elements at a specified psition. /// Returns true if elements are removed. It will crash when the index or the size is out of range. /// The index of the first element to remove. /// The number of elements to remove. bool RemoveRange(vint index, vint _count) { vint previousCount = this->count; CHECK_ERROR(index >= 0 && index <= this->count, L"ListBase::RemoveRange(vint, vint)#Argument index not in range."); CHECK_ERROR(index + _count >= 0 && index + _count <= this->count, L"ListBase::RemoveRange(vint, vint)#Argument _count not in range."); memory_management::CallMoveAssignmentsOverlapped(&this->buffer[index], &this->buffer[index + _count], this->count - index - _count); this->count -= _count; memory_management::ReleaseUnnecessaryBuffer(this->buffer, this->capacity, previousCount, this->count); return true; } /// Remove all elements. /// Returns true if all elements are removed. bool Clear() { vint previousCount = this->count; this->count = 0; this->capacity = 0; memory_management::CallDtors(this->buffer, previousCount); memory_management::DeallocateBuffer(this->buffer); this->buffer = nullptr; return true; } }; /*********************************************************************** List ***********************************************************************/ /// List: linear container with dynamic size in runtime for unordered values. All elements are contiguous in memory. /// Type of elements. template class List : public ListBase { using K = typename KeyType::Type; public: /// Create an empty list. List() = default; List(List&& container) : ListBase(std::move(container)) {} List& operator=(List&& _move) = default; /// Test does the list contain a value or not. /// Returns true if the list contains the specified value. /// The value to test. bool Contains(const K& item)const { return IndexOf(item) != -1; } /// Get the position of a value in this list. /// Returns the position of first element that equals to the specified value. Returns -1 if failed to find. /// The value to find. vint IndexOf(const K& item)const { for (vint i = 0; i < this->count; i++) { if (this->buffer[i] == item) { return i; } } return -1; } /// Append a value at the end of the list. /// The index of the added item. /// The value to add. vint Add(const T& item) { return Insert(this->count, item); } /// Append a value at the end of the list. /// The index of the added item. /// The value to add. vint Add(T&& item) { return Insert(this->count, std::move(item)); } /// Insert a value at the specified position. /// The index of the added item. It will crash if the index is out of range /// The position to insert the value. /// The value to add. vint Insert(vint index, const T& item) { CHECK_ERROR(index >= 0 && index <= this->count, L"List::Insert(vint, const T&)#Argument index not in range."); memory_management::InsertUninitializedItems(this->buffer, this->capacity, this->count, index, 1); memory_management::CallCopyCtors(&this->buffer[index], &item, 1); return index; } /// Insert a value at the specified position. /// The index of the added item. It will crash if the index is out of range /// The position to insert the value. /// The value to add. vint Insert(vint index, T&& item) { CHECK_ERROR(index >= 0 && index <= this->count, L"List::Insert(vint, const T&)#Argument index not in range."); memory_management::InsertUninitializedItems(this->buffer, this->capacity, this->count, index, 1); memory_management::CallMoveCtors(&this->buffer[index], &item, 1); return index; } /// Remove an element from the list. If multiple elements equal to the specified value, only the first one will be removed /// Returns true if the element is removed. /// The item to remove. bool Remove(const K& item) { vint index = IndexOf(item); if (index >= 0 && index < this->count) { this->RemoveAt(index); return true; } else { return false; } } /// Replace an element in the specified position. /// Returns true. It will crash when the index is out of range /// The position of the element to replace. /// The new value to replace. bool Set(vint index, const T& item) { CHECK_ERROR(index >= 0 && index < this->count, L"Array::Set(vint)#Argument index not in range."); this->buffer[index] = item; return true; } /// Replace an element in the specified position. /// Returns true. It will crash when the index is out of range /// The position of the element to replace. /// The new value to replace. bool Set(vint index, T&& item) { CHECK_ERROR(index >= 0 && index < this->count, L"Array::Set(vint)#Argument index not in range."); this->buffer[index] = std::move(item); return true; } using ListBase::operator[]; /// Get the reference to the specified element. /// The reference to the specified element. It will crash when the index is out of range. /// The index of the element. T& operator[](vint index) { CHECK_ERROR(index >= 0 && index < this->count, L"List::operator[](vint)#Argument index not in range."); return this->buffer[index]; } }; /*********************************************************************** SortedList ***********************************************************************/ /// Get the position of an element in an array by performing binary search. /// Type of elements in the array. /// Type of the element to find. /// The comparison function. /// Returns the position. Returns -1 if it does not exist. /// The array to find in. /// The number of elements in the array. /// The element to find. /// /// If the element exist, this argument returns one of the element that equals to the specified value. /// If the element doesn not exist, /// this argument returns either the greatest element that less than the specified value, /// or the least element that greater than the specified value. /// /// The comparar for two elements returning std::(strong|weak)_ordering. template vint BinarySearchLambda(const T* buffer, vint count, const K& item, vint& index, F&& orderer) { vint start = 0; vint end = count - 1; index = -1; while (start <= end) { index = start + (end - start) / 2; auto ordering = orderer(buffer[index], item); if constexpr (!std::is_same_v) { // VS2022 seems not happy with // requires(!std::is_same_v()(std::declval(), std::declval())), std::partial_ordering>) CHECK_ERROR(ordering != std::partial_ordering::unordered, L"vl::collections::BinarySearchLambda(const T*, vint, const K&, vint&, F&&)#This function could not apply on elements in partial ordering."); } if (ordering < 0) { start = index + 1; } else if (ordering > 0) { end = index - 1; } else { return index; } } return -1; } /// Get the position of an element in an array by performing binary search. /// Type of elements in the array. /// Returns the position. Returns -1 if it does not exist. /// The array to find in. /// The number of elements in the array. /// The element to find. /// /// If the element exist, this argument returns one of the element that equals to the specified value. /// If the element doesn not exist, /// this argument returns either the greatest element that less than the specified value, /// or the least element that greater than the specified value. /// template vint BinarySearchLambda(const T* buffer, vint count, const T& item, vint& index) { return BinarySearchLambda(buffer, count, item, index, [](const T& a, const T& b) { return a <=> b; }); } /// SortedList: linear container with dynamic size in runtime for ordered values. All elements are kept in order, and are contiguous in memory. /// Type of elements. template class SortedList : public ListBase { using K = typename KeyType::Type; protected: template vint IndexOfInternal(const Key& item, vint& index)const { return BinarySearchLambda(this->buffer, this->count, item, index, [](const T& a, const Key& b) { return a <=> b; }); } vint Insert(vint index, const T& item) { bool uninitialized = false; memory_management::InsertUninitializedItems(this->buffer, this->capacity, this->count, index, 1); memory_management::CallCopyCtors(&this->buffer[index], &item, 1); return index; } vint Insert(vint index, T&& item) { bool uninitialized = false; memory_management::InsertUninitializedItems(this->buffer, this->capacity, this->count, index, 1); memory_management::CallMoveCtors(&this->buffer[index], &item, 1); return index; } template vint AddInternal(TItem&& item) { if (ArrayBase::count == 0) { return Insert(0, std::forward(item)); } else { vint outputIndex = -1; if constexpr (std::is_same_v, std::remove_cvref_t>) { IndexOfInternal(item, outputIndex); } else if constexpr (std::is_same_v, std::remove_cvref_t>) { IndexOfInternal(item, outputIndex); } else { IndexOfInternal(KeyType::GetKeyValue(item), outputIndex); } CHECK_ERROR(outputIndex >= 0 && outputIndex < this->count, L"SortedList::Add(const T&)#Internal error, index not in range."); if (this->buffer[outputIndex] < item) { outputIndex++; } return Insert(outputIndex, std::forward(item)); } } public: /// Create an empty list. SortedList() = default; SortedList(SortedList&& container) : ListBase(std::move(container)) {} SortedList& operator=(SortedList && _move) = default; SortedList(const SortedList&xs) : ListBase(std::move(const_cast&>(static_cast&>(xs)))) { } /// Test does the list contain a value or not. /// Returns true if the list contains the specified value. /// The value to test. bool Contains(const K& item)const { return IndexOf(item) != -1; } /// Get the position of a value in this list. /// Returns the position of first element that equals to the specified value. Returns -1 if failed to find. /// The value to find. vint IndexOf(const K& item)const { vint outputIndex = -1; return IndexOfInternal(item, outputIndex); } /// Add a value at the correct position, all elements will be kept in order. /// The index of the added item. /// The value to add. vint Add(const T& item) { return AddInternal(item); } /// Add a value at the correct position, all elements will be kept in order. /// The index of the added item. /// The value to add. vint Add(T&& item) { return AddInternal(std::move(item)); } /// Remove an element from the list. If multiple elements equal to the specified value, only the first one will be removed /// Returns true if the element is removed. /// The item to remove. bool Remove(const K& item) { vint index = IndexOf(item); if (index >= 0 && index < ArrayBase::count) { this->RemoveAt(index); return true; } else { return false; } } }; /*********************************************************************** Special Containers ***********************************************************************/ template class PushOnlyAllocator : public Object { protected: vint blockSize; vint allocatedSize; List blocks; public: NOT_COPYABLE(PushOnlyAllocator); PushOnlyAllocator(vint _blockSize = 65536) :blockSize(_blockSize) , allocatedSize(0) { } ~PushOnlyAllocator() { for (vint i = 0; i < blocks.Count(); i++) { delete[] blocks[i]; } } T* Get(vint index) { if (index >= allocatedSize) { return 0; } vint row = index / blockSize; vint column = index % blockSize; return &blocks[row][column]; } T* Create() { if (allocatedSize == blocks.Count()*blockSize) { blocks.Add(new T[blockSize]); } vint index = allocatedSize++; return Get(index); } }; namespace bom_helper { struct TreeNode { TreeNode* nodes[4]; }; template struct Accessor { static __forceinline void* Get(TreeNode* root, vuint8_t index) { if (!root) { return nullptr; } vint fragmentIndex = (index >> (2 * (Index - 1))) % 4; TreeNode* fragmentRoot = root->nodes[fragmentIndex]; return fragmentRoot ? Accessor::Get(fragmentRoot, index) : 0; } static __forceinline void Set(TreeNode*& root, vuint8_t index, void* value, PushOnlyAllocator& allocator) { if (!root) { root = allocator.Create(); memset(root->nodes, 0, sizeof(root->nodes)); } vint fragmentIndex = (index >> (2 * (Index - 1))) % 4; TreeNode*& fragmentRoot = root->nodes[fragmentIndex]; Accessor::Set(fragmentRoot, index, value, allocator); } }; template<> struct Accessor<0> { static __forceinline void* Get(TreeNode* root, vuint8_t index) { return (void*)root; } static __forceinline void Set(TreeNode*& root, vuint8_t index, void* value, PushOnlyAllocator& allocator) { ((void*&)root) = value; } }; } template class ByteObjectMap : public Object { public: typedef PushOnlyAllocator Allocator; protected: bom_helper::TreeNode* root = nullptr; public: NOT_COPYABLE(ByteObjectMap); ByteObjectMap() = default; ~ByteObjectMap() = default; T* Get(vuint8_t index) { return (T*)bom_helper::Accessor<>::Get(root, index); } void Set(vuint8_t index, T* value, Allocator& allocator) { bom_helper::Accessor<>::Set(root, index, value, allocator); } }; /*********************************************************************** Random Access ***********************************************************************/ namespace randomaccess_internal { template struct RandomAccessable> { static const bool CanRead = true; static const bool CanResize = true; }; template struct RandomAccessable> { static const bool CanRead = true; static const bool CanResize = false; }; template struct RandomAccessable> { static const bool CanRead = true; static const bool CanResize = false; }; } } } #ifdef VCZH_CHECK_MEMORY_LEAKS_NEW #define new VCZH_CHECK_MEMORY_LEAKS_NEW #endif #endif /*********************************************************************** .\COLLECTIONS\DICTIONARY.H ***********************************************************************/ /*********************************************************************** Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ #ifndef VCZH_COLLECTIONS_DICTIONARY #define VCZH_COLLECTIONS_DICTIONARY namespace vl { namespace collections { /// Dictionary: one to one map container. /// Type of keys. /// Type of values. template class Dictionary : public EnumerableBase> { using KK = typename KeyType::Type; using VK = typename KeyType::Type; using KVPair = Pair; public: typedef SortedList KeyContainer; typedef List ValueContainer; protected: class Enumerator : public Object, public virtual IEnumerator { private: const Dictionary* container; vint index; Nullable current; void UpdateCurrent() { if(indexCount()) { current = { container->Keys().Get(index),container->Values().Get(index) }; } } public: Enumerator(const Dictionary* _container, vint _index=-1) { container=_container; index=_index; } IEnumerator* Clone()const override { return new Enumerator(container, index); } const KVPair& Current()const override { return current.Value(); } vint Index()const override { return index; } bool Next() override { index++; UpdateCurrent(); return index>=0 && indexCount(); } void Reset() override { index=-1; UpdateCurrent(); } bool Evaluated()const override { return true; } }; KeyContainer keys; ValueContainer values; template bool SetInternal(TKeyItem&& key, TValueItem&& value) { using TKeyAccept = memory_management::AcceptType; using TKeyForward = memory_management::ForwardType; TKeyAccept keyAccept = memory_management::RefOrConvert(std::forward(key)); vint index = keys.IndexOf(KeyType::GetKeyValue(keyAccept)); if (index == -1) { index = keys.Add(std::forward(keyAccept)); values.Insert(index, std::forward(value)); } else { values[index] = std::forward(value); } return true; } template bool AddInternal(TKeyItem&& key, TValueItem&& value) { using TKeyAccept = memory_management::AcceptType; using TKeyForward = memory_management::ForwardType; TKeyAccept keyAccept = memory_management::RefOrConvert(std::forward(key)); CHECK_ERROR(!keys.Contains(KeyType::GetKeyValue(keyAccept)), L"Dictionary::Add(const KT&, const VT&)#Key already exists."); vint index = keys.Add(std::forward(keyAccept)); values.Insert(index, std::forward(value)); return true; } public: /// Create an empty dictionary. Dictionary() = default; ~Dictionary() = default; Dictionary(const Dictionary&) = delete; Dictionary(Dictionary&& _move) : keys(std::move(_move.keys)) , values(std::move(_move.values)) { } Dictionary& operator=(const Dictionary&) = delete; Dictionary& operator=(Dictionary && _move) { keys = std::move(_move.keys); values = std::move(_move.values); return* this; } IEnumerator* CreateEnumerator()const { return new Enumerator(this); } /// Get all keys. /// All keys. const KeyContainer& Keys()const { return keys; } /// Get all values. /// All values. const ValueContainer& Values()const { return values; } /// Get the number of keys. /// The number of keys. It is also the number of values. vint Count()const { return keys.Count(); } /// Get the reference to the value associated to a specified key. /// The reference to the value. It will crash if the key does not exist. /// The key to find. const VT& Get(const KK& key)const { return values.Get(keys.IndexOf(key)); } /// Get the reference to the value associated to a specified key. /// The reference to the value. It will crash if the key does not exist. /// The key to find. const VT& operator[](const KK& key)const { return values.Get(keys.IndexOf(key)); } /// Replace the value associated to a specified key. /// Returns true if the value is replaced. /// The key to find. If the key does not exist, it will be added to the dictionary. /// The associated value to replace. bool Set(const KT& key, const VT& value) { return SetInternal(key, value); } /// Replace the value associated to a specified key. /// Returns true if the value is replaced. /// The key to find. If the key does not exist, it will be added to the dictionary. /// The associated value to replace. bool Set(const KT& key, VT&& value) { return SetInternal(key, std::move(value)); } /// Replace the value associated to a specified key. /// Returns true if the value is replaced. /// The key to find. If the key does not exist, it will be added to the dictionary. /// The associated value to replace. bool Set(KT&& key, const VT& value) { return SetInternal(std::move(key), value); } /// Replace the value associated to a specified key. /// Returns true if the value is replaced. /// The key to find. If the key does not exist, it will be added to the dictionary. /// The associated value to replace. bool Set(KT&& key, VT&& value) { return SetInternal(std::move(key), std::move(value)); } /// Add a key with an associated value. /// Returns true if the pair is added. If will crash if the key exists. /// The pair of key and value. bool Add(const Pair& value) { return AddInternal(value.key, value.value); } /// Add a key with an associated value. /// Returns true if the pair is added. If will crash if the key exists. /// The pair of key and value. bool Add(Pair&& value) { return AddInternal(std::move(value.key), std::move(value.value)); } /// Add a key with an associated value. /// Returns true if the pair is added. If will crash if the key exists. /// The key to add. /// The value to add. bool Add(const KT& key, const VT& value) { return AddInternal(key, value); } /// Add a key with an associated value. /// Returns true if the pair is added. If will crash if the key exists. /// The key to add. /// The value to add. bool Add(const KT& key, VT&& value) { return AddInternal(key, std::move(value)); } /// Add a key with an associated value. /// Returns true if the pair is added. If will crash if the key exists. /// The key to add. /// The value to add. bool Add(KT&& key, const VT& value) { return AddInternal(std::move(key), value); } /// Add a key with an associated value. /// Returns true if the pair is added. If will crash if the key exists. /// The key to add. /// The value to add. bool Add(KT&& key, VT&& value) { return AddInternal(std::move(key), std::move(value)); } /// Remove a key with the associated value. /// Returns true if the key and the value is removed. /// The key to find. bool Remove(const KK& key) { vint index=keys.IndexOf(key); if(index!=-1) { keys.RemoveAt(index); values.RemoveAt(index); return true; } else { return false; } } /// Remove all elements. /// Returns true if all elements are removed. bool Clear() { keys.Clear(); values.Clear(); return true; } }; /// Group: one to many map container. /// Type of keys. /// Type of values. template class Group : public EnumerableBase> { using KK = typename KeyType::Type; using VK = typename KeyType::Type; using KVPair = Pair; public: typedef SortedList KeyContainer; typedef List ValueContainer; protected: class Enumerator : public Object, public virtual IEnumerator { private: const Group* container; vint keyIndex; vint valueIndex; Nullable current; void UpdateCurrent() { if (keyIndex < container->Count()) { const ValueContainer& values = container->GetByIndex(keyIndex); if (valueIndex < values.Count()) { current = { container->Keys().Get(keyIndex) ,values.Get(valueIndex) }; } } } public: Enumerator(const Group* _container, vint _keyIndex=-1, vint _valueIndex=-1) { container=_container; keyIndex=_keyIndex; valueIndex=_valueIndex; } IEnumerator* Clone()const override { return new Enumerator(container, keyIndex, valueIndex); } const KVPair& Current()const override { return current.Value(); } vint Index()const override { if(0<=keyIndex && keyIndexCount()) { vint index=0; for(vint i=0;iGetByIndex(i).Count(); } return index+valueIndex; } else { return -1; } } bool Next() override { if(keyIndex==-1) { keyIndex=0; } while(keyIndexCount()) { valueIndex++; const ValueContainer& values=container->GetByIndex(keyIndex); if(valueIndex values; template bool AddInternal(TKeyItem&& key, TValueItem&& value) { using TKeyAccept = memory_management::AcceptType; using TKeyForward = memory_management::ForwardType; TKeyAccept keyAccept = memory_management::RefOrConvert(std::forward(key)); ValueContainer* target = nullptr; vint index = keys.IndexOf(KeyType::GetKeyValue(keyAccept)); if (index == -1) { target = new ValueContainer; values.Insert(keys.Add(std::forward(keyAccept)), target); } else { target = values[index]; } target->Add(std::forward(value)); return true; } public: /// Create an empty group. Group() = default; ~Group() { Clear(); } Group(const Group&) = delete; Group(Group&& _move) : keys(std::move(_move.keys)) , values(std::move(_move.values)) { } Group& operator=(const Group&) = delete; Group& operator=(Group && _move) { Clear(); keys = std::move(_move.keys); values = std::move(_move.values); return*this; } IEnumerator* CreateEnumerator()const { return new Enumerator(this); } /// Get all keys. /// All keys. const KeyContainer& Keys()const { return keys; } /// Get the number of keys. /// The number of keys. vint Count()const { return keys.Count(); } /// Get all values associated to a specified key. /// All associated values. It will crash if the key does not exist. /// The key to find. const ValueContainer& Get(const KK& key)const { return *values.Get(keys.IndexOf(key)); } /// Get all values associated to a key at a specified index in . /// All associaged values. It will crash if the index is out of range. /// The position of the key. const ValueContainer& GetByIndex(vint index)const { return *values.Get(index); } /// Get all values associated to a specified key. /// All associated values. It will crash if the key does not exist. /// The key to find. const ValueContainer& operator[](const KK& key)const { return *values.Get(keys.IndexOf(key)); } /// Test if there is any value associated to a specified key or not. /// Returns true there is at least one value associated to this key. /// The key to find. bool Contains(const KK& key)const { return keys.Contains(key); } /// Test if a value is associated to a specified key or not. /// Returns true if the specified value is associated to the specified key. /// The key to find. /// The value to find. bool Contains(const KK& key, const VK& value)const { vint index=keys.IndexOf(key); if(index!=-1) { return values.Get(index)->Contains(value); } else { return false; } } /// /// Add a key with an associated value. /// If the key already exists, the value will be associated to the key with other values. /// If this value has already been associated to the key, it will still be duplicated. /// /// Returns true if the pair is added. /// The pair of key and value to add. bool Add(const Pair& value) { return AddInternal(value.key, value.value); } /// /// Add a key with an associated value. /// If the key already exists, the value will be associated to the key with other values. /// If this value has already been associated to the key, it will still be duplicated. /// /// Returns true if the pair is added. /// The pair of key and value to add. bool Add(Pair&& value) { return AddInternal(std::move(value.key), std::move(value.value)); } /// /// Add a key with an associated value. /// If the key already exists, the value will be associated to the key with other values. /// If this value has already been associated to the key, it will still be duplicated. /// /// Returns true if the key and the value are added. /// The key to add. /// The value to add. bool Add(const KT& key, const VT& value) { return AddInternal(key, value); } /// /// Add a key with an associated value. /// If the key already exists, the value will be associated to the key with other values. /// If this value has already been associated to the key, it will still be duplicated. /// /// Returns true if the key and the value are added. /// The key to add. /// The value to add. bool Add(const KT& key, VT&& value) { return AddInternal(key, std::move(value)); } /// /// Add a key with an associated value. /// If the key already exists, the value will be associated to the key with other values. /// If this value has already been associated to the key, it will still be duplicated. /// /// Returns true if the key and the value are added. /// The key to add. /// The value to add. bool Add(KT&& key, const VT& value) { return AddInternal(std::move(key), value); } /// /// Add a key with an associated value. /// If the key already exists, the value will be associated to the key with other values. /// If this value has already been associated to the key, it will still be duplicated. /// /// Returns true if the key and the value are added. /// The key to add. /// The value to add. bool Add(KT&& key, VT&& value) { return AddInternal(std::move(key), std::move(value)); } /// Remove a key with all associated values. /// Returns true if the key and all associated values are removed. /// The key to find. bool Remove(const KK& key) { vint index=keys.IndexOf(key); if(index!=-1) { keys.RemoveAt(index); auto target=values[index]; values.RemoveAt(index); delete target; return true; } else { return false; } } /// /// Remove a value associated to a specified key. /// If this value is associated to the key for multiple times, only the first one will be removed. /// /// /// Returns true if the value is removed. /// /// The key to find. /// The value to remove. bool Remove(const KK& key, const VK& value) { vint index=keys.IndexOf(key); if(index!=-1) { auto target=values[index]; target->Remove(value); if(target->Count()==0) { keys.RemoveAt(index); values.RemoveAt(index); delete target; } return true; } else { return false; } } /// Remove everything. /// Returns true if all keys and values are removed. bool Clear() { for(vint i=0;iPerform inner join on two groups. /// The type of keys in two groups. /// The type of values in the first group. /// The type of values in the second group. /// The type of the first callback. /// The type of the second callback. /// The type of the third callback. /// The first group. /// The second group. /// /// Callback that is called when a value in the first group is discarded. /// This happens for values associated to a key in the first group, that no value is assocated to the same key in the second group. /// The first argument is the key, the second argument is the discarded value list in the first group. /// /// /// Callback that is called when a value in the second group is discarded. /// This happens for values associated to a key in the second group, that no value is assocated to the same key in the first group. /// The first argument is the key, the second argument is the discarded value list in the first group. /// /// /// Callback that is called when a match of values in both groups are found. /// This happens for any key that, values are associated to this key in both group. /// The first argument is the key, the second argument is the associated value list in the first group, the third argument list is the associated value in the second group. /// /// /// This function does not change data in provided groups. /// /// & values) /// { /// return L"[" + From(values).Aggregate([](const WString& a, const WString& b) { return a + L", " + b; }) + L"]"; /// }; /// /// Group as, bs; /// as.Add(1 ,L"A"); as.Add(1 ,L"B"); as.Add(2 ,L"C"); as.Add(2 ,L"D"); /// bs.Add(1 ,L"X"); bs.Add(1 ,L"Y"); bs.Add(3 ,L"Z"); bs.Add(3 ,L"W"); /// GroupInnerJoin( /// as, /// bs, /// [&](vint key, const List& values) { Console::WriteLine(L"Discarded in as: " + itow(key) + printList(values)); }, /// [&](vint key, const List& values) { Console::WriteLine(L"Discarded in bs: " + itow(key) + printList(values)); }, /// [&](vint key, const List& values1, const List& values2) { Console::WriteLine(L"Accepted: " + itow(key) + printList(values1) + printList(values2)); } /// ); /// } /// ]]> template< typename TKey, typename TValueFirst, typename TValueSecond, typename TDiscardFirst, // TKey * [TValueFirst] -> void typename TDiscardSecond, // TKey * [TValueSecond] -> void typename TAccept // TKey * [TValueFirst] * [TValueSecond] -> void > void GroupInnerJoin( const Group& first, const Group& second, const TDiscardFirst& discardFirst, const TDiscardSecond& discardSecond, const TAccept& accept ) { vint firstIndex = 0; vint secondIndex = 0; vint firstCount = first.Keys().Count(); vint secondCount = second.Keys().Count(); while (true) { if (firstIndex < firstCount) { auto firstKey = first.Keys()[firstIndex]; const List& firstValues = first.GetByIndex(firstIndex); if (secondIndex < secondCount) { auto secondKey = second.Keys()[secondIndex]; const List& secondValues = second.GetByIndex(secondIndex); if (firstKey < secondKey) { discardFirst(firstKey, firstValues); firstIndex++; } else if (firstKey > secondKey) { discardSecond(secondKey, secondValues); secondIndex++; } else { accept(firstKey, firstValues, secondValues); firstIndex++; secondIndex++; } } else { discardFirst(firstKey, firstValues); firstIndex++; } } else { if (secondIndex < secondCount) { auto secondKey = second.Keys()[secondIndex]; const List& secondValues = second.GetByIndex(secondIndex); discardSecond(secondKey, secondValues); secondIndex++; } else { break; } } } } /*********************************************************************** Random Access ***********************************************************************/ namespace randomaccess_internal { template struct RandomAccessable> { static const bool CanRead = true; static const bool CanResize = false; }; template struct RandomAccess> { static vint GetCount(const Dictionary& t) { return t.Count(); } static Pair GetValue(const Dictionary& t, vint index) { return Pair(t.Keys().Get(index), t.Values().Get(index)); } static void AppendValue(Dictionary& t, const Pair& value) { t.Set(value.key, value.value); } static void AppendValue(Dictionary& t, const Pair& value) { t.Set(value.key, value.value); } }; } } } #endif /*********************************************************************** .\COLLECTIONS\OPERATIONCOPYFROM.H ***********************************************************************/ /*********************************************************************** Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ #ifndef VCZH_COLLECTIONS_OPERATIONCOPYFROM #define VCZH_COLLECTIONS_OPERATIONCOPYFROM namespace vl { namespace collections { /*********************************************************************** Copy Functions for Containers ***********************************************************************/ namespace copyfrom_internal { using namespace randomaccess_internal; template struct CopyFromAlgorithm { }; template struct CopyFromAlgorithm { static void Perform(Ds& ds, const Ss& ss, bool append) { vint copyCount=RandomAccess::GetCount(ss); vint index=(append?RandomAccess::GetCount(ds):0); vint resizeCount=index+copyCount; RandomAccess::SetCount(ds, resizeCount); for(vint i=0;i::SetValue(ds, index+i, RandomAccess::GetValue(ss, i)); } } }; template struct CopyFromAlgorithm { static void Perform(Ds& ds, const Ss& ss, bool append) { if(!append) { ds.Clear(); } vint copyCount=RandomAccess::GetCount(ss); for(vint i=0;i::AppendValue(ds, RandomAccess::GetValue(ss, i)); } } }; template struct CopyFromAlgorithm { static void Perform(Ds& ds, const Ss& ss, bool append) { Ptr> enumerator; vint copyCount=0; enumerator = Ptr(ss.CreateEnumerator()); while(enumerator->Next()) { copyCount++; } vint index=(append?RandomAccess::GetCount(ds):0); vint resizeCount=index+copyCount; RandomAccess::SetCount(ds, resizeCount); enumerator = Ptr(ss.CreateEnumerator()); while(enumerator->Next()) { RandomAccess::SetValue(ds, index++, enumerator->Current()); } } }; template struct CopyFromAlgorithm { static void Perform(Ds& ds, const Ss& ss, bool append) { if (!append) { ds.Clear(); } auto enumerator = Ptr(ss.CreateEnumerator()); while (enumerator->Next()) { RandomAccess::AppendValue(ds, enumerator->Current()); } } }; template struct Slice { const T* items; vint count; }; } namespace randomaccess_internal { template struct RandomAccessable> { static const bool CanRead = true; static const bool CanResize = true; }; template struct RandomAccess> { static vint GetCount(const copyfrom_internal::Slice& t) { return t.count; } static const T& GetValue(const copyfrom_internal::Slice& t, vint index) { return t.items[index]; } }; } /// Copy containers. /// Type of the target container. /// Type of the source container. /// The target container. /// The source container. /// Set to true to perform appending instead of replacing. template void CopyFrom(Ds& ds, const Ss& ss, bool append = false) { copyfrom_internal::CopyFromAlgorithm::CanResize, randomaccess_internal::RandomAccessable::CanRead>::Perform(ds, ss, append); } /// Copy containers. /// Type of the target container. /// Type of values in the source container. /// The target container. /// Pointer to source values. /// The number of values to copy. /// Set to true to perform appending instead of replacing. template void CopyFrom(Ds& ds, const S* buffer, vint count, bool append = false) { copyfrom_internal::Slice slice = { buffer, count }; CopyFrom(ds, slice, append); } /// Copy containers. /// Type of the target container. /// Type of values in the source container. /// The target container. /// Pointer to the first source value. /// Pointer to the value after the last source value. /// Set to true to perform appending instead of replacing. template void CopyFrom(Ds& ds, const S* begin, const S* end, bool append = false) { copyfrom_internal::Slice slice = { begin, end - begin }; CopyFrom(ds, slice, append); } } } #endif /*********************************************************************** .\COLLECTIONS\OPERATIONENUMERABLE.H ***********************************************************************/ /*********************************************************************** Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ #ifndef VCZH_COLLECTIONS_OPERATIONENUMERABLE #define VCZH_COLLECTIONS_OPERATIONENUMERABLE namespace vl { namespace collections { /*********************************************************************** EmptyEnumerable ***********************************************************************/ template class EmptyEnumerator : public Object, public virtual IEnumerator { IEnumerator* Clone()const override { return new EmptyEnumerator(); } const T& Current()const override { CHECK_FAIL(L"EmptyEnumerable::Enumerator::Current()#This collection is empty."); } vint Index()const override { return -1; } bool Next()override { return false; } void Reset()override { } bool Evaluated()const override { return true; } }; /*********************************************************************** RangeEnumerator ***********************************************************************/ template class RangeEnumerator : public Object, public virtual IEnumerator { protected: T start; T count; T current; public: RangeEnumerator(T _start, T _count, T _current) :start(_start) ,count(_count) ,current(_current) { } RangeEnumerator(T _start, T _count) :start(_start) ,count(_count) ,current(_start-1) { } IEnumerator* Clone()const override { return new RangeEnumerator(start, count, current); } const T& Current()const override { return current; } T Index()const override { return current-start; } bool Next()override { if(start-1<=current && current class ContainerEnumerator : public Object, public virtual IEnumerator { private: Ptr container; vint index; public: ContainerEnumerator(Ptr _container, vint _index=-1) { container=_container; index=_index; } IEnumerator* Clone()const override { return new ContainerEnumerator(container, index); } const T& Current()const override { return container->Get(index); } vint Index()const override { return index; } bool Next()override { index++; return index>=0 && indexCount(); } void Reset()override { index=-1; } bool Evaluated()const override { return true; } }; /*********************************************************************** CompareEnumerable ***********************************************************************/ /// Compare two enumerables. /// Type of elements in the first enumerable. /// Type of elements in the second enumerable. /// /// Returns a positive value when the first enumerable is greater than the second enumerable. /// Returns a negative value when the first enumerable is lesser than the second enumerable. /// Returns zero when the two enumerables equal. /// /// The first enumerable to compare. /// The second enumerable to compare. /// /// The comparison result is similar to comparing two strings. /// When an enumerable contains no value but another one does, the empty one is lesser. /// When an enumerable is the prefix of another one, the prefix is lesser. /// When two enumerable contain the same values in the same order, they equals. /// In other cases, the results represents the comparison result of the first pair of inequal values in enumerables. /// template auto CompareEnumerable(const IEnumerable& a, const IEnumerable& b) -> decltype(std::declval() <=> std::declval()) { auto ator = Ptr(a.CreateEnumerator()); auto btor = Ptr(b.CreateEnumerator()); while (true) { bool a = ator->Next(); bool b = btor->Next(); if (a && !b) return std::strong_ordering::greater; if (!a&&b) return std::strong_ordering::less; if (!a && !b) break; const T& ac = ator->Current(); const U& bc = btor->Current(); auto ordering = ac <=> bc; if (ordering != 0) return ordering; } return std::strong_ordering::equal; } template struct SortedListOperations { static bool Contains(const SortedList& items, const T& item) { return items.Contains(item); } }; template struct SortedListOperations> { static bool Contains(const SortedList>& items, const Ptr& item) { return items.Contains(item.Obj()); } }; } } #endif /*********************************************************************** .\COLLECTIONS\OPERATIONCONCAT.H ***********************************************************************/ /*********************************************************************** Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ #ifndef VCZH_COLLECTIONS_OPERATIONCONCAT #define VCZH_COLLECTIONS_OPERATIONCONCAT namespace vl { namespace collections { /*********************************************************************** Concat ***********************************************************************/ template class ConcatEnumerator : public virtual IEnumerator { protected: IEnumerator* enumerator1; IEnumerator* enumerator2; vint index; bool turned; public: ConcatEnumerator(IEnumerator* _enumerator1, IEnumerator* _enumerator2, vint _index=-1, bool _turned=false) :enumerator1(_enumerator1) ,enumerator2(_enumerator2) ,index(_index) ,turned(_turned) { } ~ConcatEnumerator() { delete enumerator1; delete enumerator2; } IEnumerator* Clone()const override { return new ConcatEnumerator(enumerator1->Clone(), enumerator2->Clone(), index, turned); } const T& Current()const override { if(turned) { return enumerator2->Current(); } else { return enumerator1->Current(); } } vint Index()const override { return index; } bool Next()override { index++; if(turned) { return enumerator2->Next(); } else { if(enumerator1->Next()) { return true; } else { turned=true; return enumerator2->Next(); } } } void Reset()override { enumerator1->Reset(); enumerator2->Reset(); index=-1; turned=false; } bool Evaluated()const override { return enumerator1->Evaluated() && enumerator2->Evaluated(); } }; } } #endif /*********************************************************************** .\COLLECTIONS\OPERATIONPAIR.H ***********************************************************************/ /*********************************************************************** Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ #ifndef VCZH_COLLECTIONS_OPERATIONPAIR #define VCZH_COLLECTIONS_OPERATIONPAIR namespace vl { namespace collections { /*********************************************************************** Pairwise ***********************************************************************/ template class PairwiseEnumerator : public virtual IEnumerator> { protected: IEnumerator* enumerator1; IEnumerator* enumerator2; Nullable> current; public: PairwiseEnumerator(IEnumerator* _enumerator1, IEnumerator* _enumerator2, Nullable> _current = {}) :enumerator1(_enumerator1) ,enumerator2(_enumerator2) ,current(_current) { } ~PairwiseEnumerator() { delete enumerator1; delete enumerator2; } IEnumerator>* Clone()const override { return new PairwiseEnumerator(enumerator1->Clone(), enumerator2->Clone(), current); } const Pair& Current()const override { return current.Value(); } vint Index()const override { return enumerator1->Index(); } bool Next()override { if (enumerator1->Next() && enumerator2->Next()) { current = Pair(enumerator1->Current(), enumerator2->Current()); return true; } else { return false; } } void Reset()override { enumerator1->Reset(); enumerator2->Reset(); } bool Evaluated()const override { return enumerator1->Evaluated() && enumerator2->Evaluated(); } }; } } #endif /*********************************************************************** .\COLLECTIONS\OPERATIONSEQUENCE.H ***********************************************************************/ /*********************************************************************** Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ #ifndef VCZH_COLLECTIONS_OPERATIONSEQUENCE #define VCZH_COLLECTIONS_OPERATIONSEQUENCE namespace vl { namespace collections { /*********************************************************************** Take ***********************************************************************/ template class TakeEnumerator : public virtual IEnumerator { protected: IEnumerator* enumerator; vint count; public: TakeEnumerator(IEnumerator* _enumerator, vint _count) :enumerator(_enumerator) ,count(_count) { } ~TakeEnumerator() { delete enumerator; } IEnumerator* Clone()const override { return new TakeEnumerator(enumerator->Clone(), count); } const T& Current()const override { return enumerator->Current(); } vint Index()const override { return enumerator->Index(); } bool Next()override { if(enumerator->Index()>=count-1) return false; return enumerator->Next(); } void Reset()override { enumerator->Reset(); } bool Evaluated()const override { return enumerator->Evaluated(); } }; /*********************************************************************** Skip ***********************************************************************/ template class SkipEnumerator : public virtual IEnumerator { protected: IEnumerator* enumerator; vint count; bool skipped; public: SkipEnumerator(IEnumerator* _enumerator, vint _count, bool _skipped=false) :enumerator(_enumerator) ,count(_count) ,skipped(_skipped) { } ~SkipEnumerator() { delete enumerator; } IEnumerator* Clone()const override { return new SkipEnumerator(enumerator->Clone(), count, skipped); } const T& Current()const override { return enumerator->Current(); } vint Index()const override { return enumerator->Index()-count; } bool Next()override { if(!skipped) { skipped=true; for(vint i=0;iNext()) { return false; } } } return enumerator->Next(); } void Reset()override { enumerator->Reset(); skipped=false; } bool Evaluated()const override { return enumerator->Evaluated(); } }; /*********************************************************************** Repeat ***********************************************************************/ template class RepeatEnumerator : public virtual IEnumerator { protected: IEnumerator* enumerator; vint count; vint index; vint repeatedCount; public: RepeatEnumerator(IEnumerator* _enumerator, vint _count, vint _index=-1, vint _repeatedCount=0) :enumerator(_enumerator) ,count(_count) ,index(_index) ,repeatedCount(_repeatedCount) { } ~RepeatEnumerator() { delete enumerator; } IEnumerator* Clone()const override { return new RepeatEnumerator(enumerator->Clone(), count, index, repeatedCount); } const T& Current()const override { return enumerator->Current(); } vint Index()const override { return index; } bool Next()override { while(repeatedCountNext()) { index++; return true; } repeatedCount++; enumerator->Reset(); } return false; } void Reset()override { enumerator->Reset(); index=-1; repeatedCount=0; } bool Evaluated()const override { return enumerator->Evaluated(); } }; /*********************************************************************** Distinct ***********************************************************************/ template class DistinctEnumerator : public virtual IEnumerator { protected: IEnumerator* enumerator; SortedList distinct; Nullable lastValue; public: DistinctEnumerator(IEnumerator* _enumerator) :enumerator(_enumerator) { } DistinctEnumerator(const DistinctEnumerator& _enumerator) :lastValue(_enumerator.lastValue) { enumerator = _enumerator.enumerator->Clone(); CopyFrom(distinct, _enumerator.distinct); } ~DistinctEnumerator() { delete enumerator; } IEnumerator* Clone()const override { return new DistinctEnumerator(*this); } const T& Current()const override { return lastValue.Value(); } vint Index()const override { return distinct.Count()-1; } bool Next()override { while (enumerator->Next()) { const T& current = enumerator->Current(); if (!SortedListOperations::Contains(distinct, current)) { lastValue = current; distinct.Add(current); return true; } } return false; } void Reset()override { enumerator->Reset(); distinct.Clear(); } }; /*********************************************************************** Reverse ***********************************************************************/ template class ReverseEnumerator : public virtual IEnumerator { protected: List cache; vint index; public: ReverseEnumerator(const IEnumerable& enumerable) :index(-1) { CopyFrom(cache, enumerable); } ReverseEnumerator(const ReverseEnumerator& _enumerator) :index(_enumerator.index) { CopyFrom(cache, _enumerator.cache); } ~ReverseEnumerator() { } IEnumerator* Clone()const override { return new ReverseEnumerator(*this); } const T& Current()const override { return cache.Get(cache.Count()-1-index); } vint Index()const override { return index; } bool Next()override { index++; return index class FromIteratorEnumerable : public EnumerableBase { private: class Enumerator : public Object, public IEnumerator { private: I begin; I end; I current; public: Enumerator(I _begin, I _end, I _current) :begin(_begin) ,end(_end) ,current(_current) { } IEnumerator* Clone()const override { return new Enumerator(begin, end, current); } const T& Current()const override { return *current; } vint Index()const override { return current-begin; } bool Next()override { current++; return begin<=current && current* CreateEnumerator()const { return new Enumerator(begin, end, begin - 1); } FromIteratorEnumerable(I _begin, I _end) :begin(_begin) ,end(_end) { } FromIteratorEnumerable(const FromIteratorEnumerable& enumerable) :begin(enumerable.begin) ,end(enumerable.end) { } }; template class FromIterator { public: template static FromIteratorEnumerable Wrap(I begin, I end) { return FromIteratorEnumerable(begin, end); } }; template FromIteratorEnumerable FromPointer(const T* begin, const T* end) { return FromIteratorEnumerable(begin, end); } template FromIteratorEnumerable FromArray(T (&items)[size]) { return FromIteratorEnumerable(&items[0], &items[size]); } } } #endif /*********************************************************************** .\COLLECTIONS\OPERATIONSET.H ***********************************************************************/ /*********************************************************************** Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ #ifndef VCZH_COLLECTIONS_OPERATIONSET #define VCZH_COLLECTIONS_OPERATIONSET namespace vl { namespace collections { /*********************************************************************** Intersect/Except ***********************************************************************/ template class IntersectExceptEnumerator : public virtual IEnumerator { protected: IEnumerator* enumerator; SortedList reference; vint index; public: IntersectExceptEnumerator(IEnumerator* _enumerator, const IEnumerable& _reference) :enumerator(_enumerator) ,index(-1) { CopyFrom(reference, _reference); } IntersectExceptEnumerator(const IntersectExceptEnumerator& _enumerator) { enumerator=_enumerator.enumerator->Clone(); CopyFrom(reference, _enumerator.reference); index=_enumerator.index; } ~IntersectExceptEnumerator() { delete enumerator; } IEnumerator* Clone()const override { return new IntersectExceptEnumerator(*this); } const T& Current()const override { return enumerator->Current(); } vint Index()const override { return index; } bool Next()override { while(enumerator->Next()) { if(SortedListOperations::Contains(reference, enumerator->Current())==Intersect) { index++; return true; } } return false; } void Reset()override { enumerator->Reset(); index=-1; } }; } } #endif /*********************************************************************** .\COLLECTIONS\PARTIALORDERING.H ***********************************************************************/ /*********************************************************************** Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ #ifndef VCZH_COLLECTIONS_PARTIALORDERING #define VCZH_COLLECTIONS_PARTIALORDERING namespace vl { namespace collections { /*********************************************************************** Partial Ordering ***********************************************************************/ namespace po { /// /// Node contains extra information for sorted objects. /// struct Node { bool visited = false; /// The index used in [F:vl.collections.PartialOrderingProcessor.components], specifying the component that contain this node. vint component = -1; /// All nodes that this node depends on. const List* ins = nullptr; /// All nodes that this node is depended by. const List* outs = nullptr; /// /// If [M:vl.collections.PartialOrderingProcessor.InitWithSubClass`2] is used, /// a node becomes a sub class representing objects. /// An object will not be shared by different sub classes. /// In this case, this field is a pointer to an array of indexes of objects. /// The index is used in "items" argument in [M:vl.collections.PartialOrderingProcessor.InitWithSubClass`2] /// const vint* firstSubClassItem = nullptr; /// /// When is available, /// this field is the number of indexes in the array. /// vint subClassItemCount = 0; }; /// /// Component is a unit in sorting result. /// If a component contains more than one node, /// it means that nodes in this component depend on each other by the given relationship. /// It is not possible to tell which node should be place before others for all nodes in this component. /// If all nodes are completely partial ordered, /// there should be only one node in all components. /// struct Component { /// /// Pointer to the array of all indexes of nodes in this component. /// Index is used in [F:vl.collections.PartialOrderingProcessor.nodes]. /// const vint* firstNode = nullptr; /// The number of nodes in this component. vint nodeCount = 0; }; } } namespace collections { /// /// PartialOrderingProcessor is used to sort objects by given relationships among them. /// The relationship is defined by dependance. /// If A depends on B, then B will be place before A after sorting. /// If a group of objects depends on each other by the given relationship, /// they will be grouped together in the sorting result. /// class PartialOrderingProcessor : public Object { template using GroupOf = Group; protected: List emptyList; // make a pointer to an empty list available Group ins; // if a depends on b, ins.Contains(a, b) Group outs; // if a depends on b, outs.Contains(b, a) Array firstNodesBuffer; // one buffer for all Component::firstNode Array subClassItemsBuffer; // one buffer for all Node::firstSubClassItem void InitNodes(vint itemCount); void VisitUnvisitedNode(po::Node& node, Array& reversedOrder, vint& used); void AssignUnassignedNode(po::Node& node, vint componentIndex, vint& used); public: /// After is called, this field stores all nodes referenced by sorted components. /// /// The same order is kept as the "items" argument in , and . /// If sub classing is enabled by calling , /// a node represents a sub class of objects. /// In this case, the order in this field does not matter, /// [F:vl.collections.po.Node.firstSubClassItem] stores indexes of objects in this sub class. /// Array nodes; /// After is called, this field stores all sorted components in order. List components; /// /// Sort objects by given relationships. It will crash if this method is called for more than once. /// /// /// One and only one of , or must be called to set data for sorting. /// And then call to sort objects and store the result in . /// void Sort(); /// Set data for sorting, by providing a list for objects, and a group for their relationship. /// Type of the list for objects. , or are recommended. /// List of objects for sorting. /// Relationship of objects for sorting in . Both keys and values are elements in "items". To say that a depends on b, do depGroup.Add(a, b). /// items; /// items.Add(L"elephant"); /// items.Add(L"fish"); /// items.Add(L"ball"); /// items.Add(L"cat"); /// items.Add(L"dog"); /// items.Add(L"apple"); /// /// Group depGroup; /// depGroup.Add(L"ball", L"apple"); /// depGroup.Add(L"cat", L"ball"); /// depGroup.Add(L"ball", L"dog"); /// depGroup.Add(L"dog", L"cat"); /// depGroup.Add(L"elephant", L"cat"); /// depGroup.Add(L"fish", L"dog"); /// /// PartialOrderingProcessor pop; /// pop.InitWithGroup(items, depGroup); /// pop.Sort(); /// /// for (vint i = 0; i < pop.components.Count(); i++) /// { /// auto& c = pop.components[i]; /// Console::WriteLine( /// L"Component " + itow(i) + L": " + /// Range(0, c.nodeCount) /// .Select([&](vint ni){ return items[c.firstNode[ni]]; }) /// .Aggregate([](const WString& a, const WString& b){ return a + L" " + b; }) /// ); /// } /// /// for (vint i = 0; i < pop.nodes.Count(); i++) /// { /// auto& n = pop.nodes[i]; /// if(n.outs->Count() > 0) /// { /// Console::WriteLine( /// L"Node " + items[i] + L" <- " + /// From(*n.outs) /// .Select([&](vint ni){ return items[ni]; }) /// .Aggregate([](const WString& a, const WString& b){ return a + L" " + b; }) /// ); /// } /// } /// } /// ]]> template void InitWithGroup(const TList& items, const GroupOf& depGroup) { CHECK_ERROR(nodes.Count() == 0, L"PartialOrdering::InitWithGroup(items, depGroup)#Initializing twice is not allowed."); for (vint i = 0; i < depGroup.Count(); i++) { vint fromNode = items.IndexOf(KeyType::GetKeyValue(depGroup.Keys()[i])); CHECK_ERROR(fromNode != -1, L"PartialOrdering::InitWithGroup(items, depGroup)#The key in outsGroup does not exist in items."); auto& edges = depGroup.GetByIndex(i); for (vint j = 0; j < edges.Count(); j++) { vint toNode = items.IndexOf(KeyType::GetKeyValue(edges[j])); CHECK_ERROR(toNode != -1, L"PartialOrdering::InitWithGroup(items, depGroup)#The value in outsGroup does not exist in items."); ins.Add(fromNode, toNode); outs.Add(toNode, fromNode); } } InitNodes(items.Count()); } /// Set data for sorting, by providing a list for objects, and a function for their relationship. /// Type of the list for objects. , or are recommended. /// Type of the function that defines relationships of objects. /// List of objects for sorting. /// Relationship of objects for sorting, both arguments are elements in "items". To say that a depends on b, depFunc(a, b) must returns true. /// items; /// items.Add(L"elephant"); /// items.Add(L"fish"); /// items.Add(L"ball"); /// items.Add(L"cat"); /// items.Add(L"dog"); /// items.Add(L"apple"); /// /// auto depFunc = [](const WString& a, const WString& b) /// { /// return /// (a == L"ball" && b == L"apple") || /// (a == L"cat" && b == L"ball") || /// (a == L"ball" && b == L"dog") || /// (a == L"dog" && b == L"cat") || /// (a == L"elephant" && b == L"cat") || /// (a == L"fish" && b == L"dog") /// ; /// }; /// /// PartialOrderingProcessor pop; /// pop.InitWithFunc(items, depFunc); /// pop.Sort(); /// /// for (vint i = 0; i < pop.components.Count(); i++) /// { /// auto& c = pop.components[i]; /// Console::WriteLine( /// L"Component " + itow(i) + L": " + /// Range(0, c.nodeCount) /// .Select([&](vint ni){ return items[c.firstNode[ni]]; }) /// .Aggregate([](const WString& a, const WString& b){ return a + L" " + b; }) /// ); /// } /// /// for (vint i = 0; i < pop.nodes.Count(); i++) /// { /// auto& n = pop.nodes[i]; /// if(n.outs->Count() > 0) /// { /// Console::WriteLine( /// L"Node " + items[i] + L" <- " + /// From(*n.outs) /// .Select([&](vint ni){ return items[ni]; }) /// .Aggregate([](const WString& a, const WString& b){ return a + L" " + b; }) /// ); /// } /// } /// } /// ]]> template void InitWithFunc(const TList& items, TFunc&& depFunc) { GroupOf depGroup; for (vint i = 0; i < items.Count(); i++) { for (vint j = 0; j < items.Count(); j++) { if (depFunc(items[i], items[j])) { depGroup.Add(items[i], items[j]); } } } InitWithGroup(items, depGroup); } /// Set data for sorting, by providing a list for objects, and a group for their relationship, and a dictionary for sub classing objects. /// Type of the list for objects. , or are recommended. /// Type of a sub class. /// List of objects for sorting. /// Relationship of objects for sorting in . Both keys and values are elements in "items". To say that a depends on b, do depGroup.Add(a, b). /// Sub classing objects. Keys are elements in "items". If multiple keys have the same value in this dictionary, then these objects are in the same sub class. /// /// Relationships are defined on objects. /// By sub classing objects, /// relationships of sub classes are calculated from "depGroup". /// If object A in sub class X depends on object B in sub class Y, then sub class X depends on sub class Y. /// It is allowed that relatipnships on sub classes are not completely partial ordered, /// in this case, some components may contain multiple sub classes. /// /// items; /// for (vint i = 1; i <= 2; i++) /// { /// items.Add(L"apple_" + itow(i)); /// items.Add(L"ball_" + itow(i)); /// items.Add(L"cat_" + itow(i)); /// items.Add(L"dog_" + itow(i)); /// items.Add(L"elephant_" + itow(i)); /// items.Add(L"fish_" + itow(i)); /// } /// /// Group depGroup; /// depGroup.Add(L"ball_2", L"apple_1"); /// depGroup.Add(L"cat_2", L"ball_1"); /// depGroup.Add(L"ball_2", L"dog_1"); /// depGroup.Add(L"dog_2", L"cat_1"); /// depGroup.Add(L"elephant_2", L"cat_1"); /// depGroup.Add(L"fish_2", L"dog_1"); /// /// Dictionary subClass; /// for (vint i = 1; i <= 2; i++) /// { /// subClass.Add(L"apple_" + itow(i), 1); /// subClass.Add(L"ball_" + itow(i), 2); /// subClass.Add(L"cat_" + itow(i), 3); /// subClass.Add(L"dog_" + itow(i), 4); /// subClass.Add(L"elephant_" + itow(i), 5); /// subClass.Add(L"fish_" + itow(i), 6); /// } /// /// PartialOrderingProcessor pop; /// pop.InitWithSubClass(items, depGroup, subClass); /// pop.Sort(); /// /// for (vint i = 0; i < pop.components.Count(); i++) /// { /// auto& c = pop.components[i]; /// Console::WriteLine( /// L"Component " + itow(i) + L": sub classes" + /// Range(0, c.nodeCount) /// .Select([&](vint ni) { return c.firstNode[ni]; }) /// .Aggregate(L"", [](const WString& a, vint b) { return a + L" " + itow(b); }) /// ); /// } /// /// for (vint i = 0; i < pop.nodes.Count(); i++) /// { /// auto& n = pop.nodes[i]; /// Console::WriteLine(L"Sub class " + itow(i)); /// /// Console::WriteLine( /// Range(0, n.subClassItemCount) /// .Select([&](vint si) { return n.firstSubClassItem[si]; }) /// .Aggregate(L" :", [&](const WString& a, vint b) { return a + L" " + items[b]; }) /// ); /// /// if (n.outs->Count() > 0) /// { /// Console::WriteLine( /// From(*n.outs) /// .Aggregate(L" <- sub classes", [](const WString& a, vint b) { return a + L" " + itow(b); }) /// ); /// } /// } /// } /// ]]> template void InitWithSubClass(const TList& items, const GroupOf& depGroup, const Dictionary& subClasses) { CHECK_ERROR(nodes.Count() == 0, L"PartialOrdering::InitWithSubClass(items, degGroup, subClasses)#Initializing twice is not allowed."); using ElementType = typename TList::ElementType; using ElementKeyType = KeyType; Group scItems; SortedList singleItems; for (vint i = 0; i < subClasses.Count(); i++) { const auto& key = subClasses.Keys()[i]; const auto& value = subClasses.Values()[i]; scItems.Add(value, key); } for (vint i = 0; i < items.Count(); i++) { const auto& item = items[i]; if (!subClasses.Keys().Contains(ElementKeyType::GetKeyValue(item))) { singleItems.Add(item); } } auto getSubClass = [&](const ElementType& item) { vint index = subClasses.Keys().IndexOf(ElementKeyType::GetKeyValue(item)); if (index != -1) { index = scItems.Keys().IndexOf(KeyType::GetKeyValue(subClasses.Values()[index])); CHECK_ERROR(index != -1, L"PartialOrdering::InitWithSubClass(items, degGroup, subClasses)#Internal Error."); return index; } else { index = singleItems.IndexOf(ElementKeyType::GetKeyValue(item)); CHECK_ERROR(index != -1, L"PartialOrdering::InitWithSubClass(items, degGroup, subClasses)#Internal Error."); return scItems.Count() + index; } }; for (vint i = 0; i < depGroup.Count(); i++) { const auto& key = depGroup.Keys()[i]; vint keyIndex = getSubClass(key); const auto& values = depGroup.GetByIndex(i); for (vint j = 0; j < values.Count(); j++) { const auto& value = values[j]; vint valueIndex = getSubClass(value); if (!ins.Contains(keyIndex, valueIndex)) { ins.Add(keyIndex, valueIndex); } } } for (vint i = 0; i < ins.Count(); i++) { vint key = ins.Keys()[i]; const auto& values = ins.GetByIndex(i); for (vint j = 0; j < values.Count(); j++) { outs.Add(values[j], key); } } InitNodes(scItems.Count() + singleItems.Count()); subClassItemsBuffer.Resize(items.Count()); vint used = 0; vint scItemCount = scItems.Keys().Count(); for (vint i = 0; i < nodes.Count(); i++) { auto& node = nodes[i]; node.firstSubClassItem = &subClassItemsBuffer[used]; if (i < scItemCount) { const auto& values = scItems.GetByIndex(i); for (vint j = 0; j < values.Count(); j++) { subClassItemsBuffer[used++] = items.IndexOf(ElementKeyType::GetKeyValue(values[j])); } node.subClassItemCount = values.Count(); } else { subClassItemsBuffer[used++] = items.IndexOf(ElementKeyType::GetKeyValue(singleItems[i - scItemCount])); node.subClassItemCount = 1; } } } }; } } #endif /*********************************************************************** .\PRIMITIVES\FUNCTION.H ***********************************************************************/ /*********************************************************************** Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ #ifndef VCZH_FUNCTION #define VCZH_FUNCTION #include namespace vl { template class Func; /*********************************************************************** vl::Func ***********************************************************************/ namespace internal_invokers { template class Invoker : public Object { public: virtual R Invoke(TArgs&& ...args) = 0; }; //------------------------------------------------------ template class StaticInvoker : public Invoker { protected: R(*function)(TArgs ...args); public: StaticInvoker(R(*_function)(TArgs...)) :function(_function) { } R Invoke(TArgs&& ...args)override { return function(std::forward(args)...); } }; //------------------------------------------------------ template class MemberInvoker : public Invoker { protected: C* sender; R(C::*function)(TArgs ...args); public: MemberInvoker(C* _sender, R(C::*_function)(TArgs ...args)) :sender(_sender) ,function(_function) { } R Invoke(TArgs&& ...args)override { return (sender->*function)(std::forward(args)...); } }; //------------------------------------------------------ template class ObjectInvoker : public Invoker { protected: C function; public: ObjectInvoker(const C& _function) :function(_function) { } ObjectInvoker(C&& _function) :function(std::move(_function)) { } R Invoke(TArgs&& ...args)override { return function(std::forward(args)...); } }; //------------------------------------------------------ template class ObjectInvoker : public Invoker { protected: C function; public: ObjectInvoker(const C& _function) :function(_function) { } ObjectInvoker(C&& _function) :function(std::move(_function)) { } void Invoke(TArgs&& ...args)override { function(std::forward(args)...); } }; } /// A type for functors. /// The return type. /// Types of parameters. template class Func : public Object { protected: Ptr> invoker; template static bool IsEmptyFunc(const Func& function) { return !function; } template static bool IsEmptyFunc(Func& function) { return !function; } template static bool IsEmptyFunc(C&&) { return false; } public: typedef R FunctionType(TArgs...); typedef R ResultType; /// Create a null functor. Func() = default; /// Copy a functor. /// The functor to copy. Func(const Func& function) = default; /// Move a functor. /// The functor to move. Func(Func&& function) = default; /// Create a functor from a function pointer. /// The function pointer. Func(R(*function)(TArgs...)) { invoker = Ptr(new internal_invokers::StaticInvoker(function)); } /// Create a functor from a method. /// Type of the class that this method belongs to. /// The object that this method belongs to. /// The method pointer. template Func(C* sender, R(C::*function)(TArgs...)) { invoker = Ptr(new internal_invokers::MemberInvoker(sender, function)); } /// Create a functor from another compatible functor. /// Type of the functor to copy. /// The functor to copy. It could be a lambda expression, or any types that has operator() members. template requires(!std::is_same_v, Func>) Func(C&& function) requires ( std::is_invocable_v ) && ( std::is_same_v || std::is_convertible_v()(std::declval()...)), R> ) { if (!IsEmptyFunc(function)) { invoker = Ptr(new internal_invokers::ObjectInvoker, R, TArgs...>(std::forward(function))); } } /// Create a functor from another compatible functor. /// Type of the functor to copy. /// The functor to copy. It could be a lambda expression, or any types that has operator() members. template Func(C* function) requires ( std::is_invocable_v ) && ( std::is_same_v || std::is_convertible_v()(std::declval()...)), R> ) { if (!IsEmptyFunc(function)) { invoker = Ptr(new internal_invokers::ObjectInvoker(function)); } } /// Invoke the function. /// Returns the function result. It crashes when the functor is null. /// Arguments to invoke the function. R operator()(TArgs ...args)const { return invoker->Invoke(std::forward(args)...); } Func& operator=(const Func& function) { invoker = function.invoker; return *this; } Func& operator=(const Func&& function) { invoker = std::move(function.invoker); return *this; } bool operator==(const Func& function)const { return invoker == function.invoker; } bool operator!=(const Func& function)const { return invoker != function.invoker; } /// Test is the functor is non-null. /// Returns true if the functor is non-null. operator bool()const { return invoker; } }; /*********************************************************************** vl::function_lambda::LambdaRetriveType ***********************************************************************/ namespace function_lambda { template struct LambdaRetriveType { }; template struct LambdaRetriveType { typedef R(FunctionType)(TArgs...); typedef R ResultType; typedef TypeTuple ParameterTypes; }; template struct LambdaRetriveType { typedef R(FunctionType)(TArgs...); typedef R ResultType; typedef TypeTuple ParameterTypes; }; } template Func(C&&) -> Func::FunctionType>; template Func(R(*)(TArgs...)) -> Func; template Func(C*, R(C::*)(TArgs...)) -> Func; } #endif /*********************************************************************** .\COLLECTIONS\OPERATIONSELECT.H ***********************************************************************/ /*********************************************************************** Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ #ifndef VCZH_COLLECTIONS_OPERATIONSELECT #define VCZH_COLLECTIONS_OPERATIONSELECT namespace vl { namespace collections { /*********************************************************************** Select ***********************************************************************/ template class SelectEnumerator : public virtual IEnumerator { protected: IEnumerator* enumerator; Func selector; Nullable current; public: SelectEnumerator(IEnumerator* _enumerator, const Func& _selector, Nullable _current = {}) :enumerator(_enumerator) ,selector(_selector) ,current(_current) { } ~SelectEnumerator() { delete enumerator; } IEnumerator* Clone()const override { return new SelectEnumerator(enumerator->Clone(), selector, current); } const K& Current()const override { return current.Value(); } vint Index()const override { return enumerator->Index(); } bool Next()override { if (enumerator->Next()) { current = selector(enumerator->Current()); return true; } else { return false; } } void Reset()override { enumerator->Reset(); } }; } } #endif /*********************************************************************** .\COLLECTIONS\OPERATIONWHERE.H ***********************************************************************/ /*********************************************************************** Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ #ifndef VCZH_COLLECTIONS_OPERATIONWHERE #define VCZH_COLLECTIONS_OPERATIONWHERE namespace vl { namespace collections { /*********************************************************************** Where ***********************************************************************/ template class WhereEnumerator : public virtual IEnumerator { protected: IEnumerator* enumerator; Func selector; vint index; public: WhereEnumerator(IEnumerator* _enumerator, const Func& _selector, vint _index=-1) :enumerator(_enumerator) ,selector(_selector) ,index(_index) { } ~WhereEnumerator() { delete enumerator; } IEnumerator* Clone()const override { return new WhereEnumerator(enumerator->Clone(), selector, index); } const T& Current()const override { return enumerator->Current(); } vint Index()const override { return index; } bool Next()override { while(enumerator->Next()) { if(selector(enumerator->Current())) { index++; return true; } } return false; } void Reset()override { enumerator->Reset(); index=-1; } }; } } #endif /*********************************************************************** .\PRIMITIVES\EVENT.H ***********************************************************************/ /*********************************************************************** Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ #ifndef VCZH_EVENT #define VCZH_EVENT namespace vl { template class Event; class EventHandler : public Object { public: virtual bool IsAttached() = 0; }; /// An event for being subscribed using multiple callbacks. A callback is any functor that returns void. /// Types of callback parameters. template class Event : public Object { protected: class EventHandlerImpl : public EventHandler { public: bool attached; Func function; EventHandlerImpl(const Func& _function) :attached(true) , function(_function) { } bool IsAttached()override { return attached; } }; collections::SortedList> handlers; public: NOT_COPYABLE(Event); Event() = default; /// Add a callback to the event. /// The event handler representing the callback. /// The callback. Ptr Add(const Func& function) { auto handler = Ptr(new EventHandlerImpl(function)); handlers.Add(handler); return handler; } /// Add a callback to the event. /// The event handler representing the callback. /// The callback. Ptr Add(void(*function)(TArgs...)) { return Add(Func(function)); } /// Add a method callback to the event. /// Type of the class that the callback belongs to. /// The event handler representing the callback. /// The object that the callback belongs to. /// The method callback. template Ptr Add(C* sender, void(C::*function)(TArgs...)) { return Add(Func(sender, function)); } /// Remove a callback by an event handler returns from . /// Returns true if this operation succeeded. /// The event handler representing the callback. bool Remove(Ptr handler) { auto impl = handler.Cast(); if (!impl) return false; vint index = handlers.IndexOf(impl.Obj()); if (index == -1) return false; impl->attached = false; handlers.RemoveAt(index); return true; } /// Invoke all callbacks in the event. /// Arguments to invoke all callbacks. template void operator()(TArgs2&& ...args)const { auto count = handlers.Count(); if (count == 0) return; if (count == 1) { handlers[0]->function(std::forward(args)...); } else { for (vint i = 0; i < count; i++) { handlers[i]->function(args...); } } } }; } #endif /*********************************************************************** .\PRIMITIVES\TUPLE.H ***********************************************************************/ /*********************************************************************** Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ #ifndef VCZH_TUPLE #define VCZH_TUPLE namespace vl { namespace tuple_internal { template struct TupleElement { T element; TupleElement() = default; template TupleElement(U&& _element) :element(std::forward(_element)) { } template void AssignCopy(const TupleElement& value) { element = value.GetElement(); } template void AssignMove(TupleElement&& value) { element = std::move(value.GetElement()); } template bool operator==(const TupleElement& value) const { return element == value.GetElement(); } T& GetElement() { return element; } const T& GetElement() const { return element; } }; template struct TupleElement { T* element; TupleElement() = delete; template TupleElement(U&& _element) :element(&_element) { } template void AssignCopy(const TupleElement& value) { element = &value.GetElement(); } template void AssignMove(TupleElement&& value) { element = &value.GetElement(); } template bool operator==(const TupleElement& value) const { return *element == value.GetElement(); } T& GetElement() { return *element; } const T& GetElement() const { return *element; } }; template struct TupleElementComparison { const T& t; const U& u; TupleElementComparison(const T& _t, const U& _u) : t(_t) , u(_u) { } template friend TOrdering operator*(TPreviousOrdering order, const TupleElementComparison& t) { if (order != 0) return (TOrdering)order; return (TOrdering)(t.t <=> t.u); } }; struct TupleCtorElementsTag {}; struct TupleCtorTupleTag {}; template struct TupleBase; template requires(sizeof...(Is) == sizeof...(TArgs)) struct TupleBase, TArgs...> : TupleElement... { private: using TSelf = TupleBase, TArgs...>; template requires(sizeof...(TArgs) == sizeof...(UArgs)) using TCompatible = TupleBase, UArgs...>; public: TupleBase() = default; template TupleBase(TupleCtorElementsTag, UArgs&& ...xs) : TupleElement(std::forward(xs)) ... { } template TupleBase(TupleCtorTupleTag, const TCompatible& t) : TupleElement(static_cast&>(t).GetElement()) ... { } template TupleBase(TupleCtorTupleTag, TCompatible&& t) : TupleElement(std::move(static_cast&>(t).GetElement())) ... { } template void AssignCopy(const TCompatible& t) { (( static_cast*>(this)->AssignCopy ( static_cast&>(t) ) ), ...); } template void AssignMove(TCompatible&& t) { (( static_cast*>(this)->AssignMove ( std::move(static_cast&&>(t)) ) ), ...); } template bool AreEqual(const TCompatible& t) const { return (true && ... && ( *static_cast*>(this) == static_cast&>(t) )); } template auto Compare(const TCompatible& t) const { #define LEFT_ELEMENT (static_cast*>(this)->GetElement()) #define RIGHT_ELEMENT (static_cast&>(t).GetElement()) using TOrdering = OrderingOf RIGHT_ELEMENT)...>; return (std::strong_ordering::equal * ... * (TupleElementComparison(LEFT_ELEMENT, RIGHT_ELEMENT))); #undef LEFT_ELEMENT #undef RIGHT_ELEMENT } }; } template class Tuple : private tuple_internal::TupleBase, TArgs...> { template friend class Tuple; using TSelf = Tuple; using TBase = tuple_internal::TupleBase, TArgs...>; template requires(sizeof...(TArgs) == sizeof...(UArgs)) using TCompatible = Tuple; template requires(sizeof...(TArgs) == sizeof...(UArgs)) using TCompatibleBase = tuple_internal::TupleBase, UArgs...>; public: Tuple() = default; template Tuple(UArgs&& ...xs) requires(sizeof...(TArgs) == sizeof...(UArgs)) : TBase( tuple_internal::TupleCtorElementsTag{}, std::forward(xs)... ) { } template Tuple(const TCompatible& t) requires(sizeof...(TArgs) == sizeof...(UArgs)) : TBase( tuple_internal::TupleCtorTupleTag{}, static_cast&>(t) ) { } template Tuple(TCompatible&& t) requires(sizeof...(TArgs) == sizeof...(UArgs)) : TBase( tuple_internal::TupleCtorTupleTag{}, static_cast&&>(t) ) { } template TSelf& operator=(const TCompatible& t) { this->AssignCopy(t); return *this; } template TSelf& operator=(TCompatible&& t) { this->AssignMove(std::move(t)); return *this; } template auto operator<=>(const TCompatible& t)const requires (true && ... && std::three_way_comparable_with) { return this->Compare(t); } template bool operator==(const TCompatible& t)const requires (true && ... && std::equality_comparable_with) { return this->AreEqual(t); } template decltype(auto) get() { using TElement = tuple_internal::TupleElement>>; return static_cast(this)->GetElement(); } template decltype(auto) get() const { using TElement = tuple_internal::TupleElement>>; return static_cast(this)->GetElement(); } }; template Tuple(TArgs&&...) -> Tuple::Type...>; template decltype(auto) get(Tuple& t) { return t.template get(); } template decltype(auto) get(const Tuple& t) { return t.template get(); } } namespace std { template struct tuple_size> : integral_constant {}; template struct tuple_element> { using type = decltype(std::declval>().template get()); }; template struct tuple_element> { using type = decltype(std::declval>().template get()); }; } #endif /*********************************************************************** .\COLLECTIONS\OPERATIONFOREACH.H ***********************************************************************/ /*********************************************************************** Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ #ifndef VCZH_COLLECTIONS_FOREACH #define VCZH_COLLECTIONS_FOREACH namespace vl { namespace collections { struct RangeBasedForLoopEnding { }; /*********************************************************************** Range-Based For-Loop Iterator ***********************************************************************/ template struct RangeBasedForLoopIterator { private: IEnumerator* iterator; public: RangeBasedForLoopIterator(const IEnumerable& enumerable) : iterator(enumerable.CreateEnumerator()) { operator++(); } ~RangeBasedForLoopIterator() { if (iterator) delete iterator; } void operator++() { if (!iterator->Next()) { delete iterator; iterator = nullptr; } } const T& operator*() const { return iterator->Current(); } bool operator==(const RangeBasedForLoopEnding&) const { return iterator == nullptr; } bool operator!=(const RangeBasedForLoopEnding&) const { return iterator != nullptr; } friend bool operator==(const RangeBasedForLoopEnding&, const RangeBasedForLoopIterator& iterator) { return iterator.iterator == nullptr; } friend bool operator!=(const RangeBasedForLoopEnding&, const RangeBasedForLoopIterator& iterator) { return iterator.iterator != nullptr; } }; template RangeBasedForLoopIterator begin(const IEnumerable& enumerable) { return { enumerable }; } template RangeBasedForLoopEnding end(const IEnumerable& enumerable) { return {}; } /*********************************************************************** Range-Based For-Loop Iterator with Index ***********************************************************************/ template struct RangeBasedForLoopIteratorWithIndex { private: IEnumerator* iterator; vint index; public: RangeBasedForLoopIteratorWithIndex(const IEnumerable& enumerable) : iterator(enumerable.CreateEnumerator()) , index(-1) { operator++(); } ~RangeBasedForLoopIteratorWithIndex() { if (iterator) delete iterator; } void operator++() { if (!iterator->Next()) { delete iterator; iterator = nullptr; } index++; } Tuple operator*() const { return { iterator->Current(),index }; } bool operator==(const RangeBasedForLoopEnding&) const { return iterator == nullptr; } bool operator!=(const RangeBasedForLoopEnding&) const { return iterator != nullptr; } friend bool operator==(const RangeBasedForLoopEnding&, const RangeBasedForLoopIteratorWithIndex& iterator) { return iterator.iterator == nullptr; } friend bool operator!=(const RangeBasedForLoopEnding&, const RangeBasedForLoopIteratorWithIndex& iterator) { return iterator.iterator != nullptr; } }; template struct EnumerableWithIndex { const IEnumerable& enumerable; EnumerableWithIndex(const IEnumerable& _enumerable) : enumerable(_enumerable) { } }; template EnumerableWithIndex indexed(const IEnumerable& enumerable) { return { enumerable }; } template RangeBasedForLoopIteratorWithIndex begin(const EnumerableWithIndex& enumerable) { return { enumerable.enumerable }; } template RangeBasedForLoopEnding end(const EnumerableWithIndex& enumerable) { return {}; } /*********************************************************************** Optimized Range-Based For-Loop Iterator for ArrayBase ***********************************************************************/ template struct RangeBasedForLoopIteratorForList { protected: const ArrayBase& arrayBase; vint index; public: RangeBasedForLoopIteratorForList(const ArrayBase& _arrayBase) : arrayBase(_arrayBase) , index(0) { } void operator++() { ++index; } const T& operator*() const { return arrayBase.Get(index); } bool operator==(const RangeBasedForLoopEnding&) const { return index >= arrayBase.Count(); } bool operator!=(const RangeBasedForLoopEnding&) const { return index < arrayBase.Count(); } friend bool operator==(const RangeBasedForLoopEnding&, const RangeBasedForLoopIteratorForList& iterator) { return iterator.index >= iterator.arrayBase.Count(); } friend bool operator!=(const RangeBasedForLoopEnding&, const RangeBasedForLoopIteratorForList& iterator) { return iterator.index < iterator.arrayBase.Count(); } }; template RangeBasedForLoopIteratorForList begin(const ArrayBase& arrayBase) { return { arrayBase }; } template RangeBasedForLoopEnding end(const ArrayBase& arrayBase) { return {}; } /*********************************************************************** Optimized Range-Based For-Loop Iterator for ArrayBase with Index ***********************************************************************/ template struct RangeBasedForLoopIteratorWithIndexForList : public RangeBasedForLoopIteratorForList { public: RangeBasedForLoopIteratorWithIndexForList(const ArrayBase& arrayBase) : RangeBasedForLoopIteratorForList(arrayBase) { } Tuple operator*() const { return { this->arrayBase.Get(this->index), this->index }; } }; template struct ArrayBaseWithIndex { const ArrayBase& arrayBase; ArrayBaseWithIndex(const ArrayBase& _arrayBase) : arrayBase(_arrayBase) { } }; template ArrayBaseWithIndex indexed(const ArrayBase& arrayBase) { return { arrayBase }; } template RangeBasedForLoopIteratorWithIndexForList begin(const ArrayBaseWithIndex& wrapper) { return { wrapper.arrayBase }; } template RangeBasedForLoopEnding end(const ArrayBaseWithIndex& wrapper) { return {}; } } } #endif /*********************************************************************** .\PRIMITIVES\VARIANT.H ***********************************************************************/ /*********************************************************************** Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ #ifndef VCZH_VARIANT #define VCZH_VARIANT #ifdef VCZH_CHECK_MEMORY_LEAKS_NEW #undef new #endif namespace vl { template struct VariantIndex { static constexpr vint value = I; }; template struct Overloading : TCallbacks ... { using TCallbacks::operator()...; #ifdef VCZH_GCC Overloading(const Overloading&) = default; Overloading(Overloading&&) = default; Overloading& operator=(const Overloading&) = default; Overloading& operator=(Overloading&&) = default; template Overloading(TArguments&& ...arguments) : TCallbacks(std::forward(arguments))... { } #endif }; template Overloading(TCallbacks&&...) -> Overloading...>; } namespace vl::variant_internal { template consteval T MaxOf(T first, TArgs ...others) { T result = first; T nexts[] = { others... }; for (T next : nexts) { if (result < next) result = next; } return result; } template consteval T MaxOf(T first) { return first; } template struct VariantElement { template requires(std::is_same_v) static consteval VariantIndex IndexOf() { return {}; } static consteval VariantIndex IndexOfCast(const T&) { return {}; } static consteval VariantIndex IndexOfCast(T&&) { return {}; } template static bool i_Apply(vint index, char* buffer, TCallback&& callback) { if (I != index) return false; callback(*reinterpret_cast(buffer)); return true; } template static bool i_Apply(vint index, const char* buffer, TCallback&& callback) { if (I != index) return false; callback(*reinterpret_cast(buffer)); return true; } static bool i_CopyCtor(vint index, char* buffer, const char* source) { if constexpr (std::is_copy_constructible_v) { if (I != index) return false; new (buffer)T(*reinterpret_cast(source)); return true; } else { return false; } } static bool i_MoveCtor(vint index, char* buffer, char* source) { if constexpr (std::is_move_constructible_v) { if (I != index) return false; new (buffer)T(std::move(*reinterpret_cast(source))); return true; } else { return false; } } static bool i_Dtor(vint index, char* buffer) { if (I != index) return false; reinterpret_cast(buffer)->~T(); return true; } static bool i_CopyAssign(vint index, char* buffer, const char* source) { if constexpr (std::is_copy_assignable_v) { if (I != index) return false; *reinterpret_cast(buffer) = *reinterpret_cast(source); return true; } else { return false; } } static bool i_MoveAssign(vint index, char* buffer, char* source) { if constexpr (std::is_move_constructible_v) { if (I != index) return false; *reinterpret_cast(buffer) = std::move(*reinterpret_cast(source)); return true; } else { return false; } } template static void Ctor(VariantIndex, char* buffer, TArgs&& ...args) { new (buffer)T(std::forward(args)...); } static void DefaultCtor(VariantIndex, char* buffer) { new (buffer)T(); } static void CopyCtor(VariantIndex, char* buffer, const T& source) { new (buffer)T(source); } static void MoveCtor(VariantIndex, char* buffer, T&& source) { new (buffer)T(std::move(source)); } template static void Assign(VariantIndex, char* buffer, TArg&& value) { *reinterpret_cast(buffer) = std::forward(value); } static void CopyAssign(VariantIndex, char* buffer, const T& source) { *reinterpret_cast(buffer) = source; } static void MoveAssign(VariantIndex, char* buffer, T&& source) { *reinterpret_cast(buffer) = std::move(source); } }; template struct VariantElementPack; template struct VariantElementPack, TElements...> : VariantElement... { using VariantElement::IndexOf...; using VariantElement::IndexOfCast...; using VariantElement::DefaultCtor...; using VariantElement::Ctor...; using VariantElement::CopyCtor...; using VariantElement::MoveCtor...; using VariantElement::Assign...; using VariantElement::CopyAssign...; using VariantElement::MoveAssign...; template static void Apply(vint index, char* buffer, TCallback&& callback) { bool result = (VariantElement::i_Apply(index, buffer, std::forward(callback)) || ...); CHECK_ERROR(result, L"vl::variant_internal::VariantElementPack<...>::Apply(...)#Internal error: none of elements are selected."); } template static void Apply(vint index, const char* buffer, TCallback&& callback) { bool result = (VariantElement::i_Apply(index, buffer, std::forward(callback)) || ...); CHECK_ERROR(result, L"vl::variant_internal::VariantElementPack<...>::Apply(...)#Internal error: none of elements are selected."); } static void CopyCtor(vint index, char* buffer, const char* source) { bool result = (VariantElement::i_CopyCtor(index, buffer, source) || ...); CHECK_ERROR(result, L"vl::variant_internal::VariantElementPack<...>::CopyCtor(...)#Internal error: none of elements are selected."); } static void MoveCtor(vint index, char* buffer, char* source) { bool result = (VariantElement::i_MoveCtor(index, buffer, source) || ...); CHECK_ERROR(result, L"vl::variant_internal::VariantElementPack<...>::MoveCtor(...)#Internal error: none of elements are selected."); } static void Dtor(vint index, char* buffer) { bool result = (VariantElement::i_Dtor(index, buffer) || ...); CHECK_ERROR(result, L"vl::variant_internal::VariantElementPack<...>::Dtor(...)#Internal error: none of elements are selected."); } static void CopyAssign(vint index, char* buffer, const char* source) { bool result = (VariantElement::i_CopyAssign(index, buffer, source) || ...); CHECK_ERROR(result, L"vl::variant_internal::VariantElementPack<...>::CopyAssign(...)#Internal error: none of elements are selected."); } static void MoveAssign(vint index, char* buffer, char* source) { bool result = (VariantElement::i_MoveAssign(index, buffer, source) || ...); CHECK_ERROR(result, L"vl::variant_internal::VariantElementPack<...>::MoveAssign(...)#Internal error: none of elements are selected."); } }; } namespace vl { template concept VariantElementType = requires() { std::is_same_v>; }; template class alignas(TElements...) Variant { public: template friend class Variant; using ElementPack = variant_internal::VariantElementPack, TElements...>; template static constexpr vint IndexOf = decltype(ElementPack::template IndexOf())::value; template static constexpr vint IndexOfCast = decltype(ElementPack::IndexOfCast(std::declval()))::value; static constexpr std::size_t MaxSize = variant_internal::MaxOf(sizeof(TElements)...); vint index = -1; char buffer[MaxSize]; public: Variant() requires(std::is_default_constructible_v>>) : index(0) { ElementPack::DefaultCtor(VariantIndex<0>{}, buffer); } Variant(const Variant& variant) : index(variant.index) { ElementPack::CopyCtor(index, buffer, variant.buffer); } Variant(Variant&& variant) : index(variant.index) { ElementPack::MoveCtor(index, buffer, variant.buffer); } template requires((std::is_same_v || ...)) Variant(const T& element) { constexpr auto i = IndexOf; index = i; ElementPack::CopyCtor(VariantIndex{}, buffer, element); } template requires((std::is_same_v || ...)) Variant(T&& element) { constexpr auto i = IndexOf; index = i; ElementPack::MoveCtor(VariantIndex{}, buffer, std::move(element)); } template Variant(VariantIndex i, TArgs&& ...args) : index(I) { ElementPack::Ctor(i, buffer, std::forward(args)...); } template requires( !std::is_same_v, Variant> && ((!std::is_same_v, TElements>) && ...) ) Variant(T&& value) { constexpr auto i = IndexOfCast; index = i; ElementPack::Ctor(VariantIndex{}, buffer, std::forward(value)); } ~Variant() { ElementPack::Dtor(index, buffer); } Variant& operator=(const Variant& variant) { if (this != &variant) { if (index == variant.index) { ElementPack::CopyAssign(index, buffer, variant.buffer); } else { ElementPack::Dtor(index, buffer); index = variant.index; ElementPack::CopyCtor(index, buffer, variant.buffer); } } return *this; } Variant& operator=(Variant&& variant) { if (this != &variant) { if (index == variant.index) { ElementPack::MoveAssign(index, buffer, variant.buffer); } else { ElementPack::Dtor(index, buffer); index = variant.index; ElementPack::MoveCtor(index, buffer, variant.buffer); } } return *this; } template requires((std::is_same_v || ...)) Variant& operator=(const T& element) { constexpr auto i = IndexOf; if (index == i) { ElementPack::CopyAssign(VariantIndex{}, buffer, element); } else { ElementPack::Dtor(index, buffer); index = i; ElementPack::CopyCtor(VariantIndex{}, buffer, element); } return *this; } template requires((std::is_same_v || ...)) Variant& operator=(T&& element) { constexpr auto i = IndexOf; if (index == i) { ElementPack::MoveAssign(VariantIndex{}, buffer, std::move(element)); } else { ElementPack::Dtor(index, buffer); index = i; ElementPack::MoveCtor(VariantIndex{}, buffer, std::move(element)); } return *this; } template Variant& Set(VariantIndex i, T&& value) { if (index == I) { ElementPack::Assign(i, buffer, std::forward(value)); } else { ElementPack::Dtor(index, buffer); index = I; ElementPack::Ctor(i, buffer, std::forward(value)); } return *this; } template requires( !std::is_same_v, Variant> && ((!std::is_same_v, TElements>) && ...) ) Variant& operator=(T&& value) { constexpr auto i = IndexOfCast; return Set(VariantIndex{}, std::forward(value)); } vint Index() const { return index; } template requires((std::is_same_v || ...)) T& Get()& { auto result = TryGet(); CHECK_ERROR(result != nullptr, L"vl::Variant::Get()#Content does not match the type."); return *result; } template requires((std::is_same_v || ...)) T&& Get()&& { auto result = TryGet(); CHECK_ERROR(result != nullptr, L"vl::Variant::Get()#Content does not match the type."); return std::move(*result); } template requires((std::is_same_v || ...)) const T& Get() const& { auto result = TryGet(); CHECK_ERROR(result != nullptr, L"vl::Variant::Get()#Content does not match the type."); return *result; } template requires((std::is_same_v || ...)) T* TryGet() { return const_cast(static_cast*>(this)->TryGet()); } template requires((std::is_same_v || ...)) const T* TryGet() const { constexpr auto i = IndexOf; return index == i ? reinterpret_cast(buffer) : nullptr; } template void Apply(TCallback&& callback) { ElementPack::Apply(index, buffer, std::forward(callback)); } template void Apply(TCallback&& callback) const { ElementPack::Apply(index, buffer, std::forward(callback)); } template bool TryApply(TCallback&& callback) { bool result = true; Apply(Overloading( std::forward(callback), [&result](auto&&) { result = false; } )); return result; } template bool TryApply(TCallback&& callback) const { bool result = true; Apply(Overloading( std::forward(callback), [&result](auto&&) { result = false; } )); return result; } }; } #ifdef VCZH_CHECK_MEMORY_LEAKS_NEW #define new VCZH_CHECK_MEMORY_LEAKS_NEW #endif #endif /*********************************************************************** .\STRINGS\STRING.H ***********************************************************************/ /*********************************************************************** Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ #ifndef VCZH_STRINGS_STRING #define VCZH_STRINGS_STRING namespace vl { /// /// Immutable string. and is recommended instead. /// Locale awared operations are in [T:vl.Locale], typically by using the "INVLOC" macro. /// /// Type of code points. template class ObjectString : public Object { template friend class ObjectString; private: static const T zero; mutable T* buffer = (T*)&zero; mutable atomic_vint* counter = nullptr; mutable vint start = 0; mutable vint length = 0; mutable vint realLength = 0; static vint CalculateLength(const T* buffer) { vint result=0; while(*buffer++)result++; return result; } public: std::strong_ordering operator<=>(const ObjectString& str)const { const T* bufA = buffer + start; const T* bufB = str.buffer + str.start; if (bufA != bufB) { vint minLength = length < str.length ? length : str.length; while (minLength--) { auto diff = *bufA++ <=> *bufB++; if (diff != 0) return diff; }; } return length <=> str.length; } std::strong_ordering operator<=>(const T* str)const { return operator<=>(Unmanaged(str)); } friend std::strong_ordering operator<=>(const T* left, const ObjectString& right) { return Unmanaged(left) <=> right; } bool operator==(const ObjectString& str)const { return operator<=>(str) == 0; } bool operator==(const T* str)const { return operator<=>(Unmanaged(str)) == 0; } friend bool operator==(const T* left, const ObjectString& right) { return (left <=> right) == 0; } private: void Inc()const { if(counter) { INCRC(counter); } } void Dec()const { if(counter) { if(DECRC(counter)==0) { delete[] buffer; delete counter; } } } ObjectString SubUnsafe(vint _start, vint _length)const { if (_length <= 0) return {}; ObjectString str; str.buffer = buffer; str.counter = counter; str.start = start + _start; str.length = _length; str.realLength = realLength; Inc(); return std::move(str); } ObjectString ReplaceUnsafe(const ObjectString& source, vint index, vint count)const { if (source.length == 0 && count == 0) return *this; if (index == 0 && count == length) return source; ObjectString str; str.counter = new atomic_vint(1); str.start = 0; str.length = length - count + source.length; str.realLength = str.length; str.buffer = new T[str.length + 1]; memcpy(str.buffer, buffer + start, sizeof(T) * index); memcpy(str.buffer + index, source.buffer + source.start, sizeof(T) * source.length); memcpy(str.buffer + index + source.length, (buffer + start + index + count), sizeof(T) * (length - index - count)); str.buffer[str.length] = 0; return std::move(str); } ObjectString ReplaceUnsafe(const T* source, vint index, vint count)const { if ((!source || !*source) && count == 0) return *this; if (index == 0 && count == length) return { source }; return ReplaceUnsafe(Unmanaged(source), index, count); } public: static const ObjectString Empty; /// Create an empty string. ObjectString() = default; /// Copy a string. /// The string to copy. ObjectString(const ObjectString&string) { buffer = string.buffer; counter = string.counter; start = string.start; length = string.length; realLength = string.realLength; Inc(); } /// Move a string. /// The string to move. ObjectString(ObjectString && string) { buffer = string.buffer; counter = string.counter; start = string.start; length = string.length; realLength = string.realLength; string.buffer = (T*)&zero; string.counter = nullptr; string.start = 0; string.length = 0; string.realLength = 0; } ~ObjectString() { Dec(); } /// Copy a string. /// Memory to copy. It must be zero terminated. ObjectString(const T* _buffer) { CHECK_ERROR(_buffer != 0, L"ObjectString::ObjectString(const T*)#Cannot construct a string from nullptr."); counter = new atomic_vint(1); start = 0; length = CalculateLength(_buffer); buffer = new T[length + 1]; memcpy(buffer, _buffer, sizeof(T) * (length + 1)); realLength = length; } /// Take over a character pointer with known length. /// The created string. /// The zero-terminated character buffer which should be created using the global operator new[]. /// The number of available characters in the buffer. static ObjectString TakeOver(T* _buffer, vint _length) { CHECK_ERROR(_length >= 0, L"ObjectString::TakeOver(T*, vint)#Length should not be negative."); CHECK_ERROR(_buffer[_length] == 0, L"ObjectString::TakeOver(T*, vint)#Buffer is not properly zero-terminated."); ObjectString str; str.counter = new atomic_vint(1); str.length = _length; str.realLength = _length; str.buffer = _buffer; return std::move(str); } /// Create a string continaing one code point. /// The created string. /// The code point. static ObjectString FromChar(const T& _char) { T buffer[2]; buffer[0] = _char; buffer[1] = 0; return buffer; } /// Copy a string. /// Memory to copy. It is not required to be zero terminated. /// The created string. /// Size of the content in code points. static ObjectString CopyFrom(const T* _buffer, vint _length) { CHECK_ERROR(_length >= 0, L"ObjectString::CopyFrom(const T*, vint)#Length should not be negative."); if (_length > 0) { ObjectString str; str.buffer = new T[_length + 1]; memcpy(str.buffer, _buffer, _length * sizeof(T)); str.buffer[_length] = 0; str.counter = new atomic_vint(1); str.start = 0; str.length = _length; str.realLength = _length; return std::move(str); } return {}; } /// /// Create an unmanaged string. /// Such string uses a piece of unmanaged memory, /// hereby it doesn't release the memory when the string is destroyed. /// /// The created string. /// Unmanaged memory. It must be zero terminated. static ObjectString Unmanaged(const T* _buffer) { CHECK_ERROR(_buffer != 0, L"ObjectString::Unmanaged(const T*)#Cannot construct a string from nullptr."); ObjectString str; str.buffer = (T*)_buffer; str.length = CalculateLength(_buffer); str.realLength = str.length; return std::move(str); } /// /// Unsafe convert one string to another if their character components are in the same size. /// /// The type of the character component of the string to cast. /// The created string /// The string to cast template static ObjectString UnsafeCastFrom(const ObjectString& _string) { static_assert(sizeof(T) == sizeof(T2), "ObjectString::UnsafeCastFrom cannot be used on a string with a different size of the character component."); if (_string.Length() == 0) return {}; ObjectString str; str.buffer = (T*)_string.buffer; str.counter = _string.counter; str.start = _string.start; str.length = _string.length; str.realLength = _string.realLength; str.Inc(); return std::move(str); } /// /// Get the zero terminated buffer in the string. /// Copying parts of a string does not necessarily create a new buffer, /// so in some situation the string will not actually points to a zero terminated buffer. /// In this case, this function will copy the content to a new buffer with a zero terminator and return. /// /// The zero terminated buffer. const T* Buffer()const { if(start+length!=realLength) { T* newBuffer=new T[length+1]; memcpy(newBuffer, buffer+start, sizeof(T)*length); newBuffer[length]=0; Dec(); buffer=newBuffer; counter=new atomic_vint(1); start=0; realLength=length; } return buffer+start; } /// Replace the string by copying another string. /// The string itself. /// The string to copy. ObjectString& operator=(const ObjectString& string) { if(this!=&string) { Dec(); buffer=string.buffer; counter=string.counter; start=string.start; length=string.length; realLength=string.realLength; Inc(); } return *this; } /// Replace the string by moving another string. /// The string itself. /// The string to move. ObjectString& operator=(ObjectString&& string) { if(this!=&string) { Dec(); buffer=string.buffer; counter=string.counter; start=string.start; length=string.length; realLength=string.realLength; string.buffer=(T*)&zero; string.counter=0; string.start=0; string.length=0; string.realLength=0; } return *this; } /// Replace the string by appending another string. /// The string itself. /// The string to append. ObjectString& operator+=(const ObjectString& string) { return *this = *this + string; } /// Replace the string by appending another string. /// The string itself. /// The string to append. ObjectString& operator+=(const T* str) { return *this = *this + str; } /// Create a new string by concatenating two strings. /// The new string. /// The string to append. ObjectString operator+(const ObjectString& string)const { return ReplaceUnsafe(string, length, 0); } /// Create a new string by concatenating two strings. /// The new string. /// The string to append. ObjectString operator+(const T* str)const { return ReplaceUnsafe(str, length, 0); } friend ObjectString operator+(const T* left, const ObjectString& right) { if (right.Length() == 0) { return { left }; } else { return Unmanaged(left) + right; } } /// Get a code point in the specified position. /// Returns the code point. It will crash when the specified position is out of range. /// T operator[](vint index)const { CHECK_ERROR(index>=0 && index:operator[](vint)#Argument index not in range."); return buffer[start+index]; } /// Get the size of the string in code points. /// The size, not including the zero terminator. vint Length()const { return length; } /// Find a code point. /// The position of the code point. Returns -1 if it does not exist. /// The code point to find. vint IndexOf(T c)const { const T* reading=buffer+start; for(vint i=0;iGet the prefix of the string. /// The prefix. It will crash when the specified size is out of range. /// Size of the prefix. /// ObjectString Left(vint count)const { CHECK_ERROR(count>=0 && count<=length, L"ObjectString::Left(vint)#Argument count not in range."); return SubUnsafe(0, count); } /// Get the postfix of the string. /// The postfix. It will crash when the specified size is out of range. /// Size of the prefix. /// ObjectString Right(vint count)const { CHECK_ERROR(count>=0 && count<=length, L"ObjectString::Right(vint)#Argument count not in range."); return SubUnsafe(length-count, count); } /// Get a sub string. /// The sub string. It will crash when the specified position or size is out of range. /// The position of the first code point of the sub string. /// The size of the sub string. /// ObjectString Sub(vint index, vint count)const { CHECK_ERROR(index>=0 && index<=length, L"ObjectString::Sub(vint, vint)#Argument index not in range."); CHECK_ERROR(index+count>=0 && index+count<=length, L"ObjectString::Sub(vint, vint)#Argument count not in range."); return SubUnsafe(index, count); } /// Get a string by removing a sub string. /// The string without the sub string. It will crash when the specified position or size is out of range. /// The position of the first code point of the sub string. /// The size of the sub string. /// ObjectString Remove(vint index, vint count)const { CHECK_ERROR(index>=0 && index::Remove(vint, vint)#Argument index not in range."); CHECK_ERROR(index+count>=0 && index+count<=length, L"ObjectString::Remove(vint, vint)#Argument count not in range."); return ReplaceUnsafe(ObjectString(), index, count); } /// Get a string by inserting another string. /// The string with another string inserted. It will crash when the specified position is out of range. /// The position to insert. /// The string to insert. /// ObjectString Insert(vint index, const ObjectString& string)const { CHECK_ERROR(index>=0 && index<=length, L"ObjectString::Insert(vint)#Argument count not in range."); return ReplaceUnsafe(string, index, 0); } }; template const ObjectString ObjectString::Empty = ObjectString(); template const T ObjectString::zero=0; extern template class ObjectString; extern template class ObjectString; extern template class ObjectString; extern template class ObjectString; extern template class ObjectString; /// Ansi string in local code page. typedef ObjectString AString; /// Unicode string, UTF-16 on Windows, UTF-32 on Linux and macOS. typedef ObjectString WString; /// Utf-8 String. typedef ObjectString U8String; /// Utf-16 String. typedef ObjectString U16String; /// Utf-32 String. typedef ObjectString U32String; /// Convert a string to a signed integer. /// The converted number. If the conversion failed, the result is undefined. /// The string to convert. /// Returns true if this operation succeeded. extern vint atoi_test(const AString& string, bool& success); /// Convert a string to a signed integer. /// The converted number. If the conversion failed, the result is undefined. /// The string to convert. /// Returns true if this operation succeeded. extern vint wtoi_test(const WString& string, bool& success); /// Convert a string to a signed 64-bits integer. /// The converted number. If the conversion failed, the result is undefined. /// The string to convert. /// Returns true if this operation succeeded. extern vint64_t atoi64_test(const AString& string, bool& success); /// Convert a string to a signed 64-bits integer. /// The converted number. If the conversion failed, the result is undefined. /// The string to convert. /// Returns true if this operation succeeded. extern vint64_t wtoi64_test(const WString& string, bool& success); /// Convert a string to an unsigned integer. /// The converted number. If the conversion failed, the result is undefined. /// The string to convert. /// Returns true if this operation succeeded. extern vuint atou_test(const AString& string, bool& success); /// Convert a string to an unsigned integer. /// The converted number. If the conversion failed, the result is undefined. /// The string to convert. /// Returns true if this operation succeeded. extern vuint wtou_test(const WString& string, bool& success); /// Convert a string to an unsigned 64-bits integer. /// The converted number. If the conversion failed, the result is undefined. /// The string to convert. /// Returns true if this operation succeeded. extern vuint64_t atou64_test(const AString& string, bool& success); /// Convert a string to an unsigned 64-bits integer. /// The converted number. If the conversion failed, the result is undefined. /// The string to convert. /// Returns true if this operation succeeded. extern vuint64_t wtou64_test(const WString& string, bool& success); /// Convert a string to a 64-bits floating point number. /// The converted number. If the conversion failed, the result is undefined. /// The string to convert. /// Returns true if this operation succeeded. extern double atof_test(const AString& string, bool& success); /// Convert a string to a 64-bits floating point number. /// The converted number. If the conversion failed, the result is undefined. /// The string to convert. /// Returns true if this operation succeeded. extern double wtof_test(const WString& string, bool& success); /// Convert a string to a signed integer. /// The converted number. If the conversion failed, the result is 0. /// The string to convert. /// If you need to know whether the conversion is succeeded or not, please use instead. extern vint atoi(const AString& string); /// Convert a string to a signed integer. /// The converted number. If the conversion failed, the result is 0. /// The string to convert. /// If you need to know whether the conversion is succeeded or not, please use instead. extern vint wtoi(const WString& string); /// Convert a string to a signed 64-bits integer. /// The converted number. If the conversion failed, the result is 0. /// The string to convert. /// If you need to know whether the conversion is succeeded or not, please use instead. extern vint64_t atoi64(const AString& string); /// Convert a string to a signed 64-bits integer. /// The converted number. If the conversion failed, the result is 0. /// The string to convert. /// If you need to know whether the conversion is succeeded or not, please use instead. extern vint64_t wtoi64(const WString& string); /// Convert a string to an usigned integer. /// The converted number. If the conversion failed, the result is 0. /// The string to convert. /// If you need to know whether the conversion is succeeded or not, please use instead. extern vuint atou(const AString& string); /// Convert a string to an usigned integer. /// The converted number. If the conversion failed, the result is 0. /// The string to convert. /// If you need to know whether the conversion is succeeded or not, please use instead. extern vuint wtou(const WString& string); /// Convert a string to an usigned 64-bits integer. /// The converted number. If the conversion failed, the result is 0. /// The string to convert. /// If you need to know whether the conversion is succeeded or not, please use instead. extern vuint64_t atou64(const AString& string); /// Convert a string to an usigned 64-bits integer. /// The converted number. If the conversion failed, the result is 0. /// The string to convert. /// If you need to know whether the conversion is succeeded or not, please use instead. extern vuint64_t wtou64(const WString& string); /// Convert a string to a 64-bits floating point number. /// The converted number. If the conversion failed, the result is 0. /// The string to convert. /// If you need to know whether the conversion is succeeded or not, please use instead. extern double atof(const AString& string); /// Convert a string to a 64-bits floating point number. /// The converted number. If the conversion failed, the result is 0. /// The string to convert. /// If you need to know whether the conversion is succeeded or not, please use instead. extern double wtof(const WString& string); /// Convert a signed interger to a string. /// The converted string. /// The number to convert. extern AString itoa(vint number); /// Convert a signed interger to a string. /// The converted string. /// The number to convert. extern WString itow(vint number); /// Convert a signed 64-bits interger to a string. /// The converted string. /// The number to convert. extern AString i64toa(vint64_t number); /// Convert a signed 64-bits interger to a string. /// The converted string. /// The number to convert. extern WString i64tow(vint64_t number); /// Convert an unsigned interger to a string. /// The converted string. /// The number to convert. extern AString utoa(vuint number); /// Convert an unsigned interger to a string. /// The converted string. /// The number to convert. extern WString utow(vuint number); /// Convert an unsigned 64-bits interger to a string. /// The converted string. /// The number to convert. extern AString u64toa(vuint64_t number); /// Convert an unsigned 64-bits interger to a string. /// The converted string. /// The number to convert. extern WString u64tow(vuint64_t number); /// Convert a 64-bits floating pointer number to a string. /// The converted string. /// The number to convert. extern AString ftoa(double number); /// Convert a 64-bits floating pointer number to a string. /// The converted string. /// The number to convert. extern WString ftow(double number); /// Convert all letters to lower case letters. /// The converted string. /// The string to convert. extern AString alower(const AString& string); /// Convert all letters to lower case letters. /// The converted string. /// The string to convert. extern WString wlower(const WString& string); /// Convert all letters to upper case letters. /// The converted string. /// The string to convert. extern AString aupper(const AString& string); /// Convert all letters to upper case letters. /// The converted string. /// The string to convert. extern WString wupper(const WString& string); #if defined VCZH_GCC extern void _itoa_s(vint32_t value, char* buffer, size_t size, vint radix); extern void _itow_s(vint32_t value, wchar_t* buffer, size_t size, vint radix); extern void _i64toa_s(vint64_t value, char* buffer, size_t size, vint radix); extern void _i64tow_s(vint64_t value, wchar_t* buffer, size_t size, vint radix); extern void _uitoa_s(vuint32_t value, char* buffer, size_t size, vint radix); extern void _uitow_s(vuint32_t value, wchar_t* buffer, size_t size, vint radix); extern void _ui64toa_s(vuint64_t value, char* buffer, size_t size, vint radix); extern void _ui64tow_s(vuint64_t value, wchar_t* buffer, size_t size, vint radix); extern void _gcvt_s(char* buffer, size_t size, double value, vint numberOfDigits); extern void _strlwr_s(char* buffer, size_t size); extern void _strupr_s(char* buffer, size_t size); extern void _wcslwr_s(wchar_t* buffer, size_t size); extern void _wcsupr_s(wchar_t* buffer, size_t size); extern void wcscpy_s(wchar_t* buffer, size_t size, const wchar_t* text); #endif } #endif /*********************************************************************** .\COLLECTIONS\OPERATIONSTRING.H ***********************************************************************/ /*********************************************************************** Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ #ifndef VCZH_COLLECTIONS_OPERATIONSTRING #define VCZH_COLLECTIONS_OPERATIONSTRING namespace vl { namespace collections { /// Copy containers. /// Type of the target container. /// Type of code points in the source string. /// The target container. /// The source string. /// Set to true to perform appending instead of replacing. template void CopyFrom(Ds& ds, const ObjectString& ss, bool append = false) { const S* buffer = ss.Buffer(); vint count = ss.Length(); CopyFrom(ds, buffer, count, append); } /// Copy containers. /// Type of code points in the target string. /// Type of the source container. /// The target string. /// The source container. /// Set to true to perform appending instead of replacing. template void CopyFrom(ObjectString& ds, const Ss& ss, bool append = false) { Array da(ds.Buffer(), ds.Length()); CopyFrom(da, ss, append); if (da.Count() == 0) { ds = {}; } else { ds = ObjectString::CopyFrom(&da[0], da.Count()); } } } } #endif /*********************************************************************** .\COLLECTIONS\OPERATION.H ***********************************************************************/ /*********************************************************************** Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License Functions: CopyFrom(TargetContainer, SourceContainer) [T] .Select(T->K) => [K] [T] .Where(T->bool) => [T] [Ptr] .Cast() => [Ptr] [Ptr] .FindType() => [Ptr] [T] .OrderBy(T->T->std::strong_order) => [T] [T] .OrderByKey(T->U) => [T] [T] .OrderBySelf() => [T] [T] .Aggregate(T->T->T) => T [T] .Aggregate(T, T->T->T) => T [T] .All(T->bool) => bool [T] .Any(T->bool) => bool [T] .Max() => T [T] .Min() => T [T] .First() => T [T] .FirstOrDefault(T) => T [T] .Last() => T [T] .LastOrDefault(T) => T [T] .Count() => vint [T] .IsEmpty() => bool [T] .Concat([T]) => [T] (evaluated) [T] .Take(vint) => [T] (evaluated) [T] .Skip(vint) => [T] (evaluated) [T] .Repeat(vint) => [T] (evaluated) [T] .Distinct() => [T] [T] .Reverse() => [T] (evaluated) [T] .Pairwise([K]) => [(T,K)] (evaluated) [T] .Intersect([T]) => [T] [T] .Except([T]) => [T] [T] .Union([T]) => [T] [T] .Evaluate() => [T] [T] .SelectMany(T->[K]) => [K] [T] .GroupBy(T->K) => [(K, [T])] (evaluated) means the lazy list is evaluated when all sources are evaluated From(begin, end) => [T] From(array) => [T] Range(start, count) => [vint] for (auto x : xs); for (auto [x, i] : indexed(xs)); ***********************************************************************/ #ifndef VCZH_COLLECTIONS_OPERATION #define VCZH_COLLECTIONS_OPERATION namespace vl { namespace collections { /*********************************************************************** Quick Sort ***********************************************************************/ /// Quick sort. /// Type of elements. /// Type of the comparer. /// Pointer to element array to sort. /// The number of elements to sort. /// /// The comparar for two elements returning std::(strong|weak|partial)_ordering. /// template void SortLambda(T* items, vint length, F&& orderer) { while (true) { if (length == 0) return; vint pivot = 0; vint left = 0; vint right = 0; { bool flag = false; while (left + right + 1 != length) { vint& mine = (flag ? left : right); vint& theirs = (flag ? right : left); vint candidate = (flag ? left : length - right - 1); vint factor = (flag ? -1 : 1); auto ordering = orderer(items[pivot], items[candidate]); if constexpr (std::is_same_v) { CHECK_ERROR(ordering != std::partial_ordering::unordered, L"vl::collections::SortLambda(T*, vint, F&&)#This function could not apply on elements in partial ordering."); } if ((factor == 1 && ordering <= 0) || (factor == -1 && ordering >= 0)) { mine++; } else { theirs++; T temp = items[pivot]; items[pivot] = items[candidate]; items[candidate] = temp; pivot = candidate; flag = !flag; } } } { vint reading = left - 1; vint writing = reading; while (reading >= 0) { auto ordering = orderer(items[pivot], items[reading]); if constexpr (std::is_same_v) { CHECK_ERROR(ordering != std::partial_ordering::unordered, L"vl::collections::SortLambda(T*, vint, F&&)#This function could not apply on elements in partial ordering."); } if (ordering == 0) { if (reading != writing) { T temp = items[reading]; items[reading] = items[writing]; items[writing] = temp; } writing--; } reading--; } left = writing + 1; } { vint reading = length - right; vint writing = reading; while (reading < length) { if (orderer(items[pivot], items[reading]) == 0) { if (reading != writing) { T temp = items[reading]; items[reading] = items[writing]; items[writing] = temp; } writing++; } reading++; } right = length - writing; } if (left < right) { SortLambda(items, left, orderer); items += length - right; length = right; } else { SortLambda(items + length - right, right, orderer); length = left; } } } /// Quick sort. /// Type of elements. /// Pointer to element array to sort. /// The number of elements to sort. /// /// The comparar for two elements returning std::(strong|weak|partial)_ordering. /// template void Sort(T* items, vint length, F&& orderer) { SortLambda(items, length, orderer); } /// Quick sort. /// Type of elements. /// Pointer to element array to sort. /// The number of elements to sort. template void Sort(T* items, vint length) { SortLambda(items, length, [](const T& a, const T& b) { return a <=> b; }); } /*********************************************************************** LazyList ***********************************************************************/ /// A lazy evaluated container with rich operations. is useful to create lazy list from arrays or containers. /// The type of elements. /// ///

A lazy list is usually created directly from a container source, or from a calculation on a source.

///

Typically the lazy list cannot be used after the source is deleted.

///

/// If this lazy list needs to be used after the source is deleted, /// you are recommended to use [F:vl.collections.LazyList`1.Evaluate], with forceCopy set to true. ///

///

In this way you get a lazy list with all values copied, they do not rely on other objects.

///
template class LazyList : public EnumerableBase { protected: Ptr> enumeratorPrototype; IEnumerator* xs()const { if (enumeratorPrototype) { return enumeratorPrototype->Clone(); } else { return new EmptyEnumerator(); } } using TInput = decltype(std::declval>().Current()); public: /// Create a lazy list from an enumerator. This enumerator will be deleted when this lazy list is deleted. /// The enumerator. LazyList(IEnumerator* enumerator) :enumeratorPrototype(enumerator) { } /// Create a lazy list from an enumerator. /// The enumerator. LazyList(Ptr> enumerator) :enumeratorPrototype(enumerator) { } /// Create a lazy list from an enumerable. /// The enumerable. LazyList(const IEnumerable& enumerable) :enumeratorPrototype(enumerable.CreateEnumerator()) { } /// Create a lazy list from another lazy list. /// The lazy list. LazyList(const LazyList& lazyList) :enumeratorPrototype(lazyList.enumeratorPrototype) { // no need to clone enumeratorPrototype as it will never be iterated } /// Create a lazy list from another lazy list. /// The lazy list. LazyList(LazyList&& lazyList) :enumeratorPrototype(lazyList.enumeratorPrototype) { lazyList.enumeratorPrototype = nullptr; } /// Create a lazy list from a container. It is very useful to a container as an intermediate result and then put in a lazy list. /// Type of the container. /// The container. template LazyList(Ptr container) :enumeratorPrototype(new ContainerEnumerator(container)) { } /// Create an empty lazy list. LazyList() { } LazyList& operator=(const LazyList& lazyList) { // no need to clone enumeratorPrototype as it will never be iterated enumeratorPrototype = lazyList.enumeratorPrototype; return *this; } LazyList& operator=(LazyList&& lazyList) { enumeratorPrototype = lazyList.enumeratorPrototype; lazyList.enumeratorPrototype = nullptr; return *this; } IEnumerator* CreateEnumerator()const { return xs(); } //------------------------------------------------------- /// Create a new lazy list with all elements transformed. /// Type of the transformer. /// The created lazy list. /// The transformer. /// template auto Select(F&& f) const -> LazyList()))> { return new SelectEnumerator()))>(xs(), f); } /// Create a new lazy list with all elements filtered. /// Type of the filter. /// The created lazy list. /// The filter. /// template LazyList Where(F&& f)const { return new WhereEnumerator(xs(), f); } /// Create a new lazy list with all elements casted to a new type. /// The new type. /// The created lazy list. /// /// The lazy list being casted contains elements of type [T:vl.Ptr`1]. /// [F:vl.Ptr`1.Cast`1] is called on each elements. /// If some elements fail to cast, they become empty shared pointers. /// template LazyList> Cast()const { Func(T)> f = [](T t)->Ptr {return t.template Cast(); }; return new SelectEnumerator>(xs(), f); } /// Create a new lazy list with only elements that successfully casted to a new type. /// The new type. /// The created lazy list. /// /// The lazy list being casted contains elements of type [T:vl.Ptr`1]. /// [F:vl.Ptr`1.Cast`1] is called on each elements. /// If some elements fail to cast, they are eliminated from the result. /// template LazyList> FindType()const { return Cast().Where([](Ptr t) {return t; }); } /// Create a new lazy list with all elements sorted. /// Type of the comparer. /// The created lazy list. /// /// The comparar for two elements returning std::(strong|weak|partial)_ordering. /// /// y; }); /// for (auto y : ys) Console::Write(itow(y) + L" "); /// } /// ]]> template LazyList OrderBy(F&& f)const { auto sorted = Ptr(new List); CopyFrom(*sorted.Obj(), *this); if (sorted->Count() > 0) { SortLambda( &sorted->operator[](0), sorted->Count(), f ); } return sorted; } /// Create a new lazy list with all elements sorted. /// The created lazy list. /// /// The key retriver function. Comparing of two element a and b are defined as f(a)<=>f(b). /// /// template LazyList OrderByKey(F&& f)const { auto sorted = Ptr(new List); CopyFrom(*sorted.Obj(), *this); if (sorted->Count() > 0) { SortLambda( &sorted->operator[](0), sorted->Count(), [f](const T& a, const T& b) { return f(a) <=> f(b); } ); } return sorted; } /// Create a new lazy list with all elements sorted. /// The created lazy list. /// LazyList OrderBySelf()const { auto sorted = Ptr(new List); CopyFrom(*sorted.Obj(), *this); if (sorted->Count() > 0) { SortLambda( &sorted->operator[](0), sorted->Count(), [](const T& a, const T& b) { return a <=> b; } ); } return sorted; } //------------------------------------------------------- /// Aggregate a lazy list. It will crash if the lazy list is empty. /// Type of the aggregator. /// The aggregated value. /// /// The aggregator. /// The first argument is the aggregated value of any prefix. /// The second argument is the element right after a prefix. /// Returns the aggregated value of the new prefix. /// For the first call, the first argument is the first element in the lazy list. /// /// template T Aggregate(F&& f)const { auto enumerator = Ptr(CreateEnumerator()); if (!enumerator->Next()) { throw Error(L"LazyList::Aggregate(F)#Aggregate failed to calculate from an empty container."); } T result = enumerator->Current(); while (enumerator->Next()) { result = f(result, enumerator->Current()); } return result; } /// Aggregate a lazy list. /// Type of the initial value. /// Type of the aggregator. /// The aggregated value. /// The aggregated value defined for the empty prefix. /// /// The aggregator. /// The first argument is the aggregated value of any prefix. /// The second argument is the element right after a prefix. /// Returns the aggregated value of the new prefix. /// /// template I Aggregate(I init, F&& f)const { for (auto& t : *this) { init = f(init, t); } return init; } /// Test if all elements in the lazy list satisfy a filter. /// Type of the filter. /// Returns true if all elements satisfy the filter. /// The filter. /// template bool All(F&& f)const { return Select(f).Aggregate(true, [](bool a, bool b) { return a && b; }); } /// Test if any elements in the lazy list satisfy a filter. /// Type of the filter. /// Returns true if there is at least one element satisfies the filter. /// The filter. /// template bool Any(F&& f)const { return Select(f).Aggregate(false, [](bool a, bool b) { return a || b; }); } /// Get the maximum value in the lazy list. It will crash if the lazy list is empty. /// The maximum value. /// T Max()const { return Aggregate([](T a, T b) { return a > b ? a : b; }); } /// Get the minimum value in the lazy list. It will crash if the lazy list is empty. /// The minimum value. /// T Min()const { return Aggregate([](T a, T b) { return a < b ? a : b; }); } /// Get the first value in the lazy list. It will crash if the lazy list is empty. /// The first value. T First()const { auto enumerator = Ptr(CreateEnumerator()); if (!enumerator->Next()) { throw Error(L"LazyList::First(F)#First failed to calculate from an empty container."); } return enumerator->Current(); } /// Get the first value in the lazy list. /// The first value. If the lazy list is empty, the argument is returned. /// The argument to return if the lazy list is empty. T First(T defaultValue)const { auto enumerator = Ptr(CreateEnumerator()); if (!enumerator->Next()) { return defaultValue; } return enumerator->Current(); } /// Get the last value in the lazy list. It will crash if the lazy list is empty. /// The last value. T Last()const { auto enumerator = Ptr(CreateEnumerator()); if (!enumerator->Next()) { throw Error(L"LazyList::Last(F)#Last failed to calculate from an empty container."); } else { T value = enumerator->Current(); while (enumerator->Next()) { value = enumerator->Current(); } return value; } } /// Get the last value in the lazy list. /// The last value. If the lazy list is empty, the argument is returned. /// The argument to return if the lazy list is empty. T Last(T defaultValue)const { auto enumerator = Ptr(CreateEnumerator()); while (enumerator->Next()) { defaultValue = enumerator->Current(); } return defaultValue; } /// Get the number of elements in the lazy list. /// The number of elements. vint Count()const { vint result = 0; auto enumerator = Ptr(CreateEnumerator()); while (enumerator->Next()) { result++; } return result; } /// Test if the lazy list is empty. /// Returns true if the lazy list is empty. bool IsEmpty()const { auto enumerator = Ptr(CreateEnumerator()); return !enumerator->Next(); } //------------------------------------------------------- /// Create a new lazy list containing elements of the two container in order. /// The created lazy list. /// Elements to be appended. /// LazyList Concat(const IEnumerable& remains)const { return new ConcatEnumerator(xs(), remains.CreateEnumerator()); } /// Create a new lazy list with a prefix of the lazy list. /// The created lazy list. /// The size of the prefix. /// LazyList Take(vint count)const { return new TakeEnumerator(xs(), count); } /// Create a new lazy list with a postfix of the lazy list. /// The created lazy list. /// The number of elements to skip. /// LazyList Skip(vint count)const { return new SkipEnumerator(xs(), count); } /// Create a new lazy list with several copies of the lazy list in order. /// The created lazy list. /// The numbers of copies. /// LazyList Repeat(vint count)const { return new RepeatEnumerator(xs(), count); } /// Create a new lazy list with duplicated elements removed in this lazy list. /// The created lazy list. /// LazyList Distinct()const { return new DistinctEnumerator(xs()); } /// Create a new lazy list with all elements in this lazy list in a reverse order. /// The created lazy list. /// LazyList Reverse()const { return new ReverseEnumerator(*this); } //------------------------------------------------------- /// Create a new lazy list of pairs from elements from two containers. /// Type of all elements in the second container. /// /// The created lazy list, which contains pairs of elements from two containers at the same position. /// If the two container have different sizes, the created lazy list has the size of the shorter one. /// /// The second container. /// p){ return p.key + p.value; }); /// for (auto z : zs) Console::Write(itow(z) + L" "); /// } /// ]]> template LazyList> Pairwise(const IEnumerable& remains)const { return new PairwiseEnumerator(xs(), remains.CreateEnumerator()); } /// Create a new lazy list with elements from the lazy list, which also appear in the second container. /// The created lazy list. Elements in the create lazy list is in the same order as in this lazy list. /// The second container. /// LazyList Intersect(const IEnumerable& remains)const { return LazyList(new IntersectExceptEnumerator(xs(), remains)).Distinct(); } /// Create a new lazy list with elements from the lazy list, which do not appear in the second container. /// The created lazy list. Elements in the create lazy list is in the same order as in this lazy list. /// The second container. /// LazyList Except(const IEnumerable& remains)const { return LazyList(new IntersectExceptEnumerator(xs(), remains)).Distinct(); } /// Create a new lazy list with elements in two containers. Duplicated elements will be removed. /// The created lazy list. /// The second container. /// LazyList Union(const IEnumerable& remains)const { return Concat(remains).Distinct(); } //------------------------------------------------------- /// Get an evaluated copy of this lazy list. /// /// The created lazy list. /// If this lazy list has been evaluated before, it returns a reference to this lazy list. /// If this lazy list has not been evaluated before, it go through this lazy list and copy all values. /// /// Set to true to force copying values, regardless of whether this lazy list is evaluated or not. /// ///

"Evaluated" means reading from this lazy list cause no extra calculation.

///

In most of the cases, the created lazy list relies on its source.

///

For example, a lazy list can be created from a reference to a , or from an array on stack.

///

If this list or array is deleted, then iterating the created lazy list will crash.

///

By calling the Evaluate function with forceCopy set to true, a new lazy list is created, with all values cached in it.

///

Its connection to the source list or array is removed, and can then be passed to everywhere.

///
LazyList Evaluate(bool forceCopy = false)const { if (!enumeratorPrototype) { return *this; } else if (!forceCopy && enumeratorPrototype->Evaluated()) { return *this; } else { auto xs = Ptr(new List); CopyFrom(*xs.Obj(), *this); return xs; } } /// Create a new lazy list, whose elements are from transformed elements in this lazy list. /// Type of the transformer. /// The created lazy list. /// /// The transformer. /// The first argument is any element in this lazy list. /// Returns the transformed lazy list from this argument. /// /// template auto SelectMany(F&& f)const -> LazyList()))::ElementType> { using U = typename decltype(f(std::declval()))::ElementType; return Select(f).Aggregate(LazyList(), [](const LazyList& a, const IEnumerable& b)->LazyList {return a.Concat(b); }); } /// Create a new lazy list, with elements from this lazy list grouped by a key function. /// Type of the key function. /// The created lazy list. /// /// The key function. /// The first argument is any element in this lazy list. /// Returns a key calculated from this argument. /// Elements that have the same key will be grouped together. /// /// >; /// for (auto y : ys) /// { /// Console::Write(itow(y.key) + L":"); /// for (auto z : y.value) Console::Write(L" " + itow(z)); /// Console::WriteLine(L""); /// } /// } /// ]]> template auto GroupBy(F&& f)const -> LazyList())), LazyList>> { using K = decltype(f(std::declval())); auto self = *this; return Select(f) .Distinct() .Select([=](K k) { return Pair>( k, self.Where([=](T t) {return k == f(t); }) ); }); } }; /// Create a lazy list with a series of increasing number. /// Type of elements. /// A lazy list of increasing numbers. /// The first number. /// Total amount of increasing numbers. template LazyList Range(T start, T count) { return new RangeEnumerator(start, count); } /// Create a lazy list from an enumerable. /// Type of elements. /// The created lazy list. /// The enumerable. template LazyList From(const IEnumerable& enumerable) { return enumerable; } /// Create a lazy list from another lazy list. /// Type of elements. /// The created lazy list. /// The lazy list to copy. template LazyList From(const LazyList& enumerable) { return enumerable; } /// Create a lazy list from an array. /// Type of elements. /// The created lazy list. /// Pointer to the first element in the array. /// Pointer to the element after the last element in the array. template LazyList From(const T* begin, const T* end) { return FromPointer(begin, end); } /// Create a lazy list from an array. /// Type of elements. /// Size of the array. /// The created lazy list. /// The array. template LazyList From(T (&items)[size]) { return FromArray(items); } /// Create a lazy list from an array. /// Type of elements. /// Size of the array. /// The created lazy list. /// The array. template LazyList From(const T (&items)[size]) { return FromArray(items); } /*********************************************************************** Range-Based For-Loop Iterator with Index for LazyList ***********************************************************************/ template struct LazyListWithIndex { LazyList lazyList; LazyListWithIndex(const LazyList& _lazyList) : lazyList(_lazyList) { } }; template LazyListWithIndex indexed(const LazyList& lazyList) { return { lazyList }; } template RangeBasedForLoopIteratorWithIndex begin(const LazyListWithIndex& enumerable) { return { enumerable.lazyList }; } template RangeBasedForLoopEnding end(const LazyListWithIndex& enumerable) { return {}; } } } #endif /*********************************************************************** .\CONSOLE.H ***********************************************************************/ /*********************************************************************** Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ #ifndef VCZH_CONSOLE #define VCZH_CONSOLE namespace vl { namespace console { /// Basic I/O for command-line applications. class Console abstract { public: /// Write a string to the command-line window. /// Content to write. /// Size of the content in wchar_t, not including the zero terminator. static void Write(const wchar_t* string, vint length); /// Write a string to the command-line window. /// Content to write, must be zero terminated. static void Write(const wchar_t* string); /// Write a string to the command-line window. /// Content to write. static void Write(const WString& string); /// Write to the command-line window, following CR/LF characters. /// Content to write. static void WriteLine(const WString& string); /// Try to read a string from the command-line window or redirected input. /// The whole line read from the command-line window or redirected input. Returns null if no line is available. static Nullable TryRead(); /// Read a string from the command-line window or redirected input. /// The whole line read from the command-line window or redirected input. Returns an empty string if no line is available. static WString Read(); static void SetColor(bool red, bool green, bool blue, bool light); static void SetTitle(const WString& string); }; } } #endif /*********************************************************************** .\EXCEPTION.H ***********************************************************************/ /*********************************************************************** Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ #ifndef VCZH_EXCEPTION #define VCZH_EXCEPTION namespace vl { /// Base type of all exceptions. class Exception : public Object { protected: WString message; public: Exception(const WString& _message=WString::Empty); const WString& Message()const; }; class ArgumentException : public Exception { protected: WString function; WString name; public: ArgumentException(const WString& _message=WString::Empty, const WString& _function=WString::Empty, const WString& _name=WString::Empty); const WString& GetFunction()const; const WString& GetName()const; }; } #endif /*********************************************************************** .\GLOBALSTORAGE.H ***********************************************************************/ /*********************************************************************** Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ #ifndef VCZH_GLOBALSTORAGE #define VCZH_GLOBALSTORAGE namespace vl { /// /// Base type of all global storages. /// A global storage stores multiple values using a name. /// The "BEGIN_GLOBAL_STOREGE_CLASS" macro is recommended to create a global storage. /// /// /// All values are shared like global variables, but they are created at the first time when they need to be accessed. /// is recommended after you don't need any global storages any more, it frees memory. /// /// data; /// INITIALIZE_GLOBAL_STORAGE_CLASS /// data = new vint(100); /// FINALIZE_GLOBAL_STORAGE_CLASS /// data = nullptr; /// END_GLOBAL_STORAGE_CLASS(MyStorage) /// /// int main() /// { /// // GetMyStorage is generated by defining MyStorage /// Console::WriteLine(itow(*GetMyStorage().data.Obj())); /// FinalizeGlobalStorage(); /// } /// ]]> class GlobalStorage : public Object { private: bool initialized = false; protected: virtual void InitializeResource() = 0; virtual void FinalizeResource() = 0; public: NOT_COPYABLE(GlobalStorage); GlobalStorage(); ~GlobalStorage(); bool IsInitialized(); void EnsureInitialized(); void EnsureFinalized(); }; struct GlobalStorageDescriptor { GlobalStorage* globalStorage = nullptr; GlobalStorageDescriptor* next = nullptr; }; extern void RegisterStorageDescriptor(GlobalStorageDescriptor* globalStorageDescriptor); /// Free all memories used by global storages. extern void FinalizeGlobalStorage(); } #define BEGIN_GLOBAL_STORAGE_CLASS(NAME) \ class NAME \ : public vl::GlobalStorage \ , private vl::GlobalStorageDescriptor \ { \ public: \ NAME() \ { \ globalStorage = this; \ vl::RegisterStorageDescriptor(this); \ } \ ~NAME() \ { \ EnsureFinalized(); \ } \ #define INITIALIZE_GLOBAL_STORAGE_CLASS \ protected: \ void InitializeResource() \ { \ #define FINALIZE_GLOBAL_STORAGE_CLASS \ } \ protected: \ void FinalizeResource() \ { \ #define END_GLOBAL_STORAGE_CLASS(NAME) \ } \ }; \ NAME& Get##NAME() \ { \ static NAME __global_storage_##NAME; \ __global_storage_##NAME.EnsureInitialized(); \ return __global_storage_##NAME; \ } \ #define EXTERN_GLOBAL_STORAGE_CLASS(NAME)\ class NAME;\ extern NAME& Get##NAME();\ #endif /*********************************************************************** .\STRINGS\CONVERSION.H ***********************************************************************/ /*********************************************************************** Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ #ifndef VCZH_STRINGS_CONVERSION #define VCZH_STRINGS_CONVERSION namespace vl { struct char16be_t { char16_t value; char16be_t() = default; char16be_t(const char16be_t&) = default; char16be_t(char16be_t&&) = default; char16be_t& operator=(const char16be_t&) = default; char16be_t& operator=(char16be_t&&) = default; char16be_t(char16_t c) : value(c) { } __forceinline auto operator<=>(char16be_t c) const { return value <=> c.value; } operator bool() const { return static_cast(value); } }; namespace encoding { struct UtfCharCluster { vint index; vint size; }; template __forceinline void SwapByteForUtf16BE(T& c) { static_assert(sizeof(T) == sizeof(char16_t)); vuint8_t* bytes = (vuint8_t*)&c; vuint8_t t = bytes[0]; bytes[0] = bytes[1]; bytes[1] = t; } /*********************************************************************** UtfConversion ***********************************************************************/ template struct UtfConversion; template<> struct UtfConversion { #if defined VCZH_WCHAR_UTF16 static const vint BufferLength = 2; #elif defined VCZH_WCHAR_UTF32 static const vint BufferLength = 1; #endif static vint From32(char32_t source, wchar_t(&dest)[BufferLength]); static vint To32(const wchar_t* source, vint sourceLength, char32_t& dest); }; template<> struct UtfConversion { static const vint BufferLength = 6; static vint From32(char32_t source, char8_t(&dest)[BufferLength]); static vint To32(const char8_t* source, vint sourceLength, char32_t& dest); }; template<> struct UtfConversion { static const vint BufferLength = 2; static vint From32(char32_t source, char16_t(&dest)[BufferLength]); static vint To32(const char16_t* source, vint sourceLength, char32_t& dest); }; template<> struct UtfConversion { static const vint BufferLength = 2; static vint From32(char32_t source, char16be_t(&dest)[BufferLength]); static vint To32(const char16be_t* source, vint sourceLength, char32_t& dest); }; /*********************************************************************** UtfReaderConsumer ***********************************************************************/ template class UtfEmptyConsumerRedirection : public Object { public: UtfEmptyConsumerRedirection(TInternalConsumer&) { } }; template class TConsumerRedirection> class UtfReaderConsumer : public TConsumerRedirection { protected: TReader internalReader; auto Consume() { return internalReader.Read(); } public: template UtfReaderConsumer(TArguments&& ...arguments) : internalReader(std::forward(arguments)...) , TConsumerRedirection(internalReader) { } }; /*********************************************************************** UtfFrom32ReaderBase ***********************************************************************/ template class UtfFrom32ReaderBase : public TConsumer { static const vint BufferLength = UtfConversion::BufferLength; vint read = 0; vint available = 0; T buffer[BufferLength]; UtfCharCluster sourceCluster = { 0,0 }; vint readCounter = -1; public: using ConsumerType = TConsumer; template UtfFrom32ReaderBase(TArguments&& ...arguments) : TConsumer(std::forward(arguments)...) { } T Read() { if (available == -1) return 0; if (read == available) { char32_t c = this->Consume(); if (c) { available = UtfConversion::From32(c, buffer); if (available == -1) return 0; sourceCluster.index += sourceCluster.size; sourceCluster.size = 1; } else { available = -1; readCounter++; sourceCluster.index += sourceCluster.size; sourceCluster.size = 0; return 0; } read = 0; } readCounter++; return buffer[read++]; } vint ReadingIndex() const { return readCounter; } UtfCharCluster SourceCluster() const { return sourceCluster; } }; /*********************************************************************** UtfTo32ReaderBase ***********************************************************************/ template class UtfTo32ReaderBase : public TConsumer { static const vint BufferLength = UtfConversion::BufferLength; vint available = 0; T buffer[BufferLength]; UtfCharCluster sourceCluster = { 0,0 }; vint readCounter = -1; public: using ConsumerType = TConsumer; template UtfTo32ReaderBase(TArguments&& ...arguments) : TConsumer(std::forward(arguments)...) { } char32_t Read() { if (available == -1) return 0; while (available < BufferLength) { T c = this->Consume(); if (c) { buffer[available++] = c; } else { if (available == 0) { available = -1; readCounter++; sourceCluster.index += sourceCluster.size; sourceCluster.size = 0; return 0; } break; } } char32_t dest = 0; vint result = UtfConversion::To32(buffer, available, dest); if (result == -1) { available = -1; return 0; } available -= result; for (vint i = 0; i < available; i++) { buffer[i] = buffer[i + result]; } readCounter++; sourceCluster.index += sourceCluster.size; sourceCluster.size = result; return dest; } vint ReadingIndex() const { return readCounter; } UtfCharCluster SourceCluster() const { return sourceCluster; } }; /*********************************************************************** Utf32DirectReaderBase ***********************************************************************/ template class Utf32DirectReaderBase : public TConsumer { vint readCounter = -1; bool ended = false; public: using ConsumerType = TConsumer; template Utf32DirectReaderBase(TArguments&& ...arguments) : TConsumer(std::forward(arguments)...) { } char32_t Read() { auto dest = this->Consume(); static_assert(sizeof(dest) == sizeof(char32_t)); if (dest || !ended) { readCounter++; } ended = !dest; return static_cast(dest); } vint ReadingIndex() const { return readCounter; } UtfCharCluster SourceCluster() const { if (readCounter == -1) return { 0,0 }; if (ended) return { readCounter,0 }; return { readCounter,1 }; } }; /*********************************************************************** UtfToUtfReaderBase ***********************************************************************/ template struct UtfToUtfReaderSelector { template class TConsumerRedirection> class Reader : public UtfFrom32ReaderBase, TConsumerRedirection>> { using TBase = UtfFrom32ReaderBase, TConsumerRedirection>>; public: template Reader(TArguments&& ...arguments) : TBase(std::forward(arguments)...) { } UtfCharCluster SourceCluster() const { return this->internalReader.SourceCluster(); } }; }; template struct UtfToUtfReaderSelector { template class> using Reader = UtfFrom32ReaderBase; }; template struct UtfToUtfReaderSelector { template class> using Reader = UtfTo32ReaderBase; }; #define DEFINE_UTF32_DIRECT_READER(TFROM, TTO)\ template<>\ struct UtfToUtfReaderSelector\ {\ template class>\ using Reader = Utf32DirectReaderBase;\ }\ // in order to keep SourceCluster correct, only char32_t<->char32_t gets the special implementation DEFINE_UTF32_DIRECT_READER(char32_t, char32_t); #ifdef VCZH_WCHAR_UTF32 DEFINE_UTF32_DIRECT_READER(wchar_t, char32_t); DEFINE_UTF32_DIRECT_READER(char32_t, wchar_t); #endif #undef DEFINE_UTF32_DIRECT_READER template class TConsumerRedirection = UtfEmptyConsumerRedirection> using UtfToUtfReaderBase = typename UtfToUtfReaderSelector::template Reader; /*********************************************************************** UtfStringConsumer ***********************************************************************/ template class UtfStringConsumer : public Object { protected: const T* starting = nullptr; const T* consuming = nullptr; T Consume() { T c = *consuming; if (c) consuming++; return c; } public: UtfStringConsumer(const T* _starting) : starting(_starting) , consuming(_starting) { } }; template class UtfStringToStringReader : public UtfToUtfReaderBase> { using TBase = UtfToUtfReaderBase>; public: UtfStringToStringReader(const TFrom* _starting) : TBase(_starting) { } }; /*********************************************************************** UtfStringRangeConsumer ***********************************************************************/ template class UtfStringRangeConsumer : public Object { protected: const T* starting = nullptr; const T* ending = nullptr; const T* consuming = nullptr; T Consume() { if (consuming == ending) return 0; return *consuming++; } public: UtfStringRangeConsumer(const T* _starting, const T* _ending) : starting(_starting) , ending(_ending) , consuming(_starting) { } UtfStringRangeConsumer(const T* _starting, vint count) : starting(_starting) , ending(_starting + count) , consuming(_starting) { } }; template class UtfStringRangeToStringRangeReader : public UtfToUtfReaderBase> { using TBase = UtfToUtfReaderBase>; public: UtfStringRangeToStringRangeReader(const TFrom* _starting, const TFrom* _ending) : TBase(_starting, _ending) { } UtfStringRangeToStringRangeReader(const TFrom* _starting, vint count) : TBase(_starting, count) { } }; } /*********************************************************************** String Conversions (buffer walkthrough) ***********************************************************************/ extern vint _wtoa(const wchar_t* w, char* a, vint chars); extern vint _atow(const char* a, wchar_t* w, vint chars); template vint _utftoutf(const TFrom* s, TTo* d, vint chars); extern template vint _utftoutf(const wchar_t* s, char8_t* d, vint chars); extern template vint _utftoutf(const wchar_t* s, char16_t* d, vint chars); extern template vint _utftoutf(const char8_t* s, wchar_t* d, vint chars); extern template vint _utftoutf(const char8_t* s, char16_t* d, vint chars); extern template vint _utftoutf(const char16_t* s, wchar_t* d, vint chars); extern template vint _utftoutf(const char16_t* s, char8_t* d, vint chars); extern template vint _utftoutf(const char32_t* s, char8_t* d, vint chars); extern template vint _utftoutf(const char32_t* s, char16_t* d, vint chars); extern template vint _utftoutf(const char32_t* s, wchar_t* d, vint chars); extern template vint _utftoutf(const char8_t* s, char32_t* d, vint chars); extern template vint _utftoutf(const char16_t* s, char32_t* d, vint chars); extern template vint _utftoutf(const wchar_t* s, char32_t* d, vint chars); /*********************************************************************** String Conversions (ObjectString) ***********************************************************************/ template ObjectString ConvertUtfString(const ObjectString& source); #if defined VCZH_WCHAR_UTF16 template<> ObjectString ConvertUtfString(const ObjectString& source); template<> ObjectString ConvertUtfString(const ObjectString& source); extern template ObjectString ConvertUtfString(const ObjectString& source); extern template ObjectString ConvertUtfString(const ObjectString& source); #elif defined VCZH_WCHAR_UTF32 template<> ObjectString ConvertUtfString(const ObjectString& source); template<> ObjectString ConvertUtfString(const ObjectString& source); extern template ObjectString ConvertUtfString(const ObjectString& source); extern template ObjectString ConvertUtfString(const ObjectString& source); #endif extern template ObjectString ConvertUtfString(const ObjectString& source); extern template ObjectString ConvertUtfString(const ObjectString& source); extern template ObjectString ConvertUtfString(const ObjectString& source); extern template ObjectString ConvertUtfString(const ObjectString& source); extern template ObjectString ConvertUtfString(const ObjectString& source); extern template ObjectString ConvertUtfString(const ObjectString& source); extern template ObjectString ConvertUtfString(const ObjectString& source); extern template ObjectString ConvertUtfString(const ObjectString& source); template void ConvertUtfString(const ObjectString& source, ObjectString& dest); extern template void ConvertUtfString(const ObjectString&source, ObjectString& dest); extern template void ConvertUtfString(const ObjectString&source, ObjectString& dest); extern template void ConvertUtfString(const ObjectString&source, ObjectString& dest); extern template void ConvertUtfString(const ObjectString&source, ObjectString& dest); extern template void ConvertUtfString(const ObjectString&source, ObjectString& dest); extern template void ConvertUtfString(const ObjectString&source, ObjectString& dest); extern template void ConvertUtfString(const ObjectString&source, ObjectString& dest); extern template void ConvertUtfString(const ObjectString&source, ObjectString& dest); extern template void ConvertUtfString(const ObjectString&source, ObjectString& dest); extern template void ConvertUtfString(const ObjectString&source, ObjectString& dest); extern template void ConvertUtfString(const ObjectString&source, ObjectString& dest); extern template void ConvertUtfString(const ObjectString&source, ObjectString& dest); /*********************************************************************** String Conversions (Utf) ***********************************************************************/ extern U32String wtou32 (const WString& source); extern WString u32tow (const U32String& source); extern U32String u8tou32 (const U8String& source); extern U8String u32tou8 (const U32String& source); extern U32String u16tou32(const U16String& source); extern U16String u32tou16(const U32String& source); extern U8String wtou8 (const WString& source); extern WString u8tow (const U8String& source); extern U16String wtou16 (const WString& source); extern WString u16tow (const U16String& source); extern U16String u8tou16 (const U8String& source); extern U8String u16tou8 (const U16String& source); /*********************************************************************** String Conversions (Ansi) ***********************************************************************/ extern AString wtoa(const WString& source); extern WString atow(const AString& source); inline U8String atou8 (const AString& source) { return wtou8(atow(source)); } inline U16String atou16 (const AString& source) { return wtou16(atow(source)); } inline U32String atou32 (const AString& source) { return wtou32(atow(source)); } inline AString u8toa (const U8String& source) { return wtoa(u8tow(source)); } inline AString u16toa (const U16String& source) { return wtoa(u16tow(source)); } inline AString u32toa (const U32String& source) { return wtoa(u32tow(source)); } } #endif /*********************************************************************** .\STRINGS\LOREMIPSUM.H ***********************************************************************/ /*********************************************************************** Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ #ifndef VCZH_STRINGS_LOREMIPSUM #define VCZH_STRINGS_LOREMIPSUM namespace vl { /// Style of the random text. enum class LoremIpsumCasing { /// First letters of all words are lower cased. AllWordsLowerCase, /// first letters of first words of all sentences are upper cased. FirstWordUpperCase, /// First letters of all words are upper cased. AllWordsUpperCase, }; /// Get some random text. /// The generated random text. It may not exactly in the expected size. /// The expected size. /// The expected casing. extern WString LoremIpsum(vint bestLength, LoremIpsumCasing casing); /// Get some random text for a title, first letters of all words are upper cased. /// The generated random text. It may not be exactly in the expected size. /// The expected size. extern WString LoremIpsumTitle(vint bestLength); /// Get some random sentences. The first letter of the first word is uppder cased. /// The generated random text with a period character ".". It may not be exactly in the expected size. /// The expected size. extern WString LoremIpsumSentence(vint bestLength); /// Get some random paragraphs. First letters of first words of all sentences are upper cased. /// The generated random text with multiple sentences ending with period characters ".". It may not be exactly in the expected size. /// The expected size. extern WString LoremIpsumParagraph(vint bestLength); } #endif /*********************************************************************** .\UNITTEST\UNITTEST.H ***********************************************************************/ /*********************************************************************** Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ #ifndef VCZH_UNITTEST #define VCZH_UNITTEST namespace vl { extern bool MatchWildcardNaive(const wchar_t* wildcard, const wchar_t* text, bool caseSensitive); namespace unittest { using UnitTestFileProc = void(*)(); struct UnitTestLink { const char* fileName = nullptr; UnitTestFileProc testProc = nullptr; UnitTestLink* next = nullptr; }; /// ///

Unit test framework.

///

/// Test cases could be defined in multiple cpp files. In each cpp file, there can be one TEST_FILE call. /// ///

///

/// Both TEST_CATEGORY could be used inside TEST_FILE, or nested inside another TEST_CATEGORY. /// TEST_CASE could be used inside TEST_FILE or TEST_CATEGORY. ///

///

/// TEST_ASSERT is used to verify a boolean expression. /// It could only be used in TEST_CASE. /// TEST_ASSERT could not be used in TEST_FILE or TEST_CATEGORY. ///

///

/// When the test program is started in debug mode (Windows only), or by command line options "/D", /// A TEST_ASSERT failure will trigger a break point, it could be catched by any debugger. ///

///

/// When the test program is started in release mode, or by command line options "/R", or without command line options, /// A TEST_ASSERT failure will report an error and skip rest of the current TEST_CASE, the execution will continue. ///

///

/// When the test program is started with command line option "/C" (Copilot mode), /// A TEST_ASSERT failure will report an error and cause immediate program termination. /// This mode is designed for automated testing where you want to stop at the first failure. /// The "/D", "/R", and "/C" options are mutually exclusive. ///

///

/// TEST_ERROR execute one statement, it fails when no [T:vl.Error] is thrown. ///

///

/// TEST_EXCEPTION execute one statement, it fails when the specified exception type is not thrown. /// Another callback will be called when the exception is thrown, given a chance to check data in the exception. ///

///

/// TEST_CASE_ASSERT is an abbreviation of TEST_CASE + TEST_ASSERT. /// It is very common that are multiple independent assertions. ///

///

/// TEST_CASE_ASSERT is a test case, it can be used in TEST_CATEGORY or TEST_FILE. /// In release mode, by failing this assertion, the execution does not stop. ///

///

/// TEST_CATEGORY is very useful when multiple assertions do not have dependencies. /// During the execution of the test program, TEST_FILE, TEST_CATEGORY, TEST_CASE and failed TEST_ASSERT will be rendered with indentation and different colors. ///

///

/// is needed in the main function to execute test cases. /// TEST_PRINT could be used during test cases to print debug information to a command-line application. ///

///
/// class UnitTest { protected: static bool IsDebuggerAttached(); static int PrintUsages(); static int RunAndDisposeTests(const collections::Array& options); public: UnitTest() = delete; enum class MessageKind { Info, Error, File, Category, Case, }; enum class FailureMode { NotRunning, // UnitTest is not running Debug, // corresponds to /D - no exception suppression Release, // corresponds to /R - suppress and continue Copilot // corresponds to /C - suppress, record, and rethrow }; static FailureMode GetFailureMode(); static void PrintMessage(const WString& string, MessageKind kind); /// Run all test cases. /// The return value for the main function. If any assertion fails, it is non-zero. /// Accept the first argument of the main function. /// Accept the second argument of the main function. static int RunAndDisposeTests(int argc, wchar_t* argv[]); /// Run all test cases. /// The return value for the main function. If any assertion fails, it is non-zero. /// Accept the first argument of the main function. /// Accept the second argument of the main function. static int RunAndDisposeTests(int argc, char* argv[]); /// Dump memory leaks if memory leak detection is enabled. static void DumpMemoryLeak(int argc, wchar_t* argv[]); /// Dump memory leaks if memory leak detection is enabled. static void DumpMemoryLeak(int argc, char* argv[]); static void RegisterTestFile(UnitTestLink* link); static void RunCategoryOrCase(const WString& description, bool isCategory, Func&& callback); static void EnsureLegalToAssert(); }; class UnitTestFile { protected: UnitTestLink link; public: UnitTestFile(const char* fileName, UnitTestFileProc testProc) { link.fileName = fileName; link.testProc = testProc; UnitTest::RegisterTestFile(&link); } }; struct UnitTestAssertError { const wchar_t* message; UnitTestAssertError(const wchar_t* _message) :message(_message) {} }; struct UnitTestConfigError { const wchar_t* message; UnitTestConfigError(const wchar_t* _message) :message(_message) {} }; struct UnitTestJustCrashError { }; #define TEST_FILE\ static void VLPPTEST_TESTFILE();\ static ::vl::unittest::UnitTestFile VLPPTEST_TESTFILE_INSTANCE(__FILE__, &VLPPTEST_TESTFILE);\ static void VLPPTEST_TESTFILE()\ #define TEST_CATEGORY(DESCRIPTION)\ ::vl::unittest::UnitTest::RunCategoryOrCase((DESCRIPTION), true, [&]()\ #define TEST_CASE(DESCRIPTION)\ ::vl::unittest::UnitTest::RunCategoryOrCase((DESCRIPTION), false, [&]()\ #define TEST_ASSERT(CONDITION)\ do{\ ::vl::unittest::UnitTest::EnsureLegalToAssert();\ if(!(CONDITION))throw ::vl::unittest::UnitTestAssertError(L"Assertion failure: " #CONDITION);\ }while(0)\ #define TEST_ERROR(STATEMENT)\ do{\ ::vl::unittest::UnitTest::EnsureLegalToAssert();\ try{STATEMENT; throw ::vl::unittest::UnitTestAssertError(L"Expect an error but nothing occurred: " #STATEMENT);}\ catch(const ::vl::Error&){}\ catch(const ::vl::unittest::UnitTestAssertError&) { throw; }\ catch(const ::vl::unittest::UnitTestConfigError&) { throw; }\ }while(0)\ #define TEST_EXCEPTION(STATEMENT,EXCEPTION,ASSERT_FUNCTION)\ do{\ auto __ASSERT_FUNCTION__ = ASSERT_FUNCTION;\ try{STATEMENT; throw ::vl::unittest::UnitTestAssertError(L"Expect [" #EXCEPTION "] but nothing occurred: " #STATEMENT);}\ catch(const EXCEPTION& e){ __ASSERT_FUNCTION__(e); }\ catch(...){ throw ::vl::unittest::UnitTestAssertError(L"Expect [" #EXCEPTION "] but get unexpected exception: " #STATEMENT); }\ }while(0)\ #define TEST_PRINT(MESSAGE)\ ::vl::unittest::UnitTest::PrintMessage((MESSAGE), ::vl::unittest::UnitTest::MessageKind::Info)\ #define TEST_CASE_ASSERT(CONDITION)\ TEST_CASE(L ## # CONDITION) { TEST_ASSERT(CONDITION); })\ } } #endif