1.7 实例分析

1.7.1 PurC函数库头文件

下面通过查看笔者维护的开源HVML解释器PurC的purc.h头文件中的部分内容,来对本章所述的优良编码风格做进一步的解释。

首先是一段Doxygen格式的注释,其中包含了文件名、作者、日期、简介、版权声明等信息。注意,Doxygen格式的注释一般以/**开始。

/**
 * @file purc.h
 * @author Vincent Wei
 * @date 2021/07/02
 * @brief The main header file of PurC.
 *
 * Copyright (C) 2021, 2022 FMSoft
 *
 * This file is a part of PurC (short for Purring Cat), an HVML interpreter.
 * Licensed under LGPLv3+.
 */

之后,作为PurC函数库的主头文件,其中包含了其他必要的系统头文件以及PurC函数库的其他头文件。然后是一个结构体的定义,该结构体被定义为一个新的数据类型,同时使用Doxygen格式的注释来描述其成员的含义。

/**
 * purc_instance_extra_info:
 *
 * The structure defines the extra information for a new PurC instance.
 */
typedef struct purc_instance_extra_info {
    /**
     * ...
     */
    purc_rdrcomm_k  renderer_comm;
 
    /**
     * ...
     */
    const char      *renderer_uri;
 
    /** The SSL certification if using Secured WebSocket. */
    const char      *ssl_cert;
 
    /** The SSL key if using Secured WebSocket. */
    const char      *ssl_key;
 
    /** The default workspace of this instance. */
    const char      *workspace_name;
 
    /** The title of the workspace. */
    const char      *workspace_title;
 
    /**
     * ...
     */
    const char      *workspace_layout;
} purc_instance_extra_info;

我们可以清晰地看到,PurC函数库的接口使用了K&R命名法。注意上述结构体中的purc_rdrcomm_k renderer_comm成员,从类型名使用_k后缀可以看出,该成员是一个枚举量,对应的枚举类型则定义在另一个头文件(purc-pcrdr.h)中:

/* Renderer communication types */
typedef enum {
    PURC_RDRCOMM_HEADLESS  = 0,
#define PURC_RDRCOMM_NAME_HEADLESS      "HEADLESS"
    PURC_RDRCOMM_THREAD,
#define PURC_RDRCOMM_NAME_THREAD        "THREAD"
    PURC_RDRCOMM_SOCKET,
#define PURC_RDRCOMM_NAME_SOCKET        "SOCKET"
    PURC_RDRCOMM_HIBUS,
#define PURC_RDRCOMM_NAME_HIBUS         "HIBUS"
} purc_rdrcomm_k;

回到purc.h头文件。再往下是宏定义:

#define PURC_HAVE_UTILS         0x0001
#define PURC_HAVE_DOM           0x0002
#define PURC_HAVE_HTML          0x0004
#define PURC_HAVE_XML           0x0008
#define PURC_HAVE_VARIANT       0x0010
#define PURC_HAVE_EJSON         0x0020
#define PURC_HAVE_FETCHER       0x0200
#define PURC_HAVE_FETCHER_R     0x0400
#define PURC_HAVE_ALL (           \
        PURC_HAVE_UTILS         | \
        PURC_HAVE_DOM           | \
        PURC_HAVE_HTML          | \
        PURC_HAVE_XML           | \
        PURC_HAVE_VARIANT       | \
        PURC_HAVE_EJSON         | \
        PURC_HAVE_FETCHER       | \
        PURC_HAVE_FETCHER_R)
 
#define PURC_MODULE_UTILS      (PURC_HAVE_UTILS)
#define PURC_MODULE_DOM        (PURC_MODULE_UTILS    | PURC_HAVE_DOM)
#define PURC_MODULE_HTML       (PURC_MODULE_DOM      | PURC_HAVE_HTML)
#define PURC_MODULE_XML        (PURC_MODULE_DOM      | PURC_HAVE_XML)
#define PURC_MODULE_VARIANT    (PURC_MODULE_UTILS    | PURC_HAVE_VARIANT)
#define PURC_MODULE_EJSON      (PURC_MODULE_VARIANT  | PURC_HAVE_EJSON)
#define PURC_MODULE_ALL         0xFFFF

所有的宏都采用下画线连接的全大写单词形式,并使用PURC_作为前缀;代码行严守了“80列”这条红线,并通过空行、续行和适当的缩进,让排版整齐,使得整个代码清晰易读。

下面查看其中的3个函数接口:前两个用于初始化一个PurC实例,第三个用于清理一个PurC实例。

PCA_EXPORT int
purc_init_ex(unsigned int modules,
        const char *app_name, const char *runner_name,
        const purc_instance_extra_info *extra_info);
 
static inline int purc_init(const char *app_name, const char *runner_name,
        const purc_instance_extra_info *extra_info)
{
    return purc_init_ex(PURC_MODULE_ALL, app_name, runner_name, extra_info);
}
 
PCA_EXPORT bool
purc_cleanup(void);

这3个函数的原型定义符合本章所讨论的编码风格。比如,purc_init_ex()purc_init()两个函数均具有purc_前缀,表示指针的星号出现在形参名称的前面;为严守“80列”这条红线,函数原型声明中的参数列表被分行书写;等等。作为PurC函数库的公开接口,原头文件中包含了对以上函数的接口描述。

注意purc_init()函数被定义为调用purc_init_ex()函数的内联函数,因而使用static inline关键词来修饰其原型。而_ex后缀通常用于表示扩展(extended);也就是说,purc_init_ex()函数是purc_init()函数的扩展版本。