Data(數據結構)
ll/api/data/ · 通用
概述
Data 模塊提供通用數據結構,包括可觀察值、基於 LevelDB 的鍵值存儲、依賴圖、語義版本類型、類型擦除函數容器、可取消回調和線程安全容器。
頭文件
| 頭文件 | 說明 |
|---|---|
ll/api/data/Observable.h | 共享狀態、顯式取消訂閱的可觀察值 |
ll/api/data/KeyValueDB.h | LevelDB 鍵值數據庫 |
ll/api/data/DependencyGraph.h | 依賴解析圖 |
ll/api/data/Version.h | 語義版本類型 |
ll/api/data/VersionRequirement.h | 語義版本範圍 |
ll/api/data/AnyFunction.h | 類型擦除函數容器 |
ll/api/data/CancellableCallback.h | 可取消異步回調 |
ll/api/data/ConcurrentPriorityQueue.h | 線程安全優先隊列 |
ll/api/data/TightPair.h | 空間優化的 pair |
ll/api/data/IndirectValue.h | 具有值語義的指針包裝器 |
核心類
Observable<T>
Observable<T> 保存一個值,並在值發生變化時同步通知訂閱者。它是通用 Data API,不依賴 UI;數據驅動 UI 中的類型化 Observable 均繼承自這個模板。
#include "ll/api/data/Observable.h"
namespace ll::data {
template <class T>
class Observable {
public:
using Callback = brstd::move_only_function<void(T const&)>;
using SubscriptionId = std::uint32_t;
explicit Observable(T initial);
T getData() const;
void setData(T value);
SubscriptionId subscribe(Callback callback);
bool unsubscribe(SubscriptionId id);
};
}複製 Observable 會共享同一份數據、訂閱列表和訂閱 ID 序列;重新構造一個 Observable 才會得到獨立狀態。
setData() 會比較新值與當前值。值相同時不做任何事;值不同時先替換數據,再同步執行回調,所有回調完成後 setData() 才返回。回調異常不會被吞掉:異常會繼續拋給調用者,並終止本輪尚未執行的通知。
subscribe() 支持僅可移動回調,並返回訂閱 ID。這個 ID 不是 RAII 句柄:丟棄返回值不會取消訂閱。訂閱會一直存在,直到 unsubscribe(id) 成功,或共享的 Observable 狀態被銷燬。傳入空回調時返回 ID 0,unsubscribe(0) 返回 false。
ll::data::Observable<int> count{0};
auto copy = count; // 与 count 共享状态。
auto const subscription = count.subscribe([](int const& value) {
// 值变化时同步执行。
});
copy.setData(1); // count.getData() 现在也是 1,回调执行一次。
copy.setData(1); // 值相同,不执行回调。
bool removed = count.unsubscribe(subscription); // true注意
Observable<T> 內部不加鎖,併發訪問需要調用方自行同步。通知時會直接遍歷訂閱容器,因此不要在該 Observable 自己的回調裡增刪訂閱。遞歸調用 setData() 也會立即進入下一輪通知;除非明確需要並能控制這種行為,否則應避免這樣做。
為了支持 getData(),T 需要可複製;為了支持 setData() 的同值抑制,T 需要可比較相等。UI 派生類型 ObservableBoolean、ObservableNumber、ObservableString 和 ObservableUIRawMessage 見數據驅動 UI。
KeyValueDB
由 LevelDB 支持的持久化鍵值存儲。
namespace ll::data {
class KeyValueDB {
public:
class WriteBatch {
public:
WriteBatch& set(std::string_view key, std::string_view val);
WriteBatch& del(std::string_view key);
};
explicit KeyValueDB(std::filesystem::path const& path);
KeyValueDB(std::filesystem::path const& path, bool createIfMiss, bool fixIfError, int bloomFilterBit);
std::optional<std::string> get(std::string_view key) const;
bool has(std::string_view key) const;
bool empty() const;
bool set(std::string_view key, std::string_view val);
bool del(std::string_view key);
bool write(WriteBatch const& batch);
coro::Generator<std::pair<std::string_view, std::string_view>> iter() const;
};
}使用方法
#include "ll/api/data/KeyValueDB.h"
#include "ll/api/mod/Mod.h"
void useDatabase(ll::mod::Mod& mod) {
ll::data::KeyValueDB db(mod.getDataDir() / "mydata");
db.set("player_score", "100");
auto score = db.get("player_score");
if (score) {
// *score == "100"
}
if (db.has("player_score")) {
db.del("player_score");
}
// 原子提交多个修改
ll::data::KeyValueDB::WriteBatch batch;
batch.set("player_score", "125").set("last_reward", "daily").del("pending_reward");
db.write(batch);
// 迭代所有条目
for (auto [key, value] : db.iter()) {
// 处理每个键值对
}
}Version
Version 嚴格解析 major.minor.patch 格式的語義版本。前置版本標識符參與優先級比較,構建元數據不參與。
需要把構建元數據也作為身份的一部分比較時,應使用 isIdenticalTo()。
#include "ll/api/data/Version.h"
ll::data::Version current{"1.3.0-beta.1+windows.5"};
ll::data::Version release{"1.3.0"};
bool newer = release > current; // true
bool samePrecedence = ll::data::Version{"1.3.0+first"} == ll::data::Version{"1.3.0+second"}; // true
bool sameIdentity = ll::data::Version{"1.3.0+first"}.isIdenticalTo(ll::data::Version{"1.3.0+second"}); // falseVersionRequirement
VersionRequirement 表示規範化後的語義版本範圍。用空白分隔的比較條件屬於同一個 AND 組,|| 分隔 OR 組。
支持的比較符為 =、>、>=、< 和 <=。
| 語法 | 含義 |
|---|---|
=1.2.3 | 精確匹配 1.2.3 |
>=1.2.0 <2.0.0 | 不低於 1.2.0 且低於 2.0.0 |
^1.2.3 | 低於 2.0.0 的兼容更新 |
~1.2.3 | 低於 1.3.0 的補丁更新 |
1、1.2、1.2.x | 部分版本或通配符範圍 |
*、x、X | 任意非前置版本 |
1.2.3 - 2.0.0 | 兩端均包含的連字符區間 |
^1.2.3 || =2.0.0 | 匹配任一範圍 |
#include "ll/api/data/VersionRequirement.h"
ll::data::VersionRequirement supported{"^1.2.3 || =2.0.0"};
bool matches = supported.matches(ll::data::Version{"1.8.0"}); // true普通範圍不會意外匹配前置版本。若需要匹配前置版本,同一個 AND 組中必須有比較條件顯式包含與候選版本
major.minor.patch 核心相同的前置版本。
為保持兼容,裸完整版本 1.2.3 當前仍表示 >=1.2.3 <2.0.0。從模組清單讀取這種寫法時會輸出遷移警告。
精確匹配請寫成 =1.2.3,其他情況應顯式寫出目標範圍。
相關模塊
- Config(配置系統) — 使用反射進行序列化,可存儲配置數據
- Mod(模組系統) —
Mod::getDataDir()用於數據庫存儲位置