文件结构重构
This commit is contained in:
@@ -0,0 +1,157 @@
|
||||
//
|
||||
// Created by EmsiaetKadosh on 25-1-17.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "..\def.h"
|
||||
|
||||
[[nodiscard]] inline unsigned int wtouib16(const wchar* string) noexcept {
|
||||
unsigned int ret = 0;
|
||||
while (*string != L'\0') {
|
||||
if (ret & 0xf0000000) return 0xffffffff;
|
||||
if (*string >= L'0' && *string <= L'9') {
|
||||
ret <<= 4;
|
||||
ret += *string - L'0';
|
||||
} else if (*string >= L'A' && *string <= L'F') {
|
||||
ret <<= 4;
|
||||
ret += *string - 0x41; // 'A' - 10
|
||||
} else if (*string >= L'a' && *string <= L'f') {
|
||||
ret <<= 4;
|
||||
ret += *string - 0x61; // 'a' - 10
|
||||
} else return 0xffffffff;
|
||||
++string;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline unsigned int wtouib16(const wchar* const string, const unsigned long long length) noexcept {
|
||||
unsigned int ret = 0;
|
||||
unsigned long long i = 0;
|
||||
while (i < length) {
|
||||
if (ret & 0xf0000000) return 0xffffffff;
|
||||
if (string[i] >= L'0' && string[i] <= L'9') {
|
||||
ret <<= 4;
|
||||
ret += string[i] - L'0';
|
||||
} else if (string[i] >= L'A' && string[i] <= L'F') {
|
||||
ret <<= 4;
|
||||
ret += string[i] - 55; // L'A' - 10
|
||||
} else if (string[i] >= L'a' && string[i] <= L'f') {
|
||||
ret <<= 4;
|
||||
ret += string[i] - 87; // L'a' - 10
|
||||
} else return 0xffffffff;
|
||||
++i;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static constexpr wchar Table16[17] = L"0123456789ABCDEF";
|
||||
|
||||
/**
|
||||
* 将数字转换为字符串
|
||||
* @param value 要转换的数字
|
||||
* @param fills 填充位数。返回的字符串长度一定不小于该值
|
||||
* @return String类型
|
||||
*/
|
||||
[[nodiscard]] inline String uitowb16(unsigned int value, const unsigned int fills = 1) noexcept {
|
||||
String ret;
|
||||
if (fills >= 8 || value < static_cast<unsigned int>(1) << fills * 4) {
|
||||
ret.assign(fills, L'0');
|
||||
for (unsigned int i = fills - 1; i && value; --i) {
|
||||
ret[i] = Table16[value & 0xf];
|
||||
value >>= 4;
|
||||
}
|
||||
} else {
|
||||
unsigned int i = 0;
|
||||
while (i < 8) {
|
||||
if (value >> i & 0xf) break;
|
||||
++i;
|
||||
}
|
||||
while (i < 8) {
|
||||
ret.push_back(Table16[value >> i & 0xf]);
|
||||
++i;
|
||||
}
|
||||
if (ret.empty()) ret = L"0";
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将数字转换为字符串
|
||||
* @param value 要转换的数字
|
||||
* @param fills 填充位数。返回的字符串长度一定不小于该值
|
||||
* @return String类型
|
||||
*/
|
||||
[[nodiscard]] inline String qwtowb16(QWORD value, const unsigned int fills = 1) noexcept {
|
||||
String ret;
|
||||
if (fills >= 16 || value < static_cast<QWORD>(1) << fills * 4) {
|
||||
ret.assign(fills, L'0');
|
||||
for (QWORD i = fills - 1; i && value; --i) {
|
||||
ret[i] = Table16[value & 0xf];
|
||||
value >>= 4;
|
||||
}
|
||||
} else {
|
||||
QWORD i = 0;
|
||||
while (i < 16) {
|
||||
if (value >> i & 0xf) break;
|
||||
++i;
|
||||
}
|
||||
while (i < 16) {
|
||||
ret.push_back(Table16[value >> i & 0xf]);
|
||||
++i;
|
||||
}
|
||||
if (ret.empty()) ret = L"0";
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline String qwtowb10(QWORD value, const unsigned int fills = 1) noexcept {
|
||||
static constexpr wchar Table10[11] = L"0123456789";
|
||||
static constexpr QWORD Compare10[20] = {
|
||||
0, 10, 100, 1000, 10000, 100000, 1000000, 10000000,
|
||||
100000000, 1000000000, 10000000000, 100000000000,
|
||||
1000000000000, 10000000000000, 100000000000000,
|
||||
1000000000000000, 10000000000000000, 100000000000000000,
|
||||
1000000000000000000, 10000000000000000000
|
||||
};
|
||||
String ret;
|
||||
if (value < Compare10[fills]) {
|
||||
ret.assign(fills, L'0');
|
||||
for (unsigned int i = fills - 1; i != 0 && value; --i) {
|
||||
ret[i] = Table10[value % 10];
|
||||
value /= 10;
|
||||
}
|
||||
} else {
|
||||
unsigned int i = 0;
|
||||
while (i < 19) {
|
||||
if (value >= Compare10[i]) break;
|
||||
++i;
|
||||
}
|
||||
while (i < 19) {
|
||||
ret.push_back(Table10[value / Compare10[i]]);
|
||||
value %= Compare10[i];
|
||||
++i;
|
||||
}
|
||||
if (ret.empty()) ret = L"0";
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
[[nodiscard]] inline String atow(const char* chars) {
|
||||
const int c = MultiByteToWideChar(CP_ACP, 0, chars, -1, nullptr, 0);
|
||||
wchar* e = new wchar[c];
|
||||
MultiByteToWideChar(CP_ACP, 0, chars, -1, e, c);
|
||||
String ret(e);
|
||||
delete[] e;
|
||||
return ret;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline std::string wtoa(const wchar* wchars) {
|
||||
const int c = WideCharToMultiByte(CP_ACP, 0, wchars, -1, nullptr, 0, nullptr, nullptr);
|
||||
char* e = new char[c];
|
||||
WideCharToMultiByte(CP_ACP, 0, wchars, -1, e, c, nullptr, nullptr);
|
||||
std::string ret = e;
|
||||
delete[] e;
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
|
||||
#include "..\def.h"
|
||||
|
||||
#include "exception.h"
|
||||
#include "File.h"
|
||||
|
||||
File FileAccessor::getAccess(const String& path) {
|
||||
namespace fs = std::filesystem;
|
||||
using Path = fs::path;
|
||||
Path original = path;
|
||||
Path p = original.parent_path();
|
||||
if (!fs::exists(p)) {
|
||||
create_directories(p);
|
||||
Logger.info(L"Created directory " + p.wstring());
|
||||
}
|
||||
return File(p);
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
//
|
||||
// Created by EmsiaetKadosh on 25-3-7.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "..\def.h"
|
||||
|
||||
class File final {
|
||||
public:
|
||||
String path{};
|
||||
std::wfstream file{};
|
||||
int flags = std::ios::in | std::ios::out | std::ios::binary;
|
||||
|
||||
File(const String& path) : path(path) {}
|
||||
File(const File&) = delete;
|
||||
File(File&&) = delete;
|
||||
~File() = default;
|
||||
File& operator=(const File&) = delete;
|
||||
File& operator=(File&&) = delete;
|
||||
|
||||
File& open() {
|
||||
if (file.is_open()) file.close();
|
||||
file.open(path, flags);
|
||||
return *this;
|
||||
}
|
||||
|
||||
File& close() {
|
||||
if (file.is_open()) file.close();
|
||||
return *this;
|
||||
}
|
||||
|
||||
File& clearContent() {
|
||||
file.clear();
|
||||
if (file.is_open()) file.close();
|
||||
file.open(path, flags | std::ios::trunc);
|
||||
return *this;
|
||||
}
|
||||
|
||||
File& inputs(const bool value = true) {
|
||||
if (value) flags |= std::ios::in;
|
||||
else flags &= ~std::ios::in;
|
||||
return *this;
|
||||
}
|
||||
|
||||
File& outputs(const bool value = true) {
|
||||
if (value) flags |= std::ios::out;
|
||||
else flags &= ~std::ios::out;
|
||||
return *this;
|
||||
}
|
||||
|
||||
File& binary(const bool value = true) {
|
||||
if (value) flags |= std::ios::binary;
|
||||
else flags &= ~std::ios::binary;
|
||||
return *this;
|
||||
}
|
||||
|
||||
File& append(const bool value = true) {
|
||||
if (value) flags |= std::ios::app;
|
||||
else flags &= ~std::ios::app;
|
||||
return *this;
|
||||
}
|
||||
|
||||
File& truncate(const bool value = true) {
|
||||
if (value) flags |= std::ios::trunc;
|
||||
else flags &= ~std::ios::trunc;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
File& operator<<(T&& value) {
|
||||
if (file.is_open()) file << std::forward<T>(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
File& operator<<(decltype(std::endl<wchar, std::char_traits<wchar>>)& value) {
|
||||
if (file.is_open()) file << value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
File& operator>>(T&& value) {
|
||||
if (file.is_open()) file >> std::forward<T>(value);
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
class Data {
|
||||
public:
|
||||
enum class DataType : unsigned char {
|
||||
Integer, Double, String, Boolean, Null, List, Object
|
||||
};
|
||||
|
||||
private:
|
||||
String name;
|
||||
String value;
|
||||
DataType type;
|
||||
|
||||
public:
|
||||
Data(String&& name, String&& value, const DataType type): name(std::move(name)), value(std::move(value)), type(type) {}
|
||||
};
|
||||
|
||||
class DataLoader {
|
||||
File file;
|
||||
|
||||
int parseString(int& line, String& string) {
|
||||
while (!file.file.eof()) {
|
||||
wchar c = file.file.get();
|
||||
if (c == L'\"') Success();
|
||||
if (c == L'\\') { c = file.file.get(); }
|
||||
}
|
||||
errorInfo = L"Error: String never ends. EOF comes before a '\"'";
|
||||
Failed();
|
||||
}
|
||||
|
||||
int loadUntil(int& line, wchar at = 0) {
|
||||
String name = {};
|
||||
String val = {};
|
||||
enum : unsigned char { identifier, eq, value, end } status = identifier;
|
||||
while (!file.file.eof()) {
|
||||
const wchar c = file.file.get();
|
||||
if (c == L'\n') ++line;
|
||||
switch (status) {
|
||||
case identifier:
|
||||
if (!isspace(c)) name.append(1, c);
|
||||
else if (!name.empty()) status = eq;
|
||||
break;
|
||||
case eq:
|
||||
if (c == L'=') status = value;
|
||||
else if (!isspace(c)) {
|
||||
errorInfo = L"Error: expected '=' after identifier at line " + std::to_wstring(line);
|
||||
Failed();
|
||||
}
|
||||
break;
|
||||
case value:
|
||||
if (!isspace(c)) { if (c == L'\"') {} }
|
||||
break;
|
||||
case end:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
String errorInfo = {};
|
||||
DataLoader(const String& path) : file(path) {}
|
||||
DataLoader(const DataLoader&) = delete;
|
||||
DataLoader(DataLoader&&) = delete;
|
||||
DataLoader& operator=(const DataLoader&) = delete;
|
||||
DataLoader& operator=(DataLoader&&) = delete;
|
||||
~DataLoader() = default;
|
||||
|
||||
int load() {
|
||||
int line = 1;
|
||||
return loadUntil(line);
|
||||
}
|
||||
};
|
||||
|
||||
class FileAccessor {
|
||||
public:
|
||||
[[nodiscard]] bool exists(const String& path) const { return std::filesystem::exists(path); }
|
||||
|
||||
File getAccess(const String& path);
|
||||
};
|
||||
|
||||
inline FileAccessor fileAccessor = FileAccessor();
|
||||
@@ -0,0 +1,152 @@
|
||||
//
|
||||
// Created by EmsiaetKadosh on 25-1-18.
|
||||
//
|
||||
|
||||
#include "IText.h"
|
||||
|
||||
#include "..\def.h"
|
||||
#include "..\render\Renderer.h"
|
||||
|
||||
int RenderableString::getHeight() const noexcept {
|
||||
int height = 0;
|
||||
for (const StringConfig& config : configs) if (const Font& font = fontManager.get(config.idFont); font.getHeight() > height) height = font.getHeight();
|
||||
return height;
|
||||
}
|
||||
|
||||
int RenderableString::getWidth(const FontID defaultID) const noexcept {
|
||||
int width = 0;
|
||||
for (const StringConfig& config : configs) width += fontManager.get(config.idFont ? config.idFont : defaultID).getWidth(config);
|
||||
return width;
|
||||
}
|
||||
|
||||
int RenderableString::getWidth(RenderConfig* renderConfigs, const FontID defaultID) const noexcept {
|
||||
int width = 0;
|
||||
int i = 0;
|
||||
for (const StringConfig& config : configs) {
|
||||
renderConfigs[i].config = &config;
|
||||
const Font& font = fontManager.get(config.idFont ? config.idFont : defaultID);
|
||||
renderConfigs[i].font = &font;
|
||||
width += renderConfigs[i].width = font.getWidth(config);
|
||||
++i;
|
||||
}
|
||||
return width;
|
||||
}
|
||||
|
||||
int Font::drawSingle(const RenderableString::StringConfig& config, const int x, const int y, const unsigned int defaultColor) const {
|
||||
SelectObject(renderer.canvasDC, tryCreate(config));
|
||||
RECT rect{
|
||||
.left = x,
|
||||
.top = y + yOffsetPx,
|
||||
.right = 0,
|
||||
.bottom = 0
|
||||
};
|
||||
SetTextColor(renderer.canvasDC, config.isDefaultColor() ? defaultColor : Renderer::changeColorFormat(config.color));
|
||||
DrawTextW(renderer.canvasDC, config.text.c_str(), static_cast<int>(config.text.length()), &rect, DT_SINGLELINE | DT_NOCLIP | DT_CALCRECT);
|
||||
if (!config.isDefaultBackground()) renderer.fill(&rect, config.background);
|
||||
DrawTextW(renderer.canvasDC, config.text.c_str(), static_cast<int>(config.text.length()), &rect, DT_SINGLELINE | DT_NOCLIP);
|
||||
return rect.right;
|
||||
}
|
||||
|
||||
void Font::drawDirect(const RenderableString::StringConfig& config, const int x, const int y, const unsigned int defaultColor) const {
|
||||
SelectObject(renderer.canvasDC, tryCreate(config));
|
||||
RECT rect{
|
||||
.left = x,
|
||||
.top = y + yOffsetPx,
|
||||
.right = 0,
|
||||
.bottom = 0
|
||||
};
|
||||
SetTextColor(renderer.canvasDC, config.isDefaultColor() ? defaultColor : Renderer::changeColorFormat(config.color));
|
||||
if (!config.isDefaultBackground()) renderer.fill(&rect, config.background);
|
||||
DrawTextW(renderer.canvasDC, config.text.c_str(), static_cast<int>(config.text.length()), &rect, DT_SINGLELINE | DT_NOCLIP);
|
||||
}
|
||||
|
||||
void Font::clear() const {
|
||||
for (const auto& [_, fnt] : fonts) renderer.deleteObject(fnt);
|
||||
fonts.clear();
|
||||
}
|
||||
|
||||
void Font::draw(const RenderableString& text, int x, const int y, const unsigned int color) const {
|
||||
const COLORREF defaultColor = Renderer::changeColorFormat(color);
|
||||
for (const RenderableString::StringConfig& config : text.configs) {
|
||||
if (config.idFont) {
|
||||
x = fontManager.get(config.idFont).drawSingle(config, x, y, defaultColor);
|
||||
continue;
|
||||
}
|
||||
SelectObject(renderer.canvasDC, tryCreate(config));
|
||||
RECT rect{
|
||||
.left = x,
|
||||
.top = y + yOffsetPx,
|
||||
.right = 0,
|
||||
.bottom = 0
|
||||
};
|
||||
SetTextColor(renderer.canvasDC, config.isDefaultColor() ? defaultColor : Renderer::changeColorFormat(config.color));
|
||||
DrawTextW(renderer.canvasDC, config.text.c_str(), static_cast<int>(config.text.length()), &rect, DT_SINGLELINE | DT_NOCLIP | DT_CALCRECT);
|
||||
if (!config.isDefaultBackground()) renderer.fill(&rect, config.background);
|
||||
x = rect.right;
|
||||
DrawTextW(renderer.canvasDC, config.text.c_str(), static_cast<int>(config.text.length()), &rect, DT_SINGLELINE | DT_NOCLIP);
|
||||
}
|
||||
}
|
||||
|
||||
void Font::drawCenter(const RenderableString& text, int x, int y, const int w, const int h, const unsigned int color) const {
|
||||
const COLORREF defaultColor = Renderer::changeColorFormat(color);
|
||||
using RenderConfig = RenderableString::RenderConfig;
|
||||
const QWORD size = text.configs.size();
|
||||
RenderConfig* configs = allocatedFor(new RenderConfig[size], sizeof(RenderConfig) * size);
|
||||
const int stringWidth = text.getWidth(configs, id);
|
||||
x += w - stringWidth >> 1;
|
||||
y += h - text.getHeight() >> 1;
|
||||
for (QWORD i = 0; i < size; ++i) {
|
||||
configs[i].font->drawDirect(*configs[i].config, x, y, defaultColor);
|
||||
x += configs[i].width;
|
||||
}
|
||||
delete[] deallocating(configs);
|
||||
}
|
||||
|
||||
int Font::getWidth(const RenderableString::StringConfig& config) const {
|
||||
HFONT const font = tryCreate(config);
|
||||
RECT rect{};
|
||||
SelectObject(renderer.assistDC, font);
|
||||
DrawTextW(renderer.assistDC, config.text.c_str(), static_cast<int>(config.text.length()), &rect, DT_CALCRECT | DT_NOCLIP | DT_SINGLELINE);
|
||||
return rect.right;
|
||||
}
|
||||
|
||||
const String& TranslatableText::getText() const noexcept {
|
||||
refreshText();
|
||||
return target->getText();
|
||||
}
|
||||
|
||||
const RenderableString& TranslatableText::getRenderableString() const noexcept {
|
||||
refreshText();
|
||||
return target->getRenderableString();
|
||||
}
|
||||
|
||||
void TranslatableText::refreshText() const noexcept {
|
||||
if (target == nullptr || langConfig == translator.getConfigVersion()) {
|
||||
target = translator.getText(idSrc);
|
||||
langConfig = translator.getConfigVersion();
|
||||
}
|
||||
}
|
||||
|
||||
void languageMakeChinese(Language& lang) {
|
||||
Logger.debug(L"languageMakeChinese called");
|
||||
Map<std::wstring, TranslatedText>& map = lang.translateTable;
|
||||
map.emplace(L"hbp.title", TranslatedText(L"高血压"));
|
||||
map.emplace(L"hbp.confirm.confirm", TranslatedText(L"确认"));
|
||||
map.emplace(L"hbp.confirm.cancel", TranslatedText(L"取消"));
|
||||
map.emplace(L"hbp.caption.close", TranslatedText(L"\\#ffee0000关闭窗口"));
|
||||
map.emplace(L"hbp.caption.maximize", TranslatedText(L"\\#ff4488ee最大化窗口"));
|
||||
map.emplace(L"hbp.caption.restore", TranslatedText(L"\\#ff4488ee复原窗口"));
|
||||
map.emplace(L"hbp.caption.minimize", TranslatedText(L"\\#ffeeaaaa最小化窗口"));
|
||||
map.emplace(L"hbp.float.freshCanvas", TranslatedText(L"\\.ff4488aa\\#ff000000右键以刷新窗口绘制"));
|
||||
map.emplace(L"hbp.button.exit", TranslatedText(L"\\#ff44ee66退出"));
|
||||
map.emplace(L"hbp.float.exit", TranslatedText(L"\\#ff44ee66退出游戏"));
|
||||
map.emplace(L"hbp.confirming.exit", TranslatedText(L"是否\\#ff44ee66退出游戏\\r?"));
|
||||
map.emplace(L"hbp.button.settings", TranslatedText(L"\\#ff4488aa设置"));
|
||||
map.emplace(L"hbp.float.settings", TranslatedText(L"\\#ff4488aa设置"));
|
||||
map.emplace(L"hbp.button.start", TranslatedText(L"\\#ffee0000开始"));
|
||||
map.emplace(L"hbp.float.start", TranslatedText(L"\\#ffee0000开始游戏"));
|
||||
map.emplace(L"hbp.confirming.start", TranslatedText(L"是否\\#ffee0000退出游戏\\r?"));
|
||||
}
|
||||
|
||||
inline Translator translator = Translator();
|
||||
inline FontManager fontManager = FontManager();
|
||||
@@ -0,0 +1,598 @@
|
||||
//
|
||||
// Created by EmsiaetKadosh on 25-1-16.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
#include "..\def.h"
|
||||
#include "Chars.h"
|
||||
#include "exception.h"
|
||||
#include "..\interact\InteractManager.h"
|
||||
|
||||
class LiteralText;
|
||||
class Translator;
|
||||
// class Font;
|
||||
class FontManager;
|
||||
|
||||
using FontStyle = int;
|
||||
using FontID = unsigned short;
|
||||
|
||||
class RenderableString {
|
||||
friend class LiteralText;
|
||||
friend class Font;
|
||||
|
||||
struct StringConfig {
|
||||
String text;
|
||||
unsigned int color = -1;
|
||||
unsigned int background = -1;
|
||||
/**
|
||||
* 位意义
|
||||
* 1 - bold
|
||||
* 2 - italic
|
||||
* 4 - underline
|
||||
* 8 - strikeThrough
|
||||
* 16 - default bg
|
||||
* 32 - default clr
|
||||
*/
|
||||
FontStyle style = 0;
|
||||
FontID idFont = 0;
|
||||
|
||||
StringConfig() = default;
|
||||
|
||||
void reset() noexcept {
|
||||
idFont = 0;
|
||||
color = -1;
|
||||
background = -1;
|
||||
style = 0;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool isBold() const noexcept { return style & 1; }
|
||||
[[nodiscard]] bool isItalic() const noexcept { return style & 2; }
|
||||
[[nodiscard]] bool isUnderline() const noexcept { return style & 4; }
|
||||
[[nodiscard]] bool isStrikeThrough() const noexcept { return style & 8; }
|
||||
[[nodiscard]] bool isDefaultBackground() const noexcept { return (style & 16) == 0; }
|
||||
[[nodiscard]] bool isDefaultColor() const noexcept { return (style & 32) == 0; }
|
||||
void setBold(const bool value) noexcept { style = value ? style | 1 : style & ~1; }
|
||||
void setItalic(const bool value) noexcept { style = value ? style | 2 : style & ~2; }
|
||||
void setUnderline(const bool value) noexcept { style = value ? style | 4 : style & ~4; }
|
||||
void setStrikeThrough(const bool value) noexcept { style = value ? style | 8 : style & ~8; }
|
||||
void useDefaultBackground(const bool value) noexcept { style = value ? style & ~16 : style | 16; }
|
||||
void useDefaultColor(const bool value) noexcept { style = value ? style & ~32 : style | 32; }
|
||||
|
||||
[[nodiscard]] StringConfig copyConfig() const noexcept {
|
||||
StringConfig ret;
|
||||
ret.idFont = idFont;
|
||||
ret.color = color;
|
||||
ret.background = background;
|
||||
ret.style = style;
|
||||
return ret;
|
||||
}
|
||||
|
||||
[[nodiscard]] StringConfig copy() const noexcept {
|
||||
StringConfig ret;
|
||||
ret.idFont = idFont;
|
||||
ret.text = text;
|
||||
ret.idFont = idFont;
|
||||
ret.color = color;
|
||||
ret.background = background;
|
||||
ret.style = style;
|
||||
return ret;
|
||||
}
|
||||
|
||||
[[nodiscard]] String toString() const noexcept {
|
||||
String ret;
|
||||
ret.append(L"#");
|
||||
ret.append(isDefaultColor() ? L"~~" : uitowb16(color));
|
||||
ret.append(L".");
|
||||
ret.append(isDefaultBackground() ? L"~~" : uitowb16(background));
|
||||
ret.append(L",F");
|
||||
ret.append(std::to_wstring(idFont));
|
||||
ret.append(L",");
|
||||
if (isBold()) ret.append(L"b");
|
||||
if (isItalic()) ret.append(L"i");
|
||||
if (isUnderline()) ret.append(L"u");
|
||||
if (isStrikeThrough()) ret.append(L"s");
|
||||
ret.append(L":");
|
||||
ret.append(text);
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
struct RenderConfig {
|
||||
const Font* font;
|
||||
const StringConfig* config;
|
||||
int width;
|
||||
};
|
||||
|
||||
List<StringConfig> configs;
|
||||
using Iterator = List<StringConfig>::iterator;
|
||||
using ConstIterator = List<StringConfig>::const_iterator;
|
||||
|
||||
public:
|
||||
RenderableString(const String& string): RenderableString(string.c_str(), string.length()) {}
|
||||
RenderableString(String&& string) : RenderableString(string.c_str(), string.length()) {}
|
||||
|
||||
RenderableString(const wchar* const string, const QWORD length = static_cast<QWORD>(-1)) {
|
||||
if (length == -1) parseAppend(string);
|
||||
else parseAppend(string, length);
|
||||
}
|
||||
|
||||
RenderableString(const RenderableString&) = default;
|
||||
RenderableString(RenderableString&&) = default;
|
||||
|
||||
~RenderableString() = default;
|
||||
|
||||
[[nodiscard]] String toString() const noexcept {
|
||||
String ret;
|
||||
for (const auto& config : configs) {
|
||||
ret.append(config.toString());
|
||||
ret.append(L";\n");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
RenderableString& append(const RenderableString& other) {
|
||||
auto iterator = other.configs.cbegin();
|
||||
int flags = 0;
|
||||
bool clr = true, bg = true, font = true;
|
||||
const unsigned int color = configs.back().color;
|
||||
const unsigned int background = configs.back().background;
|
||||
const FontID idFont = configs.back().idFont;
|
||||
while (iterator != other.configs.cend()) {
|
||||
if (flags >= 3) break;
|
||||
StringConfig config = iterator->copy();
|
||||
if (font) {
|
||||
if (config.idFont == idFont || config.idFont != 0) {
|
||||
font = false;
|
||||
++flags;
|
||||
} else config.idFont = idFont;
|
||||
}
|
||||
if (clr) {
|
||||
if (config.color == color || config.color != 0xffffffff) {
|
||||
clr = false;
|
||||
++flags;
|
||||
} else config.color = color;
|
||||
}
|
||||
if (bg) {
|
||||
if (config.background == background || config.background != 0xffffffff) {
|
||||
bg = false;
|
||||
++flags;
|
||||
} else config.background = background;
|
||||
}
|
||||
configs.push_back(std::move(config));
|
||||
++iterator;
|
||||
}
|
||||
while (iterator != other.configs.cend()) configs.emplace_back(*iterator);
|
||||
return *this;
|
||||
}
|
||||
|
||||
RenderableString& append(const String& other) {
|
||||
parseAppend(other.c_str(), other.length());
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[nodiscard]] int getHeight() const noexcept;
|
||||
[[nodiscard]] int getWidth(FontID defaultID = 0) const noexcept;
|
||||
int getWidth(RenderConfig* renderConfigs, FontID defaultID) const noexcept;
|
||||
|
||||
private:
|
||||
void parseAppend(const wchar* string) noexcept {
|
||||
StringConfig config;
|
||||
if (!configs.empty()) {
|
||||
config = configs.back();
|
||||
configs.pop_back();
|
||||
}
|
||||
while (*string != L'\0') {
|
||||
const wchar* start = string;
|
||||
if (!config.text.empty()) {
|
||||
configs.push_back(std::move(config));
|
||||
config = config.copyConfig();
|
||||
}
|
||||
while (*string != L'\0' && *string != '\\') ++string;
|
||||
if (*string == L'\0') {
|
||||
if (*start != L'\0') config.text.append(start, string);
|
||||
configs.push_back(std::move(config));
|
||||
return;
|
||||
}
|
||||
if (start != string) config.text.append(start, string);
|
||||
if (*++string == L'\0') {
|
||||
config.text.append(start, string);
|
||||
configs.push_back(std::move(config));
|
||||
return;
|
||||
}
|
||||
if (!config.text.empty()) {
|
||||
configs.push_back(std::move(config));
|
||||
config = config.copyConfig();
|
||||
}
|
||||
switch (*string) {
|
||||
case L'\\':
|
||||
config.text.append(1, L'\\');
|
||||
++string;
|
||||
continue;
|
||||
case L'#': {
|
||||
unsigned long long i = 0;
|
||||
while (i < 9) if (string[i++] == L'\0') goto end;
|
||||
config.color = wtouib16(++string, 8);
|
||||
config.useDefaultColor(false);
|
||||
string += 8;
|
||||
continue;
|
||||
}
|
||||
case L'.': {
|
||||
unsigned long long i = 0;
|
||||
while (i < 9) if (string[i++] == L'\0') goto end;
|
||||
config.background = wtouib16(++string, 8);
|
||||
config.useDefaultBackground(false);
|
||||
string += 8;
|
||||
continue;
|
||||
}
|
||||
case L'F':
|
||||
case L'f':
|
||||
if (*++string == L'\0') goto end;
|
||||
config.idFont = *string;
|
||||
break;
|
||||
case L'-':
|
||||
case L's':
|
||||
case L'S':
|
||||
config.setStrikeThrough(true);
|
||||
break;
|
||||
case L'_':
|
||||
case L'u':
|
||||
case L'U':
|
||||
config.setUnderline(true);
|
||||
break;
|
||||
case L'/':
|
||||
case L'i':
|
||||
case L'I':
|
||||
config.setItalic(true);
|
||||
break;
|
||||
case L'=':
|
||||
case L'b':
|
||||
case L'B':
|
||||
config.setBold(true);
|
||||
break;
|
||||
case L'r':
|
||||
case L'R':
|
||||
if (!config.text.empty()) configs.push_back(std::move(config));
|
||||
config = StringConfig();
|
||||
break;
|
||||
default:
|
||||
config.text.append(string - 1, 2);
|
||||
break;
|
||||
}
|
||||
++string;
|
||||
}
|
||||
end:
|
||||
configs.push_back(std::move(config));
|
||||
}
|
||||
|
||||
void parseAppend(const wchar* string, const QWORD length) noexcept {
|
||||
StringConfig config = StringConfig();
|
||||
if (!configs.empty()) {
|
||||
config = configs.back();
|
||||
configs.pop_back();
|
||||
}
|
||||
const wchar* const end = string + length;
|
||||
while (string < end) {
|
||||
const wchar* start = string;
|
||||
if (!config.text.empty()) {
|
||||
configs.push_back(std::move(config));
|
||||
config = config.copyConfig();
|
||||
}
|
||||
while (string < end && *string != '\\') ++string;
|
||||
if (string >= end) {
|
||||
if (start != end) config.text.append(start, end);
|
||||
configs.push_back(std::move(config));
|
||||
return;
|
||||
}
|
||||
if (start != string) config.text.append(start, string);
|
||||
if (++string == end) {
|
||||
config.text.append(start, end);
|
||||
configs.push_back(std::move(config));
|
||||
return;
|
||||
}
|
||||
if (!config.text.empty()) {
|
||||
configs.push_back(std::move(config));
|
||||
config = config.copyConfig();
|
||||
}
|
||||
switch (*string) {
|
||||
case L'\\':
|
||||
config.text.append(1, L'\\');
|
||||
++string;
|
||||
continue;
|
||||
case L'#': {
|
||||
if (end - string < 9) break;
|
||||
config.color = wtouib16(++string, 8);
|
||||
config.useDefaultColor(false);
|
||||
string += 8;
|
||||
continue;
|
||||
}
|
||||
case L'.': {
|
||||
if (end - string < 9) break;
|
||||
config.background = wtouib16(++string, 8);
|
||||
config.useDefaultBackground(false);
|
||||
string += 8;
|
||||
continue;
|
||||
}
|
||||
case L'F':
|
||||
case L'f':
|
||||
if (++string == end) break;
|
||||
config.idFont = *string;
|
||||
break;
|
||||
case L'-':
|
||||
case L's':
|
||||
case L'S':
|
||||
config.setStrikeThrough(true);
|
||||
break;
|
||||
case L'_':
|
||||
case L'u':
|
||||
case L'U':
|
||||
config.setUnderline(true);
|
||||
break;
|
||||
case L'/':
|
||||
case L'i':
|
||||
case L'I':
|
||||
config.setItalic(true);
|
||||
break;
|
||||
case L'=':
|
||||
case L'b':
|
||||
case L'B':
|
||||
config.setBold(true);
|
||||
break;
|
||||
case L'r':
|
||||
case L'R':
|
||||
if (!config.text.empty()) configs.push_back(std::move(config));
|
||||
config = StringConfig();
|
||||
break;
|
||||
default:
|
||||
config.text.append(string - 1, 2);
|
||||
break;
|
||||
}
|
||||
++string;
|
||||
}
|
||||
configs.push_back(std::move(config));
|
||||
}
|
||||
};
|
||||
|
||||
inline RenderableString operator""_renderable(const wchar* const text, const QWORD length) noexcept { return RenderableString(String(text, length)); }
|
||||
|
||||
class Font {
|
||||
public:
|
||||
Function<void(int width, int height)> resize;
|
||||
|
||||
private:
|
||||
friend class FontManager;
|
||||
friend class RenderableString;
|
||||
const String name;
|
||||
mutable Map<FontStyle, HFONT> fonts{};
|
||||
double yOffset;
|
||||
double heightModifier;
|
||||
long height;
|
||||
long escapement;
|
||||
long orientation;
|
||||
long yOffsetPx;
|
||||
const FontID id;
|
||||
bool adaptAllSize = false;
|
||||
|
||||
Font(const FontID id, const String& name, const double heightModifier, const double yOffset, const long escapement, const long orientation, const bool adaptAllSize) : name{ name }, yOffset(yOffset), heightModifier(heightModifier), height(static_cast<long>(interactSettings.actual.fontHeight * heightModifier)), escapement(escapement), orientation(orientation), yOffsetPx(static_cast<long>(yOffset * height)), id(id), adaptAllSize(adaptAllSize) {}
|
||||
|
||||
Font(const FontID id, String&& name, const double heightModifier, const double yOffset, const long escapement, const long orientation, const bool adaptAllSize) : name{ std::move(name) }, yOffset(yOffset), heightModifier(heightModifier), height(static_cast<long>(interactSettings.actual.fontHeight * heightModifier)), escapement(escapement), orientation(orientation), yOffsetPx(static_cast<long>(yOffset * height)), id(id), adaptAllSize(adaptAllSize) {}
|
||||
|
||||
public:
|
||||
Font(const Font&) = default;
|
||||
Font(Font&&) = default;
|
||||
~Font() { clear(); }
|
||||
|
||||
private:
|
||||
HFONT tryCreate(const RenderableString::StringConfig& config) const {
|
||||
if (const auto iter = fonts.find(config.style); iter != fonts.end()) return iter->second;
|
||||
LOGFONTW f{
|
||||
.lfHeight = height,
|
||||
.lfWidth = 0,
|
||||
.lfEscapement = escapement,
|
||||
.lfOrientation = orientation,
|
||||
.lfWeight = config.isBold() ? FW_BOLD : FW_NORMAL,
|
||||
.lfItalic = config.isItalic(),
|
||||
.lfUnderline = config.isUnderline(),
|
||||
.lfStrikeOut = config.isStrikeThrough(),
|
||||
.lfCharSet = DEFAULT_CHARSET,
|
||||
.lfOutPrecision = OUT_DEFAULT_PRECIS,
|
||||
.lfClipPrecision = CLIP_DEFAULT_PRECIS,
|
||||
.lfQuality = static_cast<unsigned char>(adaptAllSize ? DEFAULT_QUALITY : PROOF_QUALITY),
|
||||
.lfPitchAndFamily = FF_DONTCARE,
|
||||
.lfFaceName{}
|
||||
};
|
||||
memcpy(f.lfFaceName, name.c_str(), 64);
|
||||
HFONT fnt = CreateFontIndirectW(&f);
|
||||
fonts.emplace(std::make_pair(config.style, fnt));
|
||||
return fnt;
|
||||
}
|
||||
|
||||
int getWidth(const RenderableString::StringConfig& config) const;
|
||||
int drawSingle(const RenderableString::StringConfig& config, int x, int y, unsigned int defaultColor) const;
|
||||
void drawDirect(const RenderableString::StringConfig& config, int x, int y, unsigned int defaultColor) const;
|
||||
|
||||
void clear() const;
|
||||
|
||||
public:
|
||||
void draw(const RenderableString& text, int x, int y, unsigned int color = 0xffeeeeee) const;
|
||||
void drawCenter(const RenderableString& text, int x, int y, int w, int h, unsigned int color = 0xffeeeeee) const;
|
||||
|
||||
[[nodiscard]] int getHeight() const noexcept { return height; }
|
||||
[[nodiscard]] int getEscapement() const noexcept { return escapement; }
|
||||
[[nodiscard]] int getOrientation() const noexcept { return orientation; }
|
||||
[[nodiscard]] FontID getID() const noexcept { return id; }
|
||||
};
|
||||
|
||||
class FontManager {
|
||||
Map<FontID, Font> fonts;
|
||||
Font *defaultFont, *captionFont;
|
||||
FontID assigned = 0;
|
||||
using IterFonts = Map<FontID, Font>::const_iterator;
|
||||
|
||||
public:
|
||||
FontManager() {
|
||||
captionFont = &newFont(L"Microsoft YaHei UI Light");
|
||||
defaultFont = &newFont(L"Microsoft YaHei UI Light");
|
||||
captionFont->height = interactSettings.actual.captionHeight >> 1;
|
||||
captionFont->resize = [this](int, int) {
|
||||
if (interactSettings.actual.captionHeight != captionFont->height) {
|
||||
captionFont->height = interactSettings.actual.captionHeight >> 1;
|
||||
captionFont->clear();
|
||||
}
|
||||
};
|
||||
newFont(L"Jetbrains Mono", 1.0, -0.078);
|
||||
newFont(L"STSong", 1.0, -0.12);
|
||||
newFont(L"Arial", 1.0, -0.13);
|
||||
newFont(L"", 1.0, -0.05);
|
||||
}
|
||||
|
||||
[[nodiscard]] const Font& get(const FontID id) const noexcept {
|
||||
const IterFonts iter = fonts.find(id);
|
||||
if (iter == fonts.cend()) return *defaultFont;
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
const Font& newFont(const String& name, const double heightModifier = 1.0, const double yOffset = 0.0, const bool adaptAllSize = true, const long escapement = 0, const long orientation = 0) noexcept {
|
||||
++assigned;
|
||||
return fonts.emplace(assigned, std::move(Font(assigned, name, heightModifier, yOffset, adaptAllSize, escapement, orientation))).first->second;
|
||||
}
|
||||
|
||||
Font& newFont(String&& name, const double heightModifier = 1.0, const double yOffset = 0.0, const bool adaptAllSize = true, const long escapement = 0, const long orientation = 0) {
|
||||
++assigned;
|
||||
return fonts.emplace(assigned, std::move(Font(assigned, std::move(name), heightModifier, yOffset, adaptAllSize, escapement, orientation))).first->second;
|
||||
}
|
||||
|
||||
[[nodiscard]] Font& getDefault() const noexcept { return *defaultFont; }
|
||||
|
||||
void resize(const int width, const int height) {
|
||||
for (auto& [_, font] : fonts)
|
||||
if (font.resize) font.resize(width, height);
|
||||
else {
|
||||
font.height = static_cast<long>(interactSettings.actual.fontHeight * font.heightModifier);
|
||||
font.yOffsetPx = static_cast<long>(font.height * font.yOffset);
|
||||
font.clear();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
interface IText {
|
||||
virtual ~IText() = default;
|
||||
[[nodiscard]] virtual const String& getText() const noexcept = 0;
|
||||
[[nodiscard]] virtual const RenderableString& getRenderableString() const noexcept = 0;
|
||||
};
|
||||
|
||||
typedef class LiteralText final : public IText {
|
||||
const String string;
|
||||
mutable RenderableString renderableString;
|
||||
|
||||
public:
|
||||
LiteralText(const String& string): string(string), renderableString(string) {}
|
||||
LiteralText(String&& string): string(std::move(string)), renderableString(this->string) {}
|
||||
|
||||
const String& getText() const noexcept override { return string; }
|
||||
|
||||
const RenderableString& getRenderableString() const noexcept override { return renderableString; }
|
||||
} TranslatedText;
|
||||
|
||||
inline LiteralText operator""_literal(const wchar* const text, const QWORD length) noexcept { return LiteralText(String(text, length)); }
|
||||
|
||||
class TranslatableText final : public IText {
|
||||
const String idSrc;
|
||||
mutable const LiteralText* target = nullptr;
|
||||
mutable QWORD langConfig = 0;
|
||||
|
||||
public:
|
||||
TranslatableText(const String& id) : idSrc(id) {}
|
||||
TranslatableText(String&& id) : idSrc(std::move(id)) {}
|
||||
TranslatableText(const TranslatableText& other) : idSrc(other.idSrc) {}
|
||||
TranslatableText(TranslatableText&& other) noexcept : idSrc(std::move(other.idSrc)) {}
|
||||
const String& getID() const noexcept { return idSrc; }
|
||||
const String& getText() const noexcept override;
|
||||
const RenderableString& getRenderableString() const noexcept override;
|
||||
void refreshText() const noexcept;
|
||||
};
|
||||
|
||||
inline TranslatableText operator""_translates(const wchar* const text, const QWORD length) noexcept { return TranslatableText(String(text, length)); }
|
||||
inline TranslatedText operator""__translated(const wchar* const text, const QWORD length) noexcept { return TranslatedText(String(text, length)); }
|
||||
|
||||
using LangID = unsigned int;
|
||||
|
||||
struct Language {
|
||||
Map<String, TranslatedText> translateTable;
|
||||
LangID id = 1;
|
||||
using IterText = Map<String, TranslatedText>::const_iterator;
|
||||
|
||||
Language(const LangID id) : id(id) {}
|
||||
Language(const Language&) = delete;
|
||||
Language(Language&&) = default;
|
||||
Language& operator=(const Language&) = delete;
|
||||
Language& operator=(Language&&) = default;
|
||||
|
||||
String toString() {
|
||||
std::wstringstream stream{};
|
||||
stream.imbue(std::locale("zh-CN.UTF-8"));
|
||||
stream << L"Language ID: " << id << std::endl;
|
||||
for (const auto& [key, value] : translateTable) stream << L" " << key << L": (" << value.getText().length() << L") " << value.getText() << std::endl;
|
||||
return stream.str();
|
||||
}
|
||||
};
|
||||
|
||||
void languageMakeChinese(Language&);
|
||||
|
||||
class Translator {
|
||||
Map<String, Language> langMap{};
|
||||
List<Language*> langList{};
|
||||
TranslatedText nullText{ L"\\#FF""EE0000<translator-null>" };
|
||||
String lang = L"zh-cn";
|
||||
LangID idLangMax = 0;
|
||||
int langConfig = 1;
|
||||
using IterLangName = Map<String, Language>::const_iterator;
|
||||
using IterLang = List<Language>::const_iterator;
|
||||
|
||||
public:
|
||||
void initialize() {
|
||||
Language& chinese = addLang(L"zh-cn");
|
||||
languageMakeChinese(chinese);
|
||||
useLanguage(L"zh-cn");
|
||||
}
|
||||
|
||||
Translator() = default;
|
||||
Language& addLang(const String& lang) noexcept { return langMap.insert(std::make_pair(lang, Language(++idLangMax))).first->second; }
|
||||
Language& addLang(String&& lang) noexcept { return langMap.insert(std::make_pair(lang, Language(++idLangMax))).first->second; }
|
||||
Language& getLang(const String& lang) noexcept { return langMap.at(lang); }
|
||||
|
||||
const Language* useLanguage(const String& lang) noexcept {
|
||||
const Map<String, Language>::iterator language = langMap.find(lang);
|
||||
if (language == langMap.cend()) return nullptr;
|
||||
for (const Language* i : langList) if (i->id == language->second.id) return i;
|
||||
langList.push_front(&language->second);
|
||||
return &language->second;
|
||||
}
|
||||
|
||||
void unuseLanguage(const String& lang) noexcept {
|
||||
const Map<String, Language>::iterator language = langMap.find(lang);
|
||||
if (language == langMap.cend()) return;
|
||||
for (const Language* i : langList)
|
||||
if (i->id == language->second.id) {
|
||||
langMap.erase(lang);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void loadLang();
|
||||
|
||||
[[nodiscard]] const TranslatedText* getText(const String& id) const noexcept {
|
||||
Logger.debug(L"getText called");
|
||||
for (const Language* language : langList) {
|
||||
const Language::IterText iterator = language->translateTable.find(id);
|
||||
if (iterator != language->translateTable.cend()) return &iterator->second;
|
||||
}
|
||||
return &nullText;
|
||||
}
|
||||
|
||||
[[nodiscard]] int getConfigVersion() const noexcept { return langConfig; }
|
||||
};
|
||||
|
||||
extern Translator translator;
|
||||
extern FontManager fontManager;
|
||||
@@ -0,0 +1,55 @@
|
||||
//
|
||||
// Created by EmsiaetKadosh on 25-3-4.
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
class Task;
|
||||
class TaskScheduler;
|
||||
|
||||
class Task final : public AnywhereEditable<Task> {
|
||||
friend class TaskScheduler;
|
||||
#ifndef __CARLBEKS_DEBUG__
|
||||
QWORD triggerInterval = 0, nextExecuteTime = 0, lastExecuteTime = 0;
|
||||
#endif
|
||||
|
||||
public:
|
||||
Function<void(Task& self)> func;
|
||||
|
||||
Task(const Function<void(Task& self)>& func) : func(func) {}
|
||||
Task(Function<void(Task& self)>&& func) : func(std::move(func)) {}
|
||||
void schedulePop(const bool pop) noexcept { reserved[0] = pop; }
|
||||
[[nodiscard]] bool scheduledPop() const noexcept { return reserved[0]; }
|
||||
|
||||
int pop() noexcept override {
|
||||
schedulePop(true);
|
||||
Success();
|
||||
}
|
||||
#ifndef __CARLBEKS_DEBUG__
|
||||
Task& every(QWORD tick);
|
||||
Task& after(QWORD tick);
|
||||
Task& until(QWORD tick);
|
||||
Task& forever();
|
||||
#endif
|
||||
};
|
||||
|
||||
class TaskScheduler {
|
||||
public:
|
||||
AnywhereEditableList<Task> tasks;
|
||||
|
||||
void runAll() {
|
||||
for (Task& task : tasks) {
|
||||
task.func(task);
|
||||
if (task.scheduledPop()) {
|
||||
task.schedulePop(false);
|
||||
tasks.pop(&task); // pop后删除了内存,迭代器的current失效
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void pushCopy(Task& task) { tasks.pushCopy(&task); }
|
||||
/* 必须是new Task,交付托管 */
|
||||
void pushNewed(Task* task) { tasks.pushNewed(task); }
|
||||
void pushThis(Task& task) { tasks.pushThis(&task); }
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
//
|
||||
// Created by EmsiaetKadosh on 25-1-20.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
inline void test() {}
|
||||
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// Created by EmsiaetKadosh on 25-1-22.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "..\def.h"
|
||||
#include "exception.h"
|
||||
#include "..\game\Game.h"
|
||||
|
||||
String PublicLogger::build(const String& msg, const String& type) const {
|
||||
return L"T-" + qwtowb10(game.getTick(), 8) + name + type + msg + L"\n";
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
//
|
||||
// Created by EmsiaetKadosh on 25-1-18.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "..\warnings.h"
|
||||
#include "..\def.h"
|
||||
#include "File.h"
|
||||
|
||||
class Exception : public std::exception {
|
||||
const String* type;
|
||||
|
||||
protected:
|
||||
String msg;
|
||||
|
||||
Exception(String&& msg, const String* type) : type(type), msg(std::move(msg)) {}
|
||||
Exception(const String& msg, const String* type) : type(type), msg(msg) {}
|
||||
|
||||
public:
|
||||
[[nodiscard]] String getMessage() const noexcept { return msg; }
|
||||
[[nodiscard]] const String* getType() const noexcept { return type; }
|
||||
[[nodiscard]] const char* what() const override { return "Use Exception::getMessage() instead."; }
|
||||
};
|
||||
|
||||
class NullPointerException final : public Exception {
|
||||
inline static const String type = L"NullPointerException";
|
||||
|
||||
public:
|
||||
NullPointerException(String&& msg) : Exception(std::move(msg), &type) {}
|
||||
NullPointerException(const String& msg) : Exception(msg, &type) {}
|
||||
};
|
||||
|
||||
class BadAllocationException final : public Exception {
|
||||
inline static const String type = L"BadAllocationException";
|
||||
|
||||
public:
|
||||
BadAllocationException(String&& msg) : Exception(std::move(msg), &type) {}
|
||||
BadAllocationException(const String& msg) : Exception(msg, &type) {}
|
||||
};
|
||||
|
||||
class ArrayIndexOutOfBoundException final : public Exception {
|
||||
inline static const String type = L"ArrayIndexOutOfBoundException";
|
||||
|
||||
public:
|
||||
ArrayIndexOutOfBoundException(String&& msg) : Exception(std::move(msg), &type) {}
|
||||
ArrayIndexOutOfBoundException(const String& msg) : Exception(msg, &type) {}
|
||||
};
|
||||
|
||||
class InvalidOperationException final : public Exception {
|
||||
inline static const String type = L"InvalidOperationException";
|
||||
|
||||
public:
|
||||
InvalidOperationException(String&& msg) : Exception(std::move(msg), &type) {}
|
||||
InvalidOperationException(const String& msg) : Exception(msg, &type) {}
|
||||
};
|
||||
|
||||
class RuntimeException final : public Exception {
|
||||
inline static const String type = L"RuntimeException";
|
||||
|
||||
public:
|
||||
RuntimeException(String&& msg) : Exception(std::move(msg), &type) {}
|
||||
RuntimeException(const String& msg) : Exception(msg, &type) {}
|
||||
};
|
||||
|
||||
|
||||
class PublicLogger final {
|
||||
File mainLogger = File(L"log.txt");
|
||||
[[nodiscard]] String build(const String& msg, const String& type) const;
|
||||
|
||||
template<typename T, typename... Ts> requires requires(std::wstringstream stream, T& t, Ts&&... ts) {
|
||||
stream << std::forward<T>(t);
|
||||
(stream << ... << std::forward<Ts>(ts));
|
||||
}
|
||||
static void prints(std::wstringstream& stream, T&& t, Ts&&... ts) {
|
||||
stream << L" " << std::forward<T>(t);
|
||||
prints(stream, std::forward<Ts>(ts)...);
|
||||
}
|
||||
|
||||
template<typename T> requires requires(std::wstringstream stream, T&& t) { stream << std::forward<T>(t); }
|
||||
static void prints(std::wstringstream& stream, T&& t) { stream << L" " << std::forward<T>(t); }
|
||||
|
||||
template<typename T, typename... Ts> requires requires(std::wstringstream stream, T&& t, Ts&&... ts) {
|
||||
stream << std::forward<T>(t);
|
||||
(stream << ... << std::forward<Ts>(ts));
|
||||
}
|
||||
static void ofs(std::wstringstream& stream, T&& t, Ts&&... ts) {
|
||||
stream << L" " << std::forward<T>(t);
|
||||
ofs(stream, std::forward<Ts>(ts)...);
|
||||
}
|
||||
|
||||
template<typename T> requires requires(std::wstringstream stream, T&& t) { stream << std::forward<T>(t); }
|
||||
static void ofs(std::wstringstream& stream, T&& t) { stream << L" " << std::forward<T>(t); }
|
||||
|
||||
public:
|
||||
const String name;
|
||||
|
||||
PublicLogger(const String& name): name(L" [" + name + L"] ") {
|
||||
std::wcout << L"PublicLogger created\n";
|
||||
std::wcout.imbue(std::locale("zh-CN.UTF-8"));
|
||||
mainLogger.truncate().open();
|
||||
}
|
||||
|
||||
PublicLogger& put(const String& msg) noexcept {
|
||||
String str = L" " + name + L" " + msg + L"\n";
|
||||
std::wcout << str;
|
||||
mainLogger << str;
|
||||
return *this;
|
||||
}
|
||||
|
||||
PublicLogger& trace(const String& msg) noexcept {
|
||||
String str = build(msg, L"[Trace] ");
|
||||
std::wcout << str;
|
||||
mainLogger << str;
|
||||
return *this;
|
||||
}
|
||||
|
||||
PublicLogger& debug(const String& msg) noexcept {
|
||||
String str = build(msg, L"[Debug] ");
|
||||
std::wcout << str;
|
||||
mainLogger << str;
|
||||
return *this;
|
||||
}
|
||||
|
||||
PublicLogger& log(const String& msg) noexcept {
|
||||
String str = build(msg, L"[Log] ");
|
||||
std::wcout << str;
|
||||
mainLogger << str;
|
||||
return *this;
|
||||
}
|
||||
|
||||
PublicLogger& info(const String& msg) noexcept {
|
||||
String str = build(msg, L"[Info] ");
|
||||
std::wcout << str;
|
||||
mainLogger << str;
|
||||
return *this;
|
||||
}
|
||||
|
||||
PublicLogger& warn(const String& msg) noexcept {
|
||||
String str = build(msg, L"[Warn] ");
|
||||
std::wcout << str;
|
||||
mainLogger << str;
|
||||
return *this;
|
||||
}
|
||||
|
||||
PublicLogger& error(const String& msg) noexcept {
|
||||
String str = build(msg, L"[Error] ");
|
||||
std::wcout << str;
|
||||
mainLogger << str;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T> requires requires(T t) { std::wcout << t; }
|
||||
PublicLogger& print(T&& msg) noexcept {
|
||||
std::wstringstream stream = {};
|
||||
stream << L" " << name << L" " << msg << std::endl;
|
||||
String str = stream.str();
|
||||
mainLogger << str;
|
||||
std::wcout << str;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T, typename... Ts> requires requires(T t, Ts... ts) {
|
||||
std::wcout << t;
|
||||
std::wcout << (ts, ...);
|
||||
}
|
||||
PublicLogger& print(T&& msg, Ts&&... other) noexcept {
|
||||
std::wstringstream stream = {};
|
||||
stream << L" " << name << L" " << msg;
|
||||
prints(stream, other...);
|
||||
stream << std::endl;
|
||||
String str = stream.str();
|
||||
mainLogger << str;
|
||||
std::wcout << str;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T, typename... Ts> requires requires(std::wstringstream stream, T&& t, Ts&&... ts) {
|
||||
stream << std::forward<T>(t);
|
||||
(stream << ... << std::forward<Ts>(ts));
|
||||
}
|
||||
String of(T&& t, Ts&&... ts) const {
|
||||
std::wstringstream stream = {};
|
||||
stream << std::forward<T>(t);
|
||||
ofs(stream, ts...);
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
template<typename T> requires requires(std::wstringstream stream, T&& t) { std::wstringstream(t); }
|
||||
String of(T&& ts) const {
|
||||
std::wstringstream stream = {};
|
||||
ofs(stream, ts...);
|
||||
return stream.str();
|
||||
}
|
||||
};
|
||||
|
||||
[[carlbeks::releasedat("main.cpp")]] inline PublicLogger& Logger = *new PublicLogger(L"Main");
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
//
|
||||
// Created by EmsiaetKadosh on 25-3-6.
|
||||
//
|
||||
|
||||
#include "gc.h"
|
||||
+150
@@ -0,0 +1,150 @@
|
||||
//
|
||||
// Created by EmsiaetKadosh on 25-3-6.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "..\warnings.h"
|
||||
#include "..\def.h"
|
||||
#include "exception.h"
|
||||
|
||||
struct IGarbage;
|
||||
template <typename T>
|
||||
class Garbage;
|
||||
class GarbageCollector;
|
||||
|
||||
struct IGarbage {
|
||||
private:
|
||||
friend class GarbageCollector;
|
||||
IGarbage* next = nullptr;
|
||||
|
||||
protected:
|
||||
void* ptr;
|
||||
IGarbage(void* ptr) : ptr(ptr) {}
|
||||
virtual ~IGarbage() = default;
|
||||
virtual void collect() = 0;
|
||||
virtual void deleteThis() = 0;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class Garbage final : public IGarbage {
|
||||
friend class GarbageCollector;
|
||||
|
||||
public:
|
||||
Garbage(T* ptr) : IGarbage(ptr) {}
|
||||
void collect() override { delete static_cast<T*>(deallocating(ptr)); }
|
||||
|
||||
protected:
|
||||
void deleteThis() override { delete deallocating(this); }
|
||||
};
|
||||
|
||||
class GarbageCollector {
|
||||
IGarbage* submitted = nullptr;
|
||||
IGarbage* submittedEnd = nullptr;
|
||||
IGarbage* packed = nullptr;
|
||||
IGarbage* packedEnd = nullptr;
|
||||
IGarbage* processing = nullptr;
|
||||
|
||||
public:
|
||||
GarbageCollector() { Logger.info(L"initialize GarbageCollector"); }
|
||||
GarbageCollector(const GarbageCollector&) = delete;
|
||||
GarbageCollector(GarbageCollector&&) = delete;
|
||||
GarbageCollector& operator=(const GarbageCollector&) = delete;
|
||||
GarbageCollector& operator=(GarbageCollector&&) = delete;
|
||||
|
||||
~GarbageCollector() {
|
||||
IGarbage* iter;
|
||||
while (processing) {
|
||||
iter = processing->next;
|
||||
processing->collect();
|
||||
processing->deleteThis();
|
||||
processing = iter;
|
||||
}
|
||||
while (packed) {
|
||||
iter = packed->next;
|
||||
packed->collect();
|
||||
packed->deleteThis();
|
||||
packed = iter;
|
||||
}
|
||||
while (submitted) {
|
||||
iter = submitted->next;
|
||||
submitted->collect();
|
||||
submitted->deleteThis();
|
||||
submitted = iter;
|
||||
}
|
||||
submittedEnd = nullptr;
|
||||
}
|
||||
|
||||
/** 只能在gameThread调用 */
|
||||
template <TypeName T>
|
||||
void submit(T* ptr) noexcept(false) {
|
||||
IGarbage* newedGarbage = allocatedFor(new Garbage<T>(ptr));
|
||||
builtinSubmit(newedGarbage);
|
||||
}
|
||||
|
||||
void builtinSubmit(IGarbage* const newedGarbage) noexcept(false) {
|
||||
if (IGarbage* end = submittedEnd) { // 后续添加,可能存在线程竞争
|
||||
while (end->next) end = end->next; // 理论上不会进入循环,但防止万一
|
||||
end->next = newedGarbage;
|
||||
if (submitted) submittedEnd = newedGarbage; // 参考pack(),submitted会被先置空
|
||||
std::atomic_thread_fence(std::memory_order_acquire);
|
||||
if (!submitted) submittedEnd = nullptr; // 防止submittedEnd = garbage在pack()中置空后进行。此函数是同步的,所以可以这样操作。
|
||||
}
|
||||
else { // 添加首个,一定不会有线程竞争;或先前的已经pack
|
||||
submitted = newedGarbage;
|
||||
submittedEnd = newedGarbage;
|
||||
}
|
||||
}
|
||||
|
||||
/** 只能在renderThread调用 */
|
||||
void pack() {
|
||||
if (!submitted) return; // 提交链为空,不进行后续操作,防止竞争
|
||||
std::atomic_thread_fence(std::memory_order_acquire);
|
||||
IGarbage* submit = submitted;
|
||||
IGarbage* submitEnd = submittedEnd;
|
||||
submitted = nullptr; // 先把这个置为nullptr
|
||||
std::atomic_thread_fence(std::memory_order_acquire);
|
||||
submittedEnd = nullptr;
|
||||
if (IGarbage* end = packedEnd) {
|
||||
while (end->next) end = end->next; // 以防万一
|
||||
end->next = submit;
|
||||
if (packed) packedEnd = submitEnd;
|
||||
std::atomic_thread_fence(std::memory_order_acquire);
|
||||
if (!packed) packedEnd = nullptr;
|
||||
}
|
||||
else {
|
||||
packed = submit;
|
||||
packedEnd = submitEnd;
|
||||
}
|
||||
while (packedEnd->next) packedEnd = packedEnd->next;
|
||||
}
|
||||
|
||||
/** 只能在gameThread调用 */
|
||||
void collect() {
|
||||
IGarbage* next;
|
||||
while (processing) {
|
||||
next = processing->next;
|
||||
processing->next = nullptr;
|
||||
processing->collect();
|
||||
processing->deleteThis();
|
||||
processing = next;
|
||||
}
|
||||
if (!packed) return;
|
||||
// 搬运packed且搬运期间的packed链必须必须完全固定
|
||||
// 保证运行顺序
|
||||
processing = packed;
|
||||
packed = nullptr;
|
||||
std::atomic_thread_fence(std::memory_order_acquire);
|
||||
packedEnd = nullptr;
|
||||
std::atomic_thread_fence(std::memory_order_acquire);
|
||||
while (processing) {
|
||||
next = processing->next;
|
||||
processing->next = nullptr;
|
||||
processing->collect();
|
||||
processing->deleteThis();
|
||||
processing = next;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
inline GarbageCollector& [[carlbeks::releasedat("main.cpp")]] gc = *new GarbageCollector();
|
||||
@@ -0,0 +1,154 @@
|
||||
//
|
||||
// Created by EmsiaetKadosh on 25-3-21.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "..\warnings.h"
|
||||
|
||||
class Vector3D;
|
||||
class Vector2D;
|
||||
|
||||
class Vector3D {
|
||||
double x, y, z;
|
||||
|
||||
public:
|
||||
Vector3D(const double x, const double y, const double z) noexcept : x(x), y(y), z(z) {}
|
||||
Vector3D() noexcept : x(0), y(0), z(0) {}
|
||||
Vector3D(const Vector3D& other) noexcept = default;
|
||||
Vector3D(Vector3D&& other) noexcept = default;
|
||||
Vector3D& operator=(const Vector3D& other) noexcept = default;
|
||||
Vector3D& operator=(Vector3D&& other) noexcept = default;
|
||||
~Vector3D() noexcept = default;
|
||||
|
||||
[[nodiscard]] double getX() const noexcept { return x; }
|
||||
[[nodiscard]] double getY() const noexcept { return y; }
|
||||
[[nodiscard]] double getZ() const noexcept { return z; }
|
||||
|
||||
[[nodiscard]] Vector3D operator+(const Vector3D& other) const noexcept { return Vector3D(x + other.x, y + other.y, z + other.z); }
|
||||
[[nodiscard]] Vector3D operator-(const Vector3D& other) const noexcept { return Vector3D(x - other.x, y - other.y, z - other.z); }
|
||||
[[nodiscard]] Vector3D operator*(const double scalar) const noexcept { return Vector3D(x * scalar, y * scalar, z * scalar); }
|
||||
[[nodiscard]] Vector3D operator/(const double scalar) const noexcept { return Vector3D(x / scalar, y / scalar, z / scalar); }
|
||||
[[nodiscard]] double operator*(const Vector3D& other) const noexcept { return x * other.x + y * other.y + z * other.z; }
|
||||
|
||||
Vector3D& operator+=(const Vector3D& other) noexcept {
|
||||
x += other.x;
|
||||
y += other.y;
|
||||
z += other.z;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Vector3D& operator-=(const Vector3D& other) noexcept {
|
||||
x -= other.x;
|
||||
y -= other.y;
|
||||
z -= other.z;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Vector3D& operator*=(const double scalar) noexcept {
|
||||
x *= scalar;
|
||||
y *= scalar;
|
||||
z *= scalar;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Vector3D& operator/=(const double scalar) noexcept {
|
||||
x /= scalar;
|
||||
y /= scalar;
|
||||
z /= scalar;
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[nodiscard]] Vector3D operator-() const noexcept { return Vector3D(-x, -y, -z); }
|
||||
bool operator==(const Vector3D& other) const noexcept { return x == other.x && y == other.y && z == other.z; }
|
||||
bool operator!=(const Vector3D& other) const noexcept { return x != other.x || y != other.y || z != other.z; }
|
||||
|
||||
[[nodiscard]] double length() const noexcept { return std::sqrt(x * x + y * y + z * z); }
|
||||
|
||||
[[nodiscard]] Vector3D getNormalized() const noexcept {
|
||||
if (x == 0.0 && y == 0.0 && z == 0) return Vector3D(0.0, 0.0, 0.0);
|
||||
const double length = this->length();
|
||||
return Vector3D(x / length, y / length, z / length);
|
||||
}
|
||||
|
||||
Vector3D& normalize() noexcept {
|
||||
if (x == 0 && y == 0 && z == 0) return *this;
|
||||
const double length = this->length();
|
||||
x /= length;
|
||||
y /= length;
|
||||
z /= length;
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[nodiscard]] double dot(const Vector3D& other) const noexcept { return x * other.x + y * other.y + z * other.z; }
|
||||
[[nodiscard]] Vector3D cross(const Vector3D& other) const noexcept { return Vector3D(y * other.z - z * other.y, z * other.x - x * other.z, x * other.y - y * other.x); }
|
||||
};
|
||||
|
||||
class Vector2D {
|
||||
double x, y;
|
||||
|
||||
public:
|
||||
Vector2D(const double x, const double y) noexcept : x(x), y(y) {}
|
||||
Vector2D() noexcept : x(0), y(0) {}
|
||||
Vector2D(const Vector2D& other) noexcept = default;
|
||||
Vector2D(Vector2D&& other) noexcept = default;
|
||||
Vector2D& operator=(const Vector2D& other) noexcept = default;
|
||||
Vector2D& operator=(Vector2D&& other) noexcept = default;
|
||||
~Vector2D() noexcept = default;
|
||||
|
||||
[[nodiscard]] double getX() const noexcept { return x; }
|
||||
[[nodiscard]] double getY() const noexcept { return y; }
|
||||
|
||||
[[nodiscard]] Vector2D operator+(const Vector2D& other) const noexcept { return Vector2D(x + other.x, y + other.y); }
|
||||
[[nodiscard]] Vector2D operator-(const Vector2D& other) const noexcept { return Vector2D(x - other.x, y - other.y); }
|
||||
[[nodiscard]] Vector2D operator*(const double scalar) const noexcept { return Vector2D(x * scalar, y * scalar); }
|
||||
[[nodiscard]] Vector2D operator/(const double scalar) const noexcept { return Vector2D(x / scalar, y / scalar); }
|
||||
[[nodiscard]] double operator*(const Vector2D& other) const noexcept { return x * other.x + y * other.y; }
|
||||
|
||||
Vector2D& operator+=(const Vector2D& other) noexcept {
|
||||
x += other.x;
|
||||
y += other.y;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Vector2D& operator-=(const Vector2D& other) noexcept {
|
||||
x -= other.x;
|
||||
y -= other.y;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Vector2D& operator*=(const double scalar) noexcept {
|
||||
x *= scalar;
|
||||
y *= scalar;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Vector2D& operator/=(const double scalar) noexcept {
|
||||
x /= scalar;
|
||||
y /= scalar;
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[nodiscard]] Vector2D operator-() const noexcept { return Vector2D(-x, -y); }
|
||||
bool operator==(const Vector2D& other) const noexcept { return x == other.x && y == other.y; }
|
||||
bool operator!=(const Vector2D& other) const noexcept { return x != other.x || y != other.y; }
|
||||
|
||||
[[nodiscard]] double length() const noexcept { return std::sqrt(x * x + y * y); }
|
||||
|
||||
[[nodiscard]] Vector2D getNormalized() const noexcept {
|
||||
if (x == 0.0 && y == 0.0) return Vector2D(0.0, 0.0);
|
||||
const double length = this->length();
|
||||
return Vector2D(x / length, y / length);
|
||||
}
|
||||
|
||||
Vector2D& normalize() noexcept {
|
||||
if (x == 0 && y == 0) return *this;
|
||||
const double length = this->length();
|
||||
x /= length;
|
||||
y /= length;
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[nodiscard]] double dot(const Vector2D& other) const noexcept { return x * other.x + y * other.y; }
|
||||
[[nodiscard]] Vector3D cross(const Vector2D& other) const noexcept { return Vector3D(0, 0, x * other.y - y * other.x); }
|
||||
};
|
||||
@@ -0,0 +1,219 @@
|
||||
//
|
||||
// Created by EmsiaetKadosh on 25-3-4.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gc.h"
|
||||
|
||||
template <typename T, typename L = void>
|
||||
class AnywhereEditable;
|
||||
template <typename T, typename L = void>
|
||||
class AnywhereEditableList;
|
||||
template <typename T, typename L = void>
|
||||
class AnywhereIterator;
|
||||
class AnywhereIteratorEnd;
|
||||
|
||||
|
||||
/**
|
||||
* 此处无法进行代码编译层面的直接约束
|
||||
* @tparam T 满足T extends AnywhereEditable<T, L>
|
||||
* @tparam L 满足L extends AnywhereEditableList<T, L>
|
||||
*/
|
||||
template <typename T, typename L>
|
||||
class AnywhereEditable {
|
||||
public:
|
||||
using Lst = std::conditional_t<std::is_same_v<L, void>, AnywhereEditableList<T, L>, L>;
|
||||
|
||||
private:
|
||||
friend class AnywhereIterator<T, L>;
|
||||
friend class AnywhereEditableList<T, L>;
|
||||
friend class AnywhereIteratorEnd;
|
||||
friend L;
|
||||
AnywhereEditable* prev = nullptr;
|
||||
AnywhereEditable* next = nullptr;
|
||||
AnywhereEditableList<T, L>* list = nullptr; // 指示自身属于某个列表
|
||||
bool managedByList = false; // 指示是否在列表内管理内存,而非列表外管理内存
|
||||
public:
|
||||
byte reserved[7]{}; // reserved[0]: Task::schedulePop
|
||||
|
||||
AnywhereEditable() = default;
|
||||
AnywhereEditable(const AnywhereEditable&) {}
|
||||
|
||||
AnywhereEditable(AnywhereEditable&& other) noexcept : prev(other->prev), next(other->next), list(other->list) {
|
||||
other->prev = nullptr;
|
||||
other->next = nullptr;
|
||||
other->list = nullptr;
|
||||
}
|
||||
|
||||
virtual ~AnywhereEditable() {
|
||||
if (list) {
|
||||
managedByList = false;
|
||||
auto lst = list;
|
||||
list = nullptr;
|
||||
lst->pop(static_cast<T*>(this));
|
||||
Logger.warn(L"~AnywhereEditable() : 析构时没有清除list,导致了析构时pop");
|
||||
}
|
||||
}
|
||||
|
||||
int pushCopy(AnywhereEditableList<T, L>& list) noexcept;
|
||||
int pushThis(AnywhereEditableList<T, L>& list) noexcept;
|
||||
virtual int pop() noexcept;
|
||||
AnywhereEditableList<T, L>* getContainer() const noexcept { return list; }
|
||||
};
|
||||
|
||||
template <typename T, typename L>
|
||||
class AnywhereIterator {
|
||||
friend class AnywhereIteratorEnd;
|
||||
AnywhereEditable<T, L>* current;
|
||||
|
||||
public:
|
||||
AnywhereIterator(AnywhereEditable<T, L>* current) : current(current) {}
|
||||
AnywhereIterator(const AnywhereIterator& other) = default;
|
||||
|
||||
T& operator*() noexcept(false) {
|
||||
if (current->next) return static_cast<T&>(*current);
|
||||
throw RuntimeException(L"AnywhereIterator::operator*() : next is null, end of list");
|
||||
}
|
||||
|
||||
T* operator->() noexcept(false) {
|
||||
if (current->next) return &static_cast<T&>(*current);
|
||||
throw RuntimeException(L"AnywhereIterator::operator*() : next is null, end of list");
|
||||
}
|
||||
|
||||
bool operator!=(const AnywhereIterator& other) const noexcept { return current != other.current; }
|
||||
bool operator==(const AnywhereIterator& other) const noexcept { return current == other.current; }
|
||||
bool operator!=(const AnywhereIteratorEnd& other) const noexcept;
|
||||
bool operator==(const AnywhereIteratorEnd& other) const noexcept;
|
||||
|
||||
AnywhereIterator& operator++() noexcept(false) {
|
||||
if (current->next) current = current->next;
|
||||
else throw InvalidOperationException(L"AnywhereIterator::operator++() : end of list");
|
||||
return *this;
|
||||
}
|
||||
|
||||
AnywhereIterator operator++(int) noexcept(false) {
|
||||
if (current->next) {
|
||||
AnywhereIterator ret = *this;
|
||||
current = current->next;
|
||||
return ret;
|
||||
}
|
||||
throw InvalidOperationException(L"AnywhereIterator::operator++() : end of list");
|
||||
}
|
||||
|
||||
AnywhereIterator& operator--() noexcept(false) {
|
||||
if (current->prev) current = current->prev;
|
||||
else throw InvalidOperationException(L"AnywhereIterator::operator--() : begin of list");
|
||||
return *this;
|
||||
}
|
||||
|
||||
AnywhereIterator operator--(int) noexcept(false) {
|
||||
if (current->prev) {
|
||||
AnywhereIterator ret = *this;
|
||||
current = current->prev;
|
||||
return ret;
|
||||
}
|
||||
throw InvalidOperationException(L"AnywhereIterator::operator--() : begin of list");
|
||||
}
|
||||
};
|
||||
|
||||
class AnywhereIteratorEnd {
|
||||
public:
|
||||
AnywhereIteratorEnd() = default;
|
||||
AnywhereIteratorEnd(const AnywhereIteratorEnd& other) = delete;
|
||||
AnywhereIteratorEnd(AnywhereIteratorEnd&& other) = delete;
|
||||
|
||||
template <typename T, typename L>
|
||||
bool operator!=(const AnywhereIterator<T, L>& other) const noexcept { return other.current && other.current->next; }
|
||||
|
||||
template <typename T, typename L>
|
||||
bool operator==(const AnywhereIterator<T, L>& other) const noexcept { return !operator!=(other); }
|
||||
};
|
||||
|
||||
template <typename T, typename L>
|
||||
class AnywhereEditableList {
|
||||
protected:
|
||||
AnywhereEditable<T, L> head;
|
||||
AnywhereEditable<T, L> tail;
|
||||
|
||||
public:
|
||||
AnywhereEditableList() {
|
||||
head.next = &tail;
|
||||
tail.prev = &head;
|
||||
}
|
||||
|
||||
virtual ~AnywhereEditableList() { for (AnywhereIterator<T, L> it = begin(); it != end(); ++it) {} }
|
||||
|
||||
int pushCopy(T* value) noexcept;
|
||||
int pushThis(T* value) noexcept;
|
||||
int pushNewed(T* value) noexcept;
|
||||
virtual int pop(T* value) noexcept;
|
||||
AnywhereIterator<T, L> begin() noexcept { return AnywhereIterator<T, L>(head.next); }
|
||||
[[nodiscard]] AnywhereIteratorEnd end() noexcept { return {}; }
|
||||
AnywhereIterator<T, L> begin() const noexcept { return AnywhereIterator<T, L>(head.next); }
|
||||
[[nodiscard]] AnywhereIteratorEnd end() const noexcept { return {}; }
|
||||
AnywhereEditable<T, L>* front() const noexcept { return head.next == &tail ? nullptr : head.next; }
|
||||
AnywhereEditable<T, L>* back() const noexcept { return tail.prev == &head ? nullptr : tail.prev; }
|
||||
};
|
||||
|
||||
template <typename T, typename L>
|
||||
int AnywhereEditable<T, L>::pushCopy(AnywhereEditableList<T, L>& list) noexcept { return list.pushCopy(static_cast<T*>(this)); }
|
||||
|
||||
template <typename T, typename L>
|
||||
int AnywhereEditable<T, L>::pushThis(AnywhereEditableList<T, L>& list) noexcept { return list.pushThis(static_cast<T*>(this)); }
|
||||
|
||||
template <typename T, typename L>
|
||||
int AnywhereEditable<T, L>::pop() noexcept {
|
||||
if (!list) {
|
||||
Logger.error(L"AnywhereEditable::pop() : list is null");
|
||||
Failed();
|
||||
}
|
||||
return list->pop(static_cast<T*>(this));
|
||||
}
|
||||
|
||||
template <typename T, typename L>
|
||||
bool AnywhereIterator<T, L>::operator!=(const AnywhereIteratorEnd& other) const noexcept { return other != *this; }
|
||||
|
||||
template <typename T, typename L>
|
||||
bool AnywhereIterator<T, L>::operator==(const AnywhereIteratorEnd& other) const noexcept { return other == *this; }
|
||||
|
||||
template <typename T, typename L>
|
||||
int AnywhereEditableList<T, L>::pushCopy(T* value) noexcept {
|
||||
T* nv = allocatedFor(new T(*value));
|
||||
return pushNewed(nv);
|
||||
}
|
||||
|
||||
template <typename T, typename L>
|
||||
int AnywhereEditableList<T, L>::pushThis(T* value) noexcept {
|
||||
if (value->list) {
|
||||
Logger.error(L"AnywhereEditableList::pushThis() : value is already in a list");
|
||||
Failed();
|
||||
}
|
||||
value->list = this;
|
||||
tail.prev->next = value;
|
||||
value->prev = tail.prev;
|
||||
value->next = &tail;
|
||||
tail.prev = value;
|
||||
Success();
|
||||
}
|
||||
|
||||
template <typename T, typename L>
|
||||
int AnywhereEditableList<T, L>::pushNewed(T* value) noexcept {
|
||||
const int ret = pushThis(value);
|
||||
if (ret) delete deallocating(value);
|
||||
else value->managedByList = true;
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename T, typename L>
|
||||
int AnywhereEditableList<T, L>::pop(T* value) noexcept {
|
||||
if (value->list != this) {
|
||||
Logger.error(L"AnywhereEditableList::pop() : value is not in this list");
|
||||
Failed();
|
||||
}
|
||||
value->list = nullptr;
|
||||
value->next->prev = value->prev;
|
||||
value->prev->next = value->next;
|
||||
if (value->managedByList) gc.submit(value);
|
||||
Success();
|
||||
}
|
||||
Reference in New Issue
Block a user