C/C++如何在循环中动态匹配对应编号的预定义STREAM宏
根本限制说明
C/C++的宏是预处理阶段就完成文本替换的,编译生成的二进制里根本不存在STREAM1、STREAM2这类宏名的信息,所以没法直接靠运行时循环里的变量i拼接宏名实现需求。预处理器扫代码的时候完全不知道运行时i会取什么值,直接写STREAM##i只会被拼成字面量STREAMi,必然编译报错。
下面两种方案都能实现你要的效果,按需选就行:
方案1:预处理批量展开(零运行时开销,和手写100行完全等价)
靠宏拼接+预处理递归展开能力,编译期直接把100次调用的代码全部生成出来,和你手动逐行写100条流输出语句没有任何区别,没有额外运行时开销。
首先要写二级拼接宏:必须加一层中间转发层,才能让传入的数值参数先被展开再拼接,否则##运算符会直接把参数当纯文本处理。
// 宏拼接工具 #define _STREAM_MERGE_IMPL(a, b) a##b #define STREAM_MERGE(a, b) _STREAM_MERGE_IMPL(a, b) // 单条输出逻辑 #define _STREAM_SINGLE_OP(n) STREAM_MERGE(STREAM, n) << some_text_method(n) // 递归展开逻辑:从100往下逐层生成分支,到0终止 #define _STREAM_LOOP_STEP(n) \ if (i == n) { _STREAM_SINGLE_OP(n); } \ else _STREAM_LOOP_IMPL(n-1) #define _STREAM_LOOP_IMPL(n) _STREAM_LOOP_STEP(n) #define _STREAM_LOOP_STEP(0) // 对外用的宏,直接放到你的循环里就行 #define SOME_MACRO_OR_METHOD(i) _STREAM_LOOP_STEP(100)
使用方式和你预期的写法完全一致:
int i; for(i = 1; i <= 100; i++) SOME_MACRO_OR_METHOD(i);
如果项目允许用Boost,直接用Boost.Preprocesser的重复宏可以省掉自己写递归展开的代码,更简洁,连for循环都不用写:
#include <boost/preprocessor/repetition/repeat.hpp> #define _STREAM_GEN_CALL(z, idx, data) STREAM_MERGE(STREAM, idx+1) << some_text_method(idx+1); // 编译期直接展开100次调用 BOOST_PP_REPEAT(100, _STREAM_GEN_CALL, )
方案2:流指针数组(逻辑直观易调试,开销可忽略)
如果觉得宏递归太绕不好维护,可以在初始化阶段把所有流对象的指针按顺序存到数组里,运行时循环直接按索引取流调用就行,性能损耗只有一次数组寻址,完全可以忽略。
// 全局/静态初始化一次即可,嫌手写100个条目麻烦可以配合上面的拼接宏批量生成 static std::ostream* stream_list[] = { &STREAM1, &STREAM2, &STREAM3, // 可以用宏批量生成后续条目,不用手动写到100 &STREAM100 }; // 循环逻辑非常直白 for(int idx = 0; idx < 100; idx++){ *stream_list[idx] << some_text_method(idx + 1); }
这个方案后续要调整调用顺序、跳过某几个流、动态增删流都很方便,调试的时候也能直接看数组里的元素,排查问题比纯宏方案简单很多。
避坑提醒
别尝试运行时拼字符串(比如拼出"STREAM1"这类字符串)再找对应流,C/C++没有内置反射能力,除非你自己额外写一整套名字到流指针的映射表,本质和方案2做的事一样,反而多了字符串匹配的开销,完全没必要。
内容的提问来源于stack exchange,提问作者sol
相关产品推荐
相关产品推荐

