跳到正文
本頁目錄

Data(數據結構)

ll/api/data/ · 通用

概述

Data 模塊提供通用數據結構,包括可觀察值、基於 LevelDB 的鍵值存儲、依賴圖、語義版本類型、類型擦除函數容器、可取消回調和線程安全容器。

頭文件

頭文件說明
ll/api/data/Observable.h共享狀態、顯式取消訂閱的可觀察值
ll/api/data/KeyValueDB.hLevelDB 鍵值數據庫
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 均繼承自這個模板。

C++
#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。

C++
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 支持的持久化鍵值存儲。

C++
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;
};
}

使用方法

C++
#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()。

C++
#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"}); // false

VersionRequirement

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匹配任一範圍
C++
#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,其他情況應顯式寫出目標範圍。

相關模塊