Skia C++ 编码风格规范详解:命名约定、类设计模式与 clang-format 自动化落地

发布时间:2026/9/25 7:57:52

Skia C++ 编码风格规范详解:命名约定、类设计模式与 clang-format 自动化落地 图形学图像处理【免费下载链接】skiaSkia is a complete 2D graphic library for drawing Text, Geometries, and Images.项目地址https://gitcode.com/gh_mirrors/skia1/skia点击查看免费下载本文基于 Skia 官方贡献文档 Coding Style Guidelines系统梳理 Skia 代码库的 C 编码风格约定从文件组织、命名规则、宏与大括号风格到类声明、onMethodName虚函数模式与参数传递惯例并逐条对照仓库根目录 .clang-format 中的机器可执行配置说明这些约定如何被格式化工具自动强制帮助贡献者在提交补丁前一次性对齐项目风格。约定沿革与适用范围原文档开头说明这些约定是随项目演进而逐渐成型的早期代码并不严格遵循全部规则但随代码演进期望存量代码逐步向规范靠拢。因此阅读代码时遇到风格不一致的旧代码属于正常现象新增与修改的代码应以现行规范为准。这一规范适用于 C 源码与头文件Python 工具脚本则单独遵循 Google Python Style Guide见文末说明。对于补丁提交流程本身CLA、AUTHORS 文件登记、提交前自查等前置要求可参考 Contributing to Skia 一文。文件组织与头文件约定扩展名与目录布局C 源文件与头文件统一使用.cpp和.h扩展名不对外暴露的私有头文件应放在src目录下使其不进入客户端client的头文件搜索路径若私有头文件需要被公开头文件包含则应放入 include/private 目录。最小化 include 与字母序排列规范强调“尽可能减少 include”如果头文件中只需前向声明forward declare某个名称就足够那么前向声明优先于完整 include。前向声明与文件 include 均应按字母顺序排列。仓库根目录的 .clang-format 通过以下配置从机器层面落实了字母序要求见第 100 行附近SortIncludes: true IncludeCategories: - Regex: ^.*\.h Priority: 1 - Regex: ^.* Priority: 2 - Regex: .* Priority: 3 IncludeIsMainRegex: (-_)?$即系统头...中的 C 头文件、其余系统头、项目内...头文件被分为三个优先级类别排序且以test/unittest结尾的文件会优先匹配对应的同名头文件。禁止在 SkTypes.h 之前使用条件编译不要在包含SkTypes.h直接或间接之前使用#if/#ifdef。原因是大多数你想做条件编译判断的宏往往要等到SkTypes.h被处理后才真正确定。这是 Skia 特有的构建顺序约束——SkTypes.h是配置宏平台、后端能力等的最终裁决点。空白与换行缩进使用 4 个空格禁止 Tab使用 Unix 风格换行符LF尽量不留行尾空白但执行上不严格行宽控制在 100 列以内除非换行会“丑得离谱”可自行判断。以上两条硬规则对应 .clang-format 的IndentWidth: 4、UseTab: Never、TabWidth: 4与ColumnLimit: 100。命名规范这是整份文档篇幅最大的部分规则可以归纳为一张“前缀表”对象命名规则示例对外可见类型/函数Sk前缀GPU 后端 Ganesh 代码用Gr前缀SkCanvas、GrContext嵌套类型无需前缀HelperClass含方法的类/结构体/联合体数据成员小写f开头 驼峰fMilesDriven纯数据访问类型可不加f前缀milesDriven全局变量小写g开头 驼峰gLoggingEnabled局部变量与参数小写开头驼峰numCatsconstexpr/const且值程序期内固定k开头 驼峰kPictureSize枚举值k前缀无作用域枚举追加_枚举名后缀kGlazed_DonutType宏全大写下划线分隔跨文件作用域的宏加SK或GR前缀SK_...、GR_GL_...实现文件内 static 非类函数小写下划线分隔snake_casetastes_like_chickenextern 函数 / static 类函数大写开头驼峰SkIsOdd类前缀与嵌套类型对外可见的类型和函数使用Sk前缀表明其属于 SkiaGaneshSkia 的 GPU 后端源码主要位于src/gpu目录Gr前缀符号集中于此中的代码使用Gr前缀。嵌套类型无需前缀class SkClass { public: class HelperClass { ... }; };数据成员f 前缀含方法的 struct/class/union 中的数据字段以小写f开头再接驼峰命名用以和其他变量区分而主要面向“直接字段访问”的类型则不需要f装饰struct GrCar { float milesDriven; Color color; }; class GrMotorcyle { public: float getMilesDriven() const { return fMilesDriven; } void setMilesDriven(float milesDriven) { fMilesDriven milesDriven; } Color getColor() const { return fColor; } private: float fMilesDriven; Color fColor; };注意对照GrCar是纯数据载体字段不加fGrMotorcyle有访问器方法字段加f。全局变量与局部变量全局变量同样遵循驼峰约定仅前缀改为gbool gLoggingEnabled;仓库中可见真实用例tools/flags/CommonFlagsConfig.cpp 中的文件作用域静态数组即gPredefinedConfigs与上述g前缀约定一致。局部变量与函数参数为小写开头驼峰int herdCats(const Array cats) { int numCats cats.count(); }常量k 前缀声明为constexpr或const、且取值在程序运行期间固定的变量以k开头再接驼峰int drawPicture() { constexpr SkISize kPictureSize {100, 100}; constexpr float kZoom 1.0f; }枚举命名枚举值同样以k为前缀具体形态分四种情况1.enum class作用域枚举——值无需后缀// Enum class does not need suffixes. enum class SkPancakeType { kBlueberry, kPlain, kChocolateChip, };2. 无作用域枚举独占值——k值_枚举名后缀枚举名用单数// Enum should have a suffix after the enum name. enum SkDonutType { kGlazed_DonutType, kSprinkles_DonutType, kChocolate_DonutType, kMaple_DonutType, kLast_DonutType kMaple_DonutType }; static const SkDonutType kDonutTypeCount kLast_DonutType 1;规则细节枚举本体对“独占值”用单数名、对“位域”用复数名枚举数量若需要命名为k单数枚举名Count且不作为枚举成员如上例的kDonutTypeCount或者在枚举内保留一个kLast成员也可以。3. 无作用域位域枚举——值以Bit结尾enum SkSausageIngredientBits { kFennel_SausageIngredientBit 0x1, kBeef_SausageIngredientBit 0x2 };4. 标志位枚举Flags——值以Flag结尾enum SkMatrixFlags { kTranslate_MatrixFlag 0x1, kRotate_MatrixFlag 0x2 };函数命名实现文件内的 static 非类函数用小写下划线分隔static inline bool tastes_like_chicken(Food food) { return kIceCream_Food ! food; }extern 函数与 static 类函数用大写开头驼峰bool SkIsOdd(int n); class SkFoo { public: static int FooInstanceCount(); // Not static. int barBaz(); };宏命名宏一律全大写下划线分隔作用域大于文件的宏应以SK或GR开头#define GR_GL_TEXTURE0 0xdeadbeefGanesh 中专门与 GL 相关的宏前缀为GR_GL。另外 Ganesh 倾向让宏始终有定义用#if MACRO而非#ifdef MACRO#define GR_GO_SLOWER 0 ... #if GR_GO_SLOWER Sleep(1000); #endifSkia 其余部分对布尔标志则惯用#ifdef SK_MACRO风格。两种风格的区别在于#if风格允许把开关值集中定义在一处并可取非而#ifdef风格下宏“存在与否”本身就是开关。大括号与流程控制大括号位置开括号不换行else/else if与前后大括号同行除非有预处理条件编译介入if、else、while、for、do后必须使用大括号即使只有一行if (...) { oneOrManyLines; } if (...) { oneOrManyLines; } else if (...) { oneOrManyLines; } else { oneOrManyLines; } for (...) { oneOrManyLines; } void function(...) { oneOrManyLines; } // 预处理条件编译打断 else 时的写法 if (!error) { proceed_as_usual(); } #if HANDLE_ERROR else { freak_out(); } #endif这与 .clang-format 中BreakBeforeBraces: Custom且所有BraceWrapping.*: false的设置完全一致Allman/KR 之外的定制风格类、函数、控制语句之后均不换行开括号。控制关键字留白流程控制关键字与左括号之间、括号与大括号之间都要留空格while (...) { } do { } while (...); switch (...) { ... }对应配置为SpaceBeforeParens: ControlStatements。switch/case缩进、fallthrough 与 case 内块case与default相对switch缩进一级switch (color) { case kBlue: ... break; case kGreen: ... break; ... default: ... break; }跨 case 的隐式贯穿必须用[[fallthrough]]标注但连续多个空 case 标签共享同一实现时不需要标注switch (recipe) { ... case kSmallCheesePizza_Recipe: case kLargeCheesePizza_Recipe: ingredients | kCheese_Ingredient | kDough_Ingredient | kSauce_Ingredient; break; case kCheeseOmelette_Recipe: ingredients | kCheese_Ingredient; [[fallthrough]] case kPlainOmelette_Recipe: ingredients | (kEgg_Ingredient | kMilk_Ingredient); break; ... }这一约定在核心代码中真实存在例如 src/core/SkBlurEngine.cppcase 2: [[fallthrough]]; case 3: [[fallthrough]];以及 src/core/SkBitmapProcState_matrixProcs.cpp 中的多 case 连续贯穿写法。当某个 case 需要声明局部变量时用花括号开块、} break;收尾switch (filter) { ... case kGaussian_Filter: { Bitmap srcCopy src-makeCopy(); ... } break; ... };case缩进由 .clang-format 的IndentCaseLabels: true强制注意Google 基础风格默认不缩进 case这里做了覆盖。类声明规范可见性排序与成员分组除非有前向声明需要类声明中的可见性区应按public、protected、private顺序排列每个可见性标签前留一个空行同一可见性区内数据字段与方法不要交叉混排建议把所有数据字段集中放在区段末尾class SkFoo { public: ... protected: ... private: void barHelper(...); ... SkBar fBar; ... };对应配置AccessModifierOffset: -4public:等标签向左缩进 4 列顶格。override 与父类方法限定被派生类重写的虚函数应使用override关键字并省略virtualvoid myVirtual() override { }当调用父类版本的同名方法需要明确体现时用Parent::method()使用了作用域限定符时不再需要this-class GrDillPickle : public GrPickle { ... bool onTasty() const override { return GrPickle::onTasty() fFreshDill; } ... private: bool fFreshDill; };构造函数初始化列表格式初始化器若能在同一行放下就放在构造函数同行否则每个初始化器独占一行、缩进对齐逗号放在下一行行首GrDillPickle::GrDillPickle() : GrPickle(), fSize(kDefaultPickleSize) {} GrDillPickle::GrDillPickle(float size, float crunchiness, const PickleOptions* options) : GrPickle(options) , fSize(size) , fCrunchiness(crunchiness) {}这是 .clang-format 中三行配置组合出的效果ConstructorInitializerAllOnOneLineOrOnePerLine: true要么全在一行要么一行一个、ConstructorInitializerIndentWidth: 8初始化器缩进 8 列、BreakConstructorInitializersBeforeComma: true逗号前置。explicit 单参构造函数接受单一实参的构造函数几乎总是应声明为explicit例外仅限极少数“自动兼容”类class Foo { explicit Foo(int x); // Good. Foo(float y); // Spooky implicit conversion from float to Foo. No no no! ... };this- 前缀在方法内部调用本对象的方法时应显式加this-this-method();核心模式公开非虚入口 私有 onMethodName 虚函数Skia 中虚方法的一个标志性模式是提供一个公开的非虚或 final入口方法与之配对的是一个私有虚方法onMethodName。其设计目的是保证基类逻辑前后置约束一定被执行由基类掌控虚方法的使用方式而不是依赖每个子类自觉调用Parent::onMethodName()class SkSandwich { public: void assemble() { // All sandwiches must have bread on the top and bottom. this-addIngredient(kBread_Ingredient); this-onAssemble(); this-addIngredient(kBread_Ingredient); } bool cook() { return this-onCook(); } private: // All sandwiches must implement onAssemble. virtual void onAssemble() 0; // Sandwiches can remain uncooked by default. virtual bool onCook() { return true; } }; class SkGrilledCheese : public SkSandwich { private: void onAssemble() override { this-addIngredient(kCheese_Ingredient); } bool onCook() override { return this-toastOnGriddle(); } }; class SkPeanutButterAndJelly : public SkSandwich { private: void onAssemble() override { this-addIngredient(kPeanutButter_Ingredient); this-addIngredient(kGrapeJelly_Ingredient); } };从源码结构看这种“入口 on 前缀钩子”的组合在 Skia 各类可扩展组件绘制、后端接口中是普遍的组织方式读者在src/gpu下检索virtual.*on[A-Z]即可看到大量实例。整数类型与函数参数整数类型Skia 对整数类型的取舍遵循 Google C Style Guide 中 “Integer Types” 一节旧代码正在逐步改造对齐。要点默认使用int只有当确需保证位宽时才使用stdint.h中的定宽类型int32_t等对“计数不为负”等语义用断言SK_ASSERT等而不是unsigned类型来表达位域一律用uint32_t除非出于打包或性能原因必须更短。参数传递惯例必须存在的常量对象参数const 引用传递可选的常量对象参数const 指针传递会被修改的对象参数非 const 指针传递非常引用传参极少使用。// src and paint are optional void SkCanvas::drawBitmapRect(const SkBitmap bitmap, const SkIRect* src, const SkRect dst, const SkPaint* paint nullptr); // metrics is mutable (it is changed by the method) SkScalar SkPaint::getFontMetrics(FontMetric* metrics, SkScalar scale) const;可选参数用“指针 默认 nullptr”表达可变参数用裸指针表达——这一惯例与 Google C 风格“optional 参数优先用指针”的建议一致。长参数列表的换行参数一行放不下时允许两种排版方式方式一——溢出参数与首参对齐void drawBitmapRect(const SkBitmap bitmap, const SkRect dst, const SkPaint* paint nullptr) { this-drawBitmapRectToRect(bitmap, nullptr, dst, paint, kNone_DrawBitmapRectFlag); }方式二——全部参数换行、缩进 8 个空格void drawBitmapRect( const SkBitmap bitmap, const SkRect dst, const SkPaint* paint nullptr) { this-drawBitmapRectToRect( bitmap, nullptr, dst, paint, kNone_DrawBitmapRectFlag); }8 空格缩进对应配置ContinuationIndentWidth: 8与AllowAllParametersOfDeclarationOnNextLine: true两种排版都合规体现了规范“100 列以内、自行判断”的弹性。用 clang-format 自动落实风格上述大部分排版规则已由仓库根目录的 .clang-format 机器化。该文件同时包含 Cpp 与 ObjC 两个配置段文件头注释特别提醒修改 Cpp 段时必须同步修改 ObjC 段。关键配置与文档规则的映射如下文档规则.clang-format 配置行宽 100 列ColumnLimit: 1004 空格缩进、禁用 TabIndentWidth: 4、TabWidth: 4、UseTab: Never开括号不换行、else同行BreakBeforeBraces: Custom 全部BraceWrapping.*: falsecase 相对 switch 缩进IndentCaseLabels: true初始化列表“一行一个且逗号前置、缩进 8 列”ConstructorInitializerAllOnOneLineOrOnePerLine: true、ConstructorInitializerIndentWidth: 8、BreakConstructorInitializersBeforeComma: true控制关键字后留空格SpaceBeforeParens: ControlStatementsinclude 字母序SortIncludes: trueIncludeCategories短函数/if/循环可单行AllowShortFunctionsOnASingleLine: All、AllowShortIfStatementsOnASingleLine: true、AllowShortLoopsOnASingleLine: true指针左对齐SkBitmap *b风格PointerAlignment: Left配置文件头部注释给出了标准工作流# Typical usage is to apply this to the lines youve modified in a local # change. Make sure to install git-clang-format [1] by adding it to your # path and make it executable. # # Stage your changes with git add and then run: # $ git clang-format # You can optionally use the -- file filter to restrict formatting to certain # files or directories. The tool will display the list of files that were # modified. These have been modified without being staged. You can review the # modifications using git diff.即git add暂存改动后运行git clang-format工具只格式化你改动过的行未暂存回文件再用git diff复核。注意适用前提由于部分客户端仍在用较老版本的 clang-format配置中刻意只保留了 clang-format 10 及更早版本支持的选项注释中记录了 Xcode 自带 clang-format 10、bin/clang-format为 11、Homebrew 安装为 14 的对应关系本地格式化工具版本过高反而可能产生与 CI 不一致的噪音。Python 脚本Skia 构建与工具链中包含大量 Python 脚本如 gn 工具目录 下的构建辅助脚本。规范对它们的约定简洁明确Python 代码遵循 Google Python Style Guide。小结Skia 的编码风格是“人读文档 机读配置”双层保障Coding Style Guidelines 给出语义层面的判断标准f/k/g前缀体系、onMethodName模式、参数传递策略.clang-format 则把缩进、括号、换行、include 排序等确定性规则交给git clang-format强制执行。对贡献者而言掌握前者的判断规则、依赖后者处理机械排版是提交补丁前对齐项目风格的最短路径。赞分享图形学图像处理【免费下载链接】skiaSkia is a complete 2D graphic library for drawing Text, Geometries, and Images.项目地址https://gitcode.com/gh_mirrors/skia1/skia点击查看免费下载相关推荐CANN pyasc 项目编码规范全解从 C/MLIR 风格约束到 clang-format 与 clang-tidy 落地实践CANN pyasc 项目编码规范全解从 C/MLIR 风格约束到 clang format 与 clang tidy 落地实践 本文档系统梳理 CANN编程语言编译器人工智能CANNAscendF3D 编码规范多组件 C/Python/Markdown 代码风格约定与 clang-format、Black、Prettier 自动化格式化实践F3D 编码规范多组件 C/Python/Markdown 代码风格约定与 clang format、Black、Prettier 自动化格式化实践 本文3D渲染图形学桌面应用GraphQLBundle类型系统完全指南掌握Object、Input、Enum等核心类型定义GraphQLBundle类型系统完全指南掌握Object、Input、Enum等核心类型定义 GraphQLBundle为Symfony应用提供了完整的Gr上一篇Payload JWT 认证如何签发带 role 的 JWT 并校验请求身份下一篇Android Sunflower深度链接终极指南如何实现URL路由导航与Jetpack Compose集成创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
延伸阅读

更多相关文章

2026/9/25 7:52:52

豆瓣图书知识图谱实战:Neo4j图数据库推荐系统搭建

简介:本资源是一套面向高校计算机及相关专业(人工智能、自动化、物联网等)学生的毕业设计级实践项目,聚焦豆瓣图书推荐系统与知识图谱构建,深度融合Neo4j图数据库应用开发。项目完整覆盖数据采集、清洗、图模型设计、实…

2026/9/25 7:52:52

Oracle 19c Windows静默安装全链路指南:从解压到远程可连

简介:本资源为Oracle Database 19c官方Windows x64平台安装包(WINDOWS.X64-193000-gsm.zip),面向数据库管理员、企业级应用开发者及Oracle认证学习者,解决本地化部署高可用、云就绪型关系数据库的核心需求,…

2026/9/25 8:57:55

Atlas 300V 24G推理加速卡YOLO部署全流程实战解析

最近后台连着收到好几条消息,都是同一个画风:“Atlas 300V 24G到底算不算运算加速卡”“能不能拿它部署YOLO模型”。这问题看着简单,但背后其实藏着一个很常见的认知断层:很多人知道NVIDIA的显卡能跑深度学习,换到昇腾…

2026/9/25 8:57:55

构建一体化客服工作台:通信数据闭环与坐席减负实战

1. 为什么做DeskcommCRM:不只是“通讯录工单”的简单叠加先交代一下背景。我所在的公司是做企业级客户服务的,业务线铺得比较宽,既有售前咨询,也有售后技术支持,还有专门的客户成功团队。最头疼的问题不是“没有工具”…

2026/9/25 8:57:55

Atlas 300V部署YOLO目标检测:从模型转换到推理调优全指南

如果你手里有一块 Atlas 300V 24G 的加速卡,又正好想把 YOLO 这类目标检测模型从 GPU 环境迁过来,那这篇文章就是为你准备的。我会从硬件定位开始讲清楚它到底是什么、适合干什么,再完整走一遍从模型转换到推理部署的全流程,最后把…

2026/9/25 8:52:55

华为IPD与ISO9000融合的研发质量管理方案:从流程框架到落地实践

简介:本资源为基于华为IPD与质量管理体系融合的研发质量管理方案PPT,面向研发管理者、质量工程师及产品经理,帮助理解IPD主业务流框架与ISO9000质量管理体系的结合路径。内容涵盖IPD核心思想、产品实现流程、管理职责、资源管理、度量分析与改…

2026/9/24 20:24:47

GAMP 5 基于风险的计算机化系统验证:软件分类与审计追踪实践

简介:《A Risk-Based Approach to Compliant GxP Computerized Systems》即业内熟知的GAMP 5指南,面向制药企业质量与IT合规人员、验证工程师及计算机化系统管理者,用于解决GxP法规环境下系统合规性难以科学落地的问题。文档以风险管理为主线…

2026/9/23 12:06:55

安全托管MSSP实战:从静态防御到人机协同的攻防运营与应急响应

简介:这份PPT围绕互联网业务安全托管服务展开,面向企业安全负责人、IT运维人员及关注MSSP/MSS选型的读者,重点回应传统安全过度依赖人工、碎片化静态防御难以对抗产业化攻击等痛点。资源共1个pptx文件,包体约30.63MB,以…

2026/9/25 0:02:35

AI元人文:从工具使用到思维重构的深度探索

最近半年我一直在琢磨一件事:AI元人文到底是什么?说白了,就是“用元视角重新审视人与AI的关系”,也在“探索AI如何反向逼着我们发现自己的思考边界”。标题里的“元探索”,在我看就是一层套一层的追问——当你用AI解决…

2026/9/25 0:02:35

Python+CNN车牌识别实战:从数据预处理到模型训练与部署

简介:基于Python与卷积神经网络的车牌识别项目,面向计算机视觉初学者及智能交通开发者,目标是帮助用户掌握从数据预处理、模型构建到实际部署的完整流程。压缩包共25个文件,包含jpg/png图像样本、py训练脚本、md说明文档、dat数据…

2026/9/25 0:02:35

Vim基础操作全攻略:保存退出、模式切换与高频命令实战

1. 项目概述1.1 核心需求解析今天聊聊Vim。写这个题目的原因是:几乎每个后端开发者、运维人员、数据工程师某天都会遇到一个场景——深夜加班,服务器登录界面只有黑底白字,编辑器只有vi/vim,你必须在五分钟内完成一次配置修改并保…

2026/9/22 16:34:32

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/22 20:01:30

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/22 13:25:41

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑