是否有自动生成ostream的工具<<结构或类的运算符?
输入(取自One Debug-Print function to rule them all):
typedef struct ReqCntrlT /* Request control record */
{
int connectionID;
int dbApplID;
char appDescr[MAX_APPDSCR];
int reqID;
int resubmitFlag;
unsigned int resubmitNo;
char VCIver[MAX_VCIVER];
int loginID;
} ReqCntrlT;
输出:
std::ostream& operator <<(std::ostream& os, const ReqCntrlT& r)
{
os << "reqControl { "
<< "\n\tconnectionID: " << r.connectionID
<< "\n\tdbApplID: " << r.dbApplID
<< "\n\tappDescr: " << r.appDescr
<< "\n\treqID: " << r.reqID
<< "\n\tresubmitFlag: " << r.resubmitFlag
<< "\n\tresubmitNo: " << r.resubmitNo
<< "\n\tVCIver: " << r.VCIver
<< "\n\tloginID: " << r.loginID
<< "\n}";
return os;
}
任何工具都可以,Python / Ruby脚本将是首选。
答案 0 :(得分:3)
这需要一个能够准确地解析C ++,枚举各种类/结构,确定并在每个类/结构基础上生成“序列化”,然后将生成的代码保存在“右边”的工具地方“(可能与发现结构的范围相同)。它需要一个完整的预处理器来处理实际代码中指令的扩展。
我们DMS Software Reengineering Toolkit及其C++11 front end可以执行此操作。 DMS通过提供通用解析/ AST构建,符号表构造,流程和自定义分析,转换和源代码重新生成功能,实现了自定义工具的构建。 C ++前端使DMS能够解析C ++并构建精确的符号表,以及将修改过的或新的AST重新打印回可编译的源代码表。 DMS及其C ++前端已用于对C ++代码进行大规模转换。
你必须向DMS解释你想做什么;枚举符号表条目,询问是否有结构/类类型声明,确定声明的范围(记录在符号表条目中),通过组合表面语法模式构造AST,然后应用转换来插入构造的AST似乎很简单。
所需的核心表面语法模式是插槽和函数体的模式:
pattern ostream_on_slot(i:IDENTIFIER):expression =
" << "\n\t" << \tostring\(\i\) << r.\i "; -- tostring is a function that generates "<name>"
pattern ostream_on_struct(i:IDENTIFIER,ostream_on_slots:expression): declaration =
" std::ostream& operator <<(std::ostream& os, const \i& r)
{ os << \tostring\(\i\) << " { " << \ostream_on_slots << "\n}";
return os;
}
必须为ostream_on_slot组成单独的树:
pattern compound_ostream(e1:expression, e2:expression): expression
= " \e1 << \e2 ";
使用这些模式,可以直接枚举struct的槽,为body构造ostream,并将其插入到struct的整个函数中。
答案 1 :(得分:2)
有两种主要方法可以做到这一点:
..当然,他们可以混在一起。
我对Clang Python绑定没有足够的知识来回答使用它们,所以我将专注于元编程。
基本上,你要求的东西需要内省。 C ++不支持完全自省,但是使用元编程技巧(和模板匹配),它可以在编译时支持有限的内省技术子集,这足以达到我们的目的。
为了轻松混合元编程和运行时操作,可以更方便地使用库:Boost.Fusion。
如果您根据Boost.Fusion序列调整结构以使其属性被描述,那么您可以自动对序列应用大量算法。在这里,associate sequence最好。
因为我们正在讨论元编程,the map将类型与类型值相关联。
然后,您可以使用for_each迭代该序列。
我会掩盖细节,因为它已经有一段时间了,我不记得所涉及的语法,但基本上我的想法是:
// Can be created using Boost.Preprocessor, but makes array types a tad difficult
DECL_ATTRIBUTES((connectionId, int)
(dbApplId, int)
(appDescr, AppDescrType)
...
);
这是声明Fusion Map及其相关标签的语法糖:
struct connectionIdTag {};
struct dbApplIdTag {};
typedef boost::fusion::map<
std::pair<connectionIdTag, int>,
std::pair<dbApplIdTag, int>,
...
> AttributesType;
AttributesType _attributes;
然后,只需使用以下命令构建需要对属性应用的任何操作:
// 1. A predicate:
struct Predicate {
template <typename T, typename U>
void operator()(std::pair<T, U> const&) const { ... }
};
// 2. The for_each function
for_each(_attributes, Predicate());
答案 2 :(得分:1)
要实现这一点,唯一的方法是使用您在源文件上运行的外部工具。
首先,您可以使用c/c++ analysing tool,并使用它从源代码中检索解析树。然后,一旦你得到了解析树,你就必须搜索结构。
对于每个结构,您现在可以生成operator<<
重载,以序列化结构的字段。您还可以生成反序列化运算符。
但这取决于你有多少结构:十几个更好的是手动编写运算符,但如果你有几百个结构,你可能想编写(反)序列化运算符生成器。
答案 3 :(得分:0)
我确实以两种方式理解你的问题。
如果您想生成程序的自动状态报告,我建议您检查Boost.Serialization。 但是,它不会在编译时生成代码作为第一步或灵感。 下面的代码将帮助您生成可以阅读的xml或txt文件。
typedef struct ReqCntrlT /* Request control record */
{
int connectionID;
int dbApplID;
char appDescr[MAX_APPDSCR];
int reqID;
int resubmitFlag;
unsigned int resubmitNo;
char VCIver[MAX_VCIVER];
int loginID;
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
ar & connectionID;
ar & reqID;
...
}
} ReqCntrlT;
有关详细信息,请参阅教程:http://www.boost.org/doc/libs/1_49_0/libs/serialization/doc/index.html
如果您只想通过提供参数名称来“编写”代码。 然后你应该看一下python或perl中的正则表达式。 此解决方案的主要默认设置是您对结构“离线”,即每次更改时都必须运行它。
贝努瓦。
答案 4 :(得分:0)
您可以使用LibClang解析源代码并生成ostream运算符:
# © 2020 Erik Rigtorp <erik@rigtorp.se>
# SPDX-License-Identifier: CC0-1.0
import sys
from clang.cindex import *
idx = Index.create()
tu = idx.parse(sys.argv[1], ['-std=c++11'])
for n in tu.cursor.walk_preorder():
if n.kind == CursorKind.ENUM_DECL:
print(
f'std::ostream &operator<<(std::ostream &os, {n.spelling} v) {{\n switch(v) {{')
for i in n.get_children():
print(' case {type}::{value}: os << "{value}"; break;'.format(
type=n.type.spelling, value=i.spelling))
print(' }\n return os;\n}')
elif n.kind == CursorKind.STRUCT_DECL:
print(
f'std::ostream &operator<<(std::ostream &os, const {n.spelling} &v) {{')
for i, m in enumerate(n.get_children()):
print(
f' os << "{", " if i != 0 else ""}{m.spelling}=" << v.{m.spelling};')
print(' return os;\n}')