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 ofchar
或其他用于传输的字符;这是给定的。如果你想建议其他逻辑序列化机制,那么它涉及字节缓冲区这一事实肯定不是区分因素?你能澄清一下吗?@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);
}