C++ 如何在不重复代码的情况下统一实现双向转换?
我在C遗留代码中使用了两个大型C结构,我需要从一个转换到另一个,反之亦然。大概是这样的:C++ 如何在不重复代码的情况下统一实现双向转换?,c++,design-patterns,data-structures,C++,Design Patterns,Data Structures,我在C遗留代码中使用了两个大型C结构,我需要从一个转换到另一个,反之亦然。大概是这样的: #include <iostream> struct A { int a; float b; }; struct B { char a; int b; }; struct C { A a; B b; }; struct D { int a; char b; float c; }; void CtoD( const C
#include <iostream>
struct A {
int a;
float b;
};
struct B {
char a;
int b;
};
struct C {
A a;
B b;
};
struct D {
int a;
char b;
float c;
};
void CtoD( const C& c, D &d ) {
d.a = c.a.a;
d.b = c.b.a;
d.c = c.a.b;
}
void DtoC( const D &d, C& c ) {
c.a.a = d.a;
c.b.a = d.b;
c.a.b = d.c;
}
int main()
{
C c = { { 1, 3.3f }, { 'a', 4 } };
D d = { 1, 'b', 5.5f };
#if 0
CtoD( c, d );
#else
DtoC( d, c );
#endif
std::cout<<"C="<<c.a.a<<" "<<c.a.b<<" "<<c.b.a<<" "<<c.b.b<<std::endl;
std::cout<<"D="<<d.a<<" "<<d.b<<" "<<d.c<<std::endl;
}
#包括
结构A{
INTA;
浮球b;
};
结构B{
字符a;
int b;
};
结构C{
A A;
B B;
};
结构D{
INTA;
字符b;
浮点数c;
};
无效CtoD(常数C&C、D&D){
d、 a=c.a.a;
d、 b=c.b.a;
d、 c=c.a.b;
}
无效DtoC(施工图设计、施工图设计、施工图设计){
c、 a.a=d.a;
c、 b.a=d.b;
c、 a.b=直流电;
}
int main()
{
C C={1,3.3f},{'a',4};
D D={1,'b',5.5f};
#如果0
CtoD(c,d);
#否则
DtoC(d,c);
#恩迪夫
std::cout在您的文字示例中,我认为不值得这么麻烦。只需编写测试,以确保转换工作正常
在实际代码中,如果结构有“数百个字段”,您的结构可能设计得不好。也许它们应该由较小的对象组成。我从未设计过任何需要在完全相同的结构对象中包含数百个字段的内容-相反,这些字段允许进行某种分类,以便可以将其分为较小的组
因为您的代码是遗留的,您不想重写它,所以只需为您的转换函数编写测试,正如我在上面的示例中所说的
经过良好测试的代码不再是遗留代码。遗留代码基本上是没有自动测试的代码
如果不能重写,则必须测试它。
关于“双向”测试的成本,Idan Arye's表示:
因为转换是对称的,所以对它进行双向测试并不是那么简单
比用一种方法测试要多得多。你所需要做的就是初始化二
结构-cc
和dd
-并将它们设置为的转换版本
然后你只需要检查一下CtoD(c)==d
DtoC(d)=c
(如果碰巧有比较函数,请使用它们
这里的主要工作是初始化c
和d
——但是你会
如果你想测试单向转换,无论如何都必须这样做,所以
为另一种方式添加测试非常便宜
您可以使用一个包含数百个std::对所涉及的子对象的引用的容器来实现这一点。使用引用,您既可以读也可以写,因此从左侧对象读取和写入右侧对象转换为单向。相反,转换为另一种方式。选择您最喜欢的脚本语言(如果您还没有,我建议使用Ruby)并编写一个小脚本,为您生成转换函数(源文件和头文件)
除非您选择一种蹩脚的脚本语言,否则在调用生成转换器的函数时,您甚至可以直接用该语言表示连接。例如,在Ruby中定义generate\u converters
后,您可以编写:
generate_converters :C,:D do
convert 'a.a','a'
convert 'b.a','b'
convert 'a.b','c'
end
我同意Daniel的观点,不值得这么麻烦,但你可以编写一个小应用程序来为你生成代码。你向应用程序提供两个结构的描述,以及结构成员之间的绑定,然后应用程序生成C代码,然后像往常一样编译
另一种选择是摆弄,但这可能会消耗更多开发人员的时间,因此比第一种选择更不值得麻烦。让我们变得淘气吧
struct rightwards_t {} rightwards;
struct leftwards_t {} leftwards;
template<typename Left, typename Right>
inline void map_field(Left& left, const Right& right, leftwards_t) {
left = right;
}
template<typename Left, typename Right>
inline void map_field(const Left& left, Right& right, rightwards_t) {
right = left;
}
template<typename Direction>
void convert(C& c, D& d, Direction direction) {
map_field(c.a.a, d.a, direction);
map_field(c.b.a, d.b, direction);
map_field(c.a.b, d.c, direction);
}
// Usage
C c;
D d;
convert(c, d, leftwards); // Converts d into c
convert(c, d, rightwards); // Converts c into d
struct righwards\u t{}righwards;
结构左向{}左向;
模板
内联无效映射字段(左侧和左侧、常量右侧和右侧、左侧){
左=右;
}
模板
内联无效映射字段(常量左和左、右和右、向右){
右=左;
}
模板
无效转换(C&C、D&D、方向){
地图区域(c.a.a、d.a、方向);
地图区域(c.b.a、d.b、方向);
地图区域(c.a.b、d.c、方向);
}
//用法
C C;
D;
convert(c,d,leftwards);//将d转换为c
转换(c,d,向右);//将c转换为d
我真的不知道它是否有效(手边没有编译器),但我想写它。如果有人能帮我纠正它,请做。我花了一段时间才弄明白怎么做。我想出了下一个解决方案:
#include <iostream>
#include <algorithm>
#include <cstring>
struct A {
int a;
float b;
};
struct B {
char a;
int b;
};
struct C {
A a;
B b;
};
struct D {
int a;
char b;
float c;
};
template< typename T1, typename T2 >
struct DataField
{
static inline void Update( const T1 & src, T2 & dst ) { dst = src; }
static inline void Update( T1 & dst, const T2 & src ) { dst = src; }
};
template<>
struct DataField< const char*, char* >
{
static inline void Update( const char* src, char* dst ) { strcpy( dst, src ); }
};
template<>
struct DataField< char*, const char* >
{
static inline void Update( char* dst, const char* src ) { strcpy( dst, src ); }
};
template< typename T1, typename T2, int N >
struct DataField< T1[N], T2[N] >
{
static inline void Update( const T1 (&src)[N], T2 (&dst)[N] ) { std::copy_n( src, N, dst ); }
static inline void Update( T1 (&dst)[N], const T1 (&src)[N] ) { std::copy_n( src, N, dst ); }
};
template< typename T1, typename T2 >
void UpdateDataField( T1 & src, T2 & dst )
{
DataField< T1, T2 >::Update( src, dst );
}
template< typename T1, typename T2 >
void UpdateMappedDataFields( T1 & src, T2 & dst )
{
UpdateDataField( src.a.a, dst.a );
UpdateDataField( src.a.b, dst.c );
UpdateDataField( src.b.a, dst.b );
}
void CtoD( const C& c, D &d ) {
UpdateMappedDataFields( c, d );
}
void DtoC( const D &d, C& c ) {
UpdateMappedDataFields( c, d );
}
int main()
{
C c = { { 1, 3.3f }, { 'a', 4 } };
D d = { 1, 'b', 5.5f };
#if 0
CtoD( c, d );
#else
DtoC( d, c );
#endif
std::cout<<"C="<<c.a.a<<" "<<c.a.b<<" "<<c.b.a<<" "<<c.b.b<<std::endl;
std::cout<<"D="<<d.a<<" "<<d.b<<" "<<d.c<<std::endl;
}
#包括
#包括
#包括
结构A{
INTA;
浮球b;
};
结构B{
字符a;
int b;
};
结构C{
A A;
B B;
};
结构D{
INTA;
字符b;
浮点数c;
};
模板
结构数据字段
{
静态内联无效更新(常量T1&src,T2&dst){dst=src;}
静态内联无效更新(T1&dst,constt2&src){dst=src;}
};
模板
结构数据字段<常量字符*,字符*>
{
静态内联无效更新(const char*src,char*dst){strcpy(dst,src);}
};
模板
结构数据字段<字符*,常量字符*>
{
静态内联无效更新(char*dst,const char*src){strcpy(dst,src);}
};
模板
结构数据字段
{
静态内联无效更新(常量T1(&src)[N],T2(&dst)[N]){std::copy_N(src,N,dst);}
静态内联无效更新(T1(&dst)[N],常数T1(&src)[N]){std::copy_N(src,N,dst);}
};
模板
无效更新数据字段(T1和src、T2和dst)
{
数据字段:更新(src,dst);
}
模板
void UpdateMappedDataFields(T1和src、T2和dst)
{
更新数据字段(src.a.a、dst.a);
更新数据字段(src.a.b、dst.c);
更新数据字段(src.b.a、dst.b);
}
无效CtoD(常数C&C、D&D){
UpdateMappedDataFields(c,d);
}
无效DtoC(施工图设计、施工图设计、施工图设计){
UpdateMappedDataFields(c,d);
}
int main()
{
C C={1,3.3f},{'a',4};
D D={1,'b',5.5f};
#如果0
CtoD(c,d);
#否则
DtoC(d,c);
#恩迪夫
std::cout与Idan和Dialogicus的建议类似,您也可以使用编辑器的搜索和替换功能:
例如,手动写入CtoD
,将正文复制到Dt
Find: ^(.*)=(.*);
Replace: $2=$1;