You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过宏技巧将5个相似C测试文件合并为单个文件?

精简多份重复测试代码的宏方案

问题背景

我有5个C语言测试源文件,核心逻辑完全一致,仅存在一处名称差异:将代码中的fread分别替换为getline、mmap_getline、mmap_memchr和read,差异点包括:

  • 最后一条#include指令(如../src/readlines_fread.c)
  • 测试函数名(如test_readlines_fread_success)
  • TEST_CHECK中调用的函数(如readlines_fread)
  • TEST_LIST中的测试条目名称和函数指针

原测试代码示例:

#include <assert.h>
#include <stdio.h>
#include <string.h>

#include "acutest.h"

#include "../src/FileBuf.h"
#include "../src/readlines_fread.c"

void test_readlines_fread_success(void)
{
    /* The lines and line lengths are at most 1024 in sample1.txt. */
    char buf[1024];
    char *lines[1024];
    size_t line_count = 0;
    FILE *const fp = fopen("test/sample.txt", "r");

    TEST_ASSERT(fp);

    while (fgets(buf, sizeof buf, fp)) {
        buf[strcspn(buf, "\n")] = '\0';
        TEST_ASSERT((lines[line_count] = strdup(buf)));
        ++line_count;
    }
    
    rewind(fp);

    FileBuf fbuf = {};

    TEST_CHECK(readlines_fread(fp, &fbuf));
    TEST_CHECK(fbuf.count == 555);
    TEST_CHECK(fbuf.size == 120635);

    for (size_t i = 0; i < fbuf.count; ++i) {
        TEST_CHECK((strcmp(lines[i], fbuf.lines[i].line) == 0));
    }

    fclose(fp);
}

TEST_LIST = {
    { "test_readlines_fread_success", test_readlines_fread_success },
    { nullptr, nullptr },
};

需要将这5份文件合并为单个文件,通过编译时的宏定义实现不同名称的替换。


解决方案:预处理器宏替换技巧

1. 修改测试文件为模板形式

将代码中所有与fread相关的名称替换为宏占位符,同时通过条件编译处理头文件引入:

#include <assert.h>
#include <stdio.h>
#include <string.h>

#include "acutest.h"
#include "../src/FileBuf.h"

// 根据编译宏选择引入对应的实现文件
#if defined(READ_IMPL) && READ_IMPL == FREAD
#include "../src/readlines_fread.c"
#elif READ_IMPL == GETLINE
#include "../src/readlines_getline.c"
#elif READ_IMPL == MMAP_GETLINE
#include "../src/readlines_mmap_getline.c"
#elif READ_IMPL == MMAP_MEMCHR
#include "../src/readlines_mmap_memchr.c"
#elif READ_IMPL == READ
#include "../src/readlines_read.c"
#else
#error "Must define READ_IMPL with valid value"
#endif

// 宏工具:拼接标识符和字符串化
#define STRINGIFY(x) #x
#define CONCAT(x, y) x##y
#define TEST_FUNC_NAME(prefix, impl) CONCAT(prefix, impl)
#define TEST_FUNC_STR(prefix, impl) STRINGIFY(CONCAT(prefix, impl))

// 用宏生成测试函数名
void TEST_FUNC_NAME(test_readlines_, READ_IMPL)_success(void)
{
    char buf[1024];
    char *lines[1024];
    size_t line_count = 0;
    FILE *const fp = fopen("test/sample.txt", "r");

    TEST_ASSERT(fp);

    while (fgets(buf, sizeof buf, fp)) {
        buf[strcspn(buf, "\n")] = '\0';
        TEST_ASSERT((lines[line_count] = strdup(buf)));
        ++line_count;
    }
    
    rewind(fp);

    FileBuf fbuf = {};

    // 调用对应的readlines_*函数
    TEST_CHECK(CONCAT(readlines_, READ_IMPL)(fp, &fbuf));
    TEST_CHECK(fbuf.count == 555);
    TEST_CHECK(fbuf.size == 120635);

    for (size_t i = 0; i < fbuf.count; ++i) {
        TEST_CHECK((strcmp(lines[i], fbuf.lines[i].line) == 0));
    }

    fclose(fp);
}

// 用宏生成TEST_LIST条目
TEST_LIST = {
    { TEST_FUNC_STR("test_readlines_", READ_IMPL)"_success", TEST_FUNC_NAME(test_readlines_, READ_IMPL)_success },
    { nullptr, nullptr },
};

2. 定义宏常量枚举(可选,提升可读性)

可创建头文件read_impl.h,定义各个实现的常量:

#define FREAD 1
#define GETLINE 2
#define MMAP_GETLINE 3
#define MMAP_MEMCHR 4
#define READ 5

编译时通过-include read_impl.h引入,或直接在编译命令中定义。

3. 编译命令示例

针对每个实现,通过-D参数指定READ_IMPL的值:

  • 编译fread版本:
gcc -DREAD_IMPL=FREAD -I. test_readlines_template.c -o test_fread
  • 编译getline版本:
gcc -DREAD_IMPL=GETLINE -I. test_readlines_template.c -o test_getline
  • 编译mmap_getline版本:
gcc -DREAD_IMPL=MMAP_GETLINE -I. test_readlines_template.c -o test_mmap_getline
  • 编译mmap_memchr版本:
gcc -DREAD_IMPL=MMAP_MEMCHR -I. test_readlines_template.c -o test_mmap_memchr
  • 编译read版本:
gcc -DREAD_IMPL=READ -I. test_readlines_template.c -o test_read

关键宏说明

  • CONCAT(x, y):用于拼接标识符(如readlines_和FREAD拼接为readlines_FREAD)
  • STRINGIFY(x):将宏拼接后的结果转换为字符串,用于TEST_LIST中的名称字段
  • 条件编译#if defined(READ_IMPL):确保编译时必须指定有效的实现类型,避免无意义的编译错误

内容的提问来源于stack exchange,提问作者Madagascar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 04:32:21