news 2026/9/12 4:47:41

SerenityOS 代码模式指南:从 TRY/MUST 错误处理到容器选型的工程实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SerenityOS 代码模式指南:从 TRY/MUST 错误处理到容器选型的工程实践

SerenityOS 代码模式指南:从 TRY/MUST 错误处理到容器选型的工程实践

【免费下载链接】serenityThe Serenity Operating System 🐞项目地址: https://gitcode.com/GitHub_Trending/se/serenity

SerenityOS(一个以 64 位 x86、Arm 与 RISC-V 为目标的图形化类 Unix 操作系统)在长期演进中沉淀出了一整套贯穿内核(Kernel)与用户态(Userland)的 C++ 编码模式。本文以 Documentation/Patterns.md 为核心骨架,逐条拆解TRY(...)/MUST(...)错误传播、Fallible 构造器、serenity_main程序入口、侵入式链表、AssertSize静态断言、字符串视图字面量、SourceLocation与四种数组容器选型,并结合仓库中的真实源码与测试用例佐证其底层实现,帮助你写出风格统一、健壮且符合 SerenityOS 社区审美的代码。

引言:为什么 SerenityOS 需要一套"模式"文档

在大型 C/C++ 代码库中,如果没有统一的约定,同一类问题往往会被写成十几种风格迥异的解法。SerenityOS 的代码库覆盖内核、系统服务、图形界面、浏览器引擎与大量基础库,参与贡献者众多,因此文档 Documentation/Patterns.md 明确承担了"追踪并描述反复出现的模式"的职责:这些模式一部分是在项目演进中自发涌现的,另一部分则是有意采纳的,目的是让新代码与既有代码保持一致,并让好模式在代码库中进一步传播。

与一般"编码规范"不同,本文档关注的是具有实质技术收益的工程模式——它们大多与 SerenityOS 独特的错误处理哲学(ErrorOr返回值)和"拒绝 OOM 恐慌"的内核级健壮性要求深度绑定,因此不能简单地照搬通用 C++ 最佳实践。

TRY(...):基于ErrorOr的无样板错误传播

模式动机

SerenityOS 的错误处理并不依赖 C++ 异常,而是通过AK::ErrorOr<T>返回值显式传播错误。AK/Error.h 中定义了[[nodiscard]]ErrorOr<T, E>:它内部是一个联合体,要么持有值T,要么持有ErrorType(默认即AK::Error),并额外用一个bool标记当前处于哪种状态。ErrorOr<void, E>则特化为持有AK::EmptyErrorOr<Empty, E>,把"无返回值"建模为一种合法的值状态。

如果每个可能失败的调用点都要手写if (result.is_error()) return result.release_error();,代码会被样板淹没。TRY(...)宏正是为此而生,它把"执行 → 判错 → 提前返回"压缩成一个表达式。

宏的实现原理

宏定义位于 AK/Try.h:

#define TRY(expression) \ ({ \ AK_IGNORE_DIAGNOSTIC("-Wshadow", \ auto&& _temporary_result = (expression)); \ static_assert(!::AK::Detail::IsLvalueReference<decltype(_temporary_result.release_value())>, \ "Do not return a reference from a fallible expression"); \ if (_temporary_result.is_error()) [[unlikely]] \ return _temporary_result.release_error(); \ _temporary_result.release_value(); \ })

逐行解读:

  • 它借助 GCC/Clang 都支持的语句表达式(statement expressions)扩展,让整个TRY(...)可以作为一个表达式嵌入赋值语句右侧;
  • auto&&承接表达式结果,配合AK_IGNORE_DIAGNOSTIC("-Wshadow")允许宏嵌套(在另一个TRY内部再写TRY);
  • 通过static_assert禁止从 fallible 表达式返回引用——因为语句表达式无论如何都会产生拷贝,返回引用会得到悬垂引用,这是宏作者刻意设计的编译期防线;
  • 若结果处于错误态,release_error()从当前函数直接return,因此TRY只能用在返回类型同样为ErrorOr(或Error/Result)的函数中;
  • 若成功,整个表达式的值即release_value()的结果。

注意宏头部注释中的说明:TRY面向任何"具有预期 API 的结果类型",主要以AK::ResultAK::Error为设计目标。而在内核态与用户态中错误载体通常都是ErrorOr<T>(默认错误类型AK::Error)。

实战示例:LibGfx 中的位图创建

文档给出的第一个完整示例来自图形库Bitmap::create_shareable(参见 AK/Try.h 的配套用法与 Userland/Libraries/LibGfx 目录):

#include <AK/Try.h> ErrorOr<NonnullRefPtr<Bitmap>> Bitmap::create_shareable(BitmapFormat format, IntSize size, int scale_factor) { if (size_would_overflow(format, size, scale_factor)) return Error::from_string_literal("Gfx::Bitmap::create_shareable size overflow"); auto const pitch = minimum_pitch(size.width() * scale_factor, format); auto const data_size = size_in_bytes(pitch, size.height() * scale_factor); auto buffer = TRY(Core::AnonymousBuffer::create_with_size(round_up_to_power_of_two(data_size, PAGE_SIZE))); auto bitmap = TRY(Bitmap::create_with_anonymous_buffer(format, buffer, size, scale_factor, {})); return bitmap; }

这段代码展示了TRY的三种典型用法:

  1. 前置校验size_would_overflow不通过时直接return Error::from_string_literal(...),错误信息采用"模块: 描述"的字符串字面量风格;
  2. 连续传播:匿名缓冲区创建失败、位图创建失败都会立即返回,不再需要层层if
  3. 返回值收尾:最后一个表达式return bitmap;直接返回NonnullRefPtr<Bitmap>,它会被隐式转换为ErrorOr<NonnullRefPtr<Bitmap>>的成功态。

实战示例:内核中的地址空间分配

TRY同样贯穿内核内存管理代码,文档引用了AddressSpace::allocate_region

#include <AK/Try.h> ErrorOr<Region*> AddressSpace::allocate_region(VirtualRange const& range, StringView name, int prot, AllocationStrategy strategy) { VERIFY(range.is_valid()); OwnPtr<KString> region_name; if (!name.is_null()) region_name = TRY(KString::try_create(name)); auto vmobject = TRY(AnonymousVMObject::try_create_with_size(range.size(), strategy)); auto region = TRY(Region::try_create_user_accessible(range, move(vmobject), 0, move(region_name), prot_to_region_access_flags(prot), MemoryType::Normal, false)); TRY(region->map(page_directory())); return add_region(move(region)); }

值得注意的细节:

  • 名称以try_开头的工厂函数(KString::try_createAnonymousVMObject::try_create_with_sizeRegion::try_create_user_accessible)是"可能失败"的显式信号,返回值通常是ErrorOr<T>
  • TRY(region->map(page_directory()))用于传播ErrorOr<void>类型的错误——即使没有值需要取出,判错返回依然成立;
  • 此处不用异常、不记录错误栈,错误类型在 AK/Error.h 中即AK::Error(持有m_codem_string_literalm_syscall标志)。

与 Rust?运算符的类比

TRY的行为与 Rust 中的?运算符几乎一致(Rust 官方文档对?的描述是"错误传播的快捷方式"):二者都做"出错即从当前函数返回错误,成功则解包出值"。区别在于?是语言语法,而TRY依赖编译器扩展,且TRY的解包会强制产生一次移动/拷贝(这也是它禁止返回引用的原因)。

MUST(...):语义明确的"绝不允许失败"

TRY的区别及使用纪律

MUST(...)TRY(...)结构几乎相同(同样定义于 AK/Try.h),唯一区别是:当结果处于错误态时,MUST调用VERIFY(!_temporary_result.is_error())直接断言崩溃,而不是返回错误:

#define MUST(expression) \ ({ \ AK_IGNORE_DIAGNOSTIC("-Wshadow", \ auto&& _temporary_result = (expression)); \ static_assert(!::AK::Detail::IsLvalueReference<decltype(_temporary_result.release_value())>, \ "Do not return a reference from a fallible expression"); \ VERIFY(!_temporary_result.is_error()); \ _temporary_result.release_value(); \ })

文档给出了两条明确的使用纪律:

  1. 不要用MUST顶替暂时无法传播错误的TRY。此时应调用ErrorOrrelease_value_but_fixme_should_propagate_errors()方法(定义见 AK/Error.h)取出值,并用方法名中的FIXME标记未来改进点;
  2. MUST仅用于两种场景:要么通过其他途径已经证明宏内代码不可能失败,要么失败后果严重到程序必须崩溃。

示例:先扩容后追加的循环

#include <AK/Vector.h> ErrorOr<void> insert_one_to_onehundred(Vector<int>& vector) { TRY(vector.try_ensure_capacity(vector.size() + 100)); for (int i = 1; i <= 100; i++) { // We previously made sure that we allocated enough space, so the append operation shouldn't ever fail. MUST(vector.try_append(i)); } return {}; }

这里先用TRY确保容量足够(容量不足时Vector的追加会因堆分配失败而报错),随后循环内的try_appendMUST包裹:因为容量已保证,追加只会在桶内写入,不可能失败,若真失败则说明发生了更严重的逻辑错误,断言崩溃是合理响应。

错误构造工具的补充

配合使用时会频繁见到 AK/Error.h 提供的错误构造方法:

  • Error::from_errno(int code):包装系统调用返回的 errno(会VERIFY(code != 0));
  • Error::from_string_literal(char const (&)[N]):用户态专用,直接书写静态错误信息,如"Class: Some failure"
  • Error::from_string_view(StringView):按视图持有字符串;为避免悬垂,该函数对ByteStringStringFlyString等所有权类型做了= delete禁用,临时字符串必须显式.view()才能传入;
  • Error::from_syscall(StringView syscall_name, int rc):记录失败的系统调用名与返回码;
  • Error::copy(Error const&):显式拷贝错误。

Fallible Constructors:用静态工厂取代可失败构造函数

为什么需要这个模式

C++ 构造函数无法返回ErrorOr<T>,而在 SerenityOS 中一切可能失败的操作(内存分配、IO、解码)都通过ErrorOr表达。若在构造函数体内失败,只能靠异常或置位标志,这与系统哲学相悖。因此约定:需要执行可失败操作的类,不提供"会失败"的构造函数,而是定义名为create的静态工厂函数

模式结构

  • create返回ErrorOr<T>ErrorOr<NonnullOwnPtr<T>>
  • 它在内部完成两类工作:为私有构造函数准备参数(其中任何一步都可能TRY返回)、在对象构造完成后执行仍需的可失败初始化;
  • 真正的构造函数保持private,只做纯成员初始化,从而保证"对象一旦构造出来就是有效的"。

文档示例:解压器

class Decompressor { public: static ErrorOr<NonnullOwnPtr<Decompressor>> create(NonnullOwnPtr<Core::Stream::Stream> stream) { auto buffer = TRY(CircularBuffer::create_empty(32 * KiB)); auto decompressor = TRY(adopt_nonnull_own_or_enomem(new (nothrow) Decompressor(move(stream), move(buffer)))); TRY(decompressor->initialize_settings_from_header()); return decompressor; } // ... snip ... private: Decompressor(NonnullOwnPtr<Core::Stream::Stream> stream, CircularBuffer buffer) : m_stream(move(stream)) , m_buffer(move(buffer)) { } CircularBuffer m_buffer; NonnullOwnPtr<Core::Stream::Stream> m_stream; }

此例同时展示了三个模式的组合:

  1. TRY(CircularBuffer::create_empty(32 * KiB)):缓冲区创建失败即返回;
  2. TRY(adopt_nonnull_own_or_enomem(new (nothrow) Decompressor(...)))new (nothrow)在分配失败时返回空指针而非抛异常,随后由adopt_nonnull_own_or_enomem将其转换为ErrorOr<NonnullOwnPtr<Decompressor>>(OOM 时返回 ENOMEM 错误);
  3. TRY(decompressor->initialize_settings_from_header()):对象构造完成后继续做可能失败的头解析初始化。

注意:new (nothrow)adopt_nonnull_own_or_enomemVERIFY这些原语广泛定义于 AK/OwnPtr.h、AK/Assertions.h 与 AK/NonnullOwnPtr.h 中,读者可在 AK 目录下继续追读。

serenity_main(...):SerenityOS 风格的程序入口

模式动机

SerenityOS 的程序不再暴露普通的 Cmain函数,而是暴露serenity_main(Main::Arguments)。动机有二:

  1. Main::Arguments以更贴近 Serenity API 的形式组织命令行参数;
  2. 返回ErrorOr<int>使入口函数可以直接用TRY(...)无缝传播错误,省去大量 C 风格if (rc < 0) return rc;样板。

从 C main 到 serenity_main

传统写法:

int main(int argc, char** argv) { return 0; }

SerenityOS 写法:

#include <LibMain/Main.h> ErrorOr<int> serenity_main(Main::Arguments arguments) { return 0; }

底层链接机制

Main::Argumentsserenity_main的声明位于 Userland/Libraries/LibMain/Main.h:

namespace Main { struct Arguments { int argc {}; char** argv {}; Span<StringView> strings; }; int return_code_for_errors(); void set_return_code_for_errors(int); } ErrorOr<int> serenity_main(Main::Arguments);
  • Arguments在保留argc/argv的同时,额外提供了Span<StringView> strings,可直接按StringView遍历参数,无需手工指针运算;
  • 实现位于 Userland/Libraries/LibMain/Main.cpp,库的构建配置见 Userland/Libraries/LibMain/CMakeLists.txt:可执行程序链接LibMain后,真正的 C 入口int main(int, char**)由库提供并在启动时调用serenity_main(...)
  • 错误码策略由Main::return_code_for_errors()/set_return_code_for_errors(int)控制——当serenity_main返回错误时,LibMain会以此决定进程退出码。

也就是说,应用程序作者只需要写serenity_mainmain的样板与错误到退出码的转换全部由LibMain统一承担。这一模式的历史由来记录在 "OS hacking: A better main() for SerenityOS C++ programs" 视频中(文档内引用,此处不展开外部链接)。

Intrusive Lists:面向 OOM 韧性的内核数据结构

什么是侵入式链表

文档引用 Intrusive linked lists 的定义:当每个元素自身持有用于记录其在数据结构中归属的元数据(对链表而言就是内嵌的节点对象)时,该数据结构就是"侵入式"的。与之相对,Vector这样的非侵入式容器由容器自身分配存储。

侵入式链表的核心收益是:插入操作不进行任何内存分配,因此插入绝不会因 OOM 失败,错误处理代码可以大幅简化。这正是内核这种"必须对 OOM 有韧性"的环境所需要的。

声明模式:私有节点 + 公开类型别名

通用约定是:把侵入式链表节点作为私有成员存储,再用公开类型别名把链表类型暴露给外部使用者。文档示例来自内核Region类(相关代码位于 Kernel/Memory 目录):

class Region final : public Weakable<Region> { public: // ... snip ... private: bool m_syscall_region : 1 { false }; IntrusiveListNode<Region> m_memory_manager_list_node; IntrusiveListNode<Region> m_vmobject_list_node; public: using ListInMemoryManager = IntrusiveList<&Region::m_memory_manager_list_node>; using ListInVMObject = IntrusiveList<&Region::m_vmobject_list_node>; };

一个对象可以同时挂入多个链表(m_memory_manager_list_nodem_vmobject_list_node是两个独立节点),对应Region同时属于"内存管理器维护的全局区域表"与"每个 VMObject 维护的区域表"两种关系。

使用方通过公开别名直接持有链表:

class MemoryManager { // ... snip ... Region::ListInMemoryManager m_kernel_regions; Vector<UsedMemoryRange> m_used_memory_ranges; Vector<PhysicalMemoryRange> m_physical_memory_ranges; Vector<ContiguousReservedMemoryRange> m_reserved_memory_ranges; };

底层实现

侵入式链表的实现在 AK/IntrusiveList.h:

  • IntrusiveListNode<V, Container>(第 157 行起)内嵌m_next/m_prev指针,节点本身可感知所属容器;
  • IntrusiveList非类型模板参数SubstitutedIntrusiveListNode<T, Container> T::* member绑定"节点在宿主类型中的成员指针",因此同一个IntrusiveList类型天然知道自己遍历的是宿主的哪个成员,node_to_value通过成员指针偏移从节点找回宿主对象;
  • IntrusiveListStorage持有m_first/m_last,实现双向链表的头尾锚点。

正是"成员指针作为模板参数"这一设计,让Region::ListInMemoryManagerRegion::ListInVMObject虽然是同一种节点类型的不同成员,却能静态地区分开来。IntrusiveList在用户态的少数特定场景也会被使用,但主体仍是内核代码。

类型大小静态断言:AK::AssertSize

为什么普通 static_assert 不够好

static_assert(sizeof(T) == N)失败时,编译器错误信息里只有你写下的期望值,没有类型的真实大小,开发者还得手动打印或用调试器查。SerenityOS 为此在 AK/StdLibExtraDetails.h 中引入了AssertSize

template<typename T, unsigned ExpectedSize, unsigned ActualSize> struct __AssertSize : TrueType { static_assert(ActualSize == ExpectedSize, "actual size does not match expected size"); consteval explicit operator bool() const { return value; } }; template<typename T, unsigned ExpectedSize> using AssertSize = __AssertSize<T, ExpectedSize, sizeof(T)>;

技巧在于:真实大小sizeof(T)被放进了模板参数。当断言失败时,编译器会把ExpectedSizeActualSize两个具体数值一并打印在错误信息中(作为模板实参的一部分),省去手工排查。consteval explicit operator bool()让它可以写进static_assert(AssertSize<T, N>());

使用示例与仓库中的真实调用

#include <AK/StdLibExtras.h> struct Empty { }; static_assert(AssertSize<Empty, 1>());

仓库中已有大量实际调用,例如 AK/FloatingPoint.h:

static_assert(AssertSize<f128, 16>()); static_assert(AssertSize<FloatExtractor<f128>, sizeof(f128)>()); static_assert(AssertSize<FloatExtractor<f80>, sizeof(f80)>()); static_assert(AssertSize<FloatExtractor<f64>, sizeof(f64)>()); static_assert(AssertSize<FloatExtractor<f32>, sizeof(f32)>());

这些断言保障了位级浮点提取器(FloatExtractor)的布局与预期一致——任何结构体布局意外变化都会在编译期被捕捉,且错误信息直接给出"实际多少字节"。

字符串视图字面量:operator""sv

零运行时开销的 StringView 构造

AK::StringView支持 C++17 引入的sv字符串字面量运算符,定义于 AK/StringView.h:

[[nodiscard]] ALWAYS_INLINE consteval AK::StringView operator""sv(char const* cstring, size_t length) { return AK::StringView(cstring, length); }
  • consteval保证编译期求值:构造StringView时不需要在运行时用strlen扫描长度;
  • 字面量本身位于二进制文件的只读数据段,StringView只是"指针 + 长度"的轻量视图,不拥有也不复制数据。

用法与测试

#include <AK/String.h> #include <AK/StringView.h> #include <LibTest/TestCase.h> TEST_CASE(string_view_literal_operator) { StringView literal_view = "foo"sv; String test_string = "foo"; EXPECT_EQ(literal_view.length(), test_string.length()); EXPECT_EQ(literal_view, test_string); }

示例中的TEST_CASE/EXPECT_EQ宏来自 Userland/Libraries/LibTest 测试框架,说明了该模式在单元测试中的标准用法:"foo"sv与运行时构造的String在长度与内容上完全相等,而前者无需动态分配。

Source Location:免预处理器宏的调用点捕获

与 C++20 std::source_location 的关系

C++20 的std::source_location允许以默认参数捕获调用者的文件 / 行号 / 函数名。AK/SourceLocation.h 提供了同名等价实现,内部直接基于__builtin_FILE()__builtin_LINE()__builtin_FUNCTION()(见SourceLocation::current()),同时为AK::SourceLocation特化了AK::Formatter,使其能直接以"[\x1b[34m{}\x1b[0m @ {}:{}]"的带色格式输出(函数名 @ 文件名:行号)。

#include <AK/SourceLocation.h> #include <AK/StringView.h> static StringView example_fn(const SourceLocation& loc = SourceLocation::current()) { return loc.function_name(); } int main(int, char**) { return example_fn().length(); }

要点是SourceLocation::current()作为默认实参,在调用点实例化,因此example_fn()内部拿到的loc是调用者那一行的位置信息,而不是example_fn自己的定义位置。这已成为 SerenityOS 添加调试插桩的惯用方式——不再需要把__FILE__/__LINE__塞进宏。

按调试宏裁剪的别名技巧

如果只想在某个调试宏开启时才真正捕获位置信息,文档建议不要在带SourceLocation参数的函数里到处加#ifdef,而是定义两个同名的类型别名/空类型:

#if LOCK_DEBUG # include <AK/SourceLocation.h> #endif #if LOCK_DEBUG using LockLocation = SourceLocation; #else struct LockLocation { static constexpr LockLocation current() { return {}; } private: constexpr LockLocation() = default; }; #endif

LOCK_DEBUG关闭时,LockLocation退化为一个可被编译器完全优化掉的空结构体,current()返回空对象,所有调用点代码照常编译,零运行时开销;开启时则自动获得真实的调用位置。这样既保留了干净的调用方代码,又实现了"按配置裁剪调试信息"。

四种连续存储容器选型:type[]ArrayVectorFixedArray

共同的视图搭档:Span<type>

在讨论四种"拥有数据"的容器之前,先明确Span<type>的角色:它不拥有数据,只是对他人数据的视图。上述四种容器都能提供Span来观察全部或部分数据。凡是"不关心容器种类、不需要调整大小"的 API,优先以Span作为参数类型——这与 AK/Span.h 中的定义一致,也是 SerenityOS 中大量函数签名采用Span的原因。

四者的定位对比

容器存储方式是否可动态扩容典型用途
C 风格数组type[]内联(inline)仅用于实现其他集合或极特殊情况;一般不鼓励直接使用,指针+长度风格同样被劝阻
Array<type, N>内联编译期已知大小的固定数组,std::array的等价物,零动态分配
Vector<type>内联 + 堆上外置存储绝大多数"列表"场景的默认选择,见 AK/Vector.h
FixedArray<type>堆上外置存储运行期才知道大小、但初始化后不再变化;仅在构造/析构时分配与释放

逐条展开:

  • C 风格数组:文档明确给出"一般不建议"的立场,包括以"指针 + 长度"方式传递数组。它们仅作为其他集合的内部实现或特殊场景的兜底存在;
  • Arraystd::array的薄封装,大小是模板参数的一部分,数据内联分配,从不做动态分配,适合编译期常量大小的场景;
  • Vectorstd::vector的等价物,可动态调整大小,是基本列表需求的默认容器。其第二模板参数是可选的内联容量(inline capacity):数据优先放在内联缓冲中,一旦超出内联容量就自动切换到堆上的外置存储,并在扩容/缩容时自动搬移;
  • FixedArray:本质是"运行期定大小"的Array。不能像Vector那样扩容,但非常适合"大小在编译期未知、初始化后不再变化"的场景;它保证除了构造函数与析构函数之外不做任何分配/释放——这对性能敏感路径与内核式资源纪律很有价值。

选型决策建议

  1. 尺寸编译期已知且固定 →Array<type, N>
  2. 尺寸运行期才知道、但定下来就不变 →FixedArray<type>
  3. 需要随时增删、动态伸缩 →Vector<type>
  4. 只是想"看看别人拥有的数据"或作为函数参数 →Span<type>
  5. 除非在实现底层集合,否则避免裸type[]与指针+长度风格。

总结:模式如何共同构成 SerenityOS 的代码气质

把文档中这些模式放在一起,可以看到一条清晰的工程主线:

  • 错误即值ErrorOr<T>承载一切可失败操作,TRY/MUST让传播与断言各得其所,release_value_but_fixme_should_propagate_errors()负责标记待改进点;
  • 构造即有效:Fallible 构造器模式(create静态工厂 + 私有构造函数)确保对象一旦创建就处于一致状态;
  • 入口即框架serenity_main+LibMain把进程样板统一收敛;
  • OOM 韧性:侵入式链表在必须对 OOM 稳健的环境(内核)中消除插入失败的路径,Vectortry_ensure_capacity则把 OOM 显式化为可处理错误;
  • 编译期把关AssertSize让布局错误在编译期暴露并给出真实数值,operator""svconsteval让字符串视图构造零运行时开销,SourceLocation让调试插桩告别预处理器宏;
  • 容器各司其职Array/Vector/FixedArray/Span的选型规则把"内存生命周期"这一最易出错的维度显式化。

对想要为 SerenityOS 贡献代码、或仅仅是想学习"如何在一个大型 C++ 代码库中建立统一且可扩展的错误处理纪律"的开发者而言,Documentation/Patterns.md 是一份值得反复对照的活文档;而 AK、Kernel、Userland 中的源码与 Tests/AK 下的测试则是验证这些模式的最佳教材。

【免费下载链接】serenityThe Serenity Operating System 🐞项目地址: https://gitcode.com/GitHub_Trending/se/serenity

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/12 4:47:37

Sway 钱包智能合约实战:ABI 声明与合约实现的双项目架构

Sway 钱包智能合约实战&#xff1a;ABI 声明与合约实现的双项目架构 【免费下载链接】sway &#x1f334; Empowering everyone to build reliable and efficient smart contracts. 项目地址: https://gitcode.com/GitHub_Trending/sw/sway 本文基于 Sway 官方文档 Wall…

作者头像 李华
网站建设 2026/9/12 4:47:27

Karakeep 自托管部署用户管理与密码重置实战指南(FAQ 精讲)

Karakeep 自托管部署用户管理与密码重置实战指南&#xff08;FAQ 精讲&#xff09; 【免费下载链接】hoarder A self-hostable bookmark-everything app (links, notes and images) with AI-based automatic tagging and full text search 项目地址: https://gitcode.com/Git…

作者头像 李华
网站建设 2026/9/12 4:46:37

一致凸空间:定义、性质与偏微分方程应用

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/12 4:45:19

Prompt韧性工程:构建高鲁棒AI Agent的五大实战模块

我注意到您提供的项目标题是“GPT - 6 Astra 的使用焚诀”&#xff0c;但需要明确说明&#xff1a;截至目前&#xff08;2024年中&#xff09;&#xff0c;OpenAI 官方从未发布、命名或确认存在名为 “GPT-6” 或 “Astra” 的模型。所有网络上关于“GPT-6 Astra”的讨论、热搜…

作者头像 李华
网站建设 2026/9/12 4:43:10

迭代傅里叶变换算法IFTA:从原理到相位片工程实战

简介&#xff1a;IFTA&#xff08;迭代傅里叶变换&#xff09;算法的MATLAB实现资源&#xff0c;面向图像处理与信号处理方向的学生和研究人员&#xff0c;聚焦图像复原、去噪与频谱分析等典型应用。压缩包共含2个文件&#xff0c;一个可直接运行的MATLAB脚本与一幅经典标准测试…

作者头像 李华