mirror of
https://github.com/vczh-libraries/Release.git
synced 2026-03-23 07:42:52 +08:00
8784 lines
264 KiB
C++
8784 lines
264 KiB
C++
/***********************************************************************
|
|
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
|
|
|
|
#ifdef VCZH_CHECK_MEMORY_LEAKS
|
|
#define _CRTDBG_MAP_ALLOC
|
|
#include <stdlib.h>
|
|
#include <crtdbg.h>
|
|
#define VCZH_CHECK_MEMORY_LEAKS_NEW new(_NORMAL_BLOCK, __FILE__, __LINE__)
|
|
#define new VCZH_CHECK_MEMORY_LEAKS_NEW
|
|
#endif
|
|
|
|
#if defined _WIN64 || __x86_64 || __LP64__
|
|
#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 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_MSVC
|
|
#include <intrin.h>
|
|
#elif defined VCZH_GCC
|
|
#include <x86intrin.h>
|
|
#include <stdint.h>
|
|
#include <stddef.h>
|
|
#include <wchar.h>
|
|
#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 <type_traits>
|
|
#include <utility>
|
|
|
|
#define L_(x) L__(x)
|
|
#define L__(x) L ## x
|
|
|
|
namespace vl
|
|
{
|
|
|
|
/***********************************************************************
|
|
x86 and x64 Compatbility
|
|
***********************************************************************/
|
|
|
|
#if defined VCZH_MSVC
|
|
/// <summary>1-byte signed integer.</summary>
|
|
typedef signed __int8 vint8_t;
|
|
/// <summary>1-byte unsigned integer.</summary>
|
|
typedef unsigned __int8 vuint8_t;
|
|
/// <summary>2-bytes signed integer.</summary>
|
|
typedef signed __int16 vint16_t;
|
|
/// <summary>2-bytes unsigned integer.</summary>
|
|
typedef unsigned __int16 vuint16_t;
|
|
/// <summary>4-bytes signed integer.</summary>
|
|
typedef signed __int32 vint32_t;
|
|
/// <summary>4-bytes unsigned integer.</summary>
|
|
typedef unsigned __int32 vuint32_t;
|
|
/// <summary>8-bytes signed integer.</summary>
|
|
typedef signed __int64 vint64_t;
|
|
/// <summary>8-bytes unsigned integer.</summary>
|
|
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
|
|
/// <summary>Signed interface whose size equals to sizeof(void*).</summary>
|
|
typedef vint64_t vint;
|
|
/// <summary>Signed interface whose size equals to sizeof(void*).</summary>
|
|
typedef vint64_t vsint;
|
|
/// <summary>Unsigned interface whose size equals to sizeof(void*).</summary>
|
|
typedef vuint64_t vuint;
|
|
#else
|
|
/// <summary>Signed interface whose size equals to sizeof(void*).</summary>
|
|
typedef vint32_t vint;
|
|
/// <summary>Signed interface whose size equals to sizeof(void*).</summary>
|
|
typedef vint32_t vsint;
|
|
/// <summary>Unsigned interface whose size equals to sizeof(void*).</summary>
|
|
typedef vuint32_t vuint;
|
|
#endif
|
|
/// <summary>Signed interger representing position.</summary>
|
|
typedef vint64_t pos_t;
|
|
|
|
#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
|
|
#if defined VCZH_MSVC
|
|
#define INCRC(x) (_InterlockedIncrement64(x))
|
|
#define DECRC(x) (_InterlockedDecrement64(x))
|
|
#elif defined VCZH_GCC
|
|
#define INCRC(x) (__sync_add_and_fetch(x, 1))
|
|
#define DECRC(x) (__sync_sub_and_fetch(x, 1))
|
|
#endif
|
|
#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
|
|
#if defined VCZH_MSVC
|
|
#define INCRC(x) (_InterlockedIncrement((volatile long*)(x)))
|
|
#define DECRC(x) (_InterlockedDecrement((volatile long*)(x)))
|
|
#elif defined VCZH_GCC
|
|
#define INCRC(x) (__sync_add_and_fetch(x, 1))
|
|
#define DECRC(x) (__sync_sub_and_fetch(x, 1))
|
|
#endif
|
|
#endif
|
|
|
|
/***********************************************************************
|
|
Basic Types
|
|
***********************************************************************/
|
|
|
|
/// <summary>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.</summary>
|
|
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)
|
|
|
|
template<typename ...TArgs>
|
|
struct TypeTuple
|
|
{
|
|
};
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
class Object
|
|
{
|
|
public:
|
|
virtual ~Object() = default;
|
|
};
|
|
|
|
/// <summary>Type for representing nullable data.</summary>
|
|
/// <typeparam name="T">Type of the data, typically it is a value type, or [T:vl.Ptr`1] could be used here.</typeparam>
|
|
template<typename T>
|
|
class Nullable
|
|
{
|
|
private:
|
|
T* object = nullptr;
|
|
public:
|
|
/// <summary>Create a null value.</summary>
|
|
Nullable() = default;
|
|
|
|
/// <summary>Create a non-null value by copying data.</summary>
|
|
/// <param name="value">The data to copy.</param>
|
|
Nullable(const T& value)
|
|
:object(new T(value))
|
|
{
|
|
}
|
|
|
|
/// <summary>Create a non-null value by moving data.</summary>
|
|
/// <param name="value">The data to move.</param>
|
|
Nullable(T&& value)
|
|
:object(new T(std::move(value)))
|
|
{
|
|
}
|
|
|
|
/// <summary>Create a nullable value by copying from another nullable value.</summary>
|
|
/// <param name="nullable">The nullable value to copy.</param>
|
|
Nullable(const Nullable<T>& nullable)
|
|
:object(nullable.object ? new T(*nullable.object) : nullptr)
|
|
{
|
|
}
|
|
|
|
/// <summary>Create a nullable value by moving from another nullable value.</summary>
|
|
/// <param name="nullable">The nullable value to move.</param>
|
|
Nullable(Nullable<T>&& nullable)
|
|
:object(nullable.object)
|
|
{
|
|
nullable.object = nullptr;
|
|
}
|
|
|
|
~Nullable()
|
|
{
|
|
if (object) delete object;
|
|
}
|
|
|
|
/// <summary>Replace the data inside this nullable value by copying from data.</summary>
|
|
/// <returns>The nullable value itself.</returns>
|
|
/// <param name="value">The data to copy.</param>
|
|
Nullable<T>& operator=(const T& value)
|
|
{
|
|
if (object) delete object;
|
|
object = new T(value);
|
|
return *this;
|
|
}
|
|
|
|
/// <summary>Replace the data inside this nullable value by copying from another nullable value.</summary>
|
|
/// <returns>The nullable value itself.</returns>
|
|
/// <param name="nullable">The nullable value to copy.</param>
|
|
Nullable<T>& operator=(const Nullable<T>& nullable)
|
|
{
|
|
if (this != &nullable)
|
|
{
|
|
if (object) delete object;
|
|
if (nullable.object)
|
|
{
|
|
object = new T(*nullable.object);
|
|
}
|
|
else
|
|
{
|
|
object = nullptr;
|
|
}
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
/// <summary>Replace the data inside this nullable value by moving from another nullable value.</summary>
|
|
/// <returns>The nullable value itself.</returns>
|
|
/// <param name="nullable">The nullable value to move.</param>
|
|
Nullable<T>& operator=(Nullable<T>&& nullable)
|
|
{
|
|
if (this != &nullable)
|
|
{
|
|
if (object) delete object;
|
|
object = nullable.object;
|
|
nullable.object = nullptr;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
/// <summary>Comparing two nullable values.</summary>
|
|
/// <returns>Returns true when these nullable values are all null, or the data inside them equals.</returns>
|
|
/// <param name="a">The first nullable value to compare.</param>
|
|
/// <param name="b">The second nullable value to compare.</param>
|
|
static bool Equals(const Nullable<T>& a, const Nullable<T>& b)
|
|
{
|
|
if (!a.object && !b.object) return true;
|
|
if (a.object && b.object) return *a.object == *b.object;
|
|
return false;
|
|
}
|
|
|
|
/// <summary>Comparing two nullable values.</summary>
|
|
/// <returns>
|
|
/// Returns a positive value when the first value is greater than the second value.
|
|
/// Returns a negative value when the first value is lesser than the second value.
|
|
/// Returns zero when the two values equal.
|
|
/// When one is null and another one is not, the non-null one is greater.
|
|
/// </returns>
|
|
/// <param name="a">The first nullable value to compare.</param>
|
|
/// <param name="b">The second nullable value to compare.</param>
|
|
static vint Compare(const Nullable<T>& a, const Nullable<T>& b)
|
|
{
|
|
if (a.object && b.object)
|
|
{
|
|
if (*a.object > *b.object) return 1;
|
|
if (*a.object < *b.object) return -1;
|
|
return 0;
|
|
}
|
|
if (a.object) return 1;
|
|
if (b.object) return -1;
|
|
return 0;
|
|
}
|
|
|
|
bool operator==(const Nullable<T>& nullable)const
|
|
{
|
|
return Equals(*this, nullable);
|
|
}
|
|
|
|
bool operator!=(const Nullable<T>& nullable)const
|
|
{
|
|
return !Equals(*this, nullable);
|
|
}
|
|
|
|
bool operator<(const Nullable<T>& nullable)const
|
|
{
|
|
return Compare(*this, nullable) < 0;
|
|
}
|
|
|
|
bool operator<=(const Nullable<T>& nullable)const
|
|
{
|
|
return Compare(*this, nullable) <= 0;
|
|
}
|
|
|
|
bool operator>(const Nullable<T>& nullable)const
|
|
{
|
|
return Compare(*this, nullable) > 0;
|
|
}
|
|
|
|
bool operator>=(const Nullable<T>& nullable)const
|
|
{
|
|
return Compare(*this, nullable) >= 0;
|
|
}
|
|
|
|
/// <summary>Test if this nullable value is non-null.</summary>
|
|
/// <returns>Returns true if it is non-null.</returns>
|
|
operator bool()const
|
|
{
|
|
return object != nullptr;
|
|
}
|
|
|
|
/// <summary>Return the data inside this nullable value</summary>
|
|
/// <returns>The data inside this nullable value. It crashes when it is null.</returns>
|
|
const T& Value()const
|
|
{
|
|
if (!object) throw Error(L"Nullable<T>::Value()#Cannot unbox from null.");
|
|
return *object;
|
|
}
|
|
};
|
|
|
|
template<typename T, size_t minSize>
|
|
union BinaryRetriver
|
|
{
|
|
T t;
|
|
char binary[sizeof(T) > minSize ? sizeof(T) : minSize];
|
|
};
|
|
|
|
/***********************************************************************
|
|
Type Traits
|
|
***********************************************************************/
|
|
|
|
/// <summary>Type for specify and create a representative value for comparing another value of a specific type for containers.</summary>
|
|
/// <typeparam name="T">The element type for containers.</typeparam>
|
|
template<typename T>
|
|
struct KeyType
|
|
{
|
|
public:
|
|
/// <summary>The type of the representative value.</summary>
|
|
typedef T Type;
|
|
|
|
/// <summary>Convert a value in a container to its representative value.</summary>
|
|
/// <returns>The representative value.</returns>
|
|
/// <param name="value">The value in a container.</param>
|
|
static const T& GetKeyValue(const T& value)
|
|
{
|
|
return value;
|
|
}
|
|
};
|
|
|
|
/***********************************************************************
|
|
Interface
|
|
***********************************************************************/
|
|
|
|
#define NOT_COPYABLE(TYPE)\
|
|
TYPE(const TYPE&) = delete;\
|
|
TYPE(TYPE&&) = delete;\
|
|
TYPE& operator=(const TYPE&) = delete;\
|
|
TYPE& operator=(TYPE&&) = delete
|
|
|
|
/// <summary>Base type of all interfaces. All interface types are encouraged to be virtual inherited.</summary>
|
|
class Interface
|
|
{
|
|
public:
|
|
NOT_COPYABLE(Interface);
|
|
|
|
Interface() = default;
|
|
virtual ~Interface() = default;
|
|
};
|
|
}
|
|
|
|
#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<typename K, typename V>
|
|
class Pair;
|
|
|
|
/// <summary>A type representing a pair of key and value.</summary>
|
|
/// <typeparam name="K">Type of the key.</typeparam>
|
|
/// <typeparam name="V">Type of the value.</typeparam>
|
|
template<typename K, typename V>
|
|
class Pair
|
|
{
|
|
public:
|
|
/// <summary>The key.</summary>
|
|
K key;
|
|
/// <summary>The value.</summary>
|
|
V value;
|
|
|
|
Pair() = default;
|
|
|
|
template<typename TKey, typename TValue>
|
|
Pair(TKey&& _key, TValue&& _value)
|
|
: key(std::forward<TKey&&>(_key))
|
|
, value(std::forward<TValue&&>(_value))
|
|
{
|
|
}
|
|
|
|
Pair(const K& _key, const V& _value)
|
|
: key(_key)
|
|
, value(_value)
|
|
{
|
|
}
|
|
|
|
Pair(const K& _key, V&& _value)
|
|
: key(_key)
|
|
, value(std::move(_value))
|
|
{
|
|
}
|
|
|
|
Pair(K&& _key, const V& _value)
|
|
: key(std::move(_key))
|
|
, value(_value)
|
|
{
|
|
}
|
|
|
|
Pair(K&& _key, V&& _value)
|
|
: key(std::move(_key))
|
|
, value(std::move(_value))
|
|
{
|
|
}
|
|
|
|
Pair(const Pair<const K&, const V&>& pair)
|
|
: key(pair.key)
|
|
, value(pair.value)
|
|
{
|
|
}
|
|
|
|
Pair(const Pair<K, V>& pair)
|
|
: key(pair.key)
|
|
, value(pair.value)
|
|
{
|
|
}
|
|
|
|
Pair(Pair<K, V>&& pair)
|
|
: key(std::move(pair.key))
|
|
, value(std::move(pair.value))
|
|
{
|
|
}
|
|
|
|
Pair<K, V>& operator=(const Pair<K, V>& pair)
|
|
{
|
|
key = pair.key;
|
|
value = pair.value;
|
|
return *this;
|
|
}
|
|
|
|
Pair<K, V>& operator=(Pair<K, V>&& pair)
|
|
{
|
|
key = std::move(pair.key);
|
|
value = std::move(pair.value);
|
|
return *this;
|
|
}
|
|
|
|
template<typename K2, typename V2>
|
|
auto CompareTo(const Pair<K2, V2>& pair) const -> std::enable_if_t<
|
|
std::is_same_v<std::remove_cvref_t<K>, std::remove_cvref_t<K2>> &&
|
|
std::is_same_v<std::remove_cvref_t<V>, std::remove_cvref_t<V2>>,
|
|
vint>
|
|
{
|
|
if (key < pair.key)
|
|
{
|
|
return -1;
|
|
}
|
|
else if (key > pair.key)
|
|
{
|
|
return 1;
|
|
}
|
|
else if (value < pair.value)
|
|
{
|
|
return -1;
|
|
}
|
|
else if (value > pair.value)
|
|
{
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
template<typename TPair>
|
|
bool operator==(TPair&& pair)const
|
|
{
|
|
return CompareTo(std::forward<TPair&&>(pair)) == 0;
|
|
}
|
|
|
|
template<typename TPair>
|
|
bool operator!=(TPair&& pair)const
|
|
{
|
|
return CompareTo(std::forward<TPair&&>(pair)) != 0;
|
|
}
|
|
|
|
template<typename TPair>
|
|
bool operator<(TPair&& pair)const
|
|
{
|
|
return CompareTo(std::forward<TPair&&>(pair)) < 0;
|
|
}
|
|
|
|
template<typename TPair>
|
|
bool operator<=(TPair&& pair)const
|
|
{
|
|
return CompareTo(std::forward<TPair&&>(pair)) <= 0;
|
|
}
|
|
|
|
template<typename TPair>
|
|
bool operator>(TPair&& pair)const
|
|
{
|
|
return CompareTo(std::forward<TPair&&>(pair)) > 0;
|
|
}
|
|
|
|
template<typename TPair>
|
|
bool operator>=(TPair&& pair)const
|
|
{
|
|
return CompareTo(std::forward<TPair&&>(pair)) >= 0;
|
|
}
|
|
};
|
|
|
|
template<typename K, typename V>
|
|
class Pair<const K&, const V&>
|
|
{
|
|
public:
|
|
const K& key;
|
|
const V& value;
|
|
|
|
#if defined(__clang__)
|
|
#pragma clang dignostic push
|
|
#pragma clang diagnostic ignored "-Wnull-dereference"
|
|
#elif defined(__GNUC__)
|
|
#pragma GCC dignostic push
|
|
#pragma GCC diagnostic ignored "-Wnull-dereference"
|
|
#endif
|
|
Pair()
|
|
: key(*(const K*)nullptr)
|
|
, value(*(const V*)nullptr)
|
|
{
|
|
}
|
|
#if defined(__clang__)
|
|
#pragma clang dignostic pop
|
|
#elif defined(__GNUC__)
|
|
#pragma GCC dignostic popd
|
|
#endif
|
|
|
|
Pair(const K& _key, const V& _value)
|
|
: key(_key)
|
|
, value(_value)
|
|
{
|
|
}
|
|
|
|
Pair(const Pair<const K&, const V&>& pair)
|
|
: key(pair.key)
|
|
, value(pair.value)
|
|
{
|
|
}
|
|
|
|
Pair<const K&, const V&>& operator=(const Pair<const K&, const V&>& pair)
|
|
{
|
|
|
|
#ifdef VCZH_CHECK_MEMORY_LEAKS_NEW
|
|
#undef new
|
|
#endif
|
|
this->~Pair<const K&, const V&>();
|
|
new(this) Pair<const K&, const V&>(pair);
|
|
return *this;
|
|
#ifdef VCZH_CHECK_MEMORY_LEAKS_NEW
|
|
#define new VCZH_CHECK_MEMORY_LEAKS_NEW
|
|
#endif
|
|
}
|
|
|
|
template<typename K2, typename V2>
|
|
auto CompareTo(const Pair<K2, V2>& pair) const -> std::enable_if_t<
|
|
std::is_same_v<std::remove_cvref_t<K>, std::remove_cvref_t<K2>>&&
|
|
std::is_same_v<std::remove_cvref_t<V>, std::remove_cvref_t<V2>>,
|
|
vint>
|
|
{
|
|
if (key < pair.key)
|
|
{
|
|
return -1;
|
|
}
|
|
else if (key > pair.key)
|
|
{
|
|
return 1;
|
|
}
|
|
else if (value < pair.value)
|
|
{
|
|
return -1;
|
|
}
|
|
else if (value > pair.value)
|
|
{
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
template<typename TPair>
|
|
bool operator==(TPair&& pair)const
|
|
{
|
|
return CompareTo(std::forward<TPair&&>(pair)) == 0;
|
|
}
|
|
|
|
template<typename TPair>
|
|
bool operator!=(TPair&& pair)const
|
|
{
|
|
return CompareTo(std::forward<TPair&&>(pair)) != 0;
|
|
}
|
|
|
|
template<typename TPair>
|
|
bool operator<(TPair&& pair)const
|
|
{
|
|
return CompareTo(std::forward<TPair&&>(pair)) < 0;
|
|
}
|
|
|
|
template<typename TPair>
|
|
bool operator<=(TPair&& pair)const
|
|
{
|
|
return CompareTo(std::forward<TPair&&>(pair)) <= 0;
|
|
}
|
|
|
|
template<typename TPair>
|
|
bool operator>(TPair&& pair)const
|
|
{
|
|
return CompareTo(std::forward<TPair&&>(pair)) > 0;
|
|
}
|
|
|
|
template<typename TPair>
|
|
bool operator>=(TPair&& pair)const
|
|
{
|
|
return CompareTo(std::forward<TPair&&>(pair)) >= 0;
|
|
}
|
|
};
|
|
}
|
|
}
|
|
|
|
#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
|
|
***********************************************************************/
|
|
|
|
/// <summary>A type representing the combination of date and time.</summary>
|
|
struct DateTime
|
|
{
|
|
/// <summary>The year.</summary>
|
|
vint year = 0;
|
|
/// <summary>The month, from 1 to 12.</summary>
|
|
vint month = 0;
|
|
/// <summary>The day, from 1 to 31.</summary>
|
|
vint day = 0;
|
|
/// <summary>The hour, from 0 to 23.</summary>
|
|
vint hour = 0;
|
|
/// <summary>The minute, from 0 to 59.</summary>
|
|
vint minute = 0;
|
|
/// <summary>The second, from 0 to 60.</summary>
|
|
vint second = 0;
|
|
/// <summary>The milliseconds, from 0 to 999.</summary>
|
|
vint milliseconds = 0;
|
|
|
|
/// <summary>
|
|
/// The calculated total milliseconds. It is OS dependent because the start time is different.
|
|
/// It is from 0 to 6, representing Sunday to Saturday.
|
|
/// </summary>
|
|
vint dayOfWeek = 0;
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
vuint64_t totalMilliseconds = 0;
|
|
|
|
/// <summary>
|
|
/// The calculated file time for the date and time. It is OS dependent.
|
|
/// 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 filetime is lesser.
|
|
/// </summary>
|
|
vuint64_t filetime = 0;
|
|
|
|
/// <summary>Get the current local time.</summary>
|
|
/// <returns>The current local time.</returns>
|
|
static DateTime LocalTime();
|
|
|
|
/// <summary>Get the current UTC time.</summary>
|
|
/// <returns>The current UTC time.</returns>
|
|
static DateTime UtcTime();
|
|
|
|
/// <summary>Create a date time value.</summary>
|
|
/// <returns>The created date time value.</returns>
|
|
/// <param name="_year">The year.</param>
|
|
/// <param name="_month">The month.</param>
|
|
/// <param name="_day">The day.</param>
|
|
/// <param name="_hour">The hour.</param>
|
|
/// <param name="_minute">The minute.</param>
|
|
/// <param name="_second">The second.</param>
|
|
/// <param name="_milliseconds">The millisecond.</param>
|
|
static DateTime FromDateTime(vint _year, vint _month, vint _day, vint _hour = 0, vint _minute = 0, vint _second = 0, vint _milliseconds = 0);
|
|
|
|
/// <summary>Create a date time value from a file time.</summary>
|
|
/// <returns>The created date time value.</returns>
|
|
/// <param name="filetime">The file time.</param>
|
|
static DateTime FromFileTime(vuint64_t filetime);
|
|
|
|
/// <summary>Create an empty date time value that is not meaningful.</summary>
|
|
DateTime() = default;
|
|
|
|
/// <summary>Convert the UTC time to the local time.</summary>
|
|
/// <returns>The UTC time.</returns>
|
|
DateTime ToLocalTime();
|
|
/// <summary>Convert the local time to the UTC time.</summary>
|
|
/// <returns>The local time.</returns>
|
|
DateTime ToUtcTime();
|
|
/// <summary>Move forward.</summary>
|
|
/// <returns>The moved time.</returns>
|
|
/// <param name="milliseconds">The delta in milliseconds.</param>
|
|
DateTime Forward(vuint64_t milliseconds);
|
|
/// <summary>Move Backward.</summary>
|
|
/// <returns>The moved time.</returns>
|
|
/// <param name="milliseconds">The delta in milliseconds.</param>
|
|
DateTime Backward(vuint64_t milliseconds);
|
|
|
|
bool operator==(const DateTime& value)const { return filetime == value.filetime; }
|
|
bool operator!=(const DateTime& value)const { return filetime != value.filetime; }
|
|
bool operator<(const DateTime& value)const { return filetime < value.filetime; }
|
|
bool operator<=(const DateTime& value)const { return filetime <= value.filetime; }
|
|
bool operator>(const DateTime& value)const { return filetime > value.filetime; }
|
|
bool operator>=(const DateTime& value)const { return filetime >= value.filetime; }
|
|
};
|
|
}
|
|
|
|
#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
|
|
***********************************************************************/
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
/// <typeparam name="T">
|
|
/// The type of the object.
|
|
/// </typeparam>
|
|
/// <typeparam name="Enabled">
|
|
/// [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.
|
|
/// </typeparam>
|
|
template<typename T, typename=void>
|
|
struct ReferenceCounterOperator
|
|
{
|
|
/// <summary>Create the reference counter of an object.</summary>
|
|
/// <returns>The pointer to the reference counter.</returns>
|
|
/// <param name="reference">The object.</param>
|
|
static __forceinline volatile vint* CreateCounter(T* reference)
|
|
{
|
|
return new vint(0);
|
|
}
|
|
|
|
/// <summary>Delete the reference counter from an object.</summary>
|
|
/// <param name="counter">The pointer to the reference counter.</param>
|
|
/// <param name="reference">The object.</param>
|
|
static __forceinline void DeleteReference(volatile vint* counter, void* reference)
|
|
{
|
|
delete counter;
|
|
delete (T*)reference;
|
|
}
|
|
};
|
|
|
|
/***********************************************************************
|
|
Ptr
|
|
***********************************************************************/
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// 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].
|
|
/// </remarks>
|
|
/// <typeparam name="T">The type of the object.</typeparam>
|
|
template<typename T>
|
|
class Ptr
|
|
{
|
|
template<typename X>
|
|
friend class Ptr;
|
|
protected:
|
|
typedef void(*Destructor)(volatile vint*, void*);
|
|
|
|
volatile 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();
|
|
}
|
|
}
|
|
}
|
|
|
|
volatile vint* Counter()const
|
|
{
|
|
return counter;
|
|
}
|
|
|
|
Ptr(volatile vint* _counter, T* _reference, void* _originalReference, Destructor _originalDestructor)
|
|
:counter(_counter)
|
|
, reference(_reference)
|
|
, originalReference(_originalReference)
|
|
, originalDestructor(_originalDestructor)
|
|
{
|
|
Inc();
|
|
}
|
|
public:
|
|
|
|
/// <summary>Create a null pointer.</summary>
|
|
Ptr() = default;
|
|
|
|
/// <summary>Convert a pointer to an object to a shared pointer.</summary>
|
|
/// <param name="pointer">The pointer to the object.</param>
|
|
Ptr(T* pointer)
|
|
{
|
|
if (pointer)
|
|
{
|
|
counter = ReferenceCounterOperator<T>::CreateCounter(pointer);
|
|
reference = pointer;
|
|
originalReference = pointer;
|
|
originalDestructor = &ReferenceCounterOperator<T>::DeleteReference;
|
|
Inc();
|
|
}
|
|
}
|
|
|
|
/// <summary>Copy a shared pointer.</summary>
|
|
/// <param name="pointer">The shared pointer to copy.</param>
|
|
Ptr(const Ptr<T>& pointer)
|
|
:counter(pointer.counter)
|
|
, reference(pointer.reference)
|
|
, originalReference(pointer.originalReference)
|
|
, originalDestructor(pointer.originalDestructor)
|
|
{
|
|
Inc();
|
|
}
|
|
|
|
/// <summary>Move a shared pointer.</summary>
|
|
/// <param name="pointer">The shared pointer to Move.</param>
|
|
Ptr(Ptr<T>&& pointer)
|
|
:counter(pointer.counter)
|
|
, reference(pointer.reference)
|
|
, originalReference(pointer.originalReference)
|
|
, originalDestructor(pointer.originalDestructor)
|
|
{
|
|
pointer.SetEmptyNoIncDec();
|
|
}
|
|
|
|
/// <summary>Cast a shared pointer implicitly by copying another shared pointer.</summary>
|
|
/// <typeparam name="C">The type of the object before casting.</typeparam>
|
|
/// <param name="pointer">The shared pointer to cast.</param>
|
|
template<typename C, typename=std::enable_if_t<std::is_convertible_v<C*, T*>>>
|
|
Ptr(const Ptr<C>& pointer)
|
|
{
|
|
if (auto converted = pointer.Obj())
|
|
{
|
|
counter = pointer.Counter();
|
|
reference = converted;
|
|
originalReference = pointer.originalReference;
|
|
originalDestructor = pointer.originalDestructor;
|
|
Inc();
|
|
}
|
|
}
|
|
|
|
/// <summary>Cast a shared pointer implicitly by moving another shared pointer.</summary>
|
|
/// <typeparam name="C">The type of the object before casting.</typeparam>
|
|
/// <param name="pointer">The shared pointer to cast.</param>
|
|
template<typename C, typename = std::enable_if_t<std::is_convertible_v<C*, T*>>>
|
|
Ptr(Ptr<C>&& pointer)
|
|
{
|
|
if (auto converted = pointer.Obj())
|
|
{
|
|
counter = pointer.Counter();
|
|
reference = converted;
|
|
originalReference = pointer.originalReference;
|
|
originalDestructor = pointer.originalDestructor;
|
|
pointer.SetEmptyNoIncDec();
|
|
}
|
|
}
|
|
|
|
~Ptr()
|
|
{
|
|
Dec();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
/// <returns>The detached object. Returns null if this shared pointer is empty.</returns>
|
|
T* Detach()
|
|
{
|
|
auto detached = reference;
|
|
Dec(false);
|
|
return detached;
|
|
}
|
|
|
|
/// <summary>Cast a shared pointer explicitly.</summary>
|
|
/// <typeparam name="C">The type of the object after casting.</typeparam>
|
|
/// <returns>The casted shared pointer. Returns null for empty shared pointer or a failed cast.</returns>
|
|
template<typename C>
|
|
Ptr<C> Cast()const
|
|
{
|
|
C* converted = dynamic_cast<C*>(reference);
|
|
return Ptr<C>((converted ? counter : 0), converted, originalReference, originalDestructor);
|
|
}
|
|
|
|
/// <summary>Replace the object inside this shared pointer.</summary>
|
|
/// <returns>The shared pointer itself.</returns>
|
|
/// <param name="pointer">The pointer to the new object.</param>
|
|
Ptr<T>& operator=(T* pointer)
|
|
{
|
|
Dec();
|
|
if (pointer)
|
|
{
|
|
counter = ReferenceCounterOperator<T>::CreateCounter(pointer);
|
|
reference = pointer;
|
|
originalReference = pointer;
|
|
originalDestructor = &ReferenceCounterOperator<T>::DeleteReference;
|
|
Inc();
|
|
}
|
|
else
|
|
{
|
|
SetEmptyNoIncDec();
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
/// <summary>Replace by copying another shared pointer.</summary>
|
|
/// <returns>The shared pointer itself.</returns>
|
|
/// <param name="pointer">The shared pointer to copy.</param>
|
|
Ptr<T>& operator=(const Ptr<T>& pointer)
|
|
{
|
|
if (this != &pointer)
|
|
{
|
|
Dec();
|
|
counter = pointer.counter;
|
|
reference = pointer.reference;
|
|
originalReference = pointer.originalReference;
|
|
originalDestructor = pointer.originalDestructor;
|
|
Inc();
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
/// <summary>Replace by moving another shared pointer.</summary>
|
|
/// <returns>The shared pointer itself.</returns>
|
|
/// <param name="pointer">The shared pointer to copy.</param>
|
|
Ptr<T>& operator=(Ptr<T>&& pointer)
|
|
{
|
|
if (this != &pointer)
|
|
{
|
|
Dec();
|
|
counter = pointer.counter;
|
|
reference = pointer.reference;
|
|
originalReference = pointer.originalReference;
|
|
originalDestructor = pointer.originalDestructor;
|
|
pointer.SetEmptyNoIncDec();
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
bool operator==(const T* pointer)const
|
|
{
|
|
return reference == pointer;
|
|
}
|
|
|
|
bool operator!=(const T* pointer)const
|
|
{
|
|
return reference != pointer;
|
|
}
|
|
|
|
bool operator>(const T* pointer)const
|
|
{
|
|
return reference > pointer;
|
|
}
|
|
|
|
bool operator>=(const T* pointer)const
|
|
{
|
|
return reference >= pointer;
|
|
}
|
|
|
|
bool operator<(const T* pointer)const
|
|
{
|
|
return reference < pointer;
|
|
}
|
|
|
|
bool operator<=(const T* pointer)const
|
|
{
|
|
return reference <= pointer;
|
|
}
|
|
|
|
bool operator==(const Ptr<T>& pointer)const
|
|
{
|
|
return reference == pointer.reference;
|
|
}
|
|
|
|
bool operator!=(const Ptr<T>& pointer)const
|
|
{
|
|
return reference != pointer.reference;
|
|
}
|
|
|
|
bool operator>(const Ptr<T>& pointer)const
|
|
{
|
|
return reference > pointer.reference;
|
|
}
|
|
|
|
bool operator>=(const Ptr<T>& pointer)const
|
|
{
|
|
return reference >= pointer.reference;
|
|
}
|
|
|
|
bool operator<(const Ptr<T>& pointer)const
|
|
{
|
|
return reference < pointer.reference;
|
|
}
|
|
|
|
bool operator<=(const Ptr<T>& pointer)const
|
|
{
|
|
return reference <= pointer.reference;
|
|
}
|
|
|
|
/// <summary>Test if it is an empty shared pointer.</summary>
|
|
/// <returns>Returns true if it is non-null.</returns>
|
|
operator bool()const
|
|
{
|
|
return reference != 0;
|
|
}
|
|
|
|
/// <summary>Get the pointer to the contained object.</summary>
|
|
/// <returns>The pointer to the contained object. Returns null for an empty shared pointer.</returns>
|
|
T* Obj()const
|
|
{
|
|
return reference;
|
|
}
|
|
|
|
/// <summary>Get the pointer to the contained object.</summary>
|
|
/// <returns>The pointer to the contained object. Returns null for an empty shared pointer.</returns>
|
|
T* operator->()const
|
|
{
|
|
return reference;
|
|
}
|
|
};
|
|
|
|
/***********************************************************************
|
|
ComPtr
|
|
***********************************************************************/
|
|
|
|
template<typename T>
|
|
class ComPtr
|
|
{
|
|
protected:
|
|
volatile vint* counter;
|
|
T* reference;
|
|
|
|
void Inc()
|
|
{
|
|
if(counter)
|
|
{
|
|
INCRC(counter);
|
|
}
|
|
}
|
|
|
|
void Dec()
|
|
{
|
|
if(counter)
|
|
{
|
|
if(DECRC(counter)==0)
|
|
{
|
|
delete counter;
|
|
reference->Release();
|
|
counter=0;
|
|
reference=0;
|
|
}
|
|
}
|
|
}
|
|
|
|
volatile vint* Counter()const
|
|
{
|
|
return counter;
|
|
}
|
|
|
|
ComPtr(volatile vint* _counter, T* _reference)
|
|
:counter(_counter)
|
|
,reference(_reference)
|
|
{
|
|
Inc();
|
|
}
|
|
public:
|
|
|
|
ComPtr()
|
|
{
|
|
counter=0;
|
|
reference=0;
|
|
}
|
|
|
|
ComPtr(T* pointer)
|
|
{
|
|
if(pointer)
|
|
{
|
|
counter=new volatile vint(1);
|
|
reference=pointer;
|
|
}
|
|
else
|
|
{
|
|
counter=0;
|
|
reference=0;
|
|
}
|
|
}
|
|
|
|
ComPtr(const ComPtr<T>& pointer)
|
|
{
|
|
counter=pointer.counter;
|
|
reference=pointer.reference;
|
|
Inc();
|
|
}
|
|
|
|
ComPtr(ComPtr<T>&& pointer)
|
|
{
|
|
counter=pointer.counter;
|
|
reference=pointer.reference;
|
|
|
|
pointer.counter=0;
|
|
pointer.reference=0;
|
|
}
|
|
|
|
~ComPtr()
|
|
{
|
|
Dec();
|
|
}
|
|
|
|
ComPtr<T>& operator=(T* pointer)
|
|
{
|
|
Dec();
|
|
if(pointer)
|
|
{
|
|
counter=new vint(1);
|
|
reference=pointer;
|
|
}
|
|
else
|
|
{
|
|
counter=0;
|
|
reference=0;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
ComPtr<T>& operator=(const ComPtr<T>& pointer)
|
|
{
|
|
if(this!=&pointer)
|
|
{
|
|
Dec();
|
|
counter=pointer.counter;
|
|
reference=pointer.reference;
|
|
Inc();
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
ComPtr<T>& operator=(ComPtr<T>&& pointer)
|
|
{
|
|
if(this!=&pointer)
|
|
{
|
|
Dec();
|
|
counter=pointer.counter;
|
|
reference=pointer.reference;
|
|
|
|
pointer.counter=0;
|
|
pointer.reference=0;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
bool operator==(const T* pointer)const
|
|
{
|
|
return reference==pointer;
|
|
}
|
|
|
|
bool operator!=(const T* pointer)const
|
|
{
|
|
return reference!=pointer;
|
|
}
|
|
|
|
bool operator>(const T* pointer)const
|
|
{
|
|
return reference>pointer;
|
|
}
|
|
|
|
bool operator>=(const T* pointer)const
|
|
{
|
|
return reference>=pointer;
|
|
}
|
|
|
|
bool operator<(const T* pointer)const
|
|
{
|
|
return reference<pointer;
|
|
}
|
|
|
|
bool operator<=(const T* pointer)const
|
|
{
|
|
return reference<=pointer;
|
|
}
|
|
|
|
bool operator==(const ComPtr<T>& pointer)const
|
|
{
|
|
return reference==pointer.reference;
|
|
}
|
|
|
|
bool operator!=(const ComPtr<T>& pointer)const
|
|
{
|
|
return reference!=pointer.reference;
|
|
}
|
|
|
|
bool operator>(const ComPtr<T>& pointer)const
|
|
{
|
|
return reference>pointer.reference;
|
|
}
|
|
|
|
bool operator>=(const ComPtr<T>& pointer)const
|
|
{
|
|
return reference>=pointer.reference;
|
|
}
|
|
|
|
bool operator<(const ComPtr<T>& pointer)const
|
|
{
|
|
return reference<pointer.reference;
|
|
}
|
|
|
|
bool operator<=(const ComPtr<T>& pointer)const
|
|
{
|
|
return reference<=pointer.reference;
|
|
}
|
|
|
|
operator bool()const
|
|
{
|
|
return reference!=0;
|
|
}
|
|
|
|
T* Obj()const
|
|
{
|
|
return reference;
|
|
}
|
|
|
|
T* operator->()const
|
|
{
|
|
return reference;
|
|
}
|
|
};
|
|
|
|
template<typename T, typename ...TArgs>
|
|
Ptr<T> MakePtr(TArgs ...args)
|
|
{
|
|
return new T(args...);
|
|
}
|
|
|
|
/***********************************************************************
|
|
Traits
|
|
***********************************************************************/
|
|
|
|
template<typename T>
|
|
struct KeyType<Ptr<T>>
|
|
{
|
|
typedef T* Type;
|
|
|
|
static T* GetKeyValue(const Ptr<T>& key)
|
|
{
|
|
return key.Obj();
|
|
}
|
|
};
|
|
|
|
template<typename T>
|
|
struct KeyType<ComPtr<T>>
|
|
{
|
|
typedef T* Type;
|
|
|
|
static T* GetKeyValue(const ComPtr<T>& 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
|
|
***********************************************************************/
|
|
|
|
/// <summary>An enumerator interface for receiving values without going back.</summary>
|
|
/// <typeparam name="T">Type of the values returned from the enumerator.</typeparam>
|
|
template<typename T>
|
|
class IEnumerator : public virtual Interface
|
|
{
|
|
public:
|
|
typedef T ElementType;
|
|
|
|
/// <summary>Copy the enumerator with the current state.</summary>
|
|
/// <returns>The copied enumerator.</returns>
|
|
virtual IEnumerator<T>* Clone()const=0;
|
|
/// <summary>Get the reference to the current value in the enumerator.</summary>
|
|
/// <returns>The reference to the current value.</returns>
|
|
/// <remarks>
|
|
/// After calling <see cref="IEnumerable`1::CreateEnumerator"/>, <see cref="Next"/> need to be called to make the first value available.
|
|
/// Return value of <see cref="Current"/> will be changed after each time <see cref="Next"/> is called.
|
|
/// If <see cref="Next"/> returns false, the behavior of <see cref="Current"/> is undefined.
|
|
/// </remarks>
|
|
virtual const T& Current()const=0;
|
|
/// <summary>Get the position of the current value in the enumerator.</summary>
|
|
/// <returns>The position of the current value.</returns>
|
|
/// <remarks>
|
|
/// After calling <see cref="IEnumerable`1::CreateEnumerator"/>, <see cref="Next"/> need to be called to make the first value available.
|
|
/// Index will be increased after each time <see cref="Next"/> is called with true returned.
|
|
/// </remarks>
|
|
virtual vint Index()const=0;
|
|
/// <summary>Prepare for the next value.</summary>
|
|
/// <returns>Returns false if there is no more value.</returns>
|
|
virtual bool Next()=0;
|
|
/// <summary>Reset the enumerator.</summary>
|
|
virtual void Reset()=0;
|
|
/// <summary>Test if all values of this enumerator have been evaluated.</summary>
|
|
/// <returns>Returns true if all values have been evaluated.</returns>
|
|
/// <remarks>An evaluated enumerator typically means, there will be no more calculation happens in <see cref="Next"/> regardless if all values have been read or not.</remarks>
|
|
virtual bool Evaluated()const{return false;}
|
|
};
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// <see cref="CopyFrom`*"/> functions work for all enumerable implementation.
|
|
/// <see cref="LazyList`1"/> provides high-level operations for enumerables, you can create a lazy list by calling <see cref="From`*"/> on any enumerables.
|
|
/// </summary>
|
|
/// <example><![CDATA[
|
|
/// int main()
|
|
/// {
|
|
/// List<vint> xs;
|
|
/// for (vint i = 0; i < 10; i++)
|
|
/// xs.Add(i);
|
|
/// List<vint> 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"");
|
|
/// }
|
|
/// ]]></example>
|
|
/// <typeparam name="T">Type of the values in the enumerable.</typeparam>
|
|
template<typename T>
|
|
class IEnumerable : public virtual Interface
|
|
{
|
|
public:
|
|
typedef T ElementType;
|
|
|
|
/// <summary>
|
|
/// Create an enumerator. [M:vl.collections.IEnumerator`1.Next] should be called before reading the first value.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// In most of the cases, you do not need to call this function.
|
|
/// "for (auto x : xs);", "for (auto [x, i] : indexed(xs));", <see cref="CopyFrom`*"/> and <see cref="LazyList`1"/> do all the jobs for you.
|
|
/// </remarks>
|
|
/// <returns>The enumerator.</returns>
|
|
virtual IEnumerator<T>* CreateEnumerator() const = 0;
|
|
|
|
/// <summary>Get the underlying collection object.</summary>
|
|
/// <returns>The underlying collection object, could be nullptr.</returns>
|
|
virtual const Object* GetCollectionObject() const = 0;
|
|
|
|
/// <summary>Get the associated collection reference.</summary>
|
|
/// <returns>The associated collection reference.</returns>
|
|
virtual Ptr<ICollectionReference> GetCollectionReference() const = 0;
|
|
|
|
/// <summary>
|
|
/// Associate a collection reference to this collection.
|
|
/// It will crash if one has been associated.
|
|
/// <see cref="ICollectionReference::OnDisposed"/> will be called when this collection is no longer available.
|
|
/// </summary>
|
|
/// <param name="ref">The associated collection reference.</param>
|
|
virtual void SetCollectionReference(Ptr<ICollectionReference> ref) const = 0;
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
/// <typeparam name="T">The expected type of the associated collection reference.</typeparam>
|
|
/// <returns>The strong-typed associated collection reference.</returns>
|
|
template<typename U>
|
|
Ptr<U> TryGetCollectionReference()
|
|
{
|
|
auto ref = GetCollectionReference();
|
|
if (!ref) return nullptr;
|
|
auto sref = ref.template Cast<U>();
|
|
CHECK_ERROR(sref, L"IEnumerable<T>::TryGetCollectionReference<U>()#The associated collection reference has an unexpected type.");
|
|
return sref;
|
|
}
|
|
};
|
|
|
|
template<typename T>
|
|
class EnumerableBase : public Object, public virtual IEnumerable<T>
|
|
{
|
|
private:
|
|
mutable Ptr<ICollectionReference> colref;
|
|
|
|
public:
|
|
~EnumerableBase()
|
|
{
|
|
if (colref) colref->OnDisposed();
|
|
}
|
|
|
|
const Object* GetCollectionObject() const override
|
|
{
|
|
return this;
|
|
}
|
|
|
|
Ptr<ICollectionReference> GetCollectionReference() const override
|
|
{
|
|
return colref;
|
|
}
|
|
|
|
void SetCollectionReference(Ptr<ICollectionReference> ref) const override
|
|
{
|
|
CHECK_ERROR(!colref, L"EnumerableBase<T>::SetCollectionReference(Ptr<ICollectionReference>)#Cannot associate another collection reference to this collection.");
|
|
colref = ref;
|
|
}
|
|
};
|
|
|
|
/***********************************************************************
|
|
Random Access
|
|
***********************************************************************/
|
|
|
|
namespace randomaccess_internal
|
|
{
|
|
template<typename T>
|
|
struct RandomAccessable
|
|
{
|
|
static const bool CanRead = false;
|
|
static const bool CanResize = false;
|
|
};
|
|
|
|
template<typename T>
|
|
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<typename V>
|
|
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 <string.h>
|
|
|
|
#ifdef VCZH_CHECK_MEMORY_LEAKS_NEW
|
|
#undef new
|
|
#endif
|
|
|
|
#include <new>
|
|
|
|
namespace vl
|
|
{
|
|
namespace collections
|
|
{
|
|
|
|
/***********************************************************************
|
|
Memory Management
|
|
***********************************************************************/
|
|
|
|
namespace memory_management
|
|
{
|
|
template<typename T>
|
|
void CallDefaultCtors(T* items, vint count)
|
|
{
|
|
if constexpr (!std::is_trivially_constructible_v<T>)
|
|
{
|
|
for (vint i = 0; i < count; i++)
|
|
{
|
|
new(&items[i])T();
|
|
}
|
|
}
|
|
}
|
|
|
|
template<typename T>
|
|
void CallCopyCtors(T* items, const T* source, vint count)
|
|
{
|
|
if constexpr (!std::is_trivially_copy_constructible_v<T>)
|
|
{
|
|
for (vint i = 0; i < count; i++)
|
|
{
|
|
new(&items[i])T(source[i]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
memcpy(items, source, sizeof(T) * count);
|
|
}
|
|
}
|
|
|
|
template<typename T>
|
|
void CallMoveCtors(T* items, T* source, vint count)
|
|
{
|
|
if constexpr (!std::is_trivially_move_constructible_v<T>)
|
|
{
|
|
for (vint i = 0; i < count; i++)
|
|
{
|
|
new(&items[i])T(std::move(source[i]));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
memcpy(items, source, sizeof(T) * count);
|
|
}
|
|
}
|
|
|
|
template<typename T>
|
|
void CallMoveAssignmentsOverlapped(T* items, T* source, vint count)
|
|
{
|
|
if constexpr (!std::is_trivially_move_assignable_v<T>)
|
|
{
|
|
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<typename T>
|
|
void CallDtors(T* items, vint count)
|
|
{
|
|
if constexpr (!std::is_trivially_destructible_v<T>)
|
|
{
|
|
for (vint i = 0; i < count; i++)
|
|
{
|
|
items[i].~T();
|
|
}
|
|
}
|
|
}
|
|
|
|
template<typename T>
|
|
T* AllocateBuffer(vint size)
|
|
{
|
|
if (size <= 0) return nullptr;
|
|
return (T*)malloc(sizeof(T) * size);
|
|
}
|
|
|
|
template<typename T>
|
|
void DeallocateBuffer(T* buffer)
|
|
{
|
|
if (buffer == nullptr) return;
|
|
free(buffer);
|
|
}
|
|
|
|
template<typename T>
|
|
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<T>(newCapacity);
|
|
CallMoveCtors(newBuffer, items, newCount);
|
|
CallDtors(items, newCount);
|
|
DeallocateBuffer(items);
|
|
capacity = newCapacity;
|
|
items = newBuffer;
|
|
}
|
|
}
|
|
}
|
|
|
|
template<typename T>
|
|
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<T>(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<typename TItem, typename TArg, bool = std::is_same_v<std::remove_cvref_t<TArg>, std::remove_cvref_t<TItem>>>
|
|
struct Accept_;
|
|
|
|
template<typename TItem, typename TArg>
|
|
struct Accept_<TItem, TArg, true>
|
|
{
|
|
using TAccept = TArg;
|
|
using TForward = TArg;
|
|
};
|
|
|
|
template<typename TItem, typename TArg>
|
|
struct Accept_<TItem, TArg, false>
|
|
{
|
|
using TAccept = TItem;
|
|
using TForward = TItem&&;
|
|
};
|
|
|
|
template<typename TItem, typename TArg>
|
|
using AcceptType = typename Accept_<TItem, TArg>::TAccept;
|
|
|
|
template<typename TItem, typename TArg>
|
|
using ForwardType = typename Accept_<TItem, TArg>::TForward;
|
|
|
|
template<typename TItem, typename TArg>
|
|
AcceptType<TItem, TArg&&> RefOrConvert(TArg&& arg)
|
|
{
|
|
return std::forward<TArg&&>(arg);
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
ArrayBase
|
|
***********************************************************************/
|
|
|
|
/// <summary>Base type of all linear container.</summary>
|
|
/// <typeparam name="T">Type of elements.</typeparam>
|
|
template<typename T>
|
|
class ArrayBase abstract : public EnumerableBase<T>
|
|
{
|
|
protected:
|
|
class Enumerator : public Object, public virtual IEnumerator<T>
|
|
{
|
|
private:
|
|
const ArrayBase<T>* container;
|
|
vint index;
|
|
|
|
public:
|
|
Enumerator(const ArrayBase<T>* _container, vint _index = -1)
|
|
{
|
|
container = _container;
|
|
index = _index;
|
|
}
|
|
|
|
IEnumerator<T>* 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<T>* CreateEnumerator()const
|
|
{
|
|
return new Enumerator(this);
|
|
}
|
|
|
|
/// <summary>Get the number of elements in the container.</summary>
|
|
/// <returns>The number of elements.</returns>
|
|
vint Count()const
|
|
{
|
|
return count;
|
|
}
|
|
|
|
/// <summary>Get the reference to the specified element.</summary>
|
|
/// <returns>The reference to the specified element. It will crash when the index is out of range.</returns>
|
|
/// <param name="index">The index of the element.</param>
|
|
const T& Get(vint index)const
|
|
{
|
|
CHECK_ERROR(index >= 0 && index < this->count, L"ArrayBase<T, K>::Get(vint)#Argument index not in range.");
|
|
return this->buffer[index];
|
|
}
|
|
|
|
/// <summary>Get the reference to the specified element.</summary>
|
|
/// <returns>The reference to the specified element. It will crash when the index is out of range.</returns>
|
|
/// <param name="index">The index of the element.</param>
|
|
const T& operator[](vint index)const
|
|
{
|
|
CHECK_ERROR(index >= 0 && index < this->count, L"ArrayBase<T, K>::operator[](vint)#Argument index not in range.");
|
|
return this->buffer[index];
|
|
}
|
|
};
|
|
|
|
/***********************************************************************
|
|
Array
|
|
***********************************************************************/
|
|
|
|
/// <summary>Array: linear container with fixed size in runtime. All elements are contiguous in memory.</summary>
|
|
/// <typeparam name="T">Type of elements.</typeparam>
|
|
/// <typeparam name="K">Type of the key type of elements. It is recommended to use the default value.</typeparam>
|
|
template<typename T, typename K = typename KeyType<T>::Type>
|
|
class Array : public ArrayBase<T>
|
|
{
|
|
public:
|
|
/// <summary>Create an array.</summary>
|
|
/// <param name="size">The size of the array.</param>
|
|
/// <remarks>
|
|
/// The default value is zero. <see cref="Resize"/> can be called to determine the size later.
|
|
/// It will crash when the size is a negative number.
|
|
/// </remarks>
|
|
Array(vint size = 0)
|
|
{
|
|
CHECK_ERROR(size >= 0, L"Array<T>::Array(vint)#Size should not be negative.");
|
|
this->buffer = memory_management::AllocateBuffer<T>(size);
|
|
memory_management::CallDefaultCtors(this->buffer, size);
|
|
this->count = size;
|
|
}
|
|
|
|
/// <summary>Create an array with elements provided.</summary>
|
|
/// <param name="_buffer">Pointer to values to copy.</param>
|
|
/// <param name="size">The number of values to copy.</param>
|
|
/// <remarks>It will crash when the size is a negative number.</remarks>
|
|
Array(const T* _buffer, vint size)
|
|
{
|
|
CHECK_ERROR(size >= 0, L"Array<T>::Array(const T*, vint)#Size should not be negative.");
|
|
this->buffer = memory_management::AllocateBuffer<T>(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<T, K>&) = delete;
|
|
Array(Array<T, K>&& _move)
|
|
{
|
|
this->buffer = _move.buffer;
|
|
this->count = _move.count;
|
|
_move.buffer = nullptr;
|
|
_move.count = 0;
|
|
}
|
|
|
|
Array<T, K>& operator=(const Array<T, K>&) = delete;
|
|
Array<T, K>& operator=(Array<T, K>&& _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;
|
|
}
|
|
|
|
/// <summary>Test does the array contain a value or not.</summary>
|
|
/// <returns>Returns true if the array contains the specified value.</returns>
|
|
/// <param name="item">The value to test.</param>
|
|
bool Contains(const K& item)const
|
|
{
|
|
return IndexOf(item) != -1;
|
|
}
|
|
|
|
/// <summary>Get the position of a value in this array.</summary>
|
|
/// <returns>Returns the position of first element that equals to the specified value. Returns -1 if failed to find.</returns>
|
|
/// <param name="item">The value to find.</param>
|
|
vint IndexOf(const K& item)const
|
|
{
|
|
for (vint i = 0; i < this->count; i++)
|
|
{
|
|
if (this->buffer[i] == item)
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/// <summary>Replace an element in the specified position.</summary>
|
|
/// <typeparam name="TItem">The type of the new value.</typeparam>
|
|
/// <returns>Returns true if this operation succeeded. It will crash when the index is out of range</returns>
|
|
/// <param name="index">The position of the element to replace.</param>
|
|
/// <param name="item">The new value to replace.</param>
|
|
template<typename TItem>
|
|
bool Set(vint index, TItem&& item)
|
|
{
|
|
CHECK_ERROR(index >= 0 && index < this->count, L"Array<T, K>::Set(vint)#Argument index not in range.");
|
|
this->buffer[index] = std::forward<TItem&&>(item);
|
|
return true;
|
|
}
|
|
|
|
bool Set(vint index, T&& item)
|
|
{
|
|
return Set<T>(index, std::move(item));
|
|
}
|
|
|
|
using ArrayBase<T>::operator[];
|
|
|
|
/// <summary>Get the reference to the specified element.</summary>
|
|
/// <returns>The reference to the specified element. It will crash when the index is out of range.</returns>
|
|
/// <param name="index">The index of the element.</param>
|
|
T& operator[](vint index)
|
|
{
|
|
CHECK_ERROR(index >= 0 && index < this->count, L"Array<T, K>::operator[](vint)#Argument index not in range.");
|
|
return this->buffer[index];
|
|
}
|
|
|
|
/// <summary>Change the size of the array. This function can be called multiple times to change the size.</summary>
|
|
/// <param name="size">The new size of the array.</param>
|
|
/// <remarks>It will crash when the size is a negative number.</remarks>
|
|
void Resize(vint size)
|
|
{
|
|
CHECK_ERROR(size >= 0, L"Array<T>::Resize(vint)#Size should not be negative.");
|
|
T* newBuffer = memory_management::AllocateBuffer<T>(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
|
|
***********************************************************************/
|
|
|
|
/// <summary>Base type for all list containers.</summary>
|
|
/// <typeparam name="T">Type of elements.</typeparam>
|
|
/// <typeparam name="K">Type of the key type of elements. It is recommended to use the default value.</typeparam>
|
|
template<typename T, typename K = typename KeyType<T>::Type>
|
|
class ListBase abstract : public ArrayBase<T>
|
|
{
|
|
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<T, K>&) = delete;
|
|
ListBase(ListBase<T, K>&& _move)
|
|
{
|
|
this->buffer = _move.buffer;
|
|
this->count = _move.count;
|
|
this->capacity = _move.capacity;
|
|
_move.buffer = nullptr;
|
|
_move.count = 0;
|
|
_move.capacity = 0;
|
|
}
|
|
|
|
ListBase<T, K>& operator=(const ListBase<T, K>&) = delete;
|
|
ListBase<T, K>& operator=(ListBase<T, K>&& _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;
|
|
}
|
|
|
|
/// <summary>Remove an element at a specified position.</summary>
|
|
/// <returns>Returns true if the element is removed. It will crash when the index is out of range.</returns>
|
|
/// <param name="index">The index of the element to remove.</param>
|
|
bool RemoveAt(vint index)
|
|
{
|
|
vint previousCount = this->count;
|
|
CHECK_ERROR(index >= 0 && index < this->count, L"ListBase<T, K>::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;
|
|
}
|
|
|
|
/// <summary>Remove contiguous elements at a specified psition.</summary>
|
|
/// <returns>Returns true if elements are removed. It will crash when the index or the size is out of range.</returns>
|
|
/// <param name="index">The index of the first element to remove.</param>
|
|
/// <param name="_count">The number of elements to remove.</param>
|
|
bool RemoveRange(vint index, vint _count)
|
|
{
|
|
vint previousCount = this->count;
|
|
CHECK_ERROR(index >= 0 && index <= this->count, L"ListBase<T, K>::RemoveRange(vint, vint)#Argument index not in range.");
|
|
CHECK_ERROR(index + _count >= 0 && index + _count <= this->count, L"ListBase<T,K>::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;
|
|
}
|
|
|
|
/// <summary>Remove all elements.</summary>
|
|
/// <returns>Returns true if all elements are removed.</returns>
|
|
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
|
|
***********************************************************************/
|
|
|
|
/// <summary>List: linear container with dynamic size in runtime for unordered values. All elements are contiguous in memory.</summary>
|
|
/// <typeparam name="T">Type of elements.</typeparam>
|
|
/// <typeparam name="K">Type of the key type of elements. It is recommended to use the default value.</typeparam>
|
|
template<typename T, typename K = typename KeyType<T>::Type>
|
|
class List : public ListBase<T, K>
|
|
{
|
|
public:
|
|
/// <summary>Create an empty list.</summary>
|
|
List() = default;
|
|
List(List<T, K>&& container) : ListBase<T, K>(std::move(container)) {}
|
|
List<T, K>& operator=(List<T, K>&& _move) = default;
|
|
|
|
/// <summary>Test does the list contain a value or not.</summary>
|
|
/// <returns>Returns true if the list contains the specified value.</returns>
|
|
/// <param name="item">The value to test.</param>
|
|
bool Contains(const K& item)const
|
|
{
|
|
return IndexOf(item) != -1;
|
|
}
|
|
|
|
/// <summary>Get the position of a value in this list.</summary>
|
|
/// <returns>Returns the position of first element that equals to the specified value. Returns -1 if failed to find.</returns>
|
|
/// <param name="item">The value to find.</param>
|
|
vint IndexOf(const K& item)const
|
|
{
|
|
for (vint i = 0; i < this->count; i++)
|
|
{
|
|
if (this->buffer[i] == item)
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/// <summary>Append a value at the end of the list.</summary>
|
|
/// <typeparam name="TItem">The type of the new value.</typeparam>
|
|
/// <returns>The index of the added item.</returns>
|
|
/// <param name="item">The value to add.</param>
|
|
template<typename TItem>
|
|
vint Add(TItem&& item)
|
|
{
|
|
return Insert(this->count, std::forward<TItem&&>(item));
|
|
}
|
|
|
|
vint Add(T&& item)
|
|
{
|
|
return Add<T>(std::move(item));
|
|
}
|
|
|
|
/// <summary>Insert a value at the specified position.</summary>
|
|
/// <returns>The index of the added item. It will crash if the index is out of range</returns>
|
|
/// <param name="index">The position to insert the value.</param>
|
|
/// <param name="item">The value to add.</param>
|
|
vint Insert(vint index, const T& item)
|
|
{
|
|
CHECK_ERROR(index >= 0 && index <= this->count, L"List<T, K>::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;
|
|
}
|
|
|
|
/// <summary>Insert a value at the specified position.</summary>
|
|
/// <returns>The index of the added item. It will crash if the index is out of range</returns>
|
|
/// <param name="index">The position to insert the value.</param>
|
|
/// <param name="item">The value to add.</param>
|
|
vint Insert(vint index, T&& item)
|
|
{
|
|
CHECK_ERROR(index >= 0 && index <= this->count, L"List<T, K>::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;
|
|
}
|
|
|
|
/// <summary>Remove an element from the list. If multiple elements equal to the specified value, only the first one will be removed</summary>
|
|
/// <returns>Returns true if the element is removed.</returns>
|
|
/// <param name="item">The item to remove.</param>
|
|
bool Remove(const K& item)
|
|
{
|
|
vint index = IndexOf(item);
|
|
if (index >= 0 && index < this->count)
|
|
{
|
|
this->RemoveAt(index);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// <summary>Replace an element in the specified position.</summary>
|
|
/// <typeparam name="TItem">The type of the new value.</typeparam>
|
|
/// <returns>Returns true if this operation succeeded. It will crash when the index is out of range</returns>
|
|
/// <param name="index">The position of the element to replace.</param>
|
|
/// <param name="item">The new value to replace.</param>
|
|
template<typename TItem>
|
|
bool Set(vint index, TItem&& item)
|
|
{
|
|
CHECK_ERROR(index >= 0 && index < this->count, L"List<T, K>::Set(vint)#Argument index not in range.");
|
|
this->buffer[index] = std::forward<TItem&&>(item);
|
|
return true;
|
|
}
|
|
|
|
bool Set(vint index, T&& item)
|
|
{
|
|
return Set<T>(index, std::move(item));
|
|
}
|
|
|
|
using ListBase<T, K>::operator[];
|
|
|
|
/// <summary>Get the reference to the specified element.</summary>
|
|
/// <returns>The reference to the specified element. It will crash when the index is out of range.</returns>
|
|
/// <param name="index">The index of the element.</param>
|
|
T& operator[](vint index)
|
|
{
|
|
CHECK_ERROR(index >= 0 && index < this->count, L"List<T, K>::operator[](vint)#Argument index not in range.");
|
|
return this->buffer[index];
|
|
}
|
|
};
|
|
|
|
/***********************************************************************
|
|
SortedList
|
|
***********************************************************************/
|
|
|
|
/// <summary>SortedList: linear container with dynamic size in runtime for ordered values. All elements are kept in order, and are contiguous in memory.</summary>
|
|
/// <typeparam name="T">Type of elements.</typeparam>
|
|
/// <typeparam name="K">Type of the key type of elements. It is recommended to use the default value.</typeparam>
|
|
template<typename T, typename K = typename KeyType<T>::Type>
|
|
class SortedList : public ListBase<T, K>
|
|
{
|
|
protected:
|
|
|
|
/// <summary>Get the position of an element in this list by performing binary search.</summary>
|
|
/// <typeparam name="Key">Type of the element to find.</typeparam>
|
|
/// <returns>Returns the position. Returns -1 if it does not exist.</returns>
|
|
/// <param name="item">The element to find.</param>
|
|
/// <param name="index">
|
|
/// 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 lest element that greater than the specified value.
|
|
/// </param>
|
|
template<typename Key>
|
|
vint IndexOfInternal(const Key& item, vint& index)const
|
|
{
|
|
vint start = 0;
|
|
vint end = this->count - 1;
|
|
index = -1;
|
|
while (start <= end)
|
|
{
|
|
index = start + (end - start) / 2;
|
|
if (this->buffer[index] == item)
|
|
{
|
|
return index;
|
|
}
|
|
else if (this->buffer[index] > item)
|
|
{
|
|
end = index - 1;
|
|
}
|
|
else
|
|
{
|
|
start = index + 1;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
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;
|
|
}
|
|
public:
|
|
/// <summary>Create an empty list.</summary>
|
|
SortedList() = default;
|
|
SortedList(SortedList<T, K>&& container) : ListBase<T, K>(std::move(container)) {}
|
|
SortedList<T, K>& operator=(SortedList<T, K> && _move) = default;
|
|
|
|
SortedList(const SortedList<T, K>&xs)
|
|
: ListBase<T, K>(std::move(const_cast<ListBase<T, K>&>(static_cast<const ListBase<T, K>&>(xs))))
|
|
{
|
|
}
|
|
|
|
/// <summary>Test does the list contain a value or not.</summary>
|
|
/// <returns>Returns true if the list contains the specified value.</returns>
|
|
/// <param name="item">The value to test.</param>
|
|
bool Contains(const K& item)const
|
|
{
|
|
return IndexOf(item) != -1;
|
|
}
|
|
|
|
/// <summary>Get the position of a value in this list.</summary>
|
|
/// <returns>Returns the position of first element that equals to the specified value. Returns -1 if failed to find.</returns>
|
|
/// <param name="item">The value to find.</param>
|
|
vint IndexOf(const K& item)const
|
|
{
|
|
vint outputIndex = -1;
|
|
return IndexOfInternal<K>(item, outputIndex);
|
|
}
|
|
|
|
/// <summary>Add a value at the correct position, all elements will be kept in order.</summary>
|
|
/// <typeparam name="TItem">The type of the new value.</typeparam>
|
|
/// <returns>The index of the added item.</returns>
|
|
/// <param name="item">The value to add.</param>
|
|
template<typename TItem>
|
|
vint Add(TItem&& item)
|
|
{
|
|
if (ArrayBase<T>::count == 0)
|
|
{
|
|
return Insert(0, std::forward<TItem&&>(item));
|
|
}
|
|
else
|
|
{
|
|
vint outputIndex = -1;
|
|
if constexpr (std::is_same_v<std::remove_cvref_t<T>, std::remove_cvref_t<K>>)
|
|
{
|
|
IndexOfInternal<K>(item, outputIndex);
|
|
}
|
|
else if constexpr (std::is_same_v<std::remove_cvref_t<TItem>, std::remove_cvref_t<K>>)
|
|
{
|
|
IndexOfInternal<K>(item, outputIndex);
|
|
}
|
|
else
|
|
{
|
|
IndexOfInternal<K>(KeyType<T>::GetKeyValue(item), outputIndex);
|
|
}
|
|
CHECK_ERROR(outputIndex >= 0 && outputIndex < this->count, L"SortedList<T, K>::Add(const T&)#Internal error, index not in range.");
|
|
if (this->buffer[outputIndex] < item)
|
|
{
|
|
outputIndex++;
|
|
}
|
|
return Insert(outputIndex, std::forward<TItem&&>(item));
|
|
}
|
|
}
|
|
|
|
vint Add(T&& item)
|
|
{
|
|
return Add<T>(std::move(item));
|
|
}
|
|
|
|
/// <summary>Remove an element from the list. If multiple elements equal to the specified value, only the first one will be removed</summary>
|
|
/// <returns>Returns true if the element is removed.</returns>
|
|
/// <param name="item">The item to remove.</param>
|
|
bool Remove(const K& item)
|
|
{
|
|
vint index = IndexOf(item);
|
|
if (index >= 0 && index < ArrayBase<T>::count)
|
|
{
|
|
this->RemoveAt(index);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
};
|
|
|
|
/***********************************************************************
|
|
Special Containers
|
|
***********************************************************************/
|
|
|
|
template<typename T>
|
|
class PushOnlyAllocator : public Object
|
|
{
|
|
protected:
|
|
vint blockSize;
|
|
vint allocatedSize;
|
|
List<T*> 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<vint Index = 4>
|
|
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<Index - 1>::Get(fragmentRoot, index) : 0;
|
|
}
|
|
|
|
static __forceinline void Set(TreeNode*& root, vuint8_t index, void* value, PushOnlyAllocator<TreeNode>& 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<Index - 1>::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<TreeNode>& allocator)
|
|
{
|
|
((void*&)root) = value;
|
|
}
|
|
};
|
|
}
|
|
|
|
template<typename T>
|
|
class ByteObjectMap : public Object
|
|
{
|
|
public:
|
|
typedef PushOnlyAllocator<bom_helper::TreeNode> 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<typename T, typename K>
|
|
struct RandomAccessable<Array<T, K>>
|
|
{
|
|
static const bool CanRead = true;
|
|
static const bool CanResize = true;
|
|
};
|
|
|
|
template<typename T, typename K>
|
|
struct RandomAccessable<List<T, K>>
|
|
{
|
|
static const bool CanRead = true;
|
|
static const bool CanResize = false;
|
|
};
|
|
|
|
template<typename T, typename K>
|
|
struct RandomAccessable<SortedList<T, K>>
|
|
{
|
|
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
|
|
{
|
|
/// <summary>Dictionary: one to one map container.</summary>
|
|
/// <typeparam name="KT">Type of keys.</typeparam>
|
|
/// <typeparam name="VT">Type of values.</typeparam>
|
|
/// <typeparam name="KK">Type of the key type of keys. It is recommended to use the default value.</typeparam>
|
|
/// <typeparam name="VK">Type of the key type of values. It is recommended to use the default value.</typeparam>
|
|
template<
|
|
typename KT,
|
|
typename VT,
|
|
typename KK=typename KeyType<KT>::Type,
|
|
typename VK=typename KeyType<VT>::Type
|
|
>
|
|
class Dictionary : public EnumerableBase<Pair<const KT&, const VT&>>
|
|
{
|
|
using KVPair = Pair<const KT&, const VT&>;
|
|
public:
|
|
typedef SortedList<KT, KK> KeyContainer;
|
|
typedef List<VT, VK> ValueContainer;
|
|
protected:
|
|
class Enumerator : public Object, public virtual IEnumerator<KVPair>
|
|
{
|
|
private:
|
|
const Dictionary<KT, VT, KK, VK>* container;
|
|
vint index;
|
|
KVPair current;
|
|
|
|
void UpdateCurrent()
|
|
{
|
|
if(index<container->Count())
|
|
{
|
|
current = { container->Keys().Get(index),container->Values().Get(index) };
|
|
}
|
|
}
|
|
public:
|
|
Enumerator(const Dictionary<KT, VT, KK, VK>* _container, vint _index=-1)
|
|
{
|
|
container=_container;
|
|
index=_index;
|
|
}
|
|
|
|
IEnumerator<KVPair>* Clone()const override
|
|
{
|
|
return new Enumerator(container, index);
|
|
}
|
|
|
|
const KVPair& Current()const override
|
|
{
|
|
return current;
|
|
}
|
|
|
|
vint Index()const override
|
|
{
|
|
return index;
|
|
}
|
|
|
|
bool Next() override
|
|
{
|
|
index++;
|
|
UpdateCurrent();
|
|
return index>=0 && index<container->Count();
|
|
}
|
|
|
|
void Reset() override
|
|
{
|
|
index=-1;
|
|
UpdateCurrent();
|
|
}
|
|
|
|
bool Evaluated()const override
|
|
{
|
|
return true;
|
|
}
|
|
};
|
|
|
|
KeyContainer keys;
|
|
ValueContainer values;
|
|
public:
|
|
/// <summary>Create an empty dictionary.</summary>
|
|
Dictionary() = default;
|
|
~Dictionary() = default;
|
|
|
|
Dictionary(const Dictionary<KT, VT, KK, VK>&) = delete;
|
|
Dictionary(Dictionary<KT, VT, KK, VK>&& _move)
|
|
: keys(std::move(_move.keys))
|
|
, values(std::move(_move.values))
|
|
{
|
|
}
|
|
|
|
Dictionary<KT, VT, KK, VK>& operator=(const Dictionary<KT, VT, KK, VK>&) = delete;
|
|
Dictionary<KT, VT, KK, VK>& operator=(Dictionary<KT, VT, KK, VK> && _move)
|
|
{
|
|
keys = std::move(_move.keys);
|
|
values = std::move(_move.values);
|
|
return* this;
|
|
}
|
|
|
|
IEnumerator<KVPair>* CreateEnumerator()const
|
|
{
|
|
return new Enumerator(this);
|
|
}
|
|
|
|
/// <summary>Get all keys.</summary>
|
|
/// <returns>All keys.</returns>
|
|
const KeyContainer& Keys()const
|
|
{
|
|
return keys;
|
|
}
|
|
|
|
/// <summary>Get all values.</summary>
|
|
/// <returns>All values.</returns>
|
|
const ValueContainer& Values()const
|
|
{
|
|
return values;
|
|
}
|
|
|
|
/// <summary>Get the number of keys.</summary>
|
|
/// <returns>The number of keys. It is also the number of values.</returns>
|
|
vint Count()const
|
|
{
|
|
return keys.Count();
|
|
}
|
|
|
|
/// <summary>Get the reference to the value associated to a specified key.</summary>
|
|
/// <returns>The reference to the value. It will crash if the key does not exist.</returns>
|
|
/// <param name="key">The key to find.</param>
|
|
const VT& Get(const KK& key)const
|
|
{
|
|
return values.Get(keys.IndexOf(key));
|
|
}
|
|
|
|
/// <summary>Get the reference to the value associated to a specified key.</summary>
|
|
/// <returns>The reference to the value. It will crash if the key does not exist.</returns>
|
|
/// <param name="key">The key to find.</param>
|
|
const VT& operator[](const KK& key)const
|
|
{
|
|
return values.Get(keys.IndexOf(key));
|
|
}
|
|
|
|
/// <summary>Replace the value associated to a specified key.</summary>
|
|
/// <typeparam name="TKeyItem">The type of the new key.</typeparam>
|
|
/// <typeparam name="TValueItem">The type of the new value.</typeparam>
|
|
/// <returns>Returns true if the value is replaced.</returns>
|
|
/// <param name="key">The key to find. If the key does not exist, it will be added to the dictionary.</param>
|
|
/// <param name="value">The associated value to replace.</param>
|
|
template<typename TKeyItem, typename TValueItem>
|
|
bool Set(TKeyItem&& key, TValueItem&& value)
|
|
{
|
|
using TKeyAccept = memory_management::AcceptType<KT, TKeyItem&&>;
|
|
using TKeyForward = memory_management::ForwardType<KT, TKeyItem&&>;
|
|
TKeyAccept keyAccept = memory_management::RefOrConvert<KT>(std::forward<TKeyItem&&>(key));
|
|
|
|
vint index = keys.IndexOf(KeyType<KT>::GetKeyValue(keyAccept));
|
|
if (index == -1)
|
|
{
|
|
index = keys.Add(std::forward<TKeyForward>(keyAccept));
|
|
values.Insert(index, std::forward<TValueItem&&>(value));
|
|
}
|
|
else
|
|
{
|
|
values[index] = std::forward<TValueItem&&>(value);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool Set(const KT& key, const VT& value) { return Set<const KT&, const VT&>(key, value); }
|
|
bool Set(const KT& key, VT&& value) { return Set<const KT&, VT>(key, std::move(value)); }
|
|
bool Set(KT&& key, const VT& value) { return Set<KT, const VT&>(std::move(key), value); }
|
|
bool Set(KT&& key, VT&& value) { return Set<KT, VT>(std::move(key), std::move(value)); }
|
|
|
|
/// <summary>Add a key with an associated value.</summary>
|
|
/// <returns>Returns true if the pair is added. If will crash if the key exists.</returns>
|
|
/// <param name="value">The pair of key and value.</param>
|
|
bool Add(const Pair<KT, VT>& value)
|
|
{
|
|
return Add(value.key, value.value);
|
|
}
|
|
|
|
/// <summary>Add a key with an associated value.</summary>
|
|
/// <returns>Returns true if the pair is added. If will crash if the key exists.</returns>
|
|
/// <param name="value">The pair of key and value.</param>
|
|
bool Add(Pair<KT, VT>&& value)
|
|
{
|
|
return Add(std::move(value.key), std::move(value.value));
|
|
}
|
|
|
|
/// <summary>Add a key with an associated value.</summary>
|
|
/// <typeparam name="TKeyItem">The type of the new key.</typeparam>
|
|
/// <typeparam name="TValueItem">The type of the new value.</typeparam>
|
|
/// <returns>Returns true if the pair is added. If will crash if the key exists.</returns>
|
|
/// <param name="key">The key to add.</param>
|
|
/// <param name="value">The value to add.</param>
|
|
template<typename TKeyItem, typename TValueItem>
|
|
bool Add(TKeyItem&& key, TValueItem&& value)
|
|
{
|
|
using TKeyAccept = memory_management::AcceptType<KT, TKeyItem&&>;
|
|
using TKeyForward = memory_management::ForwardType<KT, TKeyItem&&>;
|
|
TKeyAccept keyAccept = memory_management::RefOrConvert<KT>(std::forward<TKeyItem&&>(key));
|
|
|
|
CHECK_ERROR(!keys.Contains(KeyType<KT>::GetKeyValue(keyAccept)), L"Dictionary<KT, KK, ValueContainer, VT, VK>::Add(const KT&, const VT&)#Key already exists.");
|
|
vint index = keys.Add(std::forward<TKeyForward>(keyAccept));
|
|
values.Insert(index, std::forward<TValueItem&&>(value));
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Add(const KT& key, const VT& value) { return Add<const KT&, const VT&>(key, value); }
|
|
bool Add(const KT& key, VT&& value) { return Add<const KT&, VT>(key, std::move(value)); }
|
|
bool Add(KT&& key, const VT& value) { return Add<KT, const VT&>(std::move(key), value); }
|
|
bool Add(KT&& key, VT&& value) { return Add<KT, VT>(std::move(key), std::move(value)); }
|
|
|
|
/// <summary>Remove a key with the associated value.</summary>
|
|
/// <returns>Returns true if the key and the value is removed.</returns>
|
|
/// <param name="key">The key to find.</param>
|
|
bool Remove(const KK& key)
|
|
{
|
|
vint index=keys.IndexOf(key);
|
|
if(index!=-1)
|
|
{
|
|
keys.RemoveAt(index);
|
|
values.RemoveAt(index);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// <summary>Remove all elements.</summary>
|
|
/// <returns>Returns true if all elements are removed.</returns>
|
|
bool Clear()
|
|
{
|
|
keys.Clear();
|
|
values.Clear();
|
|
return true;
|
|
}
|
|
};
|
|
|
|
/// <summary>Group: one to many map container.</summary>
|
|
/// <typeparam name="KT">Type of keys.</typeparam>
|
|
/// <typeparam name="VT">Type of values.</typeparam>
|
|
/// <typeparam name="KK">Type of the key type of keys. It is recommended to use the default value.</typeparam>
|
|
/// <typeparam name="VK">Type of the key type of values. It is recommended to use the default value.</typeparam>
|
|
template<
|
|
typename KT,
|
|
typename VT,
|
|
typename KK=typename KeyType<KT>::Type,
|
|
typename VK=typename KeyType<VT>::Type
|
|
>
|
|
class Group : public EnumerableBase<Pair<const KT&, const VT&>>
|
|
{
|
|
using KVPair = Pair<const KT&, const VT&>;
|
|
public:
|
|
typedef SortedList<KT, KK> KeyContainer;
|
|
typedef List<VT, VK> ValueContainer;
|
|
protected:
|
|
class Enumerator : public Object, public virtual IEnumerator<KVPair>
|
|
{
|
|
private:
|
|
const Group<KT, VT, KK, VK>* container;
|
|
vint keyIndex;
|
|
vint valueIndex;
|
|
KVPair 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<KT, VT, KK, VK>* _container, vint _keyIndex=-1, vint _valueIndex=-1)
|
|
{
|
|
container=_container;
|
|
keyIndex=_keyIndex;
|
|
valueIndex=_valueIndex;
|
|
}
|
|
|
|
IEnumerator<KVPair>* Clone()const override
|
|
{
|
|
return new Enumerator(container, keyIndex, valueIndex);
|
|
}
|
|
|
|
const KVPair& Current()const override
|
|
{
|
|
return current;
|
|
}
|
|
|
|
vint Index()const override
|
|
{
|
|
if(0<=keyIndex && keyIndex<container->Count())
|
|
{
|
|
vint index=0;
|
|
for(vint i=0;i<keyIndex;i++)
|
|
{
|
|
index+=container->GetByIndex(i).Count();
|
|
}
|
|
return index+valueIndex;
|
|
}
|
|
else
|
|
{
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
bool Next() override
|
|
{
|
|
if(keyIndex==-1)
|
|
{
|
|
keyIndex=0;
|
|
}
|
|
while(keyIndex<container->Count())
|
|
{
|
|
valueIndex++;
|
|
const ValueContainer& values=container->GetByIndex(keyIndex);
|
|
if(valueIndex<values.Count())
|
|
{
|
|
UpdateCurrent();
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
keyIndex++;
|
|
valueIndex=-1;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void Reset() override
|
|
{
|
|
keyIndex=-1;
|
|
valueIndex=-1;
|
|
UpdateCurrent();
|
|
}
|
|
|
|
bool Evaluated()const override
|
|
{
|
|
return true;
|
|
}
|
|
};
|
|
|
|
KeyContainer keys;
|
|
List<ValueContainer*> values;
|
|
public:
|
|
/// <summary>Create an empty group.</summary>
|
|
Group() = default;
|
|
|
|
~Group()
|
|
{
|
|
Clear();
|
|
}
|
|
|
|
Group(const Group<KT, VT, KK, VK>&) = delete;
|
|
Group(Group<KT, VT, KK, VK>&& _move)
|
|
: keys(std::move(_move.keys))
|
|
, values(std::move(_move.values))
|
|
{
|
|
}
|
|
|
|
Group<KT, VT, KK, VK>& operator=(const Group<KT, VT, KK, VK>&) = delete;
|
|
Group<KT, VT, KK, VK>& operator=(Group<KT, VT, KK, VK> && _move)
|
|
{
|
|
Clear();
|
|
keys = std::move(_move.keys);
|
|
values = std::move(_move.values);
|
|
return*this;
|
|
}
|
|
|
|
IEnumerator<KVPair>* CreateEnumerator()const
|
|
{
|
|
return new Enumerator(this);
|
|
}
|
|
|
|
/// <summary>Get all keys.</summary>
|
|
/// <returns>All keys.</returns>
|
|
const KeyContainer& Keys()const
|
|
{
|
|
return keys;
|
|
}
|
|
|
|
/// <summary>Get the number of keys.</summary>
|
|
/// <returns>The number of keys.</returns>
|
|
vint Count()const
|
|
{
|
|
return keys.Count();
|
|
}
|
|
|
|
/// <summary>Get all values associated to a specified key.</summary>
|
|
/// <returns>All associated values. It will crash if the key does not exist.</returns>
|
|
/// <param name="key">The key to find.</param>
|
|
const ValueContainer& Get(const KK& key)const
|
|
{
|
|
return *values.Get(keys.IndexOf(key));
|
|
}
|
|
|
|
/// <summary>Get all values associated to a key at a specified index in <see cref="Keys"/>.</summary>
|
|
/// <returns>All associaged values. It will crash if the index is out of range.</returns>
|
|
/// <param name="index">The position of the key.</param>
|
|
const ValueContainer& GetByIndex(vint index)const
|
|
{
|
|
return *values.Get(index);
|
|
}
|
|
|
|
/// <summary>Get all values associated to a specified key.</summary>
|
|
/// <returns>All associated values. It will crash if the key does not exist.</returns>
|
|
/// <param name="key">The key to find.</param>
|
|
const ValueContainer& operator[](const KK& key)const
|
|
{
|
|
return *values.Get(keys.IndexOf(key));
|
|
}
|
|
|
|
/// <summary>Test if there is any value associated to a specified key or not.</summary>
|
|
/// <returns>Returns true there is at least one value associated to this key.</returns>
|
|
/// <param name="key">The key to find.</param>
|
|
bool Contains(const KK& key)const
|
|
{
|
|
return keys.Contains(key);
|
|
}
|
|
|
|
/// <summary>Test if a value is associated to a specified key or not.</summary>
|
|
/// <returns>Returns true if the specified value is associated to the specified key.</returns>
|
|
/// <param name="key">The key to find.</param>
|
|
/// <param name="value">The value to find.</param>
|
|
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;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
/// <returns>Returns true if the pair is added.</returns>
|
|
/// <param name="value">The pair of key and value to add.</param>
|
|
bool Add(const Pair<KT, VT>& value)
|
|
{
|
|
return Add(value.key, value.value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
/// <returns>Returns true if the pair is added.</returns>
|
|
/// <param name="value">The pair of key and value to add.</param>
|
|
bool Add(Pair<KT, VT>&& value)
|
|
{
|
|
return Add(std::move(value.key), std::move(value.value));
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
/// <typeparam name="TKeyItem">The type of the new key.</typeparam>
|
|
/// <typeparam name="TValueItem">The type of the new value.</typeparam>
|
|
/// <returns>Returns true if the key and the value are added.</returns>
|
|
/// <param name="key">The key to add.</param>
|
|
/// <param name="value">The value to add.</param>
|
|
template<typename TKeyItem, typename TValueItem>
|
|
bool Add(TKeyItem&& key, TValueItem&& value)
|
|
{
|
|
using TKeyAccept = memory_management::AcceptType<KT, TKeyItem&&>;
|
|
using TKeyForward = memory_management::ForwardType<KT, TKeyItem&&>;
|
|
TKeyAccept keyAccept = memory_management::RefOrConvert<KT>(std::forward<TKeyItem&&>(key));
|
|
|
|
ValueContainer* target = nullptr;
|
|
vint index = keys.IndexOf(KeyType<KT>::GetKeyValue(keyAccept));
|
|
if (index == -1)
|
|
{
|
|
target = new ValueContainer;
|
|
values.Insert(keys.Add(std::forward<TKeyForward>(keyAccept)), target);
|
|
}
|
|
else
|
|
{
|
|
target = values[index];
|
|
}
|
|
target->Add(std::forward<TValueItem&&>(value));
|
|
return true;
|
|
}
|
|
|
|
bool Add(const KT& key, const VT& value) { return Add<const KT&, const VT&>(key, value); }
|
|
bool Add(const KT& key, VT&& value) { return Add<const KT&, VT>(key, std::move(value)); }
|
|
bool Add(KT&& key, const VT& value) { return Add<KT, const VT&>(std::move(key), value); }
|
|
bool Add(KT&& key, VT&& value) { return Add<KT, VT>(std::move(key), std::move(value)); }
|
|
|
|
/// <summary>Remove a key with all associated values.</summary>
|
|
/// <returns>Returns true if the key and all associated values are removed.</returns>
|
|
/// <param name="key">The key to find.</param>
|
|
bool Remove(const KK& key)
|
|
{
|
|
vint index=keys.IndexOf(key);
|
|
if(index!=-1)
|
|
{
|
|
keys.RemoveAt(index);
|
|
List<VT, VK>* target=values[index];
|
|
values.RemoveAt(index);
|
|
delete target;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// <summary>Remove a value associated to a specified key.</summary>
|
|
/// <returns>
|
|
/// Returns true if the value is removed.
|
|
/// If this value is associated to the key for jultiple times, only the first one will be removed.
|
|
/// If this value is associated to the key for jultiple times, only the first one will be removed.
|
|
/// </returns>
|
|
/// <param name="key">The key to find.</param>
|
|
/// <param name="value">The value to remove.</param>
|
|
bool Remove(const KK& key, const VK& value)
|
|
{
|
|
vint index=keys.IndexOf(key);
|
|
if(index!=-1)
|
|
{
|
|
List<VT, VK>* target=values[index];
|
|
target->Remove(value);
|
|
if(target->Count()==0)
|
|
{
|
|
keys.RemoveAt(index);
|
|
values.RemoveAt(index);
|
|
delete target;
|
|
}
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// <summary>Remove everything.</summary>
|
|
/// <returns>Returns true if all keys and values are removed.</returns>
|
|
bool Clear()
|
|
{
|
|
for(vint i=0;i<values.Count();i++)
|
|
{
|
|
delete values[i];
|
|
}
|
|
keys.Clear();
|
|
values.Clear();
|
|
return true;
|
|
}
|
|
};
|
|
|
|
/***********************************************************************
|
|
GroupInnerJoin
|
|
***********************************************************************/
|
|
|
|
/// <summary>Perform inner join on two groups.</summary>
|
|
/// <typeparam name="TKey">The type of keys in two groups.</typeparam>
|
|
/// <typeparam name="TValueFirst">The type of values in the first group.</typeparam>
|
|
/// <typeparam name="TValueSecond">The type of values in the second group.</typeparam>
|
|
/// <typeparam name="TDiscardFirst">The type of the first callback.</typeparam>
|
|
/// <typeparam name="TDiscardSecond">The type of the second callback.</typeparam>
|
|
/// <typeparam name="TAccept">The type of the third callback.</typeparam>
|
|
/// <param name="first">The first group.</param>
|
|
/// <param name="second">The second group.</param>
|
|
/// <param name="discardFirst">
|
|
/// 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.
|
|
/// </param>
|
|
/// <param name="discardSecond">
|
|
/// 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.
|
|
/// </param>
|
|
/// <param name="accept">
|
|
/// 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.
|
|
/// </param>
|
|
/// <remarks>
|
|
/// This function does not change data in provided groups.
|
|
/// </remarks>
|
|
/// <example><
|
|
/// {
|
|
/// return L"[" + From(values).Aggregate([](const WString& a, const WString& b) { return a + L", " + b; }) + L"]";
|
|
/// };
|
|
///
|
|
/// Group<vint, WString> 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<WString>& values) { Console::WriteLine(L"Discarded in as: " + itow(key) + printList(values)); },
|
|
/// [&](vint key, const List<WString>& values) { Console::WriteLine(L"Discarded in bs: " + itow(key) + printList(values)); },
|
|
/// [&](vint key, const List<WString>& values1, const List<WString>& values2) { Console::WriteLine(L"Accepted: " + itow(key) + printList(values1) + printList(values2)); }
|
|
/// );
|
|
/// }
|
|
/// ]]></example>
|
|
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<TKey, TValueFirst>& first,
|
|
const Group<TKey, TValueSecond>& 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<TValueFirst>& firstValues = first.GetByIndex(firstIndex);
|
|
|
|
if (secondIndex < secondCount)
|
|
{
|
|
auto secondKey = second.Keys()[secondIndex];
|
|
const List<TValueSecond>& 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<TValueSecond>& secondValues = second.GetByIndex(secondIndex);
|
|
|
|
discardSecond(secondKey, secondValues);
|
|
secondIndex++;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
Random Access
|
|
***********************************************************************/
|
|
namespace randomaccess_internal
|
|
{
|
|
template<typename KT, typename VT, typename KK, typename VK>
|
|
struct RandomAccessable<Dictionary<KT, VT, KK, VK>>
|
|
{
|
|
static const bool CanRead = true;
|
|
static const bool CanResize = false;
|
|
};
|
|
|
|
template<typename KT, typename VT, typename KK, typename VK>
|
|
struct RandomAccess<Dictionary<KT, VT, KK, VK>>
|
|
{
|
|
static vint GetCount(const Dictionary<KT, VT, KK, VK>& t)
|
|
{
|
|
return t.Count();
|
|
}
|
|
|
|
static Pair<KT, VT> GetValue(const Dictionary<KT, VT, KK, VK>& t, vint index)
|
|
{
|
|
return Pair<KT, VT>(t.Keys().Get(index), t.Values().Get(index));
|
|
}
|
|
|
|
static void AppendValue(Dictionary<KT, VT, KK, VK>& t, const Pair<KT, VT>& value)
|
|
{
|
|
t.Set(value.key, value.value);
|
|
}
|
|
|
|
static void AppendValue(Dictionary<KT, VT, KK, VK>& t, const Pair<const KT&, const VT&>& 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<typename Ds, typename Ss, bool DsRA, bool SsRA>
|
|
struct CopyFromAlgorithm
|
|
{
|
|
};
|
|
|
|
template<typename Ds, typename Ss>
|
|
struct CopyFromAlgorithm<Ds, Ss, true, true>
|
|
{
|
|
static void Perform(Ds& ds, const Ss& ss, bool append)
|
|
{
|
|
vint copyCount=RandomAccess<Ss>::GetCount(ss);
|
|
vint index=(append?RandomAccess<Ds>::GetCount(ds):0);
|
|
vint resizeCount=index+copyCount;
|
|
RandomAccess<Ds>::SetCount(ds, resizeCount);
|
|
for(vint i=0;i<copyCount;i++)
|
|
{
|
|
RandomAccess<Ds>::SetValue(ds, index+i, RandomAccess<Ss>::GetValue(ss, i));
|
|
}
|
|
}
|
|
};
|
|
|
|
template<typename Ds, typename Ss>
|
|
struct CopyFromAlgorithm<Ds, Ss, false, true>
|
|
{
|
|
static void Perform(Ds& ds, const Ss& ss, bool append)
|
|
{
|
|
if(!append)
|
|
{
|
|
ds.Clear();
|
|
}
|
|
vint copyCount=RandomAccess<Ss>::GetCount(ss);
|
|
for(vint i=0;i<copyCount;i++)
|
|
{
|
|
RandomAccess<Ds>::AppendValue(ds, RandomAccess<Ss>::GetValue(ss, i));
|
|
}
|
|
}
|
|
};
|
|
|
|
template<typename Ds, typename Ss>
|
|
struct CopyFromAlgorithm<Ds, Ss, true, false>
|
|
{
|
|
static void Perform(Ds& ds, const Ss& ss, bool append)
|
|
{
|
|
Ptr<IEnumerator<typename Ss::ElementType>> enumerator;
|
|
vint copyCount=0;
|
|
|
|
enumerator=ss.CreateEnumerator();
|
|
while(enumerator->Next())
|
|
{
|
|
copyCount++;
|
|
}
|
|
|
|
vint index=(append?RandomAccess<Ds>::GetCount(ds):0);
|
|
vint resizeCount=index+copyCount;
|
|
RandomAccess<Ds>::SetCount(ds, resizeCount);
|
|
|
|
enumerator=ss.CreateEnumerator();
|
|
while(enumerator->Next())
|
|
{
|
|
RandomAccess<Ds>::SetValue(ds, index++, enumerator->Current());
|
|
}
|
|
}
|
|
};
|
|
|
|
template<typename Ds, typename Ss>
|
|
struct CopyFromAlgorithm<Ds, Ss, false, false>
|
|
{
|
|
static void Perform(Ds& ds, const Ss& ss, bool append)
|
|
{
|
|
if (!append)
|
|
{
|
|
ds.Clear();
|
|
}
|
|
Ptr<IEnumerator<typename Ss::ElementType>> enumerator = ss.CreateEnumerator();
|
|
while (enumerator->Next())
|
|
{
|
|
RandomAccess<Ds>::AppendValue(ds, enumerator->Current());
|
|
}
|
|
}
|
|
};
|
|
|
|
template<typename T>
|
|
struct Slice
|
|
{
|
|
const T* items;
|
|
vint count;
|
|
};
|
|
}
|
|
|
|
namespace randomaccess_internal
|
|
{
|
|
template<typename T>
|
|
struct RandomAccessable<copyfrom_internal::Slice<T>>
|
|
{
|
|
static const bool CanRead = true;
|
|
static const bool CanResize = true;
|
|
};
|
|
|
|
template<typename T>
|
|
struct RandomAccess<copyfrom_internal::Slice<T>>
|
|
{
|
|
static vint GetCount(const copyfrom_internal::Slice<T>& t)
|
|
{
|
|
return t.count;
|
|
}
|
|
|
|
static const T& GetValue(const copyfrom_internal::Slice<T>& t, vint index)
|
|
{
|
|
return t.items[index];
|
|
}
|
|
};
|
|
}
|
|
|
|
/// <summary>Copy containers.</summary>
|
|
/// <typeparam name="Ds">Type of the target container.</typeparam>
|
|
/// <typeparam name="Ss">Type of the source container.</typeparam>
|
|
/// <param name="ds">The target container.</param>
|
|
/// <param name="ss">The source container.</param>
|
|
/// <param name="append">Set to true to perform appending instead of replacing.</param>
|
|
template<typename Ds, typename Ss>
|
|
void CopyFrom(Ds& ds, const Ss& ss, bool append = false)
|
|
{
|
|
copyfrom_internal::CopyFromAlgorithm<Ds, Ss, randomaccess_internal::RandomAccessable<Ds>::CanResize, randomaccess_internal::RandomAccessable<Ss>::CanRead>::Perform(ds, ss, append);
|
|
}
|
|
|
|
/// <summary>Copy containers.</summary>
|
|
/// <typeparam name="Ds">Type of the target container.</typeparam>
|
|
/// <typeparam name="S">Type of values in the source container.</typeparam>
|
|
/// <param name="ds">The target container.</param>
|
|
/// <param name="buffer">Pointer to source values.</param>
|
|
/// <param name="count">The number of values to copy.</param>
|
|
/// <param name="append">Set to true to perform appending instead of replacing.</param>
|
|
template<typename Ds, typename S>
|
|
void CopyFrom(Ds& ds, const S* buffer, vint count, bool append = false)
|
|
{
|
|
copyfrom_internal::Slice<S> slice = { buffer, count };
|
|
CopyFrom(ds, slice, append);
|
|
}
|
|
|
|
/// <summary>Copy containers.</summary>
|
|
/// <typeparam name="Ds">Type of the target container.</typeparam>
|
|
/// <typeparam name="S">Type of values in the source container.</typeparam>
|
|
/// <param name="ds">The target container.</param>
|
|
/// <param name="begin">Pointer to the first source value.</param>
|
|
/// <param name="end">Pointer to the value after the last source value.</param>
|
|
/// <param name="append">Set to true to perform appending instead of replacing.</param>
|
|
template<typename Ds, typename S>
|
|
void CopyFrom(Ds& ds, const S* begin, const S* end, bool append = false)
|
|
{
|
|
copyfrom_internal::Slice<S> 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<typename T>
|
|
class EmptyEnumerator : public Object, public virtual IEnumerator<T>
|
|
{
|
|
IEnumerator<T>* Clone()const override
|
|
{
|
|
return new EmptyEnumerator<T>();
|
|
}
|
|
|
|
const T& Current()const override
|
|
{
|
|
CHECK_FAIL(L"EmptyEnumerable<T>::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<typename T>
|
|
class RangeEnumerator : public Object, public virtual IEnumerator<T>
|
|
{
|
|
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<T>* 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<start+count-1)
|
|
{
|
|
current++;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void Reset()override
|
|
{
|
|
current=start-1;
|
|
}
|
|
|
|
bool Evaluated()const override
|
|
{
|
|
return true;
|
|
}
|
|
};
|
|
|
|
/***********************************************************************
|
|
ContainerEnumerator
|
|
***********************************************************************/
|
|
|
|
template<typename T, typename TContainer>
|
|
class ContainerEnumerator : public Object, public virtual IEnumerator<T>
|
|
{
|
|
private:
|
|
Ptr<TContainer> container;
|
|
vint index;
|
|
|
|
public:
|
|
ContainerEnumerator(Ptr<TContainer> _container, vint _index=-1)
|
|
{
|
|
container=_container;
|
|
index=_index;
|
|
}
|
|
|
|
IEnumerator<T>* 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 && index<container->Count();
|
|
}
|
|
|
|
void Reset()override
|
|
{
|
|
index=-1;
|
|
}
|
|
|
|
bool Evaluated()const override
|
|
{
|
|
return true;
|
|
}
|
|
};
|
|
|
|
/***********************************************************************
|
|
CompareEnumerable
|
|
***********************************************************************/
|
|
|
|
/// <summary>Compare two enumerables.</summary>
|
|
/// <typeparam name="T">Type of elements in the first enumerable.</typeparam>
|
|
/// <typeparam name="U">Type of elements in the second enumerable.</typeparam>
|
|
/// <returns>
|
|
/// 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.
|
|
/// </returns>
|
|
/// <param name="a">The first enumerable to compare.</param>
|
|
/// <param name="b">The second enumerable to compare.</param>
|
|
/// <remarks>
|
|
/// 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.
|
|
/// </remarks>
|
|
template<typename T, typename U>
|
|
vint CompareEnumerable(const IEnumerable<T>& a, const IEnumerable<U>& b)
|
|
{
|
|
Ptr<IEnumerator<T>> ator = a.CreateEnumerator();
|
|
Ptr<IEnumerator<U>> btor = b.CreateEnumerator();
|
|
while (true)
|
|
{
|
|
bool a = ator->Next();
|
|
bool b = btor->Next();
|
|
if (a && !b) return 1;
|
|
if (!a&&b) return -1;
|
|
if (!a && !b) break;
|
|
|
|
const T& ac = ator->Current();
|
|
const U& bc = btor->Current();
|
|
if (ac < bc)
|
|
{
|
|
return -1;
|
|
}
|
|
else if (ac > bc)
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
template<typename T>
|
|
struct SortedListOperations
|
|
{
|
|
static bool Contains(const SortedList<T>& items, const T& item)
|
|
{
|
|
return items.Contains(item);
|
|
}
|
|
};
|
|
|
|
template<typename T>
|
|
struct SortedListOperations<Ptr<T>>
|
|
{
|
|
static bool Contains(const SortedList<Ptr<T>>& items, const Ptr<T>& 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<typename T>
|
|
class ConcatEnumerator : public virtual IEnumerator<T>
|
|
{
|
|
protected:
|
|
IEnumerator<T>* enumerator1;
|
|
IEnumerator<T>* enumerator2;
|
|
vint index;
|
|
bool turned;
|
|
public:
|
|
ConcatEnumerator(IEnumerator<T>* _enumerator1, IEnumerator<T>* _enumerator2, vint _index=-1, bool _turned=false)
|
|
:enumerator1(_enumerator1)
|
|
,enumerator2(_enumerator2)
|
|
,index(_index)
|
|
,turned(_turned)
|
|
{
|
|
}
|
|
|
|
~ConcatEnumerator()
|
|
{
|
|
delete enumerator1;
|
|
delete enumerator2;
|
|
}
|
|
|
|
IEnumerator<T>* 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\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<typename T>
|
|
struct RangeBasedForLoopIterator
|
|
{
|
|
private:
|
|
IEnumerator<T>* iterator;
|
|
|
|
public:
|
|
RangeBasedForLoopIterator(const IEnumerable<T>& 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==(RangeBasedForLoopEnding) const
|
|
{
|
|
return iterator == nullptr;
|
|
}
|
|
};
|
|
|
|
template<typename T>
|
|
RangeBasedForLoopIterator<T> begin(const IEnumerable<T>& enumerable)
|
|
{
|
|
return { enumerable };
|
|
}
|
|
|
|
template<typename T>
|
|
RangeBasedForLoopEnding end(const IEnumerable<T>& enumerable)
|
|
{
|
|
return {};
|
|
}
|
|
|
|
/***********************************************************************
|
|
Range-Based For-Loop Iterator with Index
|
|
***********************************************************************/
|
|
|
|
template<typename T>
|
|
struct RangeBasedForLoopIteratorWithIndex
|
|
{
|
|
struct Tuple
|
|
{
|
|
const T& value;
|
|
vint index;
|
|
|
|
Tuple(const T& _value, vint _index)
|
|
: value(_value)
|
|
, index(_index)
|
|
{
|
|
}
|
|
};
|
|
private:
|
|
IEnumerator<T>* iterator;
|
|
vint index;
|
|
|
|
public:
|
|
RangeBasedForLoopIteratorWithIndex(const IEnumerable<T>& 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==(RangeBasedForLoopEnding) const
|
|
{
|
|
return iterator == nullptr;
|
|
}
|
|
};
|
|
|
|
template<typename T>
|
|
struct EnumerableWithIndex
|
|
{
|
|
const IEnumerable<T>& enumerable;
|
|
|
|
EnumerableWithIndex(const IEnumerable<T>& _enumerable)
|
|
: enumerable(_enumerable)
|
|
{
|
|
}
|
|
};
|
|
|
|
template<typename T>
|
|
EnumerableWithIndex<T> indexed(const IEnumerable<T>& enumerable)
|
|
{
|
|
return { enumerable };
|
|
}
|
|
|
|
template<typename T>
|
|
RangeBasedForLoopIteratorWithIndex<T> begin(const EnumerableWithIndex<T>& enumerable)
|
|
{
|
|
return { enumerable.enumerable };
|
|
}
|
|
|
|
template<typename T>
|
|
RangeBasedForLoopEnding end(const EnumerableWithIndex<T>& enumerable)
|
|
{
|
|
return {};
|
|
}
|
|
}
|
|
}
|
|
|
|
#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<typename S, typename T>
|
|
class PairwiseEnumerator : public virtual IEnumerator<Pair<S, T>>
|
|
{
|
|
protected:
|
|
IEnumerator<S>* enumerator1;
|
|
IEnumerator<T>* enumerator2;
|
|
Nullable<Pair<S, T>> current;
|
|
public:
|
|
PairwiseEnumerator(IEnumerator<S>* _enumerator1, IEnumerator<T>* _enumerator2, Nullable<Pair<S, T>> _current = {})
|
|
:enumerator1(_enumerator1)
|
|
,enumerator2(_enumerator2)
|
|
,current(_current)
|
|
{
|
|
}
|
|
|
|
~PairwiseEnumerator()
|
|
{
|
|
delete enumerator1;
|
|
delete enumerator2;
|
|
}
|
|
|
|
IEnumerator<Pair<S, T>>* Clone()const override
|
|
{
|
|
return new PairwiseEnumerator(enumerator1->Clone(), enumerator2->Clone(), current);
|
|
}
|
|
|
|
const Pair<S, T>& Current()const override
|
|
{
|
|
return current.Value();
|
|
}
|
|
|
|
vint Index()const override
|
|
{
|
|
return enumerator1->Index();
|
|
}
|
|
|
|
bool Next()override
|
|
{
|
|
if (enumerator1->Next() && enumerator2->Next())
|
|
{
|
|
current = Pair<S, T>(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<typename T>
|
|
class TakeEnumerator : public virtual IEnumerator<T>
|
|
{
|
|
protected:
|
|
IEnumerator<T>* enumerator;
|
|
vint count;
|
|
public:
|
|
TakeEnumerator(IEnumerator<T>* _enumerator, vint _count)
|
|
:enumerator(_enumerator)
|
|
,count(_count)
|
|
{
|
|
}
|
|
|
|
~TakeEnumerator()
|
|
{
|
|
delete enumerator;
|
|
}
|
|
|
|
IEnumerator<T>* 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<typename T>
|
|
class SkipEnumerator : public virtual IEnumerator<T>
|
|
{
|
|
protected:
|
|
IEnumerator<T>* enumerator;
|
|
vint count;
|
|
bool skipped;
|
|
public:
|
|
SkipEnumerator(IEnumerator<T>* _enumerator, vint _count, bool _skipped=false)
|
|
:enumerator(_enumerator)
|
|
,count(_count)
|
|
,skipped(_skipped)
|
|
{
|
|
}
|
|
|
|
~SkipEnumerator()
|
|
{
|
|
delete enumerator;
|
|
}
|
|
|
|
IEnumerator<T>* 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;i<count;i++)
|
|
{
|
|
if(!enumerator->Next())
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return enumerator->Next();
|
|
}
|
|
|
|
void Reset()override
|
|
{
|
|
enumerator->Reset();
|
|
skipped=false;
|
|
}
|
|
|
|
bool Evaluated()const override
|
|
{
|
|
return enumerator->Evaluated();
|
|
}
|
|
};
|
|
|
|
/***********************************************************************
|
|
Repeat
|
|
***********************************************************************/
|
|
|
|
template<typename T>
|
|
class RepeatEnumerator : public virtual IEnumerator<T>
|
|
{
|
|
protected:
|
|
IEnumerator<T>* enumerator;
|
|
vint count;
|
|
vint index;
|
|
vint repeatedCount;
|
|
public:
|
|
RepeatEnumerator(IEnumerator<T>* _enumerator, vint _count, vint _index=-1, vint _repeatedCount=0)
|
|
:enumerator(_enumerator)
|
|
,count(_count)
|
|
,index(_index)
|
|
,repeatedCount(_repeatedCount)
|
|
{
|
|
}
|
|
|
|
~RepeatEnumerator()
|
|
{
|
|
delete enumerator;
|
|
}
|
|
|
|
IEnumerator<T>* 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(repeatedCount<count)
|
|
{
|
|
if(enumerator->Next())
|
|
{
|
|
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<typename T>
|
|
class DistinctEnumerator : public virtual IEnumerator<T>
|
|
{
|
|
protected:
|
|
IEnumerator<T>* enumerator;
|
|
SortedList<T> distinct;
|
|
Nullable<T> lastValue;
|
|
|
|
public:
|
|
DistinctEnumerator(IEnumerator<T>* _enumerator)
|
|
:enumerator(_enumerator)
|
|
{
|
|
}
|
|
|
|
DistinctEnumerator(const DistinctEnumerator& _enumerator)
|
|
:lastValue(_enumerator.lastValue)
|
|
{
|
|
enumerator = _enumerator.enumerator->Clone();
|
|
CopyFrom(distinct, _enumerator.distinct);
|
|
}
|
|
|
|
~DistinctEnumerator()
|
|
{
|
|
delete enumerator;
|
|
}
|
|
|
|
IEnumerator<T>* 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<T>::Contains(distinct, current))
|
|
{
|
|
lastValue = current;
|
|
distinct.Add(current);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void Reset()override
|
|
{
|
|
enumerator->Reset();
|
|
distinct.Clear();
|
|
}
|
|
};
|
|
|
|
/***********************************************************************
|
|
Reverse
|
|
***********************************************************************/
|
|
|
|
template<typename T>
|
|
class ReverseEnumerator : public virtual IEnumerator<T>
|
|
{
|
|
protected:
|
|
List<T> cache;
|
|
vint index;
|
|
public:
|
|
ReverseEnumerator(const IEnumerable<T>& enumerable)
|
|
:index(-1)
|
|
{
|
|
CopyFrom(cache, enumerable);
|
|
}
|
|
|
|
ReverseEnumerator(const ReverseEnumerator& _enumerator)
|
|
:index(_enumerator.index)
|
|
{
|
|
CopyFrom(cache, _enumerator.cache);
|
|
}
|
|
|
|
~ReverseEnumerator()
|
|
{
|
|
}
|
|
|
|
IEnumerator<T>* 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<cache.Count();
|
|
}
|
|
|
|
void Reset()override
|
|
{
|
|
index=-1;
|
|
}
|
|
|
|
bool Evaluated()const override
|
|
{
|
|
return true;
|
|
}
|
|
};
|
|
|
|
/***********************************************************************
|
|
FromIterator
|
|
***********************************************************************/
|
|
|
|
template<typename T, typename I>
|
|
class FromIteratorEnumerable : public EnumerableBase<T>
|
|
{
|
|
private:
|
|
class Enumerator : public Object, public IEnumerator<T>
|
|
{
|
|
private:
|
|
I begin;
|
|
I end;
|
|
I current;
|
|
|
|
public:
|
|
Enumerator(I _begin, I _end, I _current)
|
|
:begin(_begin)
|
|
,end(_end)
|
|
,current(_current)
|
|
{
|
|
}
|
|
|
|
IEnumerator<T>* 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<end;
|
|
}
|
|
|
|
void Reset()override
|
|
{
|
|
current=begin-1;
|
|
}
|
|
|
|
bool Evaluated()const override
|
|
{
|
|
return true;
|
|
}
|
|
};
|
|
private:
|
|
I begin;
|
|
I end;
|
|
public:
|
|
IEnumerator<T>* CreateEnumerator()const
|
|
{
|
|
return new Enumerator(begin, end, begin - 1);
|
|
}
|
|
|
|
FromIteratorEnumerable(I _begin, I _end)
|
|
:begin(_begin)
|
|
,end(_end)
|
|
{
|
|
}
|
|
|
|
FromIteratorEnumerable(const FromIteratorEnumerable<T, I>& enumerable)
|
|
:begin(enumerable.begin)
|
|
,end(enumerable.end)
|
|
{
|
|
}
|
|
};
|
|
|
|
template<typename T>
|
|
class FromIterator
|
|
{
|
|
public:
|
|
template<typename I>
|
|
static FromIteratorEnumerable<T, I> Wrap(I begin, I end)
|
|
{
|
|
return FromIteratorEnumerable<T, I>(begin, end);
|
|
}
|
|
};
|
|
|
|
template<typename T>
|
|
FromIteratorEnumerable<T, const T*> FromPointer(const T* begin, const T* end)
|
|
{
|
|
return FromIteratorEnumerable<T, const T*>(begin, end);
|
|
}
|
|
|
|
template<typename T, int size>
|
|
FromIteratorEnumerable<T, T*> FromArray(T (&items)[size])
|
|
{
|
|
return FromIteratorEnumerable<T, T*>(&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<typename T, bool Intersect>
|
|
class IntersectExceptEnumerator : public virtual IEnumerator<T>
|
|
{
|
|
protected:
|
|
IEnumerator<T>* enumerator;
|
|
SortedList<T> reference;
|
|
vint index;
|
|
|
|
public:
|
|
IntersectExceptEnumerator(IEnumerator<T>* _enumerator, const IEnumerable<T>& _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<T>* 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<T>::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
|
|
{
|
|
/// <summary>
|
|
/// Node contains extra information for sorted objects.
|
|
/// </summary>
|
|
struct Node
|
|
{
|
|
bool visited = false;
|
|
|
|
/// <summary>The index used in [F:vl.collections.PartialOrderingProcessor.components], specifying the component that contain this node.</summary>
|
|
vint component = -1;
|
|
/// <summary>All nodes that this node depends on.</summary>
|
|
const List<vint>* ins = nullptr;
|
|
/// <summary>All nodes that this node is depended by.</summary>
|
|
const List<vint>* outs = nullptr;
|
|
/// <summary>
|
|
/// 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]
|
|
/// </summary>
|
|
const vint* firstSubClassItem = nullptr;
|
|
/// <summary>
|
|
/// When <see cref="firstSubClassItem"/> is available,
|
|
/// this field is the number of indexes in the array.
|
|
/// </summary>
|
|
vint subClassItemCount = 0;
|
|
};
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
struct Component
|
|
{
|
|
/// <summary>
|
|
/// Pointer to the array of all indexes of nodes in this component.
|
|
/// Index is used in [F:vl.collections.PartialOrderingProcessor.nodes].
|
|
/// </summary>
|
|
const vint* firstNode = nullptr;
|
|
/// <summary>The number of nodes in this component.</summary>
|
|
vint nodeCount = 0;
|
|
};
|
|
}
|
|
}
|
|
|
|
namespace collections
|
|
{
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
class PartialOrderingProcessor : public Object
|
|
{
|
|
template<typename TList>
|
|
using GroupOf = Group<typename TList::ElementType, typename TList::ElementType>;
|
|
protected:
|
|
List<vint> emptyList; // make a pointer to an empty list available
|
|
Group<vint, vint> ins; // if a depends on b, ins.Contains(a, b)
|
|
Group<vint, vint> outs; // if a depends on b, outs.Contains(b, a)
|
|
Array<vint> firstNodesBuffer; // one buffer for all Component::firstNode
|
|
Array<vint> subClassItemsBuffer; // one buffer for all Node::firstSubClassItem
|
|
|
|
void InitNodes(vint itemCount);
|
|
void VisitUnvisitedNode(po::Node& node, Array<vint>& reversedOrder, vint& used);
|
|
void AssignUnassignedNode(po::Node& node, vint componentIndex, vint& used);
|
|
public:
|
|
/// <summary>After <see cref="Sort"/> is called, this field stores all nodes referenced by sorted components.</summary>
|
|
/// <remarks>
|
|
/// The same order is kept as the "items" argument in <see cref="InitWithGroup`1"/>, and <see cref="InitWithFunc`2"/>.
|
|
/// If sub classing is enabled by calling <see cref="InitWithSubClass`2"/>,
|
|
/// 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.
|
|
/// </remarks>
|
|
Array<po::Node> nodes;
|
|
|
|
/// <summary>After <see cref="Sort"/> is called, this field stores all sorted components in order.</summary>
|
|
List<po::Component> components;
|
|
|
|
/// <summary>
|
|
/// Sort objects by given relationships. It will crash if this method is called for more than once.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// One and only one of <see cref="InitWithGroup`1"/>, <see cref="InitWithFunc`2"/> or <see cref="InitWithSubClass`2"/> must be called to set data for sorting.
|
|
/// And then call <see cref="Sort"/> to sort objects and store the result in <see cref="components"/>.
|
|
/// </remarks>
|
|
void Sort();
|
|
|
|
/// <summary>Set data for sorting, by providing a list for objects, and a group for their relationship.</summary>
|
|
/// <typeparam name="TList">Type of the list for objects. <see cref="Array`*"/>, <see cref="List`*"/> or <see cref="SortedList`*"/> are recommended.</typeparam>
|
|
/// <param name="items">List of objects for sorting.</param>
|
|
/// <param name="depGroup">Relationship of objects for sorting in <see cref="Group`*"/>. Both keys and values are elements in "items". To say that a depends on b, do depGroup.Add(a, b).</param>
|
|
/// <example><![CDATA[
|
|
/// int main()
|
|
/// {
|
|
/// // apple
|
|
/// // ^
|
|
/// // |
|
|
/// // ball
|
|
/// // ^ \
|
|
/// // / V
|
|
/// // cat <-- dog
|
|
/// // ^ ^
|
|
/// // / \
|
|
/// // elephant fish
|
|
///
|
|
/// List<WString> 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<WString, WString> 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<vint>(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; })
|
|
/// );
|
|
/// }
|
|
/// }
|
|
/// }
|
|
/// ]]></example>
|
|
template<typename TList>
|
|
void InitWithGroup(const TList& items, const GroupOf<TList>& 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<typename TList::ElementType>::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<typename TList::ElementType>::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());
|
|
}
|
|
|
|
/// <summary>Set data for sorting, by providing a list for objects, and a function for their relationship.</summary>
|
|
/// <typeparam name="TList">Type of the list for objects. <see cref="Array`*"/>, <see cref="List`*"/> or <see cref="SortedList`*"/> are recommended.</typeparam>
|
|
/// <typeparam name="TFunc">Type of the function that defines relationships of objects.</typeparam>
|
|
/// <param name="items">List of objects for sorting.</param>
|
|
/// <param name="depFunc">Relationship of objects for sorting, both arguments are elements in "items". To say that a depends on b, depFunc(a, b) must returns true.</param>
|
|
/// <example><
|
|
/// {
|
|
/// 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<vint>(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; })
|
|
/// );
|
|
/// }
|
|
/// }
|
|
/// }
|
|
/// ]]></example>
|
|
template<typename TList, typename TFunc>
|
|
void InitWithFunc(const TList& items, TFunc&& depFunc)
|
|
{
|
|
GroupOf<TList> 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);
|
|
}
|
|
|
|
/// <summary>Set data for sorting, by providing a list for objects, and a group for their relationship, and a dictionary for sub classing objects.</summary>
|
|
/// <typeparam name="TList">Type of the list for objects. <see cref="Array`*"/>, <see cref="List`*"/> or <see cref="SortedList`*"/> are recommended.</typeparam>
|
|
/// <typeparam name="TSubClass">Type of a sub class.</typeparam>
|
|
/// <param name="items">List of objects for sorting.</param>
|
|
/// <param name="depGroup">Relationship of objects for sorting in <see cref="Group`*"/>. Both keys and values are elements in "items". To say that a depends on b, do depGroup.Add(a, b).</param>
|
|
/// <param name="subClasses">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.</param>
|
|
/// <remarks>
|
|
/// 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.
|
|
/// </remarks>
|
|
/// <example><![CDATA[
|
|
/// int main()
|
|
/// {
|
|
/// // apple
|
|
/// // ^
|
|
/// // |
|
|
/// // ball
|
|
/// // ^ \
|
|
/// // / V
|
|
/// // cat <-- dog
|
|
/// // ^ ^
|
|
/// // / \
|
|
/// // elephant fish
|
|
///
|
|
/// List<WString> 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<WString, WString> 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<WString, vint> 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<vint>(0, c.nodeCount)
|
|
/// .Select([&](vint ni) { return c.firstNode[ni]; })
|
|
/// .Aggregate<WString>(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<vint>(0, n.subClassItemCount)
|
|
/// .Select([&](vint si) { return n.firstSubClassItem[si]; })
|
|
/// .Aggregate<WString>(L" :", [&](const WString& a, vint b) { return a + L" " + items[b]; })
|
|
/// );
|
|
///
|
|
/// if (n.outs->Count() > 0)
|
|
/// {
|
|
/// Console::WriteLine(
|
|
/// From(*n.outs)
|
|
/// .Aggregate<WString>(L" <- sub classes", [](const WString& a, vint b) { return a + L" " + itow(b); })
|
|
/// );
|
|
/// }
|
|
/// }
|
|
/// }
|
|
/// ]]></example>
|
|
template<typename TList, typename TSubClass>
|
|
void InitWithSubClass(const TList& items, const GroupOf<TList>& depGroup, const Dictionary<typename TList::ElementType, TSubClass>& 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<ElementType>;
|
|
|
|
Group<TSubClass, ElementType> scItems;
|
|
SortedList<ElementType> 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<TSubClass>::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 <memory.h>
|
|
namespace vl
|
|
{
|
|
template<typename T>
|
|
class Func;
|
|
|
|
/***********************************************************************
|
|
vl::Func<R(TArgs...)>
|
|
***********************************************************************/
|
|
|
|
namespace internal_invokers
|
|
{
|
|
template<typename R, typename ...TArgs>
|
|
class Invoker : public Object
|
|
{
|
|
public:
|
|
virtual R Invoke(TArgs&& ...args) = 0;
|
|
};
|
|
|
|
//------------------------------------------------------
|
|
|
|
template<typename R, typename ...TArgs>
|
|
class StaticInvoker : public Invoker<R, TArgs...>
|
|
{
|
|
protected:
|
|
R(*function)(TArgs ...args);
|
|
|
|
public:
|
|
StaticInvoker(R(*_function)(TArgs...))
|
|
:function(_function)
|
|
{
|
|
}
|
|
|
|
R Invoke(TArgs&& ...args)override
|
|
{
|
|
return function(std::forward<TArgs>(args)...);
|
|
}
|
|
};
|
|
|
|
//------------------------------------------------------
|
|
|
|
template<typename C, typename R, typename ...TArgs>
|
|
class MemberInvoker : public Invoker<R, TArgs...>
|
|
{
|
|
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<TArgs>(args)...);
|
|
}
|
|
};
|
|
|
|
//------------------------------------------------------
|
|
|
|
template<typename C, typename R, typename ...TArgs>
|
|
class ObjectInvoker : public Invoker<R, TArgs...>
|
|
{
|
|
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<TArgs>(args)...);
|
|
}
|
|
};
|
|
|
|
//------------------------------------------------------
|
|
|
|
template<typename C, typename ...TArgs>
|
|
class ObjectInvoker<C, void, TArgs...> : public Invoker<void, TArgs...>
|
|
{
|
|
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<TArgs>(args)...);
|
|
}
|
|
};
|
|
}
|
|
|
|
/// <summary>A type for functors.</summary>
|
|
/// <typeparam name="R">The return type.</typeparam>
|
|
/// <typeparam name="TArgs">Types of parameters.</typeparam>
|
|
template<typename R, typename ...TArgs>
|
|
class Func<R(TArgs...)> : public Object
|
|
{
|
|
protected:
|
|
Ptr<internal_invokers::Invoker<R, TArgs...>> invoker;
|
|
|
|
template<typename R2, typename ...TArgs2>
|
|
static bool IsEmptyFunc(const Func<R2(TArgs2...)>& function)
|
|
{
|
|
return !function;
|
|
}
|
|
|
|
template<typename R2, typename ...TArgs2>
|
|
static bool IsEmptyFunc(Func<R2(TArgs2...)>& function)
|
|
{
|
|
return !function;
|
|
}
|
|
|
|
template<typename C>
|
|
static bool IsEmptyFunc(C&&)
|
|
{
|
|
return false;
|
|
}
|
|
public:
|
|
typedef R FunctionType(TArgs...);
|
|
typedef R ResultType;
|
|
|
|
/// <summary>Create a null functor.</summary>
|
|
Func() = default;
|
|
|
|
/// <summary>Copy a functor.</summary>
|
|
/// <param name="function">The functor to copy.</param>
|
|
Func(const Func<R(TArgs...)>& function) = default;
|
|
|
|
/// <summary>Move a functor.</summary>
|
|
/// <param name="function">The functor to move.</param>
|
|
Func(Func<R(TArgs...)>&& function) = default;
|
|
|
|
/// <summary>Create a functor from a function pointer.</summary>
|
|
/// <param name="function">The function pointer.</param>
|
|
Func(R(*function)(TArgs...))
|
|
{
|
|
invoker = new internal_invokers::StaticInvoker<R, TArgs...>(function);
|
|
}
|
|
|
|
/// <summary>Create a functor from a method.</summary>
|
|
/// <typeparam name="C">Type of the class that this method belongs to.</typeparam>
|
|
/// <param name="sender">The object that this method belongs to.</param>
|
|
/// <param name="function">The method pointer.</param>
|
|
template<typename C>
|
|
Func(C* sender, R(C::*function)(TArgs...))
|
|
{
|
|
invoker = new internal_invokers::MemberInvoker<C, R, TArgs...>(sender, function);
|
|
}
|
|
|
|
/// <summary>Create a functor from another compatible functor.</summary>
|
|
/// <typeparam name="C">Type of the functor to copy.</typeparam>
|
|
/// <param name="function">The functor to copy. It could be a lambda expression, or any types that has operator() members.</param>
|
|
template<typename C, typename=std::enable_if_t<
|
|
std::is_same_v<void, R> ||
|
|
std::is_convertible_v<decltype(std::declval<C>()(std::declval<TArgs>()...)), R>
|
|
>>
|
|
Func(C&& function)
|
|
{
|
|
if (!IsEmptyFunc(function))
|
|
{
|
|
invoker = new internal_invokers::ObjectInvoker<std::remove_cvref_t<C>, R, TArgs...>(std::forward<C&&>(function));
|
|
}
|
|
}
|
|
|
|
/// <summary>Invoke the function.</summary>
|
|
/// <returns>Returns the function result. It crashes when the functor is null.</returns>
|
|
/// <param name="args">Arguments to invoke the function.</param>
|
|
R operator()(TArgs ...args)const
|
|
{
|
|
return invoker->Invoke(std::forward<TArgs>(args)...);
|
|
}
|
|
|
|
Func<R(TArgs...)>& operator=(const Func<R(TArgs...)>& function)
|
|
{
|
|
invoker = function.invoker;
|
|
return *this;
|
|
}
|
|
|
|
Func<R(TArgs...)>& operator=(const Func<R(TArgs...)>&& function)
|
|
{
|
|
invoker = std::move(function.invoker);
|
|
return *this;
|
|
}
|
|
|
|
bool operator==(const Func<R(TArgs...)>& function)const
|
|
{
|
|
return invoker == function.invoker;
|
|
}
|
|
|
|
bool operator!=(const Func<R(TArgs...)>& function)const
|
|
{
|
|
return invoker != function.invoker;
|
|
}
|
|
|
|
/// <summary>Test is the functor is non-null.</summary>
|
|
/// <returns>Returns true if the functor is non-null.</returns>
|
|
operator bool()const
|
|
{
|
|
return invoker;
|
|
}
|
|
};
|
|
|
|
/***********************************************************************
|
|
vl::function_lambda::LambdaRetriveType<R(TArgs...)>
|
|
***********************************************************************/
|
|
|
|
namespace function_lambda
|
|
{
|
|
template<typename T>
|
|
struct LambdaRetriveType
|
|
{
|
|
};
|
|
|
|
template<typename TObject, typename R, typename ...TArgs>
|
|
struct LambdaRetriveType<R(__thiscall TObject::*)(TArgs...)const>
|
|
{
|
|
typedef Func<R(TArgs...)> Type;
|
|
typedef R(FunctionType)(TArgs...);
|
|
typedef R ResultType;
|
|
typedef TypeTuple<TArgs...> ParameterTypes;
|
|
};
|
|
|
|
template<typename TObject, typename R, typename ...TArgs>
|
|
struct LambdaRetriveType<R(__thiscall TObject::*)(TArgs...)>
|
|
{
|
|
typedef Func<R(TArgs...)> Type;
|
|
typedef R(FunctionType)(TArgs...);
|
|
typedef R ResultType;
|
|
typedef TypeTuple<TArgs...> ParameterTypes;
|
|
};
|
|
|
|
/// <summary>Create a functor in [T:vl.Func`1] from another functor, with all type arguments autotimatically inferred. The "LAMBDA" macro is recommended for the same purpose for writing compact code.</summary>
|
|
/// <typeparam name="T">Type of the functor to copy.</typeparam>
|
|
/// <returns>A copied functor in [T:vl.Func`1].</returns>
|
|
/// <param name="functionObject">The functor to copy.</param>
|
|
template<typename T>
|
|
typename LambdaRetriveType<decltype(&T::operator())>::Type Lambda(T functionObject)
|
|
{
|
|
return functionObject;
|
|
}
|
|
|
|
#define LAMBDA vl::function_lambda::Lambda
|
|
}
|
|
|
|
/***********************************************************************
|
|
vl::function_binding::Binding<R(TArgs...)>
|
|
***********************************************************************/
|
|
|
|
namespace function_binding
|
|
{
|
|
template<typename T>
|
|
struct Binding
|
|
{
|
|
};
|
|
|
|
template<typename T>
|
|
struct CR{typedef const T& Type;};
|
|
template<typename T>
|
|
struct CR<T&>{typedef T& Type;};
|
|
template<typename T>
|
|
struct CR<const T>{typedef const T& Type;};
|
|
template<typename T>
|
|
struct CR<const T&>{typedef const T& Type;};
|
|
|
|
template<typename R, typename T0, typename ...TArgs>
|
|
struct Binding<R(T0, TArgs...)>
|
|
{
|
|
typedef R FunctionType(T0, TArgs...);
|
|
typedef R CurriedType(TArgs...);
|
|
typedef T0 FirstParameterType;
|
|
|
|
class Binder : public Object
|
|
{
|
|
protected:
|
|
Func<FunctionType> target;
|
|
T0 firstArgument;
|
|
public:
|
|
Binder(const Func<FunctionType>& _target, T0 _firstArgument)
|
|
:target(_target)
|
|
,firstArgument(std::forward<T0>(_firstArgument))
|
|
{
|
|
}
|
|
|
|
R operator()(TArgs ...args)const
|
|
{
|
|
return target(firstArgument, args...);
|
|
}
|
|
};
|
|
|
|
class Currier : public Object
|
|
{
|
|
protected:
|
|
Func<FunctionType> target;
|
|
public:
|
|
Currier(const Func<FunctionType>& _target)
|
|
:target(_target)
|
|
{
|
|
}
|
|
|
|
Func<CurriedType> operator()(T0 firstArgument)const
|
|
{
|
|
return Binder(target, firstArgument);
|
|
}
|
|
};
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Currize a function pointer.
|
|
/// Currizing means to create a new functor whose argument is the first argument of the original function.
|
|
/// Calling this functor will return another functor whose arguments are all remaining arguments of the original function.
|
|
/// Calling the returned function will call the original function.
|
|
/// </summary>
|
|
/// <typeparam name="T">Type of the function pointer.</typeparam>
|
|
/// <returns>The currized functor.</returns>
|
|
/// <param name="function">The function pointer to currize.</param>
|
|
template<typename T>
|
|
Func<Func<typename function_binding::Binding<T>::CurriedType>(typename function_binding::Binding<T>::FirstParameterType)>
|
|
Curry(T* function)
|
|
{
|
|
return typename function_binding::Binding<T>::Currier(function);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Currize a functor in [T:vl.Func`1].
|
|
/// Currizing means to create a new functor whose argument is the first argument of the original function.
|
|
/// Calling this functor will return another functor whose arguments are all remaining arguments of the original function.
|
|
/// Calling the returned function will call the original function.
|
|
/// </summary>
|
|
/// <typeparam name="T">Type of the functor.</typeparam>
|
|
/// <returns>The currized functor.</returns>
|
|
/// <param name="function">The functor to currize.</param>
|
|
template<typename T>
|
|
Func<Func<typename function_binding::Binding<T>::CurriedType>(typename function_binding::Binding<T>::FirstParameterType)>
|
|
Curry(const Func<T>& function)
|
|
{
|
|
return typename function_binding::Binding<T>::Currier(function);
|
|
}
|
|
}
|
|
#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<typename T, typename K>
|
|
class SelectEnumerator : public virtual IEnumerator<K>
|
|
{
|
|
protected:
|
|
IEnumerator<T>* enumerator;
|
|
Func<K(T)> selector;
|
|
Nullable<K> current;
|
|
public:
|
|
SelectEnumerator(IEnumerator<T>* _enumerator, const Func<K(T)>& _selector, Nullable<K> _current = {})
|
|
:enumerator(_enumerator)
|
|
,selector(_selector)
|
|
,current(_current)
|
|
{
|
|
}
|
|
|
|
~SelectEnumerator()
|
|
{
|
|
delete enumerator;
|
|
}
|
|
|
|
IEnumerator<K>* 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<typename T>
|
|
class WhereEnumerator : public virtual IEnumerator<T>
|
|
{
|
|
protected:
|
|
IEnumerator<T>* enumerator;
|
|
Func<bool(T)> selector;
|
|
vint index;
|
|
|
|
public:
|
|
WhereEnumerator(IEnumerator<T>* _enumerator, const Func<bool(T)>& _selector, vint _index=-1)
|
|
:enumerator(_enumerator)
|
|
,selector(_selector)
|
|
,index(_index)
|
|
{
|
|
}
|
|
|
|
~WhereEnumerator()
|
|
{
|
|
delete enumerator;
|
|
}
|
|
|
|
IEnumerator<T>* 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<typename T>
|
|
class Event;
|
|
|
|
class EventHandler : public Object
|
|
{
|
|
public:
|
|
virtual bool IsAttached() = 0;
|
|
};
|
|
|
|
/// <summary>An event for being subscribed using multiple callbacks. A callback is any functor that returns void.</summary>
|
|
/// <typeparam name="TArgs">Types of callback parameters.</typeparam>
|
|
template<typename ...TArgs>
|
|
class Event<void(TArgs...)> : public Object
|
|
{
|
|
protected:
|
|
class EventHandlerImpl : public EventHandler
|
|
{
|
|
public:
|
|
bool attached;
|
|
Func<void(TArgs...)> function;
|
|
|
|
EventHandlerImpl(const Func<void(TArgs...)>& _function)
|
|
:attached(true)
|
|
, function(_function)
|
|
{
|
|
}
|
|
|
|
bool IsAttached()override
|
|
{
|
|
return attached;
|
|
}
|
|
};
|
|
|
|
collections::SortedList<Ptr<EventHandlerImpl>> handlers;
|
|
public:
|
|
NOT_COPYABLE(Event);
|
|
Event() = default;
|
|
|
|
/// <summary>Add a callback to the event.</summary>
|
|
/// <returns>The event handler representing the callback.</returns>
|
|
/// <param name="function">The callback.</param>
|
|
Ptr<EventHandler> Add(const Func<void(TArgs...)>& function)
|
|
{
|
|
Ptr<EventHandlerImpl> handler = new EventHandlerImpl(function);
|
|
handlers.Add(handler);
|
|
return handler;
|
|
}
|
|
|
|
/// <summary>Add a callback to the event.</summary>
|
|
/// <returns>The event handler representing the callback.</returns>
|
|
/// <param name="function">The callback.</param>
|
|
Ptr<EventHandler> Add(void(*function)(TArgs...))
|
|
{
|
|
return Add(Func<void(TArgs...)>(function));
|
|
}
|
|
|
|
/// <summary>Add a method callback to the event.</summary>
|
|
/// <typeparam name="C">Type of the class that the callback belongs to.</typeparam>
|
|
/// <returns>The event handler representing the callback.</returns>
|
|
/// <param name="sender">The object that the callback belongs to.</param>
|
|
/// <param name="function">The method callback.</param>
|
|
template<typename C>
|
|
Ptr<EventHandler> Add(C* sender, void(C::*function)(TArgs...))
|
|
{
|
|
return Add(Func<void(TArgs...)>(sender, function));
|
|
}
|
|
|
|
/// <summary>Remove a callback by an event handler returns from <see cref="Add"/>.</summary>
|
|
/// <returns>Returns true if this operation succeeded.</returns>
|
|
/// <param name="handler">The event handler representing the callback.</param>
|
|
bool Remove(Ptr<EventHandler> handler)
|
|
{
|
|
auto impl = handler.Cast<EventHandlerImpl>();
|
|
if (!impl) return false;
|
|
vint index = handlers.IndexOf(impl.Obj());
|
|
if (index == -1) return false;
|
|
impl->attached = false;
|
|
handlers.RemoveAt(index);
|
|
return true;
|
|
}
|
|
|
|
/// <summary>Invoke all callbacks in the event.</summary>
|
|
/// <param name="args">Arguments to invoke all callbacks.</param>
|
|
void operator()(TArgs ...args)const
|
|
{
|
|
for(vint i = 0; i < handlers.Count(); i++)
|
|
{
|
|
handlers[i]->function(args...);
|
|
}
|
|
}
|
|
};
|
|
}
|
|
#endif
|
|
|
|
|
|
/***********************************************************************
|
|
.\PRIMITIVES\LAZY.H
|
|
***********************************************************************/
|
|
/***********************************************************************
|
|
Author: Zihan Chen (vczh)
|
|
Licensed under https://github.com/vczh-libraries/License
|
|
***********************************************************************/
|
|
|
|
#ifndef VCZH_LAZY
|
|
#define VCZH_LAZY
|
|
|
|
|
|
namespace vl
|
|
{
|
|
/// <summary>A type representing a lazy evaluation.</summary>
|
|
/// <typeparam name="T">The type of the evaluation result.</typeparam>
|
|
template<typename T>
|
|
class Lazy : public Object
|
|
{
|
|
protected:
|
|
class Internal
|
|
{
|
|
public:
|
|
Func<T()> evaluator;
|
|
T value;
|
|
bool evaluated;
|
|
};
|
|
|
|
Ptr<Internal> internalValue;
|
|
public:
|
|
/// <summary>Create an empty evaluation.</summary>
|
|
Lazy() = default;
|
|
|
|
/// <summary>Create an evaluation using a function, which produces the evaluation result.</summary>
|
|
/// <param name="evaluator">The function.</param>
|
|
Lazy(const Func<T()>& evaluator)
|
|
{
|
|
internalValue=new Internal;
|
|
internalValue->evaluated=false;
|
|
internalValue->evaluator=evaluator;
|
|
}
|
|
|
|
/// <summary>Create an evaluation with the immediate result.</summary>
|
|
/// <param name="value">The result.</param>0
|
|
Lazy(const T& value)
|
|
{
|
|
internalValue=new Internal;
|
|
internalValue->evaluated=true;
|
|
internalValue->value=value;
|
|
}
|
|
|
|
/// <summary>Create an evaluation by copying another one.</summary>
|
|
/// <param name="lazy">The evaluation to copy.</param>
|
|
Lazy(const Lazy<T>& lazy) = default;
|
|
|
|
/// <summary>Create an evaluation by moving another one.</summary>
|
|
/// <param name="lazy">The evaluation to move.</param>
|
|
Lazy(Lazy<T>&& lazy) = default;
|
|
|
|
Lazy<T>& operator=(const Func<T()>& evaluator)
|
|
{
|
|
internalValue=new Internal;
|
|
internalValue->evaluated=false;
|
|
internalValue->evaluator=evaluator;
|
|
return *this;
|
|
}
|
|
|
|
Lazy<T>& operator=(const T& value)
|
|
{
|
|
internalValue=new Internal;
|
|
internalValue->evaluated=true;
|
|
internalValue->value=value;
|
|
return *this;
|
|
}
|
|
|
|
Lazy<T>& operator=(const Lazy<T>& lazy)
|
|
{
|
|
internalValue=lazy.internalValue;
|
|
return *this;
|
|
}
|
|
|
|
/// <summary>Get the evaluation result. If the evaluation has not been performed, it will run the evaluation function and cache the result.</summary>
|
|
/// <returns>The evaluation result.</returns>
|
|
const T& Value()const
|
|
{
|
|
if(!internalValue->evaluated)
|
|
{
|
|
internalValue->evaluated=true;
|
|
internalValue->value=internalValue->evaluator();
|
|
internalValue->evaluator=Func<T()>();
|
|
}
|
|
return internalValue->value;
|
|
}
|
|
|
|
/// <summary>Test if it has already been evaluated or not.</summary>
|
|
/// <returns>Returns true if it has already been evaluated.</returns>
|
|
bool IsEvaluated()const
|
|
{
|
|
return internalValue->evaluated;
|
|
}
|
|
|
|
/// <summary>Test if it is an empty evaluation or not.</summary>
|
|
/// <returns>Returns true if it is not empty.</returns>
|
|
operator bool()const
|
|
{
|
|
return internalValue;
|
|
}
|
|
};
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
/***********************************************************************
|
|
.\PRIMITIVES\TUPLE.H
|
|
***********************************************************************/
|
|
/***********************************************************************
|
|
Author: Zihan Chen (vczh)
|
|
Licensed under https://github.com/vczh-libraries/License
|
|
|
|
This file is generated by: Vczh Functional Macro
|
|
***********************************************************************/
|
|
#ifndef VCZH_TUPLE
|
|
#define VCZH_TUPLE
|
|
|
|
|
|
namespace vl
|
|
{
|
|
class TupleNullItem
|
|
{
|
|
};
|
|
template<typename T0 = TupleNullItem,typename T1 = TupleNullItem,typename T2 = TupleNullItem,typename T3 = TupleNullItem,typename T4 = TupleNullItem,typename T5 = TupleNullItem,typename T6 = TupleNullItem,typename T7 = TupleNullItem,typename T8 = TupleNullItem,typename T9 = TupleNullItem,typename T10 = TupleNullItem>
|
|
class Tuple
|
|
{
|
|
};
|
|
|
|
/***********************************************************************
|
|
vl::Tuple<T0>
|
|
***********************************************************************/
|
|
template<typename T0>
|
|
class Tuple<T0> : public Object
|
|
{
|
|
public:
|
|
T0 f0;
|
|
|
|
Tuple()
|
|
{
|
|
}
|
|
|
|
Tuple(T0 p0)
|
|
:f0(p0)
|
|
{
|
|
}
|
|
|
|
static int Compare(const Tuple<T0>& a, const Tuple<T0>& b)
|
|
{
|
|
if (a.f0 < b.f0) return -1; else if (a.f0 > b.f0) return 1;
|
|
return 0;
|
|
}
|
|
|
|
bool operator==(const Tuple<T0>& value)const{ return Compare(*this, value) == 0; }
|
|
bool operator!=(const Tuple<T0>& value)const{ return Compare(*this, value) != 0; }
|
|
bool operator< (const Tuple<T0>& value)const{ return Compare(*this, value) < 0; }
|
|
bool operator<=(const Tuple<T0>& value)const{ return Compare(*this, value) <= 0; }
|
|
bool operator> (const Tuple<T0>& value)const{ return Compare(*this, value) > 0; }
|
|
bool operator>=(const Tuple<T0>& value)const{ return Compare(*this, value) >= 0; }
|
|
};
|
|
|
|
/***********************************************************************
|
|
vl::Tuple<T0,T1>
|
|
***********************************************************************/
|
|
template<typename T0,typename T1>
|
|
class Tuple<T0,T1> : public Object
|
|
{
|
|
public:
|
|
T0 f0;T1 f1;
|
|
|
|
Tuple()
|
|
{
|
|
}
|
|
|
|
Tuple(T0 p0,T1 p1)
|
|
:f0(p0),f1(p1)
|
|
{
|
|
}
|
|
|
|
static int Compare(const Tuple<T0,T1>& a, const Tuple<T0,T1>& b)
|
|
{
|
|
if (a.f0 < b.f0) return -1; else if (a.f0 > b.f0) return 1;if (a.f1 < b.f1) return -1; else if (a.f1 > b.f1) return 1;
|
|
return 0;
|
|
}
|
|
|
|
bool operator==(const Tuple<T0,T1>& value)const{ return Compare(*this, value) == 0; }
|
|
bool operator!=(const Tuple<T0,T1>& value)const{ return Compare(*this, value) != 0; }
|
|
bool operator< (const Tuple<T0,T1>& value)const{ return Compare(*this, value) < 0; }
|
|
bool operator<=(const Tuple<T0,T1>& value)const{ return Compare(*this, value) <= 0; }
|
|
bool operator> (const Tuple<T0,T1>& value)const{ return Compare(*this, value) > 0; }
|
|
bool operator>=(const Tuple<T0,T1>& value)const{ return Compare(*this, value) >= 0; }
|
|
};
|
|
|
|
/***********************************************************************
|
|
vl::Tuple<T0,T1,T2>
|
|
***********************************************************************/
|
|
template<typename T0,typename T1,typename T2>
|
|
class Tuple<T0,T1,T2> : public Object
|
|
{
|
|
public:
|
|
T0 f0;T1 f1;T2 f2;
|
|
|
|
Tuple()
|
|
{
|
|
}
|
|
|
|
Tuple(T0 p0,T1 p1,T2 p2)
|
|
:f0(p0),f1(p1),f2(p2)
|
|
{
|
|
}
|
|
|
|
static int Compare(const Tuple<T0,T1,T2>& a, const Tuple<T0,T1,T2>& b)
|
|
{
|
|
if (a.f0 < b.f0) return -1; else if (a.f0 > b.f0) return 1;if (a.f1 < b.f1) return -1; else if (a.f1 > b.f1) return 1;if (a.f2 < b.f2) return -1; else if (a.f2 > b.f2) return 1;
|
|
return 0;
|
|
}
|
|
|
|
bool operator==(const Tuple<T0,T1,T2>& value)const{ return Compare(*this, value) == 0; }
|
|
bool operator!=(const Tuple<T0,T1,T2>& value)const{ return Compare(*this, value) != 0; }
|
|
bool operator< (const Tuple<T0,T1,T2>& value)const{ return Compare(*this, value) < 0; }
|
|
bool operator<=(const Tuple<T0,T1,T2>& value)const{ return Compare(*this, value) <= 0; }
|
|
bool operator> (const Tuple<T0,T1,T2>& value)const{ return Compare(*this, value) > 0; }
|
|
bool operator>=(const Tuple<T0,T1,T2>& value)const{ return Compare(*this, value) >= 0; }
|
|
};
|
|
|
|
/***********************************************************************
|
|
vl::Tuple<T0,T1,T2,T3>
|
|
***********************************************************************/
|
|
template<typename T0,typename T1,typename T2,typename T3>
|
|
class Tuple<T0,T1,T2,T3> : public Object
|
|
{
|
|
public:
|
|
T0 f0;T1 f1;T2 f2;T3 f3;
|
|
|
|
Tuple()
|
|
{
|
|
}
|
|
|
|
Tuple(T0 p0,T1 p1,T2 p2,T3 p3)
|
|
:f0(p0),f1(p1),f2(p2),f3(p3)
|
|
{
|
|
}
|
|
|
|
static int Compare(const Tuple<T0,T1,T2,T3>& a, const Tuple<T0,T1,T2,T3>& b)
|
|
{
|
|
if (a.f0 < b.f0) return -1; else if (a.f0 > b.f0) return 1;if (a.f1 < b.f1) return -1; else if (a.f1 > b.f1) return 1;if (a.f2 < b.f2) return -1; else if (a.f2 > b.f2) return 1;if (a.f3 < b.f3) return -1; else if (a.f3 > b.f3) return 1;
|
|
return 0;
|
|
}
|
|
|
|
bool operator==(const Tuple<T0,T1,T2,T3>& value)const{ return Compare(*this, value) == 0; }
|
|
bool operator!=(const Tuple<T0,T1,T2,T3>& value)const{ return Compare(*this, value) != 0; }
|
|
bool operator< (const Tuple<T0,T1,T2,T3>& value)const{ return Compare(*this, value) < 0; }
|
|
bool operator<=(const Tuple<T0,T1,T2,T3>& value)const{ return Compare(*this, value) <= 0; }
|
|
bool operator> (const Tuple<T0,T1,T2,T3>& value)const{ return Compare(*this, value) > 0; }
|
|
bool operator>=(const Tuple<T0,T1,T2,T3>& value)const{ return Compare(*this, value) >= 0; }
|
|
};
|
|
|
|
/***********************************************************************
|
|
vl::Tuple<T0,T1,T2,T3,T4>
|
|
***********************************************************************/
|
|
template<typename T0,typename T1,typename T2,typename T3,typename T4>
|
|
class Tuple<T0,T1,T2,T3,T4> : public Object
|
|
{
|
|
public:
|
|
T0 f0;T1 f1;T2 f2;T3 f3;T4 f4;
|
|
|
|
Tuple()
|
|
{
|
|
}
|
|
|
|
Tuple(T0 p0,T1 p1,T2 p2,T3 p3,T4 p4)
|
|
:f0(p0),f1(p1),f2(p2),f3(p3),f4(p4)
|
|
{
|
|
}
|
|
|
|
static int Compare(const Tuple<T0,T1,T2,T3,T4>& a, const Tuple<T0,T1,T2,T3,T4>& b)
|
|
{
|
|
if (a.f0 < b.f0) return -1; else if (a.f0 > b.f0) return 1;if (a.f1 < b.f1) return -1; else if (a.f1 > b.f1) return 1;if (a.f2 < b.f2) return -1; else if (a.f2 > b.f2) return 1;if (a.f3 < b.f3) return -1; else if (a.f3 > b.f3) return 1;if (a.f4 < b.f4) return -1; else if (a.f4 > b.f4) return 1;
|
|
return 0;
|
|
}
|
|
|
|
bool operator==(const Tuple<T0,T1,T2,T3,T4>& value)const{ return Compare(*this, value) == 0; }
|
|
bool operator!=(const Tuple<T0,T1,T2,T3,T4>& value)const{ return Compare(*this, value) != 0; }
|
|
bool operator< (const Tuple<T0,T1,T2,T3,T4>& value)const{ return Compare(*this, value) < 0; }
|
|
bool operator<=(const Tuple<T0,T1,T2,T3,T4>& value)const{ return Compare(*this, value) <= 0; }
|
|
bool operator> (const Tuple<T0,T1,T2,T3,T4>& value)const{ return Compare(*this, value) > 0; }
|
|
bool operator>=(const Tuple<T0,T1,T2,T3,T4>& value)const{ return Compare(*this, value) >= 0; }
|
|
};
|
|
|
|
/***********************************************************************
|
|
vl::Tuple<T0,T1,T2,T3,T4,T5>
|
|
***********************************************************************/
|
|
template<typename T0,typename T1,typename T2,typename T3,typename T4,typename T5>
|
|
class Tuple<T0,T1,T2,T3,T4,T5> : public Object
|
|
{
|
|
public:
|
|
T0 f0;T1 f1;T2 f2;T3 f3;T4 f4;T5 f5;
|
|
|
|
Tuple()
|
|
{
|
|
}
|
|
|
|
Tuple(T0 p0,T1 p1,T2 p2,T3 p3,T4 p4,T5 p5)
|
|
:f0(p0),f1(p1),f2(p2),f3(p3),f4(p4),f5(p5)
|
|
{
|
|
}
|
|
|
|
static int Compare(const Tuple<T0,T1,T2,T3,T4,T5>& a, const Tuple<T0,T1,T2,T3,T4,T5>& b)
|
|
{
|
|
if (a.f0 < b.f0) return -1; else if (a.f0 > b.f0) return 1;if (a.f1 < b.f1) return -1; else if (a.f1 > b.f1) return 1;if (a.f2 < b.f2) return -1; else if (a.f2 > b.f2) return 1;if (a.f3 < b.f3) return -1; else if (a.f3 > b.f3) return 1;if (a.f4 < b.f4) return -1; else if (a.f4 > b.f4) return 1;if (a.f5 < b.f5) return -1; else if (a.f5 > b.f5) return 1;
|
|
return 0;
|
|
}
|
|
|
|
bool operator==(const Tuple<T0,T1,T2,T3,T4,T5>& value)const{ return Compare(*this, value) == 0; }
|
|
bool operator!=(const Tuple<T0,T1,T2,T3,T4,T5>& value)const{ return Compare(*this, value) != 0; }
|
|
bool operator< (const Tuple<T0,T1,T2,T3,T4,T5>& value)const{ return Compare(*this, value) < 0; }
|
|
bool operator<=(const Tuple<T0,T1,T2,T3,T4,T5>& value)const{ return Compare(*this, value) <= 0; }
|
|
bool operator> (const Tuple<T0,T1,T2,T3,T4,T5>& value)const{ return Compare(*this, value) > 0; }
|
|
bool operator>=(const Tuple<T0,T1,T2,T3,T4,T5>& value)const{ return Compare(*this, value) >= 0; }
|
|
};
|
|
|
|
/***********************************************************************
|
|
vl::Tuple<T0,T1,T2,T3,T4,T5,T6>
|
|
***********************************************************************/
|
|
template<typename T0,typename T1,typename T2,typename T3,typename T4,typename T5,typename T6>
|
|
class Tuple<T0,T1,T2,T3,T4,T5,T6> : public Object
|
|
{
|
|
public:
|
|
T0 f0;T1 f1;T2 f2;T3 f3;T4 f4;T5 f5;T6 f6;
|
|
|
|
Tuple()
|
|
{
|
|
}
|
|
|
|
Tuple(T0 p0,T1 p1,T2 p2,T3 p3,T4 p4,T5 p5,T6 p6)
|
|
:f0(p0),f1(p1),f2(p2),f3(p3),f4(p4),f5(p5),f6(p6)
|
|
{
|
|
}
|
|
|
|
static int Compare(const Tuple<T0,T1,T2,T3,T4,T5,T6>& a, const Tuple<T0,T1,T2,T3,T4,T5,T6>& b)
|
|
{
|
|
if (a.f0 < b.f0) return -1; else if (a.f0 > b.f0) return 1;if (a.f1 < b.f1) return -1; else if (a.f1 > b.f1) return 1;if (a.f2 < b.f2) return -1; else if (a.f2 > b.f2) return 1;if (a.f3 < b.f3) return -1; else if (a.f3 > b.f3) return 1;if (a.f4 < b.f4) return -1; else if (a.f4 > b.f4) return 1;if (a.f5 < b.f5) return -1; else if (a.f5 > b.f5) return 1;if (a.f6 < b.f6) return -1; else if (a.f6 > b.f6) return 1;
|
|
return 0;
|
|
}
|
|
|
|
bool operator==(const Tuple<T0,T1,T2,T3,T4,T5,T6>& value)const{ return Compare(*this, value) == 0; }
|
|
bool operator!=(const Tuple<T0,T1,T2,T3,T4,T5,T6>& value)const{ return Compare(*this, value) != 0; }
|
|
bool operator< (const Tuple<T0,T1,T2,T3,T4,T5,T6>& value)const{ return Compare(*this, value) < 0; }
|
|
bool operator<=(const Tuple<T0,T1,T2,T3,T4,T5,T6>& value)const{ return Compare(*this, value) <= 0; }
|
|
bool operator> (const Tuple<T0,T1,T2,T3,T4,T5,T6>& value)const{ return Compare(*this, value) > 0; }
|
|
bool operator>=(const Tuple<T0,T1,T2,T3,T4,T5,T6>& value)const{ return Compare(*this, value) >= 0; }
|
|
};
|
|
|
|
/***********************************************************************
|
|
vl::Tuple<T0,T1,T2,T3,T4,T5,T6,T7>
|
|
***********************************************************************/
|
|
template<typename T0,typename T1,typename T2,typename T3,typename T4,typename T5,typename T6,typename T7>
|
|
class Tuple<T0,T1,T2,T3,T4,T5,T6,T7> : public Object
|
|
{
|
|
public:
|
|
T0 f0;T1 f1;T2 f2;T3 f3;T4 f4;T5 f5;T6 f6;T7 f7;
|
|
|
|
Tuple()
|
|
{
|
|
}
|
|
|
|
Tuple(T0 p0,T1 p1,T2 p2,T3 p3,T4 p4,T5 p5,T6 p6,T7 p7)
|
|
:f0(p0),f1(p1),f2(p2),f3(p3),f4(p4),f5(p5),f6(p6),f7(p7)
|
|
{
|
|
}
|
|
|
|
static int Compare(const Tuple<T0,T1,T2,T3,T4,T5,T6,T7>& a, const Tuple<T0,T1,T2,T3,T4,T5,T6,T7>& b)
|
|
{
|
|
if (a.f0 < b.f0) return -1; else if (a.f0 > b.f0) return 1;if (a.f1 < b.f1) return -1; else if (a.f1 > b.f1) return 1;if (a.f2 < b.f2) return -1; else if (a.f2 > b.f2) return 1;if (a.f3 < b.f3) return -1; else if (a.f3 > b.f3) return 1;if (a.f4 < b.f4) return -1; else if (a.f4 > b.f4) return 1;if (a.f5 < b.f5) return -1; else if (a.f5 > b.f5) return 1;if (a.f6 < b.f6) return -1; else if (a.f6 > b.f6) return 1;if (a.f7 < b.f7) return -1; else if (a.f7 > b.f7) return 1;
|
|
return 0;
|
|
}
|
|
|
|
bool operator==(const Tuple<T0,T1,T2,T3,T4,T5,T6,T7>& value)const{ return Compare(*this, value) == 0; }
|
|
bool operator!=(const Tuple<T0,T1,T2,T3,T4,T5,T6,T7>& value)const{ return Compare(*this, value) != 0; }
|
|
bool operator< (const Tuple<T0,T1,T2,T3,T4,T5,T6,T7>& value)const{ return Compare(*this, value) < 0; }
|
|
bool operator<=(const Tuple<T0,T1,T2,T3,T4,T5,T6,T7>& value)const{ return Compare(*this, value) <= 0; }
|
|
bool operator> (const Tuple<T0,T1,T2,T3,T4,T5,T6,T7>& value)const{ return Compare(*this, value) > 0; }
|
|
bool operator>=(const Tuple<T0,T1,T2,T3,T4,T5,T6,T7>& value)const{ return Compare(*this, value) >= 0; }
|
|
};
|
|
|
|
/***********************************************************************
|
|
vl::Tuple<T0,T1,T2,T3,T4,T5,T6,T7,T8>
|
|
***********************************************************************/
|
|
template<typename T0,typename T1,typename T2,typename T3,typename T4,typename T5,typename T6,typename T7,typename T8>
|
|
class Tuple<T0,T1,T2,T3,T4,T5,T6,T7,T8> : public Object
|
|
{
|
|
public:
|
|
T0 f0;T1 f1;T2 f2;T3 f3;T4 f4;T5 f5;T6 f6;T7 f7;T8 f8;
|
|
|
|
Tuple()
|
|
{
|
|
}
|
|
|
|
Tuple(T0 p0,T1 p1,T2 p2,T3 p3,T4 p4,T5 p5,T6 p6,T7 p7,T8 p8)
|
|
:f0(p0),f1(p1),f2(p2),f3(p3),f4(p4),f5(p5),f6(p6),f7(p7),f8(p8)
|
|
{
|
|
}
|
|
|
|
static int Compare(const Tuple<T0,T1,T2,T3,T4,T5,T6,T7,T8>& a, const Tuple<T0,T1,T2,T3,T4,T5,T6,T7,T8>& b)
|
|
{
|
|
if (a.f0 < b.f0) return -1; else if (a.f0 > b.f0) return 1;if (a.f1 < b.f1) return -1; else if (a.f1 > b.f1) return 1;if (a.f2 < b.f2) return -1; else if (a.f2 > b.f2) return 1;if (a.f3 < b.f3) return -1; else if (a.f3 > b.f3) return 1;if (a.f4 < b.f4) return -1; else if (a.f4 > b.f4) return 1;if (a.f5 < b.f5) return -1; else if (a.f5 > b.f5) return 1;if (a.f6 < b.f6) return -1; else if (a.f6 > b.f6) return 1;if (a.f7 < b.f7) return -1; else if (a.f7 > b.f7) return 1;if (a.f8 < b.f8) return -1; else if (a.f8 > b.f8) return 1;
|
|
return 0;
|
|
}
|
|
|
|
bool operator==(const Tuple<T0,T1,T2,T3,T4,T5,T6,T7,T8>& value)const{ return Compare(*this, value) == 0; }
|
|
bool operator!=(const Tuple<T0,T1,T2,T3,T4,T5,T6,T7,T8>& value)const{ return Compare(*this, value) != 0; }
|
|
bool operator< (const Tuple<T0,T1,T2,T3,T4,T5,T6,T7,T8>& value)const{ return Compare(*this, value) < 0; }
|
|
bool operator<=(const Tuple<T0,T1,T2,T3,T4,T5,T6,T7,T8>& value)const{ return Compare(*this, value) <= 0; }
|
|
bool operator> (const Tuple<T0,T1,T2,T3,T4,T5,T6,T7,T8>& value)const{ return Compare(*this, value) > 0; }
|
|
bool operator>=(const Tuple<T0,T1,T2,T3,T4,T5,T6,T7,T8>& value)const{ return Compare(*this, value) >= 0; }
|
|
};
|
|
|
|
/***********************************************************************
|
|
vl::Tuple<T0,T1,T2,T3,T4,T5,T6,T7,T8,T9>
|
|
***********************************************************************/
|
|
template<typename T0,typename T1,typename T2,typename T3,typename T4,typename T5,typename T6,typename T7,typename T8,typename T9>
|
|
class Tuple<T0,T1,T2,T3,T4,T5,T6,T7,T8,T9> : public Object
|
|
{
|
|
public:
|
|
T0 f0;T1 f1;T2 f2;T3 f3;T4 f4;T5 f5;T6 f6;T7 f7;T8 f8;T9 f9;
|
|
|
|
Tuple()
|
|
{
|
|
}
|
|
|
|
Tuple(T0 p0,T1 p1,T2 p2,T3 p3,T4 p4,T5 p5,T6 p6,T7 p7,T8 p8,T9 p9)
|
|
:f0(p0),f1(p1),f2(p2),f3(p3),f4(p4),f5(p5),f6(p6),f7(p7),f8(p8),f9(p9)
|
|
{
|
|
}
|
|
|
|
static int Compare(const Tuple<T0,T1,T2,T3,T4,T5,T6,T7,T8,T9>& a, const Tuple<T0,T1,T2,T3,T4,T5,T6,T7,T8,T9>& b)
|
|
{
|
|
if (a.f0 < b.f0) return -1; else if (a.f0 > b.f0) return 1;if (a.f1 < b.f1) return -1; else if (a.f1 > b.f1) return 1;if (a.f2 < b.f2) return -1; else if (a.f2 > b.f2) return 1;if (a.f3 < b.f3) return -1; else if (a.f3 > b.f3) return 1;if (a.f4 < b.f4) return -1; else if (a.f4 > b.f4) return 1;if (a.f5 < b.f5) return -1; else if (a.f5 > b.f5) return 1;if (a.f6 < b.f6) return -1; else if (a.f6 > b.f6) return 1;if (a.f7 < b.f7) return -1; else if (a.f7 > b.f7) return 1;if (a.f8 < b.f8) return -1; else if (a.f8 > b.f8) return 1;if (a.f9 < b.f9) return -1; else if (a.f9 > b.f9) return 1;
|
|
return 0;
|
|
}
|
|
|
|
bool operator==(const Tuple<T0,T1,T2,T3,T4,T5,T6,T7,T8,T9>& value)const{ return Compare(*this, value) == 0; }
|
|
bool operator!=(const Tuple<T0,T1,T2,T3,T4,T5,T6,T7,T8,T9>& value)const{ return Compare(*this, value) != 0; }
|
|
bool operator< (const Tuple<T0,T1,T2,T3,T4,T5,T6,T7,T8,T9>& value)const{ return Compare(*this, value) < 0; }
|
|
bool operator<=(const Tuple<T0,T1,T2,T3,T4,T5,T6,T7,T8,T9>& value)const{ return Compare(*this, value) <= 0; }
|
|
bool operator> (const Tuple<T0,T1,T2,T3,T4,T5,T6,T7,T8,T9>& value)const{ return Compare(*this, value) > 0; }
|
|
bool operator>=(const Tuple<T0,T1,T2,T3,T4,T5,T6,T7,T8,T9>& value)const{ return Compare(*this, value) >= 0; }
|
|
};
|
|
|
|
}
|
|
#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
|
|
{
|
|
/// <summary>
|
|
/// Immutable string. <see cref="AString"/> and <see cref="WString"/> is recommended instead.
|
|
/// Locale awared operations are in [T:vl.Locale], typically by using the "INVLOC" macro.
|
|
/// </summary>
|
|
/// <typeparam name="T">Type of code points.</typeparam>
|
|
template<typename T>
|
|
class ObjectString : public Object
|
|
{
|
|
template<typename T2>
|
|
friend class ObjectString;
|
|
private:
|
|
static const T zero;
|
|
|
|
mutable T* buffer = (T*)&zero;
|
|
mutable volatile 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;
|
|
}
|
|
|
|
static vint64_t Compare(const T* bufA, const ObjectString<T>& strB)
|
|
{
|
|
const T* bufB = strB.buffer + strB.start;
|
|
const T* bufAOld = bufA;
|
|
vint length = strB.length;
|
|
while (true)
|
|
{
|
|
if (*bufA && length)
|
|
{
|
|
length--;
|
|
vint64_t diff = (vint64_t)(*bufA++) - (vint64_t)(*bufB++);
|
|
if (diff != 0)
|
|
{
|
|
return diff;
|
|
}
|
|
}
|
|
else if (*bufA)
|
|
{
|
|
return CalculateLength(bufA);
|
|
}
|
|
else if (length)
|
|
{
|
|
return -length;
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
};
|
|
}
|
|
|
|
public:
|
|
|
|
static vint64_t Compare(const ObjectString<T>& strA, const ObjectString<T>& strB)
|
|
{
|
|
const T* bufA = strA.buffer + strA.start;
|
|
const T* bufB = strB.buffer + strB.start;
|
|
vint length = strA.length < strB.length ? strA.length : strB.length;
|
|
while (length--)
|
|
{
|
|
vint64_t diff = (vint64_t)(*bufA++) - (vint64_t)(*bufB++);
|
|
if (diff != 0)
|
|
{
|
|
return diff;
|
|
}
|
|
};
|
|
return strA.length - strB.length;
|
|
}
|
|
|
|
private:
|
|
|
|
void Inc()const
|
|
{
|
|
if(counter)
|
|
{
|
|
INCRC(counter);
|
|
}
|
|
}
|
|
|
|
void Dec()const
|
|
{
|
|
if(counter)
|
|
{
|
|
if(DECRC(counter)==0)
|
|
{
|
|
delete[] buffer;
|
|
delete counter;
|
|
}
|
|
}
|
|
}
|
|
|
|
ObjectString<T> SubUnsafe(vint _start, vint _length)const
|
|
{
|
|
if (_length <= 0) return {};
|
|
ObjectString<T> str;
|
|
str.buffer = buffer;
|
|
str.counter = counter;
|
|
str.start = start + _start;
|
|
str.length = _length;
|
|
str.realLength = realLength;
|
|
Inc();
|
|
return std::move(str);
|
|
}
|
|
|
|
ObjectString<T> ReplaceUnsafe(const ObjectString<T>& source, vint index, vint count)const
|
|
{
|
|
if (source.length == 0 && count == 0) return *this;
|
|
if (index == 0 && count == length) return source;
|
|
|
|
ObjectString<T> str;
|
|
str.counter = new 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);
|
|
}
|
|
public:
|
|
static ObjectString<T> Empty;
|
|
|
|
/// <summary>Create an empty string.</summary>
|
|
ObjectString() = default;
|
|
|
|
/// <summary>Copy a string.</summary>
|
|
/// <param name="string">The string to copy.</param>
|
|
ObjectString(const ObjectString<T>&string)
|
|
{
|
|
buffer = string.buffer;
|
|
counter = string.counter;
|
|
start = string.start;
|
|
length = string.length;
|
|
realLength = string.realLength;
|
|
Inc();
|
|
}
|
|
|
|
/// <summary>Move a string.</summary>
|
|
/// <param name="string">The string to move.</param>
|
|
ObjectString(ObjectString<T> && 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();
|
|
}
|
|
|
|
/// <summary>Copy a string.</summary>
|
|
/// <param name="_buffer">Memory to copy. It must be zero terminated.</param>
|
|
ObjectString(const T* _buffer)
|
|
{
|
|
CHECK_ERROR(_buffer != 0, L"ObjectString<T>::ObjectString(const T*)#Cannot construct a string from nullptr.");
|
|
counter = new vint(1);
|
|
start = 0;
|
|
length = CalculateLength(_buffer);
|
|
buffer = new T[length + 1];
|
|
memcpy(buffer, _buffer, sizeof(T) * (length + 1));
|
|
realLength = length;
|
|
}
|
|
|
|
/// <summary>Take over a character pointer with known length.</summary>
|
|
/// <returns>The created string.</returns>
|
|
/// <param name="_buffer">The zero-terminated character buffer which should be created using the global operator new[].</param>
|
|
/// <param name="_length">The number of available characters in the buffer.</param>
|
|
static ObjectString<T> TakeOver(T* _buffer, vint _length)
|
|
{
|
|
CHECK_ERROR(_length >= 0, L"ObjectString<T>::TakeOver(T*, vint)#Length should not be negative.");
|
|
CHECK_ERROR(_buffer[_length] == 0, L"ObjectString<T>::TakeOver(T*, vint)#Buffer is not properly zero-terminated.");
|
|
ObjectString<T> str;
|
|
str.counter = new vint(1);
|
|
str.length = _length;
|
|
str.realLength = _length;
|
|
str.buffer = _buffer;
|
|
return std::move(str);
|
|
}
|
|
|
|
/// <summary>Create a string continaing one code point.</summary>
|
|
/// <returns>The created string.</returns>
|
|
/// <param name="_char">The code point.</param>
|
|
static ObjectString<T> FromChar(const T& _char)
|
|
{
|
|
T buffer[2];
|
|
buffer[0] = _char;
|
|
buffer[1] = 0;
|
|
return buffer;
|
|
}
|
|
|
|
/// <summary>Copy a string.</summary>
|
|
/// <param name="_buffer">Memory to copy. It is not required to be zero terminated.</param>
|
|
/// <returns>The created string.</returns>
|
|
/// <param name="_length">Size of the content in code points.</param>
|
|
static ObjectString<T> CopyFrom(const T* _buffer, vint _length)
|
|
{
|
|
CHECK_ERROR(_length >= 0, L"ObjectString<T>::CopyFrom(const T*, vint)#Length should not be negative.");
|
|
if (_length > 0)
|
|
{
|
|
ObjectString<T> str;
|
|
str.buffer = new T[_length + 1];
|
|
memcpy(str.buffer, _buffer, _length * sizeof(T));
|
|
str.buffer[_length] = 0;
|
|
str.counter = new vint(1);
|
|
str.start = 0;
|
|
str.length = _length;
|
|
str.realLength = _length;
|
|
return std::move(str);
|
|
}
|
|
return {};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create an unmanaged string.
|
|
/// Such string uses a piece of unmanaged memory,
|
|
/// hereby it doesn't release the memory when the string is destroyed.
|
|
/// </summary>
|
|
/// <returns>The created string.</returns>
|
|
/// <param name="_buffer">Unmanaged memory. It must be zero terminated.</param>
|
|
static ObjectString<T> Unmanaged(const T* _buffer)
|
|
{
|
|
CHECK_ERROR(_buffer != 0, L"ObjectString<T>::Unmanaged(const T*)#Cannot construct a string from nullptr.");
|
|
ObjectString<T> str;
|
|
str.buffer = (T*)_buffer;
|
|
str.length = CalculateLength(_buffer);
|
|
str.realLength = str.length;
|
|
return std::move(str);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Unsafe convert one string to another if their character components are in the same size.
|
|
/// </summary>
|
|
/// <typeparam name="T2">The type of the character component of the string to cast.</typeparam>
|
|
/// <returns>The created string</returns>
|
|
/// <param name="_string">The string to cast</param>
|
|
template<typename T2>
|
|
static ObjectString<T> UnsafeCastFrom(const ObjectString<T2>& _string)
|
|
{
|
|
static_assert(sizeof(T) == sizeof(T2), "ObjectString<T>::UnsafeCastFrom<T2> cannot be used on a string with a different size of the character component.");
|
|
if (_string.Length() == 0) return {};
|
|
ObjectString<T> 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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
/// <returns>The zero terminated buffer.</returns>
|
|
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 vint(1);
|
|
start=0;
|
|
realLength=length;
|
|
}
|
|
return buffer+start;
|
|
}
|
|
|
|
/// <summary>Replace the string by copying another string.</summary>
|
|
/// <returns>The string itself.</returns>
|
|
/// <param name="string">The string to copy.</param>
|
|
ObjectString<T>& operator=(const ObjectString<T>& string)
|
|
{
|
|
if(this!=&string)
|
|
{
|
|
Dec();
|
|
buffer=string.buffer;
|
|
counter=string.counter;
|
|
start=string.start;
|
|
length=string.length;
|
|
realLength=string.realLength;
|
|
Inc();
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
/// <summary>Replace the string by moving another string.</summary>
|
|
/// <returns>The string itself.</returns>
|
|
/// <param name="string">The string to move.</param>
|
|
ObjectString<T>& operator=(ObjectString<T>&& 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;
|
|
}
|
|
|
|
/// <summary>Replace the string by appending another string.</summary>
|
|
/// <returns>The string itself.</returns>
|
|
/// <param name="string">The string to append.</param>
|
|
ObjectString<T>& operator+=(const ObjectString<T>& string)
|
|
{
|
|
return *this=*this+string;
|
|
}
|
|
|
|
/// <summary>Create a new string by concatenating two strings.</summary>
|
|
/// <returns>The new string.</returns>
|
|
/// <param name="string">The string to append.</param>
|
|
ObjectString<T> operator+(const ObjectString<T>& string)const
|
|
{
|
|
return ReplaceUnsafe(string, length, 0);
|
|
}
|
|
|
|
bool operator==(const ObjectString<T>& string)const
|
|
{
|
|
return Compare(*this, string)==0;
|
|
}
|
|
|
|
bool operator!=(const ObjectString<T>& string)const
|
|
{
|
|
return Compare(*this, string)!=0;
|
|
}
|
|
|
|
bool operator>(const ObjectString<T>& string)const
|
|
{
|
|
return Compare(*this, string)>0;
|
|
}
|
|
|
|
bool operator>=(const ObjectString<T>& string)const
|
|
{
|
|
return Compare(*this, string)>=0;
|
|
}
|
|
|
|
bool operator<(const ObjectString<T>& string)const
|
|
{
|
|
return Compare(*this, string)<0;
|
|
}
|
|
|
|
bool operator<=(const ObjectString<T>& string)const
|
|
{
|
|
return Compare(*this, string)<=0;
|
|
}
|
|
|
|
bool operator==(const T* buffer)const
|
|
{
|
|
return Compare(buffer, *this)==0;
|
|
}
|
|
|
|
bool operator!=(const T* buffer)const
|
|
{
|
|
return Compare(buffer, *this)!=0;
|
|
}
|
|
|
|
bool operator>(const T* buffer)const
|
|
{
|
|
return Compare(buffer, *this)<0;
|
|
}
|
|
|
|
bool operator>=(const T* buffer)const
|
|
{
|
|
return Compare(buffer, *this)<=0;
|
|
}
|
|
|
|
bool operator<(const T* buffer)const
|
|
{
|
|
return Compare(buffer, *this)>0;
|
|
}
|
|
|
|
bool operator<=(const T* buffer)const
|
|
{
|
|
return Compare(buffer, *this)>=0;
|
|
}
|
|
|
|
/// <summary>Get a code point in the specified position.</summary>
|
|
/// <returns>Returns the code point. It will crash when the specified position is out of range.</returns>
|
|
/// <param name="index"></param>
|
|
T operator[](vint index)const
|
|
{
|
|
CHECK_ERROR(index>=0 && index<length, L"ObjectString:<T>:operator[](vint)#Argument index not in range.");
|
|
return buffer[start+index];
|
|
}
|
|
|
|
/// <summary>Get the size of the string in code points.</summary>
|
|
/// <returns>The size, not including the zero terminator.</returns>
|
|
vint Length()const
|
|
{
|
|
return length;
|
|
}
|
|
|
|
/// <summary>Find a code point.</summary>
|
|
/// <returns>The position of the code point. Returns -1 if it does not exist.</returns>
|
|
/// <param name="c">The code point to find.</param>
|
|
vint IndexOf(T c)const
|
|
{
|
|
const T* reading=buffer+start;
|
|
for(vint i=0;i<length;i++)
|
|
{
|
|
if(reading[i]==c)
|
|
return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/// <summary>Get the prefix of the string.</summary>
|
|
/// <returns>The prefix. It will crash when the specified size is out of range.</returns>
|
|
/// <param name="count">Size of the prefix.</param>
|
|
/// <example><![CDATA[
|
|
/// int main()
|
|
/// {
|
|
/// WString s = L"Hello, world!";
|
|
/// Console::WriteLine(s.Left(5));
|
|
/// }
|
|
/// ]]></example>
|
|
ObjectString<T> Left(vint count)const
|
|
{
|
|
CHECK_ERROR(count>=0 && count<=length, L"ObjectString<T>::Left(vint)#Argument count not in range.");
|
|
return SubUnsafe(0, count);
|
|
}
|
|
|
|
/// <summary>Get the postfix of the string.</summary>
|
|
/// <returns>The postfix. It will crash when the specified size is out of range.</returns>
|
|
/// <param name="count">Size of the prefix.</param>
|
|
/// <example><![CDATA[
|
|
/// int main()
|
|
/// {
|
|
/// WString s = L"Hello, world!";
|
|
/// Console::WriteLine(s.Right(6));
|
|
/// }
|
|
/// ]]></example>
|
|
ObjectString<T> Right(vint count)const
|
|
{
|
|
CHECK_ERROR(count>=0 && count<=length, L"ObjectString<T>::Right(vint)#Argument count not in range.");
|
|
return SubUnsafe(length-count, count);
|
|
}
|
|
|
|
/// <summary>Get a sub string.</summary>
|
|
/// <returns>The sub string. It will crash when the specified position or size is out of range.</returns>
|
|
/// <param name="index">The position of the first code point of the sub string.</param>
|
|
/// <param name="count">The size of the sub string.</param>
|
|
/// <example><![CDATA[
|
|
/// int main()
|
|
/// {
|
|
/// WString s = L"Hello, world!";
|
|
/// Console::WriteLine(s.Sub(7, 5));
|
|
/// }
|
|
/// ]]></example>
|
|
ObjectString<T> Sub(vint index, vint count)const
|
|
{
|
|
CHECK_ERROR(index>=0 && index<=length, L"ObjectString<T>::Sub(vint, vint)#Argument index not in range.");
|
|
CHECK_ERROR(index+count>=0 && index+count<=length, L"ObjectString<T>::Sub(vint, vint)#Argument count not in range.");
|
|
return SubUnsafe(index, count);
|
|
}
|
|
|
|
/// <summary>Get a string by removing a sub string.</summary>
|
|
/// <returns>The string without the sub string. It will crash when the specified position or size is out of range.</returns>
|
|
/// <param name="index">The position of the first code point of the sub string.</param>
|
|
/// <param name="count">The size of the sub string.</param>
|
|
/// <example><![CDATA[
|
|
/// int main()
|
|
/// {
|
|
/// WString s = L"Hello, world!";
|
|
/// Console::WriteLine(s.Remove(5, 7));
|
|
/// }
|
|
/// ]]></example>
|
|
ObjectString<T> Remove(vint index, vint count)const
|
|
{
|
|
CHECK_ERROR(index>=0 && index<length, L"ObjectString<T>::Remove(vint, vint)#Argument index not in range.");
|
|
CHECK_ERROR(index+count>=0 && index+count<=length, L"ObjectString<T>::Remove(vint, vint)#Argument count not in range.");
|
|
return ReplaceUnsafe(ObjectString<T>(), index, count);
|
|
}
|
|
|
|
/// <summary>Get a string by inserting another string.</summary>
|
|
/// <returns>The string with another string inserted. It will crash when the specified position is out of range.</returns>
|
|
/// <param name="index">The position to insert.</param>
|
|
/// <param name="string">The string to insert.</param>
|
|
/// <example><![CDATA[
|
|
/// int main()
|
|
/// {
|
|
/// WString s = L"Hello, world!";
|
|
/// Console::WriteLine(s.Insert(7, L"a great "));
|
|
/// }
|
|
/// ]]></example>
|
|
ObjectString<T> Insert(vint index, const ObjectString<T>& string)const
|
|
{
|
|
CHECK_ERROR(index>=0 && index<=length, L"ObjectString<T>::Insert(vint)#Argument count not in range.");
|
|
return ReplaceUnsafe(string, index, 0);
|
|
}
|
|
|
|
friend bool operator<(const T* left, const ObjectString<T>& right)
|
|
{
|
|
return Compare(left, right)<0;
|
|
}
|
|
|
|
friend bool operator<=(const T* left, const ObjectString<T>& right)
|
|
{
|
|
return Compare(left, right)<=0;
|
|
}
|
|
|
|
friend bool operator>(const T* left, const ObjectString<T>& right)
|
|
{
|
|
return Compare(left, right)>0;
|
|
}
|
|
|
|
friend bool operator>=(const T* left, const ObjectString<T>& right)
|
|
{
|
|
return Compare(left, right)>=0;
|
|
}
|
|
|
|
friend bool operator==(const T* left, const ObjectString<T>& right)
|
|
{
|
|
return Compare(left, right)==0;
|
|
}
|
|
|
|
friend bool operator!=(const T* left, const ObjectString<T>& right)
|
|
{
|
|
return Compare(left, right)!=0;
|
|
}
|
|
|
|
friend ObjectString<T> operator+(const T* left, const ObjectString<T>& right)
|
|
{
|
|
return ObjectString<T>::Unmanaged(left)+right;
|
|
}
|
|
};
|
|
|
|
template<typename T>
|
|
ObjectString<T> ObjectString<T>::Empty=ObjectString<T>();
|
|
template<typename T>
|
|
const T ObjectString<T>::zero=0;
|
|
|
|
/// <summary>Ansi string in local code page.</summary>
|
|
typedef ObjectString<char> AString;
|
|
/// <summary>Unicode string, UTF-16 on Windows, UTF-32 on Linux and macOS.</summary>
|
|
typedef ObjectString<wchar_t> WString;
|
|
/// <summary>Utf-8 String.</summary>
|
|
typedef ObjectString<char8_t> U8String;
|
|
/// <summary>Utf-16 String.</summary>
|
|
typedef ObjectString<char16_t> U16String;
|
|
/// <summary>Utf-32 String.</summary>
|
|
typedef ObjectString<char32_t> U32String;
|
|
|
|
/// <summary>Convert a string to a signed integer.</summary>
|
|
/// <returns>The converted number. If the conversion failed, the result is undefined.</returns>
|
|
/// <param name="string">The string to convert.</param>
|
|
/// <param name="success">Returns true if this operation succeeded.</param>
|
|
extern vint atoi_test(const AString& string, bool& success);
|
|
/// <summary>Convert a string to a signed integer.</summary>
|
|
/// <returns>The converted number. If the conversion failed, the result is undefined.</returns>
|
|
/// <param name="string">The string to convert.</param>
|
|
/// <param name="success">Returns true if this operation succeeded.</param>
|
|
extern vint wtoi_test(const WString& string, bool& success);
|
|
/// <summary>Convert a string to a signed 64-bits integer.</summary>
|
|
/// <returns>The converted number. If the conversion failed, the result is undefined.</returns>
|
|
/// <param name="string">The string to convert.</param>
|
|
/// <param name="success">Returns true if this operation succeeded.</param>
|
|
extern vint64_t atoi64_test(const AString& string, bool& success);
|
|
/// <summary>Convert a string to a signed 64-bits integer.</summary>
|
|
/// <returns>The converted number. If the conversion failed, the result is undefined.</returns>
|
|
/// <param name="string">The string to convert.</param>
|
|
/// <param name="success">Returns true if this operation succeeded.</param>
|
|
extern vint64_t wtoi64_test(const WString& string, bool& success);
|
|
/// <summary>Convert a string to an unsigned integer.</summary>
|
|
/// <returns>The converted number. If the conversion failed, the result is undefined.</returns>
|
|
/// <param name="string">The string to convert.</param>
|
|
/// <param name="success">Returns true if this operation succeeded.</param>
|
|
extern vuint atou_test(const AString& string, bool& success);
|
|
/// <summary>Convert a string to an unsigned integer.</summary>
|
|
/// <returns>The converted number. If the conversion failed, the result is undefined.</returns>
|
|
/// <param name="string">The string to convert.</param>
|
|
/// <param name="success">Returns true if this operation succeeded.</param>
|
|
extern vuint wtou_test(const WString& string, bool& success);
|
|
/// <summary>Convert a string to an unsigned 64-bits integer.</summary>
|
|
/// <returns>The converted number. If the conversion failed, the result is undefined.</returns>
|
|
/// <param name="string">The string to convert.</param>
|
|
/// <param name="success">Returns true if this operation succeeded.</param>
|
|
extern vuint64_t atou64_test(const AString& string, bool& success);
|
|
/// <summary>Convert a string to an unsigned 64-bits integer.</summary>
|
|
/// <returns>The converted number. If the conversion failed, the result is undefined.</returns>
|
|
/// <param name="string">The string to convert.</param>
|
|
/// <param name="success">Returns true if this operation succeeded.</param>
|
|
extern vuint64_t wtou64_test(const WString& string, bool& success);
|
|
/// <summary>Convert a string to a 64-bits floating point number.</summary>
|
|
/// <returns>The converted number. If the conversion failed, the result is undefined.</returns>
|
|
/// <param name="string">The string to convert.</param>
|
|
/// <param name="success">Returns true if this operation succeeded.</param>
|
|
extern double atof_test(const AString& string, bool& success);
|
|
/// <summary>Convert a string to a 64-bits floating point number.</summary>
|
|
/// <returns>The converted number. If the conversion failed, the result is undefined.</returns>
|
|
/// <param name="string">The string to convert.</param>
|
|
/// <param name="success">Returns true if this operation succeeded.</param>
|
|
extern double wtof_test(const WString& string, bool& success);
|
|
|
|
/// <summary>Convert a string to a signed integer.</summary>
|
|
/// <returns>The converted number. If the conversion failed, the result is undefined.</returns>
|
|
/// <param name="string">The string to convert.</param>
|
|
/// <remarks>If you need to know whether the conversion is succeeded or not, please use <see cref="atoi_test"/> instead.</remarks>
|
|
extern vint atoi(const AString& string);
|
|
/// <summary>Convert a string to a signed integer.</summary>
|
|
/// <returns>The converted number. If the conversion failed, the result is undefined.</returns>
|
|
/// <param name="string">The string to convert.</param>
|
|
/// <remarks>If you need to know whether the conversion is succeeded or not, please use <see cref="wtoi_test"/> instead.</remarks>
|
|
extern vint wtoi(const WString& string);
|
|
/// <summary>Convert a string to a signed 64-bits integer.</summary>
|
|
/// <returns>The converted number. If the conversion failed, the result is undefined.</returns>
|
|
/// <param name="string">The string to convert.</param>
|
|
/// <remarks>If you need to know whether the conversion is succeeded or not, please use <see cref="atoi64_test"/> instead.</remarks>
|
|
extern vint64_t atoi64(const AString& string);
|
|
/// <summary>Convert a string to a signed 64-bits integer.</summary>
|
|
/// <returns>The converted number. If the conversion failed, the result is undefined.</returns>
|
|
/// <param name="string">The string to convert.</param>
|
|
/// <remarks>If you need to know whether the conversion is succeeded or not, please use <see cref="wtoi64_test"/> instead.</remarks>
|
|
extern vint64_t wtoi64(const WString& string);
|
|
/// <summary>Convert a string to an usigned integer.</summary>
|
|
/// <returns>The converted number. If the conversion failed, the result is undefined.</returns>
|
|
/// <param name="string">The string to convert.</param>
|
|
/// <remarks>If you need to know whether the conversion is succeeded or not, please use <see cref="atou_test"/> instead.</remarks>
|
|
extern vuint atou(const AString& string);
|
|
/// <summary>Convert a string to an usigned integer.</summary>
|
|
/// <returns>The converted number. If the conversion failed, the result is undefined.</returns>
|
|
/// <param name="string">The string to convert.</param>
|
|
/// <remarks>If you need to know whether the conversion is succeeded or not, please use <see cref="wtou_test"/> instead.</remarks>
|
|
extern vuint wtou(const WString& string);
|
|
/// <summary>Convert a string to an usigned 64-bits integer.</summary>
|
|
/// <returns>The converted number. If the conversion failed, the result is undefined.</returns>
|
|
/// <param name="string">The string to convert.</param>
|
|
/// <remarks>If you need to know whether the conversion is succeeded or not, please use <see cref="atou64_test"/> instead.</remarks>
|
|
extern vuint64_t atou64(const AString& string);
|
|
/// <summary>Convert a string to an usigned 64-bits integer.</summary>
|
|
/// <returns>The converted number. If the conversion failed, the result is undefined.</returns>
|
|
/// <param name="string">The string to convert.</param>
|
|
/// <remarks>If you need to know whether the conversion is succeeded or not, please use <see cref="wtou64_test"/> instead.</remarks>
|
|
extern vuint64_t wtou64(const WString& string);
|
|
/// <summary>Convert a string to a 64-bits floating point number.</summary>
|
|
/// <returns>The converted number. If the conversion failed, the result is undefined.</returns>
|
|
/// <param name="string">The string to convert.</param>
|
|
/// <remarks>If you need to know whether the conversion is succeeded or not, please use <see cref="atof_test"/> instead.</remarks>
|
|
extern double atof(const AString& string);
|
|
/// <summary>Convert a string to a 64-bits floating point number.</summary>
|
|
/// <returns>The converted number. If the conversion failed, the result is undefined.</returns>
|
|
/// <param name="string">The string to convert.</param>
|
|
/// <remarks>If you need to know whether the conversion is succeeded or not, please use <see cref="wtof_test"/> instead.</remarks>
|
|
extern double wtof(const WString& string);
|
|
|
|
/// <summary>Convert a signed interger to a string.</summary>
|
|
/// <returns>The converted string.</returns>
|
|
/// <param name="number">The number to convert.</param>
|
|
extern AString itoa(vint number);
|
|
/// <summary>Convert a signed interger to a string.</summary>
|
|
/// <returns>The converted string.</returns>
|
|
/// <param name="number">The number to convert.</param>
|
|
extern WString itow(vint number);
|
|
/// <summary>Convert a signed 64-bits interger to a string.</summary>
|
|
/// <returns>The converted string.</returns>
|
|
/// <param name="number">The number to convert.</param>
|
|
extern AString i64toa(vint64_t number);
|
|
/// <summary>Convert a signed 64-bits interger to a string.</summary>
|
|
/// <returns>The converted string.</returns>
|
|
/// <param name="number">The number to convert.</param>
|
|
extern WString i64tow(vint64_t number);
|
|
/// <summary>Convert an unsigned interger to a string.</summary>
|
|
/// <returns>The converted string.</returns>
|
|
/// <param name="number">The number to convert.</param>
|
|
extern AString utoa(vuint number);
|
|
/// <summary>Convert an unsigned interger to a string.</summary>
|
|
/// <returns>The converted string.</returns>
|
|
/// <param name="number">The number to convert.</param>
|
|
extern WString utow(vuint number);
|
|
/// <summary>Convert an unsigned 64-bits interger to a string.</summary>
|
|
/// <returns>The converted string.</returns>
|
|
/// <param name="number">The number to convert.</param>
|
|
extern AString u64toa(vuint64_t number);
|
|
/// <summary>Convert an unsigned 64-bits interger to a string.</summary>
|
|
/// <returns>The converted string.</returns>
|
|
/// <param name="number">The number to convert.</param>
|
|
extern WString u64tow(vuint64_t number);
|
|
/// <summary>Convert a 64-bits floating pointer number to a string.</summary>
|
|
/// <returns>The converted string.</returns>
|
|
/// <param name="number">The number to convert.</param>
|
|
extern AString ftoa(double number);
|
|
/// <summary>Convert a 64-bits floating pointer number to a string.</summary>
|
|
/// <returns>The converted string.</returns>
|
|
/// <param name="number">The number to convert.</param>
|
|
extern WString ftow(double number);
|
|
/// <summary>Convert all letters to lower case letters.</summary>
|
|
/// <returns>The converted string.</returns>
|
|
/// <param name="string">The string to convert.</param>
|
|
extern AString alower(const AString& string);
|
|
/// <summary>Convert all letters to lower case letters.</summary>
|
|
/// <returns>The converted string.</returns>
|
|
/// <param name="string">The string to convert.</param>
|
|
extern WString wlower(const WString& string);
|
|
/// <summary>Convert all letters to upper case letters.</summary>
|
|
/// <returns>The converted string.</returns>
|
|
/// <param name="string">The string to convert.</param>
|
|
extern AString aupper(const AString& string);
|
|
/// <summary>Convert all letters to upper case letters.</summary>
|
|
/// <returns>The converted string.</returns>
|
|
/// <param name="string">The string to convert.</param>
|
|
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
|
|
{
|
|
/// <summary>Copy containers.</summary>
|
|
/// <typeparam name="Ds">Type of the target container.</typeparam>
|
|
/// <typeparam name="S">Type of code points in the source string.</typeparam>
|
|
/// <param name="ds">The target container.</param>
|
|
/// <param name="ss">The source string.</param>
|
|
/// <param name="append">Set to true to perform appending instead of replacing.</param>
|
|
template<typename Ds, typename S>
|
|
void CopyFrom(Ds& ds, const ObjectString<S>& ss, bool append = false)
|
|
{
|
|
const S* buffer = ss.Buffer();
|
|
vint count = ss.Length();
|
|
CopyFrom(ds, buffer, count, append);
|
|
}
|
|
|
|
/// <summary>Copy containers.</summary>
|
|
/// <typeparam name="D">Type of code points in the target string.</typeparam>
|
|
/// <typeparam name="Ss">Type of the source container.</typeparam>
|
|
/// <param name="ds">The target string.</param>
|
|
/// <param name="ss">The source container.</param>
|
|
/// <param name="append">Set to true to perform appending instead of replacing.</param>
|
|
template<typename D, typename Ss>
|
|
void CopyFrom(ObjectString<D>& ds, const Ss& ss, bool append = false)
|
|
{
|
|
Array<D> da(ds.Buffer(), ds.Length());
|
|
CopyFrom(da, ss, append);
|
|
if (da.Count() == 0)
|
|
{
|
|
ds = {};
|
|
}
|
|
else
|
|
{
|
|
ds = ObjectString<D>::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<T>] .Cast<K>() => [Ptr<K>]
|
|
[Ptr<T>] .FindType<K>() => [Ptr<K>]
|
|
[T] .OrderBy(T->T->int) => [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
|
|
***********************************************************************/
|
|
|
|
/// <summary>Quick sort.</summary>
|
|
/// <typeparam name="T">Type of elements.</typeparam>
|
|
/// <typeparam name="F">Type of the comparer.</typeparam>
|
|
/// <param name="items">Pointer to element array to sort.</param>
|
|
/// <param name="length">The number of elements to sort.</param>
|
|
/// <param name="orderer">
|
|
/// The comparar for two elements.
|
|
/// Both arguments are elements to compare.
|
|
/// Returns a positive number when the first argument is greater.
|
|
/// Returns a negative number when the second argument is greater.
|
|
/// Returns zero when two arguments equal.
|
|
/// </param>
|
|
template<typename T, typename F>
|
|
void SortLambda(T* items, vint length, F orderer)
|
|
{
|
|
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);
|
|
|
|
if (orderer(items[pivot], items[candidate]) * factor <= 0)
|
|
{
|
|
mine++;
|
|
}
|
|
else
|
|
{
|
|
theirs++;
|
|
T temp = items[pivot];
|
|
items[pivot] = items[candidate];
|
|
items[candidate] = temp;
|
|
pivot = candidate;
|
|
flag = !flag;
|
|
}
|
|
}
|
|
|
|
SortLambda(items, left, orderer);
|
|
SortLambda(items + left + 1, right, orderer);
|
|
}
|
|
|
|
/// <summary>Quick sort.</summary>
|
|
/// <typeparam name="T">Type of elements.</typeparam>
|
|
/// <param name="items">Pointer to element array to sort.</param>
|
|
/// <param name="length">The number of elements to sort.</param>
|
|
/// <param name="orderer">
|
|
/// The comparar for two elements.
|
|
/// Both arguments are elements to compare.
|
|
/// Returns a positive number when the first argument is greater.
|
|
/// Returns a negative number when the second argument is greater.
|
|
/// Returns zero when two arguments equal.
|
|
/// </param>
|
|
template<typename T>
|
|
void Sort(T* items, vint length, const Func<vint64_t(T, T)>& orderer)
|
|
{
|
|
SortLambda<T, Func<vint64_t(T, T)>>(items, length, orderer);
|
|
}
|
|
|
|
/***********************************************************************
|
|
LazyList
|
|
***********************************************************************/
|
|
|
|
/// <summary>A lazy evaluated container with rich operations. <see cref="From`*"/> is useful to create lazy list from arrays or containers.</summary>
|
|
/// <typeparam name="T">The type of elements.</typeparam>
|
|
/// <remarks>
|
|
/// <p>A lazy list is usually created directly from a container source, or from a calculation on a source.</p>
|
|
/// <p>Typically the lazy list cannot be used after the source is deleted.</p>
|
|
/// <p>
|
|
/// If this lazy list needs to be used after the source is deleted,
|
|
/// you are recommended to use [F:vl.collections.LazyList`1.Evaluate], <b>with forceCopy set to true</b>.
|
|
/// </p>
|
|
/// <p>In this way you get a lazy list with all values copied, they do not rely on other objects.</p>
|
|
/// </remarks>
|
|
template<typename T>
|
|
class LazyList : public EnumerableBase<T>
|
|
{
|
|
protected:
|
|
Ptr<IEnumerator<T>> enumeratorPrototype;
|
|
|
|
IEnumerator<T>* xs()const
|
|
{
|
|
return enumeratorPrototype->Clone();
|
|
}
|
|
|
|
using TInput = decltype(std::declval<IEnumerator<T>>().Current());
|
|
public:
|
|
/// <summary>Create a lazy list from an enumerator. This enumerator will be deleted when this lazy list is deleted.</summary>
|
|
/// <param name="enumerator">The enumerator.</param>
|
|
LazyList(IEnumerator<T>* enumerator)
|
|
:enumeratorPrototype(enumerator)
|
|
{
|
|
}
|
|
|
|
/// <summary>Create a lazy list from an enumerator.</summary>
|
|
/// <param name="enumerator">The enumerator.</param>
|
|
LazyList(Ptr<IEnumerator<T>> enumerator)
|
|
:enumeratorPrototype(enumerator)
|
|
{
|
|
}
|
|
|
|
/// <summary>Create a lazy list from an enumerable.</summary>
|
|
/// <param name="enumerable">The enumerable.</param>
|
|
LazyList(const IEnumerable<T>& enumerable)
|
|
:enumeratorPrototype(enumerable.CreateEnumerator())
|
|
{
|
|
}
|
|
|
|
/// <summary>Create a lazy list from another lazy list.</summary>
|
|
/// <param name="lazyList">The lazy list.</param>
|
|
LazyList(const LazyList<T>& lazyList)
|
|
:enumeratorPrototype(lazyList.enumeratorPrototype)
|
|
{
|
|
// no need to clone enumeratorPrototype as it will never be iterated
|
|
}
|
|
|
|
/// <summary>Create a lazy list from another lazy list.</summary>
|
|
/// <param name="lazyList">The lazy list.</param>
|
|
LazyList(LazyList<T>&& lazyList)
|
|
:enumeratorPrototype(lazyList.enumeratorPrototype)
|
|
{
|
|
lazyList.enumeratorPrototype = nullptr;
|
|
}
|
|
|
|
/// <summary>Create a lazy list from a container. It is very useful to <see cref="MakePtr`2"/> a container as an intermediate result and then put in a lazy list.</summary>
|
|
/// <typeparam name="TContainer">Type of the container.</typeparam>
|
|
/// <param name="container">The container.</param>
|
|
template<typename TContainer>
|
|
LazyList(Ptr<TContainer> container)
|
|
:enumeratorPrototype(new ContainerEnumerator<T, TContainer>(container))
|
|
{
|
|
}
|
|
|
|
/// <summary>Create an empty lazy list.</summary>
|
|
LazyList()
|
|
:enumeratorPrototype(new EmptyEnumerator<T>())
|
|
{
|
|
}
|
|
|
|
LazyList<T>& operator=(const LazyList<T>& lazyList)
|
|
{
|
|
// no need to clone enumeratorPrototype as it will never be iterated
|
|
enumeratorPrototype = lazyList.enumeratorPrototype;
|
|
return *this;
|
|
}
|
|
|
|
LazyList<T>& operator=(LazyList<T>&& lazyList)
|
|
{
|
|
enumeratorPrototype = lazyList.enumeratorPrototype;
|
|
lazyList.enumeratorPrototype = nullptr;
|
|
return *this;
|
|
}
|
|
|
|
IEnumerator<T>* CreateEnumerator()const
|
|
{
|
|
return enumeratorPrototype->Clone();
|
|
}
|
|
|
|
//-------------------------------------------------------
|
|
|
|
/// <summary>Create a new lazy list with all elements transformed.</summary>
|
|
/// <typeparam name="F">Type of the transformer.</typeparam>
|
|
/// <returns>The created lazy list.</returns>
|
|
/// <param name="f">The transformer.</param>
|
|
/// <example><![CDATA[
|
|
/// int main()
|
|
/// {
|
|
/// vint xs[] = {1, 2, 3, 4, 5};
|
|
/// auto ys = From(xs).Select([](vint x){ return x * 2; });
|
|
/// for (auto y : ys) Console::Write(itow(y) + L" ");
|
|
/// }
|
|
/// ]]></example>
|
|
template<typename F>
|
|
auto Select(F f) const -> LazyList<decltype(f(std::declval<TInput>()))>
|
|
{
|
|
return new SelectEnumerator<T, decltype(f(std::declval<TInput>()))>(xs(), f);
|
|
}
|
|
|
|
/// <summary>Create a new lazy list with all elements filtered.</summary>
|
|
/// <typeparam name="F">Type of the filter.</typeparam>
|
|
/// <returns>The created lazy list.</returns>
|
|
/// <param name="f">The filter.</param>
|
|
/// <example><![CDATA[
|
|
/// int main()
|
|
/// {
|
|
/// vint xs[] = {1, 2, 3, 4, 5};
|
|
/// auto ys = From(xs).Where([](vint x){ return x % 2 == 0; });
|
|
/// for (auto y : ys) Console::Write(itow(y) + L" ");
|
|
/// }
|
|
/// ]]></example>
|
|
template<typename F>
|
|
LazyList<T> Where(F f)const
|
|
{
|
|
return new WhereEnumerator<T>(xs(), f);
|
|
}
|
|
|
|
/// <summary>Create a new lazy list with all elements casted to a new type.</summary>
|
|
/// <typeparam name="U">The new type.</typeparam>
|
|
/// <returns>The created lazy list.</returns>
|
|
/// <remarks>
|
|
/// 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.
|
|
/// </remarks>
|
|
template<typename U>
|
|
LazyList<Ptr<U>> Cast()const
|
|
{
|
|
Func<Ptr<U>(T)> f = [](T t)->Ptr<U> {return t.template Cast<U>(); };
|
|
return new SelectEnumerator<T, Ptr<U>>(xs(), f);
|
|
}
|
|
|
|
/// <summary>Create a new lazy list with only elements that successfully casted to a new type.</summary>
|
|
/// <typeparam name="U">The new type.</typeparam>
|
|
/// <returns>The created lazy list.</returns>
|
|
/// <remarks>
|
|
/// 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.
|
|
/// </remarks>
|
|
template<typename U>
|
|
LazyList<Ptr<U>> FindType()const
|
|
{
|
|
return Cast<U>().Where([](Ptr<U> t) {return t; });
|
|
}
|
|
|
|
/// <summary>Create a new lazy list with all elements sorted.</summary>
|
|
/// <typeparam name="F">Type of the comparer.</typeparam>
|
|
/// <returns>The created lazy list.</returns>
|
|
/// <param name="f">
|
|
/// The comparar for two elements.
|
|
/// Both arguments are elements to compare.
|
|
/// Returns a positive number when the first argument is greater.
|
|
/// Returns a negative number when the second argument is greater.
|
|
/// Returns zero when two arguments equal.
|
|
/// </param>
|
|
/// <example><![CDATA[
|
|
/// int main()
|
|
/// {
|
|
/// vint xs[] = {1, 2, 3, 4, 5};
|
|
/// auto ys = From(xs).OrderBy([](vint x, vint y){ return x - y; });
|
|
/// for (auto y : ys) Console::Write(itow(y) + L" ");
|
|
/// }
|
|
/// ]]></example>
|
|
template<typename F>
|
|
LazyList<T> OrderBy(F f)const
|
|
{
|
|
Ptr<List<T>> sorted = new List<T>;
|
|
CopyFrom(*sorted.Obj(), *this);
|
|
if (sorted->Count() > 0)
|
|
{
|
|
SortLambda<T, F>(&sorted->operator[](0), sorted->Count(), f);
|
|
}
|
|
return sorted;
|
|
}
|
|
|
|
//-------------------------------------------------------
|
|
|
|
/// <summary>Aggregate a lazy list. It will crash if the lazy list is empty.</summary>
|
|
/// <typeparam name="F">Type of the aggregator.</typeparam>
|
|
/// <returns>The aggregated value.</returns>
|
|
/// <param name="f">
|
|
/// 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.
|
|
/// </param>
|
|
/// <example><![CDATA[
|
|
/// int main()
|
|
/// {
|
|
/// vint xs[] = {1, 2, 3, 4, 5};
|
|
/// auto ys = From(xs).Aggregate([](vint x, vint y){ return x + y; });
|
|
/// Console::WriteLine(itow(ys));
|
|
/// }
|
|
/// ]]></example>
|
|
template<typename F>
|
|
T Aggregate(F f)const
|
|
{
|
|
Ptr<IEnumerator<T>> enumerator = CreateEnumerator();
|
|
if (!enumerator->Next())
|
|
{
|
|
throw Error(L"LazyList<T>::Aggregate(F)#Aggregate failed to calculate from an empty container.");
|
|
}
|
|
T result = enumerator->Current();
|
|
while (enumerator->Next())
|
|
{
|
|
result = f(result, enumerator->Current());
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/// <summary>Aggregate a lazy list.</summary>
|
|
/// <typeparam name="I">Type of the initial value.</typeparam>
|
|
/// <typeparam name="F">Type of the aggregator.</typeparam>
|
|
/// <returns>The aggregated value.</returns>
|
|
/// <param name="init">The aggregated value defined for the empty prefix.</param>
|
|
/// <param name="f">
|
|
/// 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.
|
|
/// </param>
|
|
/// <example><![CDATA[
|
|
/// int main()
|
|
/// {
|
|
/// vint xs[] = {1, 2, 3, 4, 5};
|
|
/// auto ys = From(xs).Aggregate(1, [](vint x, vint y){ return x * y; });
|
|
/// Console::WriteLine(itow(ys));
|
|
/// }
|
|
/// ]]></example>
|
|
template<typename I, typename F>
|
|
I Aggregate(I init, F f)const
|
|
{
|
|
for (auto& t : *this)
|
|
{
|
|
init = f(init, t);
|
|
}
|
|
return init;
|
|
}
|
|
|
|
/// <summary>Test if all elements in the lazy list satisfy a filter.</summary>
|
|
/// <typeparam name="F">Type of the filter.</typeparam>
|
|
/// <returns>Returns true if all elements satisfy the filter.</returns>
|
|
/// <param name="f">The filter.</param>
|
|
/// <example><![CDATA[
|
|
/// int main()
|
|
/// {
|
|
/// vint xs[] = {1, 2, 3, 4, 5};
|
|
/// auto ys = From(xs).All([](vint x){ return x % 2 == 0; });
|
|
/// Console::WriteLine(ys ? L"All numbers are even" : L"Not all numbers are even");
|
|
/// }
|
|
/// ]]></example>
|
|
template<typename F>
|
|
bool All(F f)const
|
|
{
|
|
return Select(f).Aggregate(true, [](bool a, bool b) { return a && b; });
|
|
}
|
|
|
|
/// <summary>Test if any elements in the lazy list satisfy a filter.</summary>
|
|
/// <typeparam name="F">Type of the filter.</typeparam>
|
|
/// <returns>Returns true if there is at least one element satisfies the filter.</returns>
|
|
/// <param name="f">The filter.</param>
|
|
/// <example><![CDATA[
|
|
/// int main()
|
|
/// {
|
|
/// vint xs[] = {1, 2, 3, 4, 5};
|
|
/// auto ys = From(xs).Any([](vint x){ return x % 2 == 0; });
|
|
/// Console::WriteLine(ys ? L"There are even numbers" : L"There is no even number");
|
|
/// }
|
|
/// ]]></example>
|
|
template<typename F>
|
|
bool Any(F f)const
|
|
{
|
|
return Select(f).Aggregate(false, [](bool a, bool b) { return a || b; });
|
|
}
|
|
|
|
/// <summary>Get the maximum value in the lazy list. It will crash if the lazy list is empty.</summary>
|
|
/// <returns>The maximum value.</returns>
|
|
/// <example><![CDATA[
|
|
/// int main()
|
|
/// {
|
|
/// vint xs[] = {1, 2, 3, 4, 5};
|
|
/// auto ys = From(xs).Max();
|
|
/// Console::WriteLine(itow(ys));
|
|
/// }
|
|
/// ]]></example>
|
|
T Max()const
|
|
{
|
|
return Aggregate([](T a, T b) { return a > b ? a : b; });
|
|
}
|
|
|
|
/// <summary>Get the minimum value in the lazy list. It will crash if the lazy list is empty.</summary>
|
|
/// <returns>The minimum value.</returns>
|
|
/// <example><![CDATA[
|
|
/// int main()
|
|
/// {
|
|
/// vint xs[] = {1, 2, 3, 4, 5};
|
|
/// auto ys = From(xs).Min();
|
|
/// Console::WriteLine(itow(ys));
|
|
/// }
|
|
/// ]]></example>
|
|
T Min()const
|
|
{
|
|
return Aggregate([](T a, T b) { return a < b ? a : b; });
|
|
}
|
|
|
|
/// <summary>Get the first value in the lazy list. It will crash if the lazy list is empty.</summary>
|
|
/// <returns>The first value.</returns>
|
|
T First()const
|
|
{
|
|
Ptr<IEnumerator<T>> enumerator = CreateEnumerator();
|
|
if (!enumerator->Next())
|
|
{
|
|
throw Error(L"LazyList<T>::First(F)#First failed to calculate from an empty container.");
|
|
}
|
|
return enumerator->Current();
|
|
}
|
|
|
|
/// <summary>Get the first value in the lazy list.</summary>
|
|
/// <returns>The first value. If the lazy list is empty, the argument is returned.</returns>
|
|
/// <param name="defaultValue">The argument to return if the lazy list is empty.</param>
|
|
T First(T defaultValue)const
|
|
{
|
|
Ptr<IEnumerator<T>> enumerator = CreateEnumerator();
|
|
if (!enumerator->Next())
|
|
{
|
|
return defaultValue;
|
|
}
|
|
return enumerator->Current();
|
|
}
|
|
|
|
/// <summary>Get the last value in the lazy list. It will crash if the lazy list is empty.</summary>
|
|
/// <returns>The last value.</returns>
|
|
T Last()const
|
|
{
|
|
Ptr<IEnumerator<T>> enumerator = CreateEnumerator();
|
|
if (!enumerator->Next())
|
|
{
|
|
throw Error(L"LazyList<T>::Last(F)#Last failed to calculate from an empty container.");
|
|
}
|
|
else
|
|
{
|
|
T value = enumerator->Current();
|
|
while (enumerator->Next())
|
|
{
|
|
value = enumerator->Current();
|
|
}
|
|
return value;
|
|
}
|
|
}
|
|
|
|
/// <summary>Get the last value in the lazy list.</summary>
|
|
/// <returns>The last value. If the lazy list is empty, the argument is returned.</returns>
|
|
/// <param name="defaultValue">The argument to return if the lazy list is empty.</param>
|
|
T Last(T defaultValue)const
|
|
{
|
|
Ptr<IEnumerator<T>> enumerator = CreateEnumerator();
|
|
while (enumerator->Next())
|
|
{
|
|
defaultValue = enumerator->Current();
|
|
}
|
|
return defaultValue;
|
|
}
|
|
|
|
/// <summary>Get the number of elements in the lazy list.</summary>
|
|
/// <returns>The number of elements.</returns>
|
|
vint Count()const
|
|
{
|
|
vint result = 0;
|
|
Ptr<IEnumerator<T>> enumerator = CreateEnumerator();
|
|
while (enumerator->Next())
|
|
{
|
|
result++;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/// <summary>Test if the lazy list is empty.</summary>
|
|
/// <returns>Returns true if the lazy list is empty.</returns>
|
|
bool IsEmpty()const
|
|
{
|
|
Ptr<IEnumerator<T>> enumerator = CreateEnumerator();
|
|
return !enumerator->Next();
|
|
}
|
|
|
|
//-------------------------------------------------------
|
|
|
|
/// <summary>Create a new lazy list containing elements of the two container in order.</summary>
|
|
/// <returns>The created lazy list.</returns>
|
|
/// <param name="remains">Elements to be appended.</param>
|
|
/// <example><![CDATA[
|
|
/// int main()
|
|
/// {
|
|
/// vint xs[] = {1, 2, 3, 4, 5};
|
|
/// vint ys[] = {6, 7, 8, 9, 10};
|
|
/// auto zs = From(xs).Concat(From(ys));
|
|
/// for (auto z : zs) Console::Write(itow(z) + L" ");
|
|
/// }
|
|
/// ]]></example>
|
|
LazyList<T> Concat(const IEnumerable<T>& remains)const
|
|
{
|
|
return new ConcatEnumerator<T>(xs(), remains.CreateEnumerator());
|
|
}
|
|
|
|
/// <summary>Create a new lazy list with a prefix of the lazy list.</summary>
|
|
/// <returns>The created lazy list.</returns>
|
|
/// <param name="count">The size of the prefix.</param>
|
|
/// <example><![CDATA[
|
|
/// int main()
|
|
/// {
|
|
/// vint xs[] = {1, 2, 3, 4, 5};
|
|
/// auto ys = From(xs).Take(3);
|
|
/// for (auto y : ys) Console::Write(itow(y) + L" ");
|
|
/// }
|
|
/// ]]></example>
|
|
LazyList<T> Take(vint count)const
|
|
{
|
|
return new TakeEnumerator<T>(xs(), count);
|
|
}
|
|
|
|
/// <summary>Create a new lazy list with a postfix of the lazy list.</summary>
|
|
/// <returns>The created lazy list.</returns>
|
|
/// <param name="count">The number of elements to skip.</param>
|
|
/// <example><![CDATA[
|
|
/// int main()
|
|
/// {
|
|
/// vint xs[] = {1, 2, 3, 4, 5};
|
|
/// auto ys = From(xs).Skip(3);
|
|
/// for (auto y : ys) Console::Write(itow(y) + L" ");
|
|
/// }
|
|
/// ]]></example>
|
|
LazyList<T> Skip(vint count)const
|
|
{
|
|
return new SkipEnumerator<T>(xs(), count);
|
|
}
|
|
|
|
/// <summary>Create a new lazy list with several copies of the lazy list in order.</summary>
|
|
/// <returns>The created lazy list.</returns>
|
|
/// <param name="count">The numbers of copies.</param>
|
|
/// <example><![CDATA[
|
|
/// int main()
|
|
/// {
|
|
/// vint xs[] = {1, 2, 3, 4, 5};
|
|
/// auto ys = From(xs).Repeat(3);
|
|
/// for (auto y : ys) Console::Write(itow(y) + L" ");
|
|
/// }
|
|
/// ]]></example>
|
|
LazyList<T> Repeat(vint count)const
|
|
{
|
|
return new RepeatEnumerator<T>(xs(), count);
|
|
}
|
|
|
|
/// <summary>Create a new lazy list with duplicated elements removed in this lazy list.</summary>
|
|
/// <returns>The created lazy list.</returns>
|
|
/// <example><![CDATA[
|
|
/// int main()
|
|
/// {
|
|
/// vint xs[] = {1, 2, 2, 3, 3, 3, 4, 4, 5};
|
|
/// auto ys = From(xs).Distinct();
|
|
/// for (auto y : ys) Console::Write(itow(y) + L" ");
|
|
/// }
|
|
/// ]]></example>
|
|
LazyList<T> Distinct()const
|
|
{
|
|
return new DistinctEnumerator<T>(xs());
|
|
}
|
|
|
|
/// <summary>Create a new lazy list with all elements in this lazy list in a reverse order.</summary>
|
|
/// <returns>The created lazy list.</returns>
|
|
/// <example><![CDATA[
|
|
/// int main()
|
|
/// {
|
|
/// vint xs[] = {1, 2, 3, 4, 5};
|
|
/// auto ys = From(xs).Reverse();
|
|
/// for (auto y : ys) Console::Write(itow(y) + L" ");
|
|
/// }
|
|
/// ]]></example>
|
|
LazyList<T> Reverse()const
|
|
{
|
|
return new ReverseEnumerator<T>(*this);
|
|
}
|
|
|
|
//-------------------------------------------------------
|
|
|
|
/// <summary>Create a new lazy list of pairs from elements from two containers.</summary>
|
|
/// <typeparam name="U">Type of all elements in the second container.</typeparam>
|
|
/// <returns>
|
|
/// 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.
|
|
/// </returns>
|
|
/// <param name="remains">The second container.</param>
|
|
/// <example><![CDATA[
|
|
/// int main()
|
|
/// {
|
|
/// vint xs[] = {1, 2, 3, 4, 5, 6, 7};
|
|
/// vint ys[] = {60, 70, 80, 90, 100};
|
|
/// auto zs = From(xs).Pairwise(From(ys)).Select([](Pair<vint, vint> p){ return p.key + p.value; });
|
|
/// for (auto z : zs) Console::Write(itow(z) + L" ");
|
|
/// }
|
|
/// ]]></example>
|
|
template<typename U>
|
|
LazyList<Pair<T, U>> Pairwise(const IEnumerable<U>& remains)const
|
|
{
|
|
return new PairwiseEnumerator<T, U>(xs(), remains.CreateEnumerator());
|
|
}
|
|
|
|
/// <summary>Create a new lazy list with elements from the lazy list, which also appear in the second container.</summary>
|
|
/// <returns>The created lazy list. Elements in the create lazy list is in the same order as in this lazy list.</returns>
|
|
/// <param name="remains">The second container.</param>
|
|
/// <example><![CDATA[
|
|
/// int main()
|
|
/// {
|
|
/// vint xs[] = {1, 2, 3, 4, 5};
|
|
/// vint ys[] = {3, 4, 5, 6, 7};
|
|
/// auto zs = From(xs).Intersect(From(ys));
|
|
/// for (auto z : zs) Console::Write(itow(z) + L" ");
|
|
/// }
|
|
/// ]]></example>
|
|
LazyList<T> Intersect(const IEnumerable<T>& remains)const
|
|
{
|
|
return LazyList<T>(new IntersectExceptEnumerator<T, true>(xs(), remains)).Distinct();
|
|
}
|
|
|
|
/// <summary>Create a new lazy list with elements from the lazy list, which do not appear in the second container.</summary>
|
|
/// <returns>The created lazy list. Elements in the create lazy list is in the same order as in this lazy list.</returns>
|
|
/// <param name="remains">The second container.</param>
|
|
/// <example><![CDATA[
|
|
/// int main()
|
|
/// {
|
|
/// vint xs[] = {1, 2, 3, 4, 5};
|
|
/// vint ys[] = {3, 4, 5, 6, 7};
|
|
/// auto zs = From(xs).Except(From(ys));
|
|
/// for (auto z : zs) Console::Write(itow(z) + L" ");
|
|
/// }
|
|
/// ]]></example>
|
|
LazyList<T> Except(const IEnumerable<T>& remains)const
|
|
{
|
|
return LazyList<T>(new IntersectExceptEnumerator<T, false>(xs(), remains)).Distinct();
|
|
}
|
|
|
|
/// <summary>Create a new lazy list with elements in two containers. Duplicated elements will be removed.</summary>
|
|
/// <returns>The created lazy list.</returns>
|
|
/// <param name="remains">The second container.</param>
|
|
/// <example><![CDATA[
|
|
/// int main()
|
|
/// {
|
|
/// vint xs[] = {1, 2, 3, 4, 5};
|
|
/// vint ys[] = {3, 4, 5, 6, 7};
|
|
/// auto zs = From(xs).Union(From(ys));
|
|
/// for (auto z : zs) Console::Write(itow(z) + L" ");
|
|
/// }
|
|
/// ]]></example>
|
|
LazyList<T> Union(const IEnumerable<T>& remains)const
|
|
{
|
|
return Concat(remains).Distinct();
|
|
}
|
|
|
|
//-------------------------------------------------------
|
|
|
|
/// <summary>Get an evaluated copy of this lazy list.</summary>
|
|
/// <returns>
|
|
/// 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.
|
|
/// </returns>
|
|
/// <param name="forceCopy">Set to true to force copying values, regardless of whether this lazy list is evaluated or not.</param>
|
|
/// <remarks>
|
|
/// <p>"Evaluated" means reading from this lazy list cause no extra calculation.</p>
|
|
/// <p>In most of the cases, the created lazy list relies on its source.</p>
|
|
/// <p>For example, a lazy list can be created from a reference to a <see cref="List`*"/>, or from an array on stack.</p>
|
|
/// <p>If this list or array is deleted, then iterating the created lazy list will crash.</p>
|
|
/// <p>By calling the Evaluate function <b>with forceCopy set to true</b>, a new lazy list is created, with all values cached in it.</p>
|
|
/// <p>Its connection to the source list or array is removed, and can then be passed to everywhere.</p>
|
|
/// </remarks>
|
|
LazyList<T> Evaluate(bool forceCopy = false)const
|
|
{
|
|
if (!forceCopy && enumeratorPrototype->Evaluated())
|
|
{
|
|
return *this;
|
|
}
|
|
else
|
|
{
|
|
Ptr<List<T>> xs = new List<T>;
|
|
CopyFrom(*xs.Obj(), *this);
|
|
return xs;
|
|
}
|
|
}
|
|
|
|
/// <summary>Create a new lazy list, whose elements are from transformed elements in this lazy list.</summary>
|
|
/// <typeparam name="F">Type of the transformer.</typeparam>
|
|
/// <returns>The created lazy list.</returns>
|
|
/// <param name="f">
|
|
/// The transformer.
|
|
/// The first argument is any element in this lazy list.
|
|
/// Returns the transformed lazy list from this argument.
|
|
/// </param>
|
|
/// <example><![CDATA[
|
|
/// int main()
|
|
/// {
|
|
/// vint xs[] = {1, 2, 3, 4, 5};
|
|
/// auto ys = From(xs).SelectMany([](vint x)
|
|
/// {
|
|
/// vint factors[] = {1, 10, 100};
|
|
/// return From(factors).Select([=](vint f){ return f * x; }).Evaluate(true);
|
|
/// });
|
|
/// for (auto y : ys) Console::Write(itow(y) + L" ");
|
|
/// }
|
|
/// ]]></example>
|
|
template<typename F>
|
|
auto SelectMany(F f)const -> LazyList<typename decltype(f(std::declval<TInput>()))::ElementType>
|
|
{
|
|
using U = typename decltype(f(std::declval<TInput>()))::ElementType;
|
|
return Select(f).Aggregate(LazyList<U>(), [](const LazyList<U>& a, const IEnumerable<U>& b)->LazyList<U> {return a.Concat(b); });
|
|
}
|
|
|
|
/// <summary>Create a new lazy list, with elements from this lazy list grouped by a key function.</summary>
|
|
/// <typeparam name="F">Type of the key function.</typeparam>
|
|
/// <returns>The created lazy list.</returns>
|
|
/// <param name="f">
|
|
/// 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.
|
|
/// </param>
|
|
/// <example><![CDATA[
|
|
/// int main()
|
|
/// {
|
|
/// vint xs[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
|
|
/// auto ys = From(xs).GroupBy([](vint x){ return x % 3; });
|
|
/// using TY = Pair<vint, LazyList<vint>>;
|
|
/// for (auto y : ys)
|
|
/// {
|
|
/// Console::Write(itow(y.key) + L":");
|
|
/// for (auto z : y.value) Console::Write(L" " + itow(z));
|
|
/// Console::WriteLine(L"");
|
|
/// }
|
|
/// }
|
|
/// ]]></example>
|
|
template<typename F>
|
|
auto GroupBy(F f)const -> LazyList<Pair<decltype(f(std::declval<TInput>())), LazyList<T>>>
|
|
{
|
|
using K = decltype(f(std::declval<TInput>()));
|
|
auto self = *this;
|
|
return Select(f)
|
|
.Distinct()
|
|
.Select([=](K k)
|
|
{
|
|
return Pair<K, LazyList<T>>(
|
|
k,
|
|
self.Where([=](T t) {return k == f(t); })
|
|
);
|
|
});
|
|
}
|
|
};
|
|
|
|
/// <summary>Create a lazy list with a series of increasing number.</summary>
|
|
/// <typeparam name="T">Type of elements.</typeparam>
|
|
/// <returns>A lazy list of increasing numbers.</returns>
|
|
/// <param name="start">The first number.</param>
|
|
/// <param name="count">Total amount of increasing numbers.</param>
|
|
template<typename T>
|
|
LazyList<T> Range(T start, T count)
|
|
{
|
|
return new RangeEnumerator<T>(start, count);
|
|
}
|
|
|
|
/// <summary>Create a lazy list from an enumerable.</summary>
|
|
/// <typeparam name="T">Type of elements.</typeparam>
|
|
/// <returns>The created lazy list.</returns>
|
|
/// <param name="enumerable">The enumerable.</param>
|
|
template<typename T>
|
|
LazyList<T> From(const IEnumerable<T>& enumerable)
|
|
{
|
|
return enumerable;
|
|
}
|
|
|
|
/// <summary>Create a lazy list from another lazy list.</summary>
|
|
/// <typeparam name="T">Type of elements.</typeparam>
|
|
/// <returns>The created lazy list.</returns>
|
|
/// <param name="enumerable">The lazy list to copy.</param>
|
|
template<typename T>
|
|
LazyList<T> From(const LazyList<T>& enumerable)
|
|
{
|
|
return enumerable;
|
|
}
|
|
|
|
/// <summary>Create a lazy list from an array.</summary>
|
|
/// <typeparam name="T">Type of elements.</typeparam>
|
|
/// <returns>The created lazy list.</returns>
|
|
/// <param name="begin">Pointer to the first element in the array.</param>
|
|
/// <param name="end">Pointer to the element after the last element in the array.</param>
|
|
template<typename T>
|
|
LazyList<T> From(const T* begin, const T* end)
|
|
{
|
|
return FromPointer(begin, end);
|
|
}
|
|
|
|
/// <summary>Create a lazy list from an array.</summary>
|
|
/// <typeparam name="T">Type of elements.</typeparam>
|
|
/// <typeparam name="size">Size of the array.</typeparam>
|
|
/// <returns>The created lazy list.</returns>
|
|
/// <param name="items">The array.</param>
|
|
template<typename T, int size>
|
|
LazyList<T> From(T (&items)[size])
|
|
{
|
|
return FromArray(items);
|
|
}
|
|
|
|
/// <summary>Create a lazy list from an array.</summary>
|
|
/// <typeparam name="T">Type of elements.</typeparam>
|
|
/// <typeparam name="size">Size of the array.</typeparam>
|
|
/// <returns>The created lazy list.</returns>
|
|
/// <param name="items">The array.</param>
|
|
template<typename T, int size>
|
|
LazyList<T> From(const T (&items)[size])
|
|
{
|
|
return FromArray(items);
|
|
}
|
|
|
|
/***********************************************************************
|
|
Range-Based For-Loop Iterator with Index for LazyList<T>
|
|
***********************************************************************/
|
|
|
|
template<typename T>
|
|
struct LazyListWithIndex
|
|
{
|
|
LazyList<T> lazyList;
|
|
|
|
LazyListWithIndex(const LazyList<T>& _lazyList)
|
|
: lazyList(_lazyList)
|
|
{
|
|
}
|
|
};
|
|
|
|
template<typename T>
|
|
LazyListWithIndex<T> indexed(const LazyList<T>& lazyList)
|
|
{
|
|
return { lazyList };
|
|
}
|
|
|
|
template<typename T>
|
|
RangeBasedForLoopIteratorWithIndex<T> begin(const LazyListWithIndex<T>& enumerable)
|
|
{
|
|
return { enumerable.lazyList };
|
|
}
|
|
|
|
template<typename T>
|
|
RangeBasedForLoopEnding end(const LazyListWithIndex<T>& 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
|
|
{
|
|
/// <summary>Basic I/O for command-line applications.</summary>
|
|
class Console abstract
|
|
{
|
|
public:
|
|
/// <summary>Write a string to the command-line window.</summary>
|
|
/// <param name="string">Content to write.</param>
|
|
/// <param name="length">Size of the content in wchar_t, not including the zero terminator.</param>
|
|
static void Write(const wchar_t* string, vint length);
|
|
|
|
/// <summary>Write a string to the command-line window.</summary>
|
|
/// <param name="string">Content to write, must be zero terminated.</param>
|
|
static void Write(const wchar_t* string);
|
|
|
|
/// <summary>Write a string to the command-line window.</summary>
|
|
/// <param name="string">Content to write.</param>
|
|
static void Write(const WString& string);
|
|
|
|
/// <summary>Write to the command-line window, following CR/LF characters.</summary>
|
|
/// <param name="string">Content to write.</param>
|
|
static void WriteLine(const WString& string);
|
|
|
|
/// <summary>Read a string from the command-line window.</summary>
|
|
/// <returns>The whole line read from the command-line window.</returns>
|
|
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
|
|
{
|
|
/// <summary>Base type of all exceptions.</summary>
|
|
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
|
|
{
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// All values are shared like global variables, but they are created at the first time when they need to be accessed.
|
|
/// <see cref="FinalizeGlobalStorage"/> is recommended after you don't need any global storages any more, it frees memory.
|
|
/// </remarks>
|
|
/// <example><![CDATA[
|
|
/// BEGIN_GLOBAL_STORAGE_CLASS(MyStorage)
|
|
/// Ptr<vint> 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();
|
|
/// }
|
|
/// ]]></example>
|
|
class GlobalStorage : public Object
|
|
{
|
|
private:
|
|
bool cleared = false;
|
|
public:
|
|
NOT_COPYABLE(GlobalStorage);
|
|
GlobalStorage(const wchar_t* key);
|
|
~GlobalStorage();
|
|
|
|
bool Cleared();
|
|
virtual void ClearResource() = 0;
|
|
};
|
|
|
|
extern GlobalStorage* GetGlobalStorage(const wchar_t* key);
|
|
extern GlobalStorage* GetGlobalStorage(const WString& key);
|
|
|
|
extern void InitializeGlobalStorage();
|
|
/// <summary>Free all memories used by global storages.</summary>
|
|
extern void FinalizeGlobalStorage();
|
|
}
|
|
|
|
#define BEGIN_GLOBAL_STORAGE_CLASS(NAME) \
|
|
class NAME : public vl::GlobalStorage \
|
|
{ \
|
|
public: \
|
|
NAME() \
|
|
:vl::GlobalStorage(L ## #NAME) \
|
|
{ \
|
|
InitializeClearResource(); \
|
|
} \
|
|
~NAME() \
|
|
{ \
|
|
if(!Cleared())ClearResource(); \
|
|
} \
|
|
|
|
#define INITIALIZE_GLOBAL_STORAGE_CLASS \
|
|
void InitializeClearResource() \
|
|
{ \
|
|
|
|
#define FINALIZE_GLOBAL_STORAGE_CLASS \
|
|
} \
|
|
void ClearResource() \
|
|
{ \
|
|
|
|
#define END_GLOBAL_STORAGE_CLASS(NAME) \
|
|
} \
|
|
}; \
|
|
NAME& Get##NAME() \
|
|
{ \
|
|
static NAME __global_storage_##NAME; \
|
|
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
|
|
{
|
|
namespace encoding
|
|
{
|
|
|
|
/***********************************************************************
|
|
UtfConversion<T>
|
|
***********************************************************************/
|
|
|
|
template<typename T>
|
|
struct UtfConversion;
|
|
|
|
template<>
|
|
struct UtfConversion<wchar_t>
|
|
{
|
|
#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<char8_t>
|
|
{
|
|
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<char16_t>
|
|
{
|
|
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);
|
|
};
|
|
|
|
/***********************************************************************
|
|
Utfto32ReaderBase<T> and UtfFrom32ReaerBase<T>
|
|
***********************************************************************/
|
|
|
|
struct UtfCharCluster
|
|
{
|
|
vint index;
|
|
vint size;
|
|
};
|
|
|
|
template<typename T, typename TBase>
|
|
class UtfFrom32ReaderBase : public Object
|
|
{
|
|
static const vint BufferLength = UtfConversion<T>::BufferLength;
|
|
vint read = 0;
|
|
vint available = 0;
|
|
T buffer[BufferLength];
|
|
|
|
UtfCharCluster sourceCluster = { 0,0 };
|
|
vint readCounter = -1;
|
|
bool error = false;
|
|
public:
|
|
T Read()
|
|
{
|
|
if (available == -1) return 0;
|
|
if (read == available)
|
|
{
|
|
char32_t c = static_cast<TBase*>(this)->Consume();
|
|
if (c)
|
|
{
|
|
available = UtfConversion<T>::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;
|
|
}
|
|
|
|
bool HasIllegalChar() const
|
|
{
|
|
return error;
|
|
}
|
|
};
|
|
|
|
template<typename T, typename TBase>
|
|
class UtfTo32ReaderBase : public Object
|
|
{
|
|
static const vint BufferLength = UtfConversion<T>::BufferLength;
|
|
vint available = 0;
|
|
T buffer[BufferLength];
|
|
|
|
UtfCharCluster sourceCluster = { 0,0 };
|
|
vint readCounter = -1;
|
|
bool error = false;
|
|
public:
|
|
char32_t Read()
|
|
{
|
|
if (available == -1) return 0;
|
|
while (available < BufferLength)
|
|
{
|
|
T c = static_cast<TBase*>(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<T>::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;
|
|
}
|
|
|
|
bool HasIllegalChar() const
|
|
{
|
|
return error;
|
|
}
|
|
};
|
|
|
|
/***********************************************************************
|
|
UtfStringTo32Reader<T> and UtfStringFrom32Reader<T>
|
|
***********************************************************************/
|
|
|
|
template<typename T, typename TBase>
|
|
class UtfStringConsumer : public TBase
|
|
{
|
|
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<typename T>
|
|
class UtfStringFrom32Reader : public UtfStringConsumer<char32_t, UtfFrom32ReaderBase<T, UtfStringFrom32Reader<T>>>
|
|
{
|
|
template<typename T2, typename TBase>
|
|
friend class UtfFrom32ReaderBase;
|
|
public:
|
|
UtfStringFrom32Reader(const char32_t* _starting)
|
|
: UtfStringConsumer<char32_t, UtfFrom32ReaderBase<T, UtfStringFrom32Reader<T>>>(_starting)
|
|
{
|
|
}
|
|
};
|
|
|
|
template<typename T>
|
|
class UtfStringTo32Reader : public UtfStringConsumer<T, UtfTo32ReaderBase<T, UtfStringTo32Reader<T>>>
|
|
{
|
|
template<typename T2, typename TBase>
|
|
friend class UtfTo32ReaderBase;
|
|
public:
|
|
UtfStringTo32Reader(const T* _starting)
|
|
: UtfStringConsumer<T, UtfTo32ReaderBase<T, UtfStringTo32Reader<T>>>(_starting)
|
|
{
|
|
}
|
|
};
|
|
|
|
template<typename TFrom, typename TTo>
|
|
class UtfStringToStringReader : public UtfFrom32ReaderBase<TTo, UtfStringToStringReader<TFrom, TTo>>
|
|
{
|
|
template<typename T, typename TBase>
|
|
friend class UtfFrom32ReaderBase;
|
|
protected:
|
|
UtfStringTo32Reader<TFrom> internalReader;
|
|
|
|
char32_t Consume()
|
|
{
|
|
return internalReader.Read();
|
|
}
|
|
public:
|
|
UtfStringToStringReader(const TFrom* _starting)
|
|
: internalReader(_starting)
|
|
{
|
|
}
|
|
|
|
UtfCharCluster SourceCluster() const
|
|
{
|
|
return internalReader.SourceCluster();
|
|
}
|
|
|
|
bool HasIllegalChar() const
|
|
{
|
|
return UtfFrom32ReaderBase<TTo, UtfStringToStringReader<TFrom, TTo>>::HasIllegalChar() || internalReader.HasIllegalChar();
|
|
}
|
|
};
|
|
}
|
|
|
|
/***********************************************************************
|
|
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<typename T>
|
|
vint _utftou32(const T* s, char32_t* d, vint chars);
|
|
template<typename T>
|
|
vint _u32toutf(const char32_t* s, T* d, vint chars);
|
|
|
|
extern template vint _utftou32<wchar_t>(const wchar_t* s, char32_t* d, vint chars);
|
|
extern template vint _utftou32<char8_t>(const char8_t* s, char32_t* d, vint chars);
|
|
extern template vint _utftou32<char16_t>(const char16_t* s, char32_t* d, vint chars);
|
|
extern template vint _u32toutf<wchar_t>(const char32_t* s, wchar_t* d, vint chars);
|
|
extern template vint _u32toutf<char8_t>(const char32_t* s, char8_t* d, vint chars);
|
|
extern template vint _u32toutf<char16_t>(const char32_t* s, char16_t* d, vint chars);
|
|
|
|
/***********************************************************************
|
|
String Conversions (direct)
|
|
***********************************************************************/
|
|
|
|
extern AString wtoa (const WString& source);
|
|
extern WString atow (const AString& source);
|
|
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);
|
|
|
|
/***********************************************************************
|
|
String Conversions (buffer walkthrough indirect)
|
|
***********************************************************************/
|
|
|
|
template<typename TFrom, typename TTo>
|
|
vint _utftoutf(const TFrom* s, TTo* d, vint chars);
|
|
|
|
extern template vint _utftoutf<wchar_t, char8_t>(const wchar_t* s, char8_t* d, vint chars);
|
|
extern template vint _utftoutf<wchar_t, char16_t>(const wchar_t* s, char16_t* d, vint chars);
|
|
extern template vint _utftoutf<char8_t, wchar_t>(const char8_t* s, wchar_t* d, vint chars);
|
|
extern template vint _utftoutf<char8_t, char16_t>(const char8_t* s, char16_t* d, vint chars);
|
|
extern template vint _utftoutf<char16_t, wchar_t>(const char16_t* s, wchar_t* d, vint chars);
|
|
extern template vint _utftoutf<char16_t, char8_t>(const char16_t* s, char8_t* d, vint chars);
|
|
|
|
/***********************************************************************
|
|
String Conversions (unicode indirect)
|
|
***********************************************************************/
|
|
|
|
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 indirect)
|
|
***********************************************************************/
|
|
|
|
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
|
|
{
|
|
/// <summary>Style of the random text.</summary>
|
|
enum class LoremIpsumCasing
|
|
{
|
|
/// <summary>First letters of all words are lower cased.</summary>
|
|
AllWordsLowerCase,
|
|
/// <summary>first letters of first words of all sentences are upper cased.</summary>
|
|
FirstWordUpperCase,
|
|
/// <summary>First letters of all words are upper cased.</summary>
|
|
AllWordsUpperCase,
|
|
};
|
|
|
|
/// <summary>Get some random text.</summary>
|
|
/// <returns>The generated random text. It may not exactly in the expected size.</returns>
|
|
/// <param name="bestLength">The expected size.</param>
|
|
/// <param name="casing">The expected casing.</param>
|
|
extern WString LoremIpsum(vint bestLength, LoremIpsumCasing casing);
|
|
/// <summary>Get some random text for a title, first letters of all words are upper cased.</summary>
|
|
/// <returns>The generated random text. It may not be exactly in the expected size.</returns>
|
|
/// <param name="bestLength">The expected size.</param>
|
|
extern WString LoremIpsumTitle(vint bestLength);
|
|
/// <summary>Get some random sentences. The first letter of the first word is uppder cased.</summary>
|
|
/// <returns>The generated random text with a period character ".". It may not be exactly in the expected size.</returns>
|
|
/// <param name="bestLength">The expected size.</param>
|
|
extern WString LoremIpsumSentence(vint bestLength);
|
|
/// <summary>Get some random paragraphs. First letters of first words of all sentences are upper cased.</summary>
|
|
/// <returns>The generated random text with multiple sentences ending with period characters ".". It may not be exactly in the expected size.</returns>
|
|
/// <param name="bestLength">The expected size.</param>
|
|
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
|
|
{
|
|
namespace unittest
|
|
{
|
|
using UnitTestFileProc = void(*)();
|
|
|
|
/// <summary>
|
|
/// <p>Unit test framework.</p>
|
|
/// <p>
|
|
/// Test cases could be defined in multiple cpp files. In each cpp file, there can be one <b>TEST_FILE</b> call.
|
|
/// <program><code><![CDATA[
|
|
/// TEST_FILE
|
|
/// {
|
|
/// // here could be multiple TEST_CATEGORY and TEST_CASE
|
|
/// }
|
|
/// ]]></code></program>
|
|
/// </p>
|
|
/// <p>
|
|
/// Both <b>TEST_CATEGORY</b> could be used inside <b>TEST_FILE</b>, or nested inside another <b>TEST_CATEGORY</b>.
|
|
/// <b>TEST_CASE</b> could be used inside <b>TEST_FILE</b> or <b>TEST_CATEGORY</b>.
|
|
/// </p>
|
|
/// <p>
|
|
/// <b>TEST_ASSERT</b> is used to verify a boolean expression.
|
|
/// It could only be used in <b>TEST_CASE</b>.
|
|
/// <b>TEST_ASSERT</b> could not be used in <b>TEST_FILE</b> or <b>TEST_CATEGORY</b>.
|
|
/// </p>
|
|
/// <p>
|
|
/// When the test program is started in debug mode (Windows only), or by command line options "/D",
|
|
/// A <b>TEST_ASSERT</b> failure will trigger a break point, it could be catched by any debugger.
|
|
/// </p>
|
|
/// <p>
|
|
/// When the test program is started in release mode, or by command line options "/R", or without command line options,
|
|
/// A <b>TEST_ASSERT</b> failure will report an error and skip rest of the current <b>TEST_CASE</b>, the execution will continue.
|
|
/// </p>
|
|
/// <p>
|
|
/// <b>TEST_ERROR</b> execute one statement, it fails when no [T:vl.Error] is thrown.
|
|
/// </p>
|
|
/// <p>
|
|
/// <b>TEST_EXCEPTION</b> 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.
|
|
/// </p>
|
|
/// <p>
|
|
/// <b>TEST_CASE_ASSERT</b> is an abbreviation of <b>TEST_CASE</b> + <b>TEST_ASSERT</b>.
|
|
/// It is very common that are multiple independent assertions.
|
|
/// </p>
|
|
/// <p>
|
|
/// <b>TEST_CASE_ASSERT</b> is a test case, it can be used in <b>TEST_CATEGORY</b> or <b>TEST_FILE</b>.
|
|
/// In release mode, by failing this assertion, the execution does not stop.
|
|
/// </p>
|
|
/// <p>
|
|
/// <b>TEST_CATEGORY</b> is very useful when multiple assertions do not have dependencies.
|
|
/// During the execution of the test program, <b>TEST_FILE</b>, <b>TEST_CATEGORY</b>, <b>TEST_CASE</b> and failed <b>TEST_ASSERT</b> will be rendered with indentation and different colors.
|
|
/// </p>
|
|
/// <p>
|
|
/// <see cref="UnitTest::RunAndDisposeTests"/> is needed in the main function to execute test cases.
|
|
/// <b>TEST_PRINT</b> could be used during test cases to print debug information to a command-line application.
|
|
/// </p>
|
|
/// </summary>
|
|
/// <example><![CDATA[
|
|
/// TEST_FILE
|
|
/// {
|
|
/// TEST_CATEGORY(L"This is a test category")
|
|
/// {
|
|
/// TEST_CASE(L"This is a test case")
|
|
/// {
|
|
/// TEST_ASSERT(true);
|
|
/// TEST_ERROR({WString::Empty[0];});
|
|
/// TEST_EXCEPTION({throw Exception();}, Exception, [](const Exception&){});
|
|
/// });
|
|
/// TEST_CASE_ASSERT(true);
|
|
/// });
|
|
///
|
|
/// TEST_CATEGORY(L"This is another test category")
|
|
/// {
|
|
/// TEST_PRINT(L"some information");
|
|
/// TEST_CASE_ASSERT(true);
|
|
/// });
|
|
/// }
|
|
///
|
|
/// int main(int argc, wchar_t* argv[])
|
|
/// {
|
|
/// // in Linux or macOS, argv must be char*[]
|
|
/// return unittest::UnitTest::RunAndDisposeTests(argc, argv);
|
|
/// }
|
|
/// ]]></example>
|
|
class UnitTest
|
|
{
|
|
protected:
|
|
static bool IsDebuggerAttached();
|
|
static int PrintUsages();
|
|
static int RunAndDisposeTests(Nullable<WString> option);
|
|
|
|
public:
|
|
UnitTest() = delete;
|
|
|
|
enum class MessageKind
|
|
{
|
|
Info,
|
|
Error,
|
|
File,
|
|
Category,
|
|
Case,
|
|
};
|
|
|
|
static void PrintMessage(const WString& string, MessageKind kind);
|
|
|
|
/// <summary>Run all test cases.</summary>
|
|
/// <returns>The return value for the main function. If any assertion fails, it is non-zero.</returns>
|
|
/// <param name="argc">Accept the first argument of the main function.</param>
|
|
/// <param name="argv">Accept the second argument of the main function.</param>
|
|
static int RunAndDisposeTests(int argc, wchar_t* argv[]);
|
|
|
|
/// <summary>Run all test cases.</summary>
|
|
/// <returns>The return value for the main function. If any assertion fails, it is non-zero.</returns>
|
|
/// <param name="argc">Accept the first argument of the main function.</param>
|
|
/// <param name="argv">Accept the second argument of the main function.</param>
|
|
static int RunAndDisposeTests(int argc, char* argv[]);
|
|
|
|
static void RegisterTestFile(const char* fileName, UnitTestFileProc testProc);
|
|
static void RunCategoryOrCase(const WString& description, bool isCategory, Func<void()>&& callback);
|
|
static void EnsureLegalToAssert();
|
|
};
|
|
|
|
class UnitTestFile
|
|
{
|
|
public:
|
|
UnitTestFile(const char* fileName, UnitTestFileProc testProc)
|
|
{
|
|
UnitTest::RegisterTestFile(fileName, testProc);
|
|
}
|
|
};
|
|
|
|
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) {}
|
|
};
|
|
|
|
#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
|
|
|