(原创)结构体自动化转为char数组的实现
结构体自动化转换为char数组这个需求,来自于一个最近开发的一个项目,在项目开发过程中遇到一个小问题,需要将各种结构体拷贝到char数组中,这对于一个简单的结构体来说是很简单的事情,比如下面这个只有整形字段的结构体:
struct A
{
int a;
int b;
}; char buf[];
A a = {,};
memcpy(buf, &a, sizeof(A));
一句memcpy就能将结构体a拷贝到char数组中去了,直接通过memcpy拷贝结构体只对于内存连续的结构体有效。如果结构体内存不连续,结构体中含有double、string、指针甚至嵌套结构体时,直接拷贝是错误的,这时需要一个一个字段的转换,比如下面的结构体:
struct A
{
int x;
string y;
};
char buf[];
A a = {, "test"};
char* p = buf;
*((int*)p) = x;
p+=sizeof(int);
strcpy(p, t.c_str());
可以看到这种一个一个字段转换的方法是很繁琐的,字段越多转换就越繁琐,而且偏移量还容易写错。当结构体字段是指针或者结构体时就更繁琐了,比如下面的结构体:
struct A
{
int x;
int y;
};
struct B
{
int x;
int count; //标示指针p中的元素个数
int *p;
A a;
};
char buf[];
B b = {, , new int[]{, }, {, }};
char* p = buf;
*((int*)p) = b.x;
p+=sizeof(int);
*((int*)p) = b.count;
p+=sizeof(int);
for(int i=; i<count; i++)
{
*((int*)p) = b.p[i];
}
p+=sizeof(int)*b.count;
*((int*)p) = b.a.x;
p+=sizeof(int);
*((int*)p) = b.a.y;
可以看到这种带指针或者嵌套结构体的结构体转换为char数组时非常繁琐,而且很容易出错,其实大部分的工作都是重复的,比如不断的赋值与偏移。这个过程如果能做到自动化就很方便了,直接传一个结构体,然后自动将结构体中的字段一个一个拷贝到数组中,不必关心偏移是否出错了,也不用关心内部的字符串或者嵌套结构体如何拷贝等细节,总之 ,只要传一个结构体就能自动化的将其转换为char数组。
这种自动化的转换不仅仅大大降低了转换的复杂度还解决了手工拷贝的时候容易出错的问题,程序自动化的拷贝保证了拷贝的正确性。目前我还没看到有类似的转换类或者函数能够自动地很方便地将复杂结构体转换为char数组,想想自己实现一个也不是难事,其实要实现自动转换最关键的是要获取结构体的元信息,有了元信息我们就能对结构体中的每个字段进行转换了。在c#中可以通过反射很方便的获取结构体的元信息,而c++中没有反射,就要想其它办法来获取元信息了。而且这个获取元信息的方法还要非常简单,几乎不增加额外的负担。这里我是通过tuple来获取原信息,要求每个结构体要定义一个Get方法来返回其字段的基本信息,比如:
struct A
{
int x;
double y; auto Get()->decltype(std::make_tuple(x,y))
{
return std::make_tuple(x,y);
}
};
这个Get方法非常简单,只要将字段放到tuple中返回出去就行了,几乎没有增加额外的负担,这个看似简单函数却有着巨大的作用,只要有这个Get函数我就可以获取结构体的基本元信息了,有了这个以后,所有的转换都可以实现自动化了。还是来看看具体是如何实现自动化的转换吧:
#include <type_traits>
#include <TpForeach.hpp>
#include <Any.hpp> namespace Cosmos
{
namespace Detail
{
struct Functor
{
Functor(char* buf, int len) :m_buf(buf), m_bufLen(len)
{
} template<typename T>
typename std::enable_if<!std::is_class<T>::value>::type operator()(T t)
{
FillForward(t);
} template<typename T>
typename std::enable_if<std::is_class<T>::value>::type operator()(T& t)
{
FillForward(t);
} private:
template<typename T>
void FillForward(T&& t)
{
if (std::is_same<T, int>::value || std::is_same<T, unsigned int>::value)
m_latestInt = t; FillIn(std::forward<T>(t), m_buf + m_curPos);
m_curPos += sizeof(T);
} template<typename T>
typename std::enable_if<std::is_integral<T>::value>::type FillIn(T t, char* p)
{
*((T*) p) = t;
} template<typename T>
typename std::enable_if<std::is_floating_point<T>::value>::type FillIn(T t, char* p)
{
if(std::is_same<T,double>::value)
sprintf(p, "%.15f", t);
else
sprintf(p, "%f", t);
} template<typename T>
typename std::enable_if<std::is_pointer<T>::value&&std::is_class<typename std::remove_pointer<T>::type>::value>::type FillIn(T t, char* p)
{
Fill(t, p, [this, &t](int i, char* p, int size){Put(p + i*size, size, t[i]); });
} template<typename T>
typename std::enable_if<std::is_pointer<T>::value&&std::is_arithmetic<typename std::remove_pointer<T>::type>::value>::type FillIn(T t, char* p)
{
Fill(t, p, [this, &t](int i, char* p, int size){FillIn(t[i], p + i*size); });
} template<typename T, typename F>
void Fill(T t, char* p, F&& f)
{
int count = m_latestInt.AnyCast<int>();
using U = typename std::remove_pointer<T>::type;
for (int i = ; i < count; i++)
{
f(i, p, sizeof(U));
}
} template<typename T>
typename std::enable_if<std::is_pointer<T>::value&&std::is_void<typename std::remove_pointer<T>::type>::value>::type FillIn(T t, char* p)
{
int count = m_latestInt.AnyCast<int>();
int* temp = (int*) t;
for (int i = ; i < count; i++)
{
FillIn(temp[i], p + i*sizeof(int));
}
} template<typename T>
typename std::enable_if<std::is_same<std::string, T>::value>::type FillIn(T& t, char* p)
{
strcpy(p, t.c_str());
} template<typename T>
typename std::enable_if<std::is_class<T>::value&&!std::is_same<std::string, T>::value>::type FillIn(T& t, char* p)
{
Put(p, sizeof(T), t);
} char* m_buf;
int m_bufLen;
int m_curPos = ;
Any m_latestInt = ;
}; template<typename Func, typename Last>
void for_each_impl(Func&& f, Last&& last)
{
f(last);
} template<typename Func, typename First, typename ... Rest>
void for_each_impl(Func&& f, First&& first, Rest&&...rest)
{
f(first);
for_each_impl(std::forward<Func>(f), rest...);
} template<typename Func, int ... Indexes, typename ... Args>
void for_each_helper(Func&& f, IndexTuple<Indexes...>, std::tuple<Args...>&& tup)
{
for_each_impl(std::forward<Func>(f), std::forward<Args>(std::get<Indexes>(tup))...);
}
} template<typename Func, typename ... Args>
void tp_for_each(Func&& f, std::tuple<Args...>& tup)
{
using namespace details;
for_each_helper(forward<Func>(f), typename make_indexes<Args...>::type(), std::tuple<Args...>(tup));
} template<typename Func, typename ... Args>
void tp_for_each(Func&& f, std::tuple<Args...>&& tup)
{
using namespace details;
for_each_helper(forward<Func>(f), typename make_indexes<Args...>::type(), forward<std::tuple<Args...>>(tup));
} template<typename T>
void Put(char* p, int len, T&& t)
{
using namespace Detail;
tp_for_each(Functor(p, len), t.Get());
}
}
代码中用到了Any,这个Any就是我前面的博文中实现的Any,想查看可以点这里。
再看看测试代码:
//嵌套的结构体
struct MySubStruct
{
int a;
double b;
auto Get()->decltype(std::make_tuple(a, b))
{
return std::make_tuple(a, b);
}
}; //含指针和嵌套结构体的结构体
struct MyStruct
{
int a;
double b;
int count; //对于指针,必须将指针元素个数count放在指针元素的前面
int* p;
MySubStruct t;
auto Get()->decltype(std::make_tuple(a, b, count, p, t))
{
return std::make_tuple(a, b, count, p, t);
}
}; //嵌套结构体指针的结构体
struct MyStruct2
{
int a;
double b;
int count;//对于指针,必须将指针元素个数count放在指针元素的前面
MySubStruct* t;
auto Get()->decltype(std::make_tuple(a, b, count, t))
{
return std::make_tuple(a, b, count, t);
}
}; //含void指针的结构体
struct MyStruct3
{
bool r;
int a;//对于指针,必须将指针元素个数count放在指针元素的前面
void* b;
auto Get()->decltype(std::make_tuple(r,a, b))
{
return std::make_tuple(r, a, b);
}
}; //含字符串的结构体
struct MyStruct4
{
int a;
string b;
auto Get()->decltype(std::make_tuple(a, b))
{
return std::make_tuple(a, b);
}
}; void TestArray()
{
MyStruct t = { , 1.256, , new int[]{, }, {, 5.36} };
char buf[];
Put(buf, sizeof(buf), t); char buf1[];
MySubStruct* st = new MySubStruct[];;
st[] = { , };
st[] = { , };
MyStruct2 t2 = { , , , st };
Put(buf1, sizeof(buf1), t2); int* p3 = new int[]{,};
MyStruct3 t3 = { false, , (void*) p3 };
char buf3[];
Put(buf3, sizeof(buf3), t3); MyStruct4 t4 = { , "test" };
char buf4[];
Put(buf4, sizeof(buf4), t4);
}
可以看到不管结构体有多少字段,还是是否含有字符串、指针或者嵌套了结构体,都可以通过一个Put函数搞定,没有了繁琐而又重复的赋值和偏移操作,不用担心偏移错了,整个繁琐的过程程序都自动化的完成了,简单利落。
要用这个自动化的将结构体转换为char数组的功能有一点约束条件:
- 每个结构体需要提供一个Get函数返回元信息;
- 结构体字段如果为指针的话,必须要将指针元素个数放到该字段前面;数组的话也需要提供数组元素个数的字段,因为Get函数会将数组转为指针,数组长度信息会丢掉。
我觉得这个约束条件相对于它实现的自动化的转换的便利性来说是几乎可以忽略的负担,是微不足道的。
如果你觉得这篇文章对你有用,可以点一下推荐,谢谢。
c++11 boost技术交流群:296561497,欢迎大家来交流技术。
(原创)结构体自动化转为char数组的实现的更多相关文章
- 深入理解指针—>结构体里的成员数组和指针
单看这文章的标题,你可能会觉得好像没什么意思.你先别下这个结论,相信这篇文章会对你理解C语言有帮助.这篇文章产生的背景是在微博上,看到@Laruence同学出了一个关于C语言的题,微博链接.微博截图如 ...
- C# 调用C/C++动态链接库,结构体中的char*类型
用C#掉用C++的dll直接import就可以之前有不同的类型对应,当要传递结构体的时候就有点麻烦了,这里有一个结构体里边有char*类型,这个类型在C#中调用没法声明,传string是不行的默认st ...
- 读陈浩的《C语言结构体里的成员数组和指针》总结,零长度数组
原文链接:C语言结构体里的成员数组和指针 复制例如以下: 单看这文章的标题,你可能会认为好像没什么意思.你先别下这个结论,相信这篇文章会对你理解C语言有帮助.这篇文章产生的背景是在微博上,看到@Lar ...
- C# 结构体定义 转换字节数组 z
客户端采用C++开发,服务端采用C#开发,所以双方必须保证各自定义结构体成员类型和长度一致才能保证报文解析的正确性. [StructLayoutAttribute(LayoutKind.Sequent ...
- C语言结构体里的成员数组和指针
struct test{ int i; char *p; }; struct test *str; ; char *b = "ioiodddddddddddd"; str = (s ...
- JNA结构体参数传递,Java数组
JNA以结构体数组为参数进行调用: ////// C++ // student 结构体定义 typedef struct { int age; char name[20]; }Student; // ...
- 结构体的malloc与数组空间
结构体的malloc 如果结构体中有指针,对结构体的malloc 和其指针成员变量的malloc是没有关系的 结构体malloc的是存储自己地址的 忘记了面试常考试的sizeof的几个主要点 ==== ...
- C++/C#结构体转化-二维数组-bytes To Strings
C++结构体 typedef struct VidyoClientRequestGetWindowsAndDesktops_ { /*! The number of application windo ...
- C++结构体与Delphi结构体相互传参,结构体中包含结构体的嵌套,数组指针
//结构体的声明 typedef struct Mwinddirectbaseline { char* p; int s; int i; }Mwinddirectbaseline; typedef s ...
随机推荐
- 【mysql】关于ICP、MRR、BKA等特性
一.Index Condition Pushdown(ICP) Index Condition Pushdown (ICP)是mysql使用索引从表中检索行数据的一种优化方式,从mysql5.6开始支 ...
- ios中常用的方法
图片分类 @implementation UIImageView (ext) +(UIImageView*)imageViewWith:(UIImage*)img imgFrame:(CGRect)_ ...
- Ubuntu 如何downgrade降级系统
大家都熟悉通过网络upgrade升级Linux系统,这里要介绍的是如何downgrade降级系统.背景情况是这样的:我将系统升级到feisty h3之后,发现libvte-0.15.3有个bug,会影 ...
- hibernate的批量删除
转自:hibernate的批量删除一般而言,hibernate的批量删除的写法有两种,一种是hibernate内置的批量删除,不过他的批量删除是将每条记录逐一生成删除语句,其效率极低,当然我们可以使用 ...
- js触摸事件
touch事件的绑定 电脑端的mouseDown,mouseUp,mouseMove分别对应移动端的touchstart,touchend,touchmove 下面的代码判断浏览器是电脑端还是移动端, ...
- 使用btrace来找出执行慢的方法
转载于:https://shaojun.name/2016/07/260 btrace script import static com.sun.btrace.BTraceUtils.name; im ...
- Asynchronous and non-Blocking I/O 翻译[收藏好文]
http://www.tornadoweb.org/en/stable/guide/async.html Real-time web features require a long-lived mos ...
- 阿里面试的一点感受 阿里ali片式经历和面试题
阿里面试的一点感受 <!-- [废话开始] 百度实习三个月,明天就要离职了,感觉还挺开心的,同事们都很照顾我,Boss也比较欣赏我,我很满足了.掐指一算,这大四其实也没几个月了,同事们都在感叹大 ...
- C语言学习笔记 (007) - 数组指针和通过指针引用数组元素的方法总结
1.数组指针:即指向数组的指针 那么, 如何声明一个数组指针呢? ]; /*括号是必须写的,不然就是指针数组:10是数组的大小*/ 拓展:有指针类型元素的数组称为指针数组. 2.通过指针引用数组元素的 ...
- C/C++函数指针(typedef简化定义)
学习要点: 1,函数地址的一般定义和typedef简化定义; 2,函数地址的获取; 3,A函数地址作为B函数参数的传递; 函数存放在内存的代码区域内,它 ...