C 使用宏转换结构中整型字段的endianness

C 使用宏转换结构中整型字段的endianness,c,metaprogramming,preprocessor,x-macros,C,Metaprogramming,Preprocessor,X Macros,考虑以下结构和函数 typedef struct __attribute__((__packed__)) req_file { uint32_t start_pos; uint32_t byte_count; uint16_t name_len; } req_file; void req_file_hton(req_file *d){ d->name_len = htons(d->name_len); d->start_pos = htonl(d->

考虑以下结构和函数

typedef struct __attribute__((__packed__)) req_file {
  uint32_t start_pos;
  uint32_t byte_count;
  uint16_t name_len;
} req_file;

void req_file_hton(req_file *d){
  d->name_len = htons(d->name_len);
  d->start_pos = htonl(d->start_pos);
  d->byte_count = htonl(d->byte_count);
}

void req_file_ntoh(req_file *d){
  d->name_len = ntohs(d->name_len);
  d->start_pos = ntohl(d->start_pos);
  d->byte_count = ntohl(d->byte_count);
}

上面的代码对于许多具有许多字段的结构来说是乏味的。我想配置一次结构的名称和字段,并为我生成函数
struct\u name\u hton
struct\u name\u ntoh
。我试过玩x宏,但运气不好。一个可移植的C预处理器解决方案将受到高度赞赏(不是C++)

xmacros工作。诀窍是根据类型对函数使用标记粘贴和别名:

#define htonuint32_t htonl
#define htonuint16_t htons
#define ntohuint32_t ntohl
#define ntohuint16_t ntohl

#define DEF_FIELDS \
   DEF_FIELD(uint32_t,start_pos); \
   DEF_FIELD(uint32_t,byte_count); \
   DEF_FIELD(uint16_t,name_len)

#define DEF_FIELD(t,v)  t v

typedef struct __attribute__((__packed__)) req_file {
    DEF_FIELDS;
} req_file;

#undef DEF_FIELD
#define DEF_FIELD(t,v) d->v = hton##t(d->v)

void req_file_hton(req_file *d) {
    DEF_FIELDS;
}
#undef DEF_FIELD
#define DEF_FIELD(t,v) d->v = ntoh##t(d->v)

void req_file_hton(req_file *d) {
    DEF_FIELDS;
}
预处理代码(重新格式化以更清晰地显示):

如果有多个结构,则可以使宏系统复杂化,以生成所有结构和函数。具有两种不同结构的示例:

#define htonuint32_t htonl
#define htonuint16_t htons
#define ntohuint32_t ntohl
#define ntohuint16_t ntohl

#define DEF_FIELDS_req_file \
   DEF_FIELD(uint32_t,start_pos); \
   DEF_FIELD(uint32_t,byte_count); \
   DEF_FIELD(uint16_t,name_len)

#define DEF_FIELDS_other_file \
   DEF_FIELD(uint32_t,foo_pos); \
   DEF_FIELD(uint32_t,char_count); \
   DEF_FIELD(uint16_t,bar_len)

#define STRUCT_DEF(s) \
    START_DECL(s) \
    DEF_FIELDS_##s; \
    END_DECL(s)


#define START_DECL(s) typedef struct __attribute__((__packed__)) s {
#define END_DECL(s) } s
#define DEF_FIELD(t,v)  t v

STRUCT_DEF(req_file);
STRUCT_DEF(other_file);

#undef DEF_FIELD
#undef START_DECL
#undef END_DECL
#define DEF_FIELD(t,v) d->v = hton##t(d->v)
#define START_DECL(s) void s##_hton(s *d) {
#define END_DECL(s) }

STRUCT_DEF(req_file);
STRUCT_DEF(other_file);

#undef DEF_FIELD
#undef START_DECL
#define DEF_FIELD(t,v) d->v = ntoh##t(d->v)
#define START_DECL(s) void s##_ntoh(s *d) {

STRUCT_DEF(req_file);
STRUCT_DEF(other_file);
结果:

typedef struct __attribute__((__packed__)) req_file { uint32_t start_pos; uint32_t byte_count; uint16_t name_len; } req_file;
typedef struct __attribute__((__packed__)) other_file { uint32_t foo_pos; uint32_t char_count; uint16_t bar_len; } other_file;

void req_file_hton(req_file *d) { d->start_pos = htonl(d->start_pos); d->byte_count = htonl(d->byte_count); d->name_len = htons(d->name_len); };
void other_file_hton(other_file *d) { d->foo_pos = htonl(d->foo_pos); d->char_count = htonl(d->char_count); d->bar_len = htons(d->bar_len); };

void req_file_ntoh(req_file *d) { d->start_pos = ntohl(d->start_pos); d->byte_count = ntohl(d->byte_count); d->name_len = ntohl(d->name_len); };
void other_file_ntoh(other_file *d) { d->foo_pos = ntohl(d->foo_pos); d->char_count = ntohl(d->char_count); d->bar_len = ntohl(d->bar_len); };

您可以修改Antony Polukhin的库,以便能够将任何(任意)结构转换为不同的字节顺序-就像它现在可以将任何任意结构打印到ostream一样。

IMHO,您应该使用原始缓冲区进行输入/输出。这比猜测编译器对每个系统上的字段或结构进行排序的方式更方便(也更安全)

此外,这将允许您打包/解包数据,而无需担心字节顺序或内存对齐

此示例代码中的宏是从以下内容中提取的:

在实现文件中,信息可能如下所示(同样,可以更改内联方法):

这可以调整,以便
SERIALIZE\u TYPE
也声明函数(不定义它们),并且函数不是内联的(因此每个类型只有实现文件包含3次头文件。

好吧,这很简单

#include <stdint.h>
#include <arpa/inet.h>

/* the NETSTRUCT library ------------------------------- */

// for uint32_t
#define NETSTRUCT_dec_uint32_t(n)  uint32_t n;
#define NETSTRUCT_hton_uint32_t(n)  t->n = htonl(t->n);
#define NETSTRUCT_ntoh_uint32_t(n)  t->n = ntohl(t->n);

// for uint16_t
#define NETSTRUCT_dec_uint16_t(n)  uint16_t n;
#define NETSTRUCT_hton_uint16_t(n)  t->n = htons(t->n);
#define NETSTRUCT_ntoh_uint16_t(n)  t->n = ntohs(t->n);

// dec hton ntoh switch
#define NETSTRUCT_dec(type, name)  NETSTRUCT_dec_##type(name)
#define NETSTRUCT_hton(type, name) NETSTRUCT_hton_##type(name)
#define NETSTRUCT_ntoh(type, name) NETSTRUCT_ntoh_##type(name)

// calls NETSTRUCT_mod
#define NETSTRUCT1(mod, a)       NETSTRUCT_##mod a
#define NETSTRUCT2(mod, a, ...)  NETSTRUCT1(mod, a) NETSTRUCT1(mod, __VA_ARGS__)
#define NETSTRUCT3(mod, a, ...)  NETSTRUCT1(mod, a) NETSTRUCT2(mod, __VA_ARGS__)
#define NETSTRUCT4(mod, a, ...)  NETSTRUCT1(mod, a) NETSTRUCT3(mod, __VA_ARGS__)
// TO DO: all up to NETSTRUCT64

// variadic macro overload
#define NETSTRUCT_GET(_1,_2,_3,_4,NAME,...) NAME
// Overlads VA_ARGS with specified mod
#define NETSTRUCT_IN(mod, ...) \
        NETSTRUCT_GET(__VA_ARGS__, NETSTRUCT4, NETSTRUCT3, NETSTRUCT2, NETSTRUCT1) \
            (mod, __VA_ARGS__)

// entrypoint of out library
#define NETSTRUCT(name, ...)  \
    \
    struct name { \
        NETSTRUCT_IN(dec, __VA_ARGS__) \
    } __attribute__((__packed__)); \
    \
    void name##_hton(struct name *t) { \
        NETSTRUCT_IN(hton, __VA_ARGS__) \
    } \
    \
    void name##_ntoh(struct name *t) { \
        NETSTRUCT_IN(ntoh, __VA_ARGS__) \
    }

/* -------------------------------------------------------- */

// adding custom type
#define NETSTRUCT_dec_uint8_t_arr_8(n) uint8_t n[8];
#define NETSTRUCT_hton_uint8_t_arr_8(n) do{}while(0);
#define NETSTRUCT_ntoh_uint8_t_arr_8(n) do{}while(0);

NETSTRUCT(reg_file, 
    (uint32_t, start_pos),
    (uint32_t, byte_count),
    (uint16_t, name_len),
    (uint8_t_arr_8, example_custom_array)
);

int main() {
    struct reg_file t;
    reg_file_hton(&t);
    reg_file_ntoh(&t);
}
#包括
#包括
/*NETSTRUCT库-----------------*/
//对于uint32\u t
#定义NETSTRUCT_dec_uint32_t(n)uint32_t n;
#定义NETSTRUCT\u hton\u uint32\u t(n)t->n=htonl(t->n);
#定义NETSTRUCT\u ntoh\u uint32\u t(n)t->n=ntohl(t->n);
//对于uint16\u t
#定义NETSTRUCT_dec_uint16_t(n)uint16_t n;
#定义NETSTRUCT\u hton\u uint16\u t(n)t->n=htons(t->n);
#定义NETSTRUCT\u ntoh\u uint16\u t(n)t->n=ntohs(t->n);
//dec hton ntoh开关
#定义NETSTRUCT_dec(类型、名称)NETSTRUCT_dec_35;#类型(名称)
#定义网络结构(类型、名称)网络结构(类型、名称)
#定义NETSTRUCT_ntoh(类型、名称)NETSTRUCT_ntoh_35;#类型(名称)
//调用NETSTRUCT\u mod
#定义NETSTRUCT1(mod,a)NETSTRUCT###mod a
#定义NETSTRUCT2(mod,a,…)NETSTRUCT1(mod,a)NETSTRUCT1(mod,VA,ARGS)
#定义NETSTRUCT3(mod,a,…)NETSTRUCT1(mod,a)NETSTRUCT2(mod,VA,ARGS)
#定义NETSTRUCT4(mod,a,…)NETSTRUCT1(mod,a)NETSTRUCT3(mod,VA,ARGS)
//要执行的操作:全部取决于NETSTRUCT64
//可变宏过载
#定义NETSTRUCT_GET(_1,_2,_3,_4,NAME,…)名称
//用指定的mod覆盖VA_参数
#在(mod,…)中定义NETSTRUCT_\
NETSTRUCT_GET(uu VA_ARGS_uu,NETSTRUCT4,NETSTRUCT3,NETSTRUCT2,NETSTRUCT1)\
(mod,_VA_ARGS__;
//出库入口点
#定义NETSTRUCT(名称,…)\
\
结构名称{\
网络结构(12月,星期日)\
}_uuu属性_uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu\
\
void name###u hton(结构名*t){\
NETSTRUCT_IN(hton,VA_ARGS)\
} \
\
void name###u ntoh(结构名*t){\
NETSTRUCT_IN(ntoh,VA_ARGS)\
}
/* -------------------------------------------------------- */
//添加自定义类型
#定义NETSTRUCT_dec_uint8_t_arr_8(n)uint8_t n[8];
#定义NETSTRUCT_hton_uint8_t_arr_8(n)do{}while(0);
#定义NETSTRUCT_ntoh_uint8_t_arru_8(n)do{}while(0);
NETSTRUCT(注册表文件,
(uint32,启动位置),
(uint32_t,字节计数),
(uint16,名称),
(uint8_t_arr_8,示例_自定义_数组)
);
int main(){
结构注册表文件t;
注册文件(t&t);
注册文件(t&t);
}
我编写了mactos,因此很容易添加另一个函数,很可能是
void name###serialize(char*in)
void name###反序列化(const char*out)
。设计可以稍微进行重构,以便类型回调
NETSTRUCT\u dec*
使用两个甚至未知数量的参数,例如
NETSTRUCT(name,(键入回调后缀(参数、参数2))


@编辑添加的自定义数组类型示例和某些行的顺序更改。

我真的认为
无符号字符
缓冲区是可移植性的最佳选择。从缓冲区读写比在不同的系统上对每个字段进行打包和排序更安全。@Myst不确定您的意思。对象将已经在b中uffer of
char
或其他用于传输的字符;这是给定的。如果你想建议其他逻辑序列化机制,那么它涉及字节缓冲区这一事实肯定不是区分因素?你能澄清一下吗?@Myst你的意思是将整数12序列化为字符串“12”@samvel1024“你的意思是将整数12序列化为字符串吗?”“不,他是说你应该使用序列化来提供一个无符号的字符缓冲区,而不是使用
\uuuu attribute\uuuuuuuuuu((\ uuuu packed\uuuuuuuuuu))
@samvel1024我给出了一个解释我意思的答案ῥεῖ (理解我)我真的认为你应该避免将输入/输出缓冲区转换成<代码> ReqyFix*/Cube >。如果代码在非x86系统上运行,可能会遇到不一致的内存访问问题。用户只是通过删除C++标签来破解你的。C++标签引起了混乱。最初我添加它是因为我认为C++宏也可以应用在这里。OLVEN C++中的这个问题并不像C.@让弗兰Cou.OISFabRe那么复杂:嗯,读者知道这个好的黑客(即使不是C)也不是一个坏主意……@ Jeon弗兰CouoOsFabRe我编辑了删除C++标签(同行评议)就像我准备回答一样,但是我得到了他不想要任何宏免费、安全、C++解决方案的意思。但是我不知道如何修改这个函数。
/** Reads an unaligned network ordered byte stream to a 16 bit number. */
#define fio_str2u16(c)                                                         \
  ((uint16_t)(((uint16_t)(((uint8_t *)(c))[0]) << 8) |                         \
              (uint16_t)(((uint8_t *)(c))[1])))

/** Reads an unaligned network ordered byte stream to a 32 bit number. */
#define fio_str2u32(c)                                                         \
  ((uint32_t)(((uint32_t)(((uint8_t *)(c))[0]) << 24) |                        \
              ((uint32_t)(((uint8_t *)(c))[1]) << 16) |                        \
              ((uint32_t)(((uint8_t *)(c))[2]) << 8) |                         \
              (uint32_t)(((uint8_t *)(c))[3])))

/** Writes a local 16 bit number to an unaligned buffer in network order. */
#define fio_u2str16(buffer, i)                                                 \
  do {                                                                         \
    ((uint8_t *)(buffer))[0] = ((uint16_t)(i) >> 8) & 0xFF;                    \
    ((uint8_t *)(buffer))[1] = ((uint16_t)(i)) & 0xFF;                         \
  } while (0);

/** Writes a local 32 bit number to an unaligned buffer in network order. */
#define fio_u2str32(buffer, i)                                                 \
  do {                                                                         \
    ((uint8_t *)(buffer))[0] = ((uint32_t)(i) >> 24) & 0xFF;                   \
    ((uint8_t *)(buffer))[1] = ((uint32_t)(i) >> 16) & 0xFF;                   \
    ((uint8_t *)(buffer))[2] = ((uint32_t)(i) >> 8) & 0xFF;                    \
    ((uint8_t *)(buffer))[3] = ((uint32_t)(i)) & 0xFF;                         \
  } while (0);

void req_file_read(req_file *d, unsigned char * buffer){
  d->byte_count = fio_str2u32(buffer);
  d->start_pos = fio_str2u32(buffer + 4);
  d->name_len = fio_str2u16(buffer + 8);
}

void req_file_write(unsigned char * buffer, req_file *d){
  fio_u2str32(buffer, d->byte_count);
  fio_u2str32(buffer + 4, d->start_pos);
  fio_u2str16(buffer + 8, d->name_len);
}
/* note there's NO include guard in the header file */
#ifndef H__FACIL_IO_MACROS
#define H__FACIL_IO_MACROS

/** Reads an unaligned network ordered byte stream to a 16 bit number. */
#define fio_str2u16(c)                                                         \
  ((uint16_t)(((uint16_t)(((uint8_t *)(c))[0]) << 8) |                         \
              (uint16_t)(((uint8_t *)(c))[1])))

/** Reads an unaligned network ordered byte stream to a 32 bit number. */
#define fio_str2u32(c)                                                         \
  ((uint32_t)(((uint32_t)(((uint8_t *)(c))[0]) << 24) |                        \
              ((uint32_t)(((uint8_t *)(c))[1]) << 16) |                        \
              ((uint32_t)(((uint8_t *)(c))[2]) << 8) |                         \
              (uint32_t)(((uint8_t *)(c))[3])))

/** Writes a local 16 bit number to an unaligned buffer in network order. */
#define fio_u2str16(buffer, i)                                                 \
  do {                                                                         \
    ((uint8_t *)(buffer))[0] = ((uint16_t)(i) >> 8) & 0xFF;                    \
    ((uint8_t *)(buffer))[1] = ((uint16_t)(i)) & 0xFF;                         \
  } while (0);

/** Writes a local 32 bit number to an unaligned buffer in network order. */
#define fio_u2str32(buffer, i)                                                 \
  do {                                                                         \
    ((uint8_t *)(buffer))[0] = ((uint32_t)(i) >> 24) & 0xFF;                   \
    ((uint8_t *)(buffer))[1] = ((uint32_t)(i) >> 16) & 0xFF;                   \
    ((uint8_t *)(buffer))[2] = ((uint32_t)(i) >> 8) & 0xFF;                    \
    ((uint8_t *)(buffer))[3] = ((uint32_t)(i)) & 0xFF;                         \
  } while (0);

/* convert SERIAL_STRUCT_NAME to actual name */
#define SERIAL_STRUCT_MAKE(struct_name) SERIAL_STRUCT_MAKE2(struct_name)

#endif
#if SERIALIZE_TYPE /* create the type */
#undef SERIALIZE_TYPE

#undef SERIAL_STRUCT_FIELD
#define SERIAL_STRUCT_FIELD(name, bits, pos) uint##bits##_t name

#undef SERIAL_STRUCT_MAKE2
#define SERIAL_STRUCT_MAKE2(struct_name)                                       \
  typedef struct {                                                             \
    SERIAL_STRUCT_FIELDS;                                                      \
  } struct_name##_s;

/* perform macros */
SERIAL_STRUCT_MAKE(SERIAL_STRUCT_NAME)

#elif SERIALIZE_READ /* create reader function */
#undef SERIALIZE_READ

#undef SERIAL_STRUCT_FIELD
#define SERIAL_STRUCT_FIELD(name, bits, pos)                                   \
  dest->name = fio_str2u##bits((src + (pos)))

#undef SERIAL_STRUCT_MAKE2
#define SERIAL_STRUCT_MAKE2(struct_name)                                       \
  inline static void struct_name_read(struct_name##_s *dest,                   \
                                      unsigned char *src) {                    \
    SERIAL_STRUCT_FIELDS;                                                      \
  }

/* perform macros */
SERIAL_STRUCT_MAKE(SERIAL_STRUCT_NAME)

#elif SERIALIZE_WRITE /* create writer function */
#undef SERIALIZE_WRITE

#undef SERIAL_STRUCT_FIELD
#define SERIAL_STRUCT_FIELD(name, bits, pos)                                   \
  fio_u2str##bits((dest + (pos)), src->name)

#undef SERIAL_STRUCT_MAKE2
#define SERIAL_STRUCT_MAKE2(struct_name)                                       \
  inline static void struct_name##_write(unsigned char *dest,                  \
                                         struct_name##_s *src) {               \
    SERIAL_STRUCT_FIELDS;                                                      \
  }

/* perform macros */
SERIAL_STRUCT_MAKE(SERIAL_STRUCT_NAME)

#endif
/* will produce req_file_s as the struct name, but you can change that */
#define SERIAL_STRUCT_NAME req_file
#define SERIAL_STRUCT_FIELDS                                                   \
  SERIAL_STRUCT_FIELD(start_pos, 32, 0);                                       \
  SERIAL_STRUCT_FIELD(byte_count, 32, 4);                                      \
  SERIAL_STRUCT_FIELD(name_len, 16, 8)

#define SERIALIZE_TYPE 1
#include "serialize.h"
#define SERIALIZE_READ 1
#include "serialize.h"
#define SERIALIZE_WRITE 1
#include "serialize.h"
#include <stdint.h>
#include <arpa/inet.h>

/* the NETSTRUCT library ------------------------------- */

// for uint32_t
#define NETSTRUCT_dec_uint32_t(n)  uint32_t n;
#define NETSTRUCT_hton_uint32_t(n)  t->n = htonl(t->n);
#define NETSTRUCT_ntoh_uint32_t(n)  t->n = ntohl(t->n);

// for uint16_t
#define NETSTRUCT_dec_uint16_t(n)  uint16_t n;
#define NETSTRUCT_hton_uint16_t(n)  t->n = htons(t->n);
#define NETSTRUCT_ntoh_uint16_t(n)  t->n = ntohs(t->n);

// dec hton ntoh switch
#define NETSTRUCT_dec(type, name)  NETSTRUCT_dec_##type(name)
#define NETSTRUCT_hton(type, name) NETSTRUCT_hton_##type(name)
#define NETSTRUCT_ntoh(type, name) NETSTRUCT_ntoh_##type(name)

// calls NETSTRUCT_mod
#define NETSTRUCT1(mod, a)       NETSTRUCT_##mod a
#define NETSTRUCT2(mod, a, ...)  NETSTRUCT1(mod, a) NETSTRUCT1(mod, __VA_ARGS__)
#define NETSTRUCT3(mod, a, ...)  NETSTRUCT1(mod, a) NETSTRUCT2(mod, __VA_ARGS__)
#define NETSTRUCT4(mod, a, ...)  NETSTRUCT1(mod, a) NETSTRUCT3(mod, __VA_ARGS__)
// TO DO: all up to NETSTRUCT64

// variadic macro overload
#define NETSTRUCT_GET(_1,_2,_3,_4,NAME,...) NAME
// Overlads VA_ARGS with specified mod
#define NETSTRUCT_IN(mod, ...) \
        NETSTRUCT_GET(__VA_ARGS__, NETSTRUCT4, NETSTRUCT3, NETSTRUCT2, NETSTRUCT1) \
            (mod, __VA_ARGS__)

// entrypoint of out library
#define NETSTRUCT(name, ...)  \
    \
    struct name { \
        NETSTRUCT_IN(dec, __VA_ARGS__) \
    } __attribute__((__packed__)); \
    \
    void name##_hton(struct name *t) { \
        NETSTRUCT_IN(hton, __VA_ARGS__) \
    } \
    \
    void name##_ntoh(struct name *t) { \
        NETSTRUCT_IN(ntoh, __VA_ARGS__) \
    }

/* -------------------------------------------------------- */

// adding custom type
#define NETSTRUCT_dec_uint8_t_arr_8(n) uint8_t n[8];
#define NETSTRUCT_hton_uint8_t_arr_8(n) do{}while(0);
#define NETSTRUCT_ntoh_uint8_t_arr_8(n) do{}while(0);

NETSTRUCT(reg_file, 
    (uint32_t, start_pos),
    (uint32_t, byte_count),
    (uint16_t, name_len),
    (uint8_t_arr_8, example_custom_array)
);

int main() {
    struct reg_file t;
    reg_file_hton(&t);
    reg_file_ntoh(&t);
}