结构体自动化转换为char数组这个需求,来自于一个最近开发的一个项目,在项目开发过程中遇到一个小问题,需要将各种结构体拷贝到char数组中,这对于一个简单的结构体来说是很简单的事情,比如下面这个只有整形字段的结构体:

struct A
{int a;int b;
};char buf[100];
A a = {1,2};
memcpy(buf, &a, sizeof(A));

一句memcpy就能将结构体a拷贝到char数组中去了,直接通过memcpy拷贝结构体只对于内存连续的结构体有效。如果结构体内存不连续,结构体中含有double、string、指针甚至嵌套结构体时,直接拷贝是错误的,这时候需要一个一个字段的转换,比如下面的结构体:

struct A
{int x;string y;
};
char buf[100];
A a = {1, "test"};
char *p = buf;
*((int *)p) = x;
p += sizeof(int);
strcpy(p, y.c_str());

可以看到这种一个一个字段转换的方法是很繁琐的,字段越多转换就越繁琐,而且偏移量还容易写错。当结构体字段是指针或者结构体时就更繁琐了,比如下面的结构体:

struct A
{int x;int y;
};
struct B
{int x;int count;   //标识指针p中的元素个数int *p;A a;
};
char buf[100];
B b = {1, 2, new int[2]{3, 4}, {2, 3}};
char *p = buf;
*((int*)p) = b.x;
p += sizeof(int);
*((int *)p) = b.count;
p += sizeof(int);
for (int i=0; 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);elsesprintf(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=0; 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=0; 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 = 0;Any m_latestInt = 0;};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 = {9, 1.256, 2, new int[2]{3, 4}, {14, 5.36} };char buf[100];Put(buf, sizeof(buf), t);char buf1[100];MySubStruct *st = new MySubStruct[2];st[0] = { 6, 7 };st[1] = { 8, 9 };MyStruct2 t2 = { 3, 4, 2, st };Put(buf1, sizeof(buf1), t2);int *p3 = new int[2]{5,6};MyStruct3 t3 = { false, 2, (void *) p3 };char buf3[100];Put(buf3, sizeof(buf3), t3);MyStruct4 t4 = { 6, "test" };char buf4[100];Put(buf4, sizeof(buf4), t4);
}

可以看到不管结构体有多少字段,还是是否含有字符串、指针或者嵌套了结构体,都可以通过一个Put函数搞定,没有了繁琐而又重复的赋值和偏移操作,不用担心偏移错了,整个繁琐的过程程序都自动化的完成了,简单利落。

要用这个自动化的将结构体转换为char数组的功能有一点约束条件:

1.每个结构体需要提供一个Get函数返回元信息;

2.结构体字段如果为指针的话,必须要将指针元素个数放到该字段前面;数组的话也需要提供数组元素个数的字段,因为Get函数会将数组转为指针,数组长度信息会丢掉。

我觉得这个约束条件相对于它实现的自动化的转换的便利性来说是几乎可以忽略的负担,是微不足道的。

结构体自动化转为char数组的实现相关推荐

  1. (原创)结构体自动化转为char数组的实现

    结构体自动化转换为char数组这个需求,来自于一个最近开发的一个项目,在项目开发过程中遇到一个小问题,需要将各种结构体拷贝到char数组中,这对于一个简单的结构体来说是很简单的事情,比如下面这个只有整 ...

  2. C语言结构体里的成员数组和指针

    单看这文章的标题,你可能会觉得好像没什么意思.你先别下这个结论,相信这篇文章会对你理解C语言有帮助.这篇文章产生的背景是在微博上,看到@Laruence同学出了一个关于C语言的题,微博链接.微博截图如 ...

  3. C语言学习笔记---结构体中的字符数组和字符指针

      在结构体中可以使用字符数组来存储字符串,也可以使用字符指针来存储字符串.比如: struct str{char s1[5];char s2[5];};struct str str1= {" ...

  4. C# 将结构体转化为byte数组,byte数组转化为结构体

     1.将结构体转化为byte数组 #region /// <summary>/// 结构体转为byte数组/// </summary>/// <typeparam nam ...

  5. C语言结构体及函数传递数组參数演示样例

    C语言结构体及函数传递数组參数演示样例 注:makeSphere()函数返回Sphere结构体,main函数中.调用makeSphere()函数,传递的第一个參数为数组,传递的数组作为指针. post ...

  6. C# 结构体定义 转换字节数组 z

    客户端采用C++开发,服务端采用C#开发,所以双方必须保证各自定义结构体成员类型和长度一致才能保证报文解析的正确性. [StructLayoutAttribute(LayoutKind.Sequent ...

  7. linux c之memcpy拷贝结构体到结构体、拷贝字符数组到结构体

    1 memcpy 我们知道这个函数主要是拷贝内存数据,我们一般可以使用拷贝结构体到结构体.也可以拷贝字符数组到结构体,但是这个字符数组数据应该是同一个结构体拷贝先拷贝到这个字符数组,如果是其他格式的字 ...

  8. C++对类(或者结构体)中字符数组赋值时,出现表达式必须是可修改的左值的问题

    最近自己遇到了这类问题,在csdn上找到了很多大神给的解答,非常到位 特别感谢这位: https://blog.csdn.net/JQ_AK47/article/details/53169799 问题 ...

  9. C语言结构体末端定义空数组

    在某些情况,数据结构末端会定义一个可选区域,如下所示: struct abc {int age;char *name[20];...char placeholder[0]; } 而结构体最后使用0长度 ...

最新文章

  1. Android OpenGL ES 离屏渲染(offscreen render)
  2. spark on k8s配置日志存储路径:spark-defaults.conf
  3. Integer.valueOf(String) 方法之惑
  4. 写给即将离开校园准备进入 SAP 研究院实习的朋友
  5. assert和if的使用
  6. JS 进阶知识点及常考面试题
  7. 力扣69-x的平方根(解决一个问题:我的答案和题解很像,但是为什么过不了?C++、Java版)
  8. Html 解决长串英文字母显示不能自动换行
  9. devops什么意思_devops是什么意思
  10. [备忘]Vim的一个插件扩展版本——exVim
  11. 赤峰中考计算机考试软件,2017赤峰中考信息技术与实验操作考试实施细则
  12. u-boot中nand相关命令使用---- ubi, ubifsls, ubifsmount, ubifsumount
  13. 键盘调节台式计算机声音,键盘打字音效怎么设置 让键盘打字发出机械键盘声音方法...
  14. yuque-helper 1.0 发布了
  15. 宽带常见的有20 30 50 100兆指的是什么意思???
  16. android 的导入crosswalk 用xwalkview 替换webview
  17. 表格无法无法计算机,电脑为什么不能新建excel表格怎么办
  18. Puppeteer之Pyppeteer-Pyppeteer基础用法入门(1)
  19. 汽车改装贴膜小程序开发制作功能介绍
  20. 张坤 2012.5.4 sed去掉所有的HTML标签

热门文章

  1. C++易被忽略的知识点:移动语义 左值右值
  2. 红黑树(RB-Tree)比AVL强在哪?
  3. 伸展树(Splay tree)浅谈
  4. 【一起去大厂系列】深入理解MySQL中where 1 = 1的用处
  5. linux-RPM与YUM
  6. 模拟器不全屏_刺激战场:腾讯模拟器怎么设置才不卡
  7. signature=8cc1e8491a741a9dc954b549013b75e5,基于小波的SAR影像纹理分析
  8. mysql事务用法介绍及储存引擎介绍(MyLSAM,Innodb)
  9. abap 函数远程启用的模块参数_SAP AMDP介绍 - ABAP托管的HANA数据库过程
  10. MySQL之日期时间处理函数_MySQL之日期时间处理函数