代码之家  ›  专栏  ›  技术社区  ›  Buddhika Chaturanga

C和C++中自包含源文件的原理和用法是什么?

  •  5
  • Buddhika Chaturanga  · 技术社区  · 7 年前

    请参考此 FASTLZ.C 源代码。

    • 113 以及# 128 它包含自己的源文件。

    我认为它的意图是定义以下两个函数名称 FASTLZ_LEVEL 宏值。

    #define FASTLZ_COMPRESSOR fastlz1_compress
    #define FASTLZ_DECOMPRESSOR fastlz1_decompress
    static FASTLZ_INLINE int FASTLZ_COMPRESSOR(const void* input, int length, void* output);
    static FASTLZ_INLINE int FASTLZ_DECOMPRESSOR(const void* input, int length, void* output, int maxout);
    #include "fastlz.c"
    

    #define FASTLZ_COMPRESSOR fastlz2_compress
    #define FASTLZ_DECOMPRESSOR fastlz2_decompress
    static FASTLZ_INLINE int FASTLZ_COMPRESSOR(const void* input, int length, void* output);
    static FASTLZ_INLINE int FASTLZ_DECOMPRESSOR(const void* input, int length, void* output, int maxout);
    #include "fastlz.c"
    

    但我想不出来 C语言中该宏背后的原理或关键特性 ,请有人简要解释一下这种情况吗?

    3 回复  |  直到 7 年前
        1
  •  4
  •   dbush    7 年前

    这定义了两对函数,称为 fastlz1_compress fastlz1_decompress fastlz2_compress fastlz2_decompress . 这两个压缩函数非常相似,除了这里和那里的几行之外,解压函数也是如此。进行两次自包含是为了消除这两对函数定义中的重复。

    以下是该文件所包含内容的缩写版本:

    #if !defined(FASTLZ__COMPRESSOR) && !defined(FASTLZ_DECOMPRESSOR)
    
    ...
    #undef FASTLZ_LEVEL
    #define FASTLZ_LEVEL 1
    
    #undef FASTLZ_COMPRESSOR
    #undef FASTLZ_DECOMPRESSOR
    #define FASTLZ_COMPRESSOR fastlz1_compress
    #define FASTLZ_DECOMPRESSOR fastlz1_decompress
    static FASTLZ_INLINE int FASTLZ_COMPRESSOR(const void* input, int length, void* output);
    static FASTLZ_INLINE int FASTLZ_DECOMPRESSOR(const void* input, int length, void* output, int maxout);
    #include "fastlz.c"
    
    #undef FASTLZ_LEVEL
    #define FASTLZ_LEVEL 2
    
    #undef MAX_DISTANCE
    #define MAX_DISTANCE 8191
    #define MAX_FARDISTANCE (65535+MAX_DISTANCE-1)
    
    #undef FASTLZ_COMPRESSOR
    #undef FASTLZ_DECOMPRESSOR
    #define FASTLZ_COMPRESSOR fastlz2_compress
    #define FASTLZ_DECOMPRESSOR fastlz2_decompress
    static FASTLZ_INLINE int FASTLZ_COMPRESSOR(const void* input, int length, void* output);
    static FASTLZ_INLINE int FASTLZ_DECOMPRESSOR(const void* input, int length, void* output, int maxout);
    #include "fastlz.c"
    
    ...
    
    #else /* !defined(FASTLZ_COMPRESSOR) && !defined(FASTLZ_DECOMPRESSOR) */
    
    static FASTLZ_INLINE int FASTLZ_COMPRESSOR(const void* input, int length, void* output)
    {
       ...
       #if FASTLZ_LEVEL==2
       ...
       #endif
       ...
       #if FASTLZ_LEVEL==1
       ...
       #else
       ...
       #endif
       ...
    }
    
    static FASTLZ_INLINE int FASTLZ_DECOMPRESSOR(const void* input, int length, void* output, int maxout)
    {
       ...
       #if FASTLZ_LEVEL==2
       ...
       #endif
       ...
       #if FASTLZ_LEVEL==1
       ...
       #else
       ...
       #endif
       ...    
    }
    
    #endif
    

    包含 #if 块包含一系列宏定义,但您会注意到它们定义了两次。文件的第二部分包含 #else 块基本上包含一对函数模板。

    第一部分定义一些宏,然后包括宏本身。在自我包容方面 #其他 零件生效。这定义了 fastlz1\u压缩 fastlz1\u解压缩 基于 FASTLZ_COMPRESSOR FASTLZ_DECOMPRESSOR 宏。因为 FASTLZ_LEVEL 设置为1,这将激活 fastlz1\u压缩 fastlz1\u解压缩 特定代码。

    在第一次自包含之后,这些宏将被取消定义,然后重新定义为 fastlz2\u压缩 fastlz2\u解压缩 ,则文件将再次自包含。所以 #其他 零件再次拉入,但这次效果是 fastlz2\u压缩 fastlz2\u解压缩 定义,并且特定于这些功能的代码通过 FASTLZ\U级别

    要做到这一点,一种稍微不那么令人困惑的方法是将所有内容放在外部 #如果 #其他 在一个文件中 #其他 #endif 在另一个文件中。

    更好的方法是创建一个压缩函数和一个解压缩函数,每个函数都接受一个参数来指定级别,而不是使用宏技巧。例如:

    static FASTLZ_INLINE int fastlz_compress(const void* input, int length, void* output, int level)
    {
       ...
       if (level==2) {
       ...
       }
       ...
       if (level==1) {
       ...
       } else {
       ...
       }
       ...
    }
    
        2
  •  1
  •   Pethead    7 年前

    如果使用得当,这可能是一种有用的技术。

    假设您有一个复杂的、性能关键的子系统,它有一个相当小的公共接口和许多不可重用的实现代码。代码可以运行数千行、大约100个私有函数和相当多的私有数据。如果您使用的是非平凡的嵌入式系统,那么您可能会足够频繁地处理这种情况。

    您的解决方案可能是分层、模块化和解耦的,通过在不同的文件中对子系统的不同部分进行编码,可以有效地表示和增强这些方面。

    使用C,这样做可能会损失很多。几乎所有的工具链都为单个编译单元提供了不错的优化,但对任何声明为外部的东西都非常悲观。

    如果将所有内容都放在一个C源代码模块中,那么-

    • 性能和;代码大小的改进—在许多情况下,函数调用将内联。即使没有内联,编译器也有机会生成更高效的代码。
    • 链接级数据(&A);函数隐藏。
    • 避免名称空间污染及其必然结果—您可以使用更简单的名称。
    • 更快的编译速度;悬挂机构。

    但在编辑此文件时,您也会遇到一个不洁的混乱,并且您会失去隐含的模块性。可以通过将源代码拆分为多个文件并包含这些文件来生成单个编译单元来克服这一问题。

    不过,您需要强制实施一些约定来正确管理这一点。这些在某种程度上取决于您的工具链,但一些通用指针是-

    • 将公共接口放在一个单独的头文件中-无论如何都应该这样做。
    • 有一条主线。包含所有子公司的c文件。c文件。这 还可以包含公共接口的代码。

    • 使用编译器保护来确保私有头和源模块 不包括在外部编译单元中。

    • 所有私有数据;函数应声明为静态。

    • 保持概念上的区别。c和。h文件。这 利用现有约定。不同的是 标题中有很多静态声明。

    • 如果您的工具链没有强加任何不这样做的理由,请将 实现文件为。c和。h、 如果使用include guards 不会生成代码,也不会引入新名称 链接期间的一些空段)。最大的优势是 工具(如IDE)将适当处理这些文件。

        3
  •  1
  •   Michał Łoś    7 年前

    直列式 27 :

    #if !defined(FASTLZ__COMPRESSOR) && !defined(FASTLZ_DECOMPRESSOR)

    您有条件预处理器指令 164 开始它的“else”部分,在文件末尾结束。这将文件分为两部分:通用fastlz函数的主声明,编译器会对其进行一次解析;第二部分包含两个函数的实现( FASTLZ\u压缩机 FASTLZ\u减压器 ).

    这两个函数实现的名称恰好是宏定义,它们被文件的第一部分包含了两次,在包含之前不久,一些参数被声明为宏定义(它们的最终名称和其他一些参数)。

    这是保持代码干燥的C方式,不要重复两个几乎相同的实现。在C++中(由于添加了C++标记),您还有其他用于相同目的的机制:继承、模板、模板专门化、if constexpr等。

    顺便说一句:前面提到过 first if-def line 可能包含错误-它检查是否定义了任何定义,但第一个定义的名称“ FASTLZ\uu压缩机 “包含” __ '而不是' _ '稍后使用。幸运的是,这段代码运行良好,因为选中的定义总是一起定义:)。