如何消除C++模板化命令行选项处理的末尾多余空格?
问题
我有一个包含多个选项的结构体my_options_t,每个选项会根据自身值转换为带值或不带值的命令行选项,选项顺序不影响结果。由于反标记化的目标不一定是字符串,分隔符也不一定是字符(可能是操作编组结构的函子对象),所以需要用模板代码处理。
目前实现的模板函数如下:
template <typename MarshalTarget, typename Delimiter> void process(const my_options_t& opts, MarshalTarget& marshalled, Delimiter optend) { if (opts.generate_relocatable_code) { marshalled << "--relocatable-device-code=true" << optend; } if (opts.compile_extensible_whole_program) { marshalled << "--extensible-whole-program=true" << optend; } if (opts.debug) { marshalled << "--device-debug" << optend; } if (opts.generate_line_info) { marshalled << "--generate-line-info" << optend; } if (opts.support_128bit_integers) { marshalled << "--device-int128" << optend; } if (opts.indicate_function_inlining) { marshalled << "--optimization-info=inline" << optend; } if (opts.compiler_self_identification) { marshalled << "--version-ident=true" << optend; } if (not opts.builtin_initializer_list) { marshalled << "--builtin-initializer-list=false" << optend; } if (opts.specify_language_dialect) { marshalled << "--std=" << detail_::cpp_dialect_names[(unsigned) opts.language_dialect] << optend; } // etc. etc. }
但这个函数存在问题:当MarshalTarget为std::ostream、Delimiter为char时,生成的命令行片段末尾会多出一个空格,希望找到合适的解决方法避免这个多余空格。
解决方法
方案1:用状态标记控制分隔符输出
添加一个布尔变量标记是否已输出第一个选项,仅在已有选项输出的情况下才插入分隔符。用lambda封装选项添加逻辑,简化重复代码:
template <typename MarshalTarget, typename Delimiter> void process(const my_options_t& opts, MarshalTarget& marshalled, Delimiter optend) { bool first_option = true; auto add_option = [&](auto&&... args) { if (!first_option) { marshalled << optend; } marshalled << std::forward<decltype(args)>(args)...; first_option = false; }; if (opts.generate_relocatable_code) { add_option("--relocatable-device-code=true"); } if (opts.compile_extensible_whole_program) { add_option("--extensible-whole-program=true"); } if (opts.debug) { add_option("--device-debug"); } if (opts.generate_line_info) { add_option("--generate-line-info"); } if (opts.support_128bit_integers) { add_option("--device-int128"); } if (opts.indicate_function_inlining) { add_option("--optimization-info=inline"); } if (opts.compiler_self_identification) { add_option("--version-ident=true"); } if (not opts.builtin_initializer_list) { add_option("--builtin-initializer-list=false"); } if (opts.specify_language_dialect) { add_option("--std=", detail_::cpp_dialect_names[(unsigned) opts.language_dialect]); } // etc. etc. }
这种方式确保分隔符只出现在选项之间,不会在末尾生成多余符号。
方案2:自定义分隔器适配器
如果不想大幅修改原有选项输出逻辑,可以封装一个适配器类,自动处理分隔符的插入时机:
template <typename Target, typename Delim> class DelimiterAdapter { public: DelimiterAdapter(Target& target, Delim delim) : target_(target), delim_(delim), first_(true) {} template <typename T> DelimiterAdapter& operator<<(const T& value) { if (!first_) { target_ << delim_; } target_ << value; first_ = false; return *this; } private: Target& target_; Delim delim_; bool first_; }; template <typename MarshalTarget, typename Delimiter> void process(const my_options_t& opts, MarshalTarget& marshalled, Delimiter optend) { DelimiterAdapter adapter(marshalled, optend); if (opts.generate_relocatable_code) { adapter << "--relocatable-device-code=true"; } if (opts.compile_extensible_whole_program) { adapter << "--extensible-whole-program=true"; } if (opts.debug) { adapter << "--device-debug"; } // ... 其余选项逻辑保持不变 }
适配器会接管分隔符的插入逻辑,对外接口和原代码几乎一致,无需修改每个选项的输出写法。
方案3:先收集选项再统一输出
如果MarshalTarget支持批量操作,可以先收集所有有效选项,再用分隔符连接后一次性输出。注意这个方案仅适用于目标为字符串或支持批量处理的场景,对函子对象类目标可能不适用:
template <typename MarshalTarget, typename Delimiter> void process(const my_options_t& opts, MarshalTarget& marshalled, Delimiter optend) { std::vector<std::string> options; if (opts.generate_relocatable_code) { options.push_back("--relocatable-device-code=true"); } if (opts.compile_extensible_whole_program) { options.push_back("--extensible-whole-program=true"); } // ... 收集所有有效选项 for (size_t i = 0; i < options.size(); ++i) { if (i != 0) marshalled << optend; marshalled << options[i]; } }
内容的提问来源于stack exchange,提问作者einpoklum
相关产品推荐
相关产品推荐

