結構體與char型轉換

因udp編程需要發送的內容爲char型,而定義的發送數據爲結構體,所以需要進行格式轉換。

 

轉自:http://www.cnblogs.com/qicosmos/p/3601737.html

 

結構體自動化轉換爲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, t.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; p="" 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

  #include

  #include

  namespace Cosmos

  {

  namespace Detail

  {

  struct Functor

  {

  Functor(char* buf, int len) :m_buf(buf), m_bufLen(len)

  {

  }

  template

  typename std::enable_if::value>::type operator()(T t)

  {

  FillForward(t);

  }

  template

  typename std::enable_if<std::is_class::value>::type operator()(T& t)

  {

  FillForward(t);

  }

  private:

  template

  void FillForward(T&& t)

  {

  if (std::is_same::value || std::is_same::value)

  m_latestInt = t;

  FillIn(std::forward(t), m_buf + m_curPos);

  m_curPos += sizeof(T);

  }

  template

  typename std::enable_if<std::is_integral::value>::type FillIn(T t, char* p)

  {

  *((T*) p) = t;

  }

  template

  typename std::enable_if<std::is_floating_point::value>::type FillIn(T t, char* p)

  {

  if(std::is_same::value)

  sprintf(p, "%.15f", t);

  else

  sprintf(p, "%f", t);

  }

  template

  typename std::enable_if<std::is_pointer::value&&std::is_class<typename std::remove_pointer::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 std::enable_if<std::is_pointer::value&&std::is_arithmetic<typename std::remove_pointer::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

  void Fill(T t, char* p, F&& f)

  {

  int count = m_latestInt.AnyCast();

  using U = typename std::remove_pointer::type;

  for (int i = 0; i < count; i++)

  {

  f(i, p, sizeof(U));

  }

  }

  template

  typename std::enable_if<std::is_pointer::value&&std::is_void<typename std::remove_pointer::type>::value>::type FillIn(T t,

  char* p)

  {

  int count = m_latestInt.AnyCast();

  int* temp = (int*) t;

  for (int i = 0; i < count; i++)

  {

  FillIn(temp[i], p + i*sizeof(int));

  }

  }

  template

  typename std::enable_if<std::is_same::value>::type FillIn(T& t, char* p)

  {

  strcpy(p, t.c_str());

  }

  template

  typename std::enable_if<std::is_class::value&&!std::is_same::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

  void for_each_impl(Func&& f, Last&& last)

  {

  f(last);

  }

  template

  void for_each_impl(Func&& f, First&& first, Rest&&...rest)

  {

  f(first);

  for_each_impl(std::forward(f), rest...);

  }

  template

  void for_each_helper(Func&& f, IndexTuple, std::tuple&& tup)

  {

  for_each_impl(std::forward(f), std::forward(std::get(tup))...);

  }

  }

  template

  void tp_for_each(Func&& f, std::tuple& tup)

  {

  using namespace details;

  for_each_helper(forward(f), typename make_indexes::type(), std::tuple(tup));

  }

  template

  void tp_for_each(Func&& f, std::tuple&& tup)

  {

  using namespace details;

  for_each_helper(forward(f), typename make_indexes::type(), forward<std::tuple>(tup));

  }

  template

  void Put(char* p, int len, T&& t)

  {

  using namespace Detail;

  tp_for_each(Functor(p, len), t.Get());

  }

  }

  View Code

  代碼中用到了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數組的功能有一點約束條件:

  每個結構體需要提供一個Get函數返回元信息;

  結構體字段如果爲指針的話,必須要將指針元素個數放到該字段前面;數組的話也需要提供數組元素個數的字段,因爲Get函數會將數組轉爲指針,數組長度信息會丟掉。

  我覺得這個約束條件相對於它實現的自動化的轉換的便利性來說是幾乎可以忽略的負擔,是微不足道的。


 

 

轉自:http://blog.sina.com.cn/s/blog_6748cd1f0100juq8.html

 

對C的指針一直有所欠缺,這裏學到一些皮毛,分享一下:

結構體:
#defined MAX_LENGTH 200
typedef struct _TEST_EXAMPLE
{
    char name[MAX_LENGTH+1];
    int age;
} test_example;

結構體的首位置轉換成Char指針,比如有10位同學:
test_example *te = (test_example *)malloc(sizeof(test_example) * 10);
//...結構體讀取數據..
for (size_t i = 0; i < 10; i ++)
{
te[i].name = student[0];
te[i++].age = student[1];
}
char *result = (char *)te;

當用指針(char *)result做了一些操作後,最後要將Char*指到的首位和長度還原成結構體數組(結構體數組在內存中是連續的);打印結構體如下:
方法一:
size_t value_length = sizeof(test_example) * 10;
for (size_t i = 0; i < value_length; i ++)
{
    printf("%s\t", ((test_example *)(result + i))->name);
    printf("%d\n", ((test_example *)(result + i))->age);
    i += sizeof(test_example) - 1;
}

另外一種方法:
test_example *tmp = (test_example *)result;
for (size_t i = 0; i < value_length / sizeof(test_example); i ++)
{
    printf("%s\t%d\n", (tmp + i)->name, (tmp + i)->age);
}

看起來非常簡單,起先我是使用純指針去截取每個name和age的位置,但是結構體有個問題,隨平臺不同,結構體每個對象中所佔用的空間也不一樣,一般是int的整數倍,比如char a[10]是結構的唯一成員變量,其實相當於結構體仍然需要佔用12個字節,具體bool這種類型是否也是int的整數倍,需要進行測試確認;所以最好的辦法還是使用直接OO的方法指定,雖然指針也是一種OO。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章