我想用C(比如说c11,没有分词风格)写一个通用的全局const结构数组,比如下面的伪代码:
void * generic[] ={&(struct a va),&(struct b vb)};
现在我想创建一个函数,给定所需结构的位置id(我计划为每个id使用一个常量,硬编码),它将复制它
因为copy()
也是通用的(接受void *
作为目的地),所以它需要知道严格的大小,这可能由调用者指定(非常容易出错,他已经需要提供目标结构和正确的id),或者我也将为每个结构维护正确的sizeof
的并行数组。
有没有一种方法可以在编译时自动初始化sizeof数组?或者甚至实现某种技巧,让用户指定只将指针传递到没有id的结构,而不必编写专门的get_struct_x()
?
不允许动态分配(alloc()
,局部变量很好),所有结构和泛型数组内容在编译时都是已知的。
很抱歉我的解释不好,请随时改进/更正。
编辑以澄清:我需要从存储了许多结构类型的数组中深度复制一个已知的结构类型,但同一类型永远不会重复。我需要从一个通用的get函数中完成,这个函数将从不同的线程调用,所以在复制之前和之后都会锁定一个互斥对象,我想把所有的锁定和强制转换代码都放在一个点上,以最大限度地减少调试并创建更有效的测试。
在不必修改底层数据结构的情况下,这是我能看到的唯一一种可以管理的方法。数组中的每个条目都是而不是void*
,而是一个包含void*
(到元素)和size_t
(该元素的数据大小)的结构:
#include <stdio.h>
#include <stdlib.h>
typedef struct Entry
{
size_t len;
void const *elem;
} Entry;
#define ELEM_ENTRY(x) { sizeof(x), &(x) }
struct A
{
int a1;
long a2;
} a;
struct B
{
float b1;
char b2[6];
} b;
struct C
{
unsigned int c1;
double c2[5];
} c;
Entry generic[] =
{
ELEM_ENTRY(a),
ELEM_ENTRY(b),
ELEM_ENTRY(c)
};
size_t generic_n = sizeof(generic)/sizeof(*generic);
int main()
{
for (size_t i=0; i<generic_n; ++i)
printf("[%zu] = %zun", i, generic[i].len);
}
输出
[0] = 8
[1] = 12
[2] = 44
我认为这就是你想要做的。如果不是,我会放弃这个答案。请注意,如果元素是指针类型,则可能无法按您希望的方式运行(但如果它是本机数组类型,则将按您想要的方式运行)。所以要小心。
我的一个没有stackoverflow帐户[/s]的朋友[s](@Francesco)重新阐述了@WhozCraig的答案,添加了(非常旧的)X宏(感谢Randy Meyers的伟大文章)来构建通用结构数组和枚举开关帮助访问数组的结构,让用户只需编辑一个位置;
回到工作中,我告诉他,我想尝试制作特定的结构getter(因为数组中不会有重复的结构类型。如果你弄错了,编译时会出错,所以最终你会在开发时低头,但一旦编译,它就会更健壮),所以我们会对请求的结构的类型进行编译检查;在家里,他已经实施了。KUDOS!
然后我又浪费了一些时间,让开发人员只需键入一次结构,就可以防止这方面的错位;这是一个优化螺旋的开始;
- 由于专门的get和put,不再需要大小数组,因此删除了泛型结构
- 不需要初始化结构,我们使用"匿名"结构初始化
- 由于"匿名"初始化,因此更难直接访问该结构
- 直接访问需要了解系统,并引导意识
- 将其编码为可读性更强
- 易于导出
GENERIC_TABLE
和外部独立文件上的结构定义 - #
GENERIC_TABLE
上的ifdef工作正常,将其移动到外部标头以增加可读性 - 本地变量外部不需要动态初始化(无垃圾,嵌入友好)
- 极易使用
成本
- 可能是预处理器有点膨胀
- 看起来很OOP xD
- 您可以使用未初始化的指针调用get和put
TL;DR:这里是完整的编译代码和基本示例:
#include <memory.h>
#include <stdio.h>
#include <stdlib.h>
/* BEGIN: just some struct to test, fell free to move them on external header file */
struct A
{
int a1;
long a2;
};
struct B
{
float b1;
char b2[6];
};
struct C
{
unsigned int c1;
double c2[5];
};
/* END: just some struct to test, fell free to move them on external header file */
/* this macro will create the right X macro element, and also initiliaze the "anonymous" struct */
#define ADD_STRUCT_TO_ARRAY(xu) X(xu, &(struct xu){})SEP
/* BEGIN: add or remove your own struct here!
* fell free to move them on external header file
* just need to pass struct name without "struct" to ADD_STRUCT_TO_ARRAY macro */
#define GENERIC_TABLE
ADD_STRUCT_TO_ARRAY(A)
ADD_STRUCT_TO_ARRAY(B)
ADD_STRUCT_TO_ARRAY(C)
/* END: add or remove your own struct here! */
/* here we initialize the enum, where the type of the struct is the key, and its position in the array the value */
#define SEP ,
#define X(a,b) a
enum STRUCT {
GENERIC_TABLE
};
#undef X
/* here we initalize the array of pointer to the structure */
#define X(a,b) b
void * const generic[] =
{
GENERIC_TABLE
};
#undef X
#undef SEP
/* here we create all the getter function. add here your array locking code */
#define SEP ;
#define X(a,b) void get_##a(struct a * dest){memcpy(dest, generic[a], sizeof(struct a) );}
GENERIC_TABLE
#undef X
#undef SEP
/* here we create all the putter function. add here your array locking code */
#define SEP ;
#define X(a,b) void put_##a(struct a * source){memcpy(generic[a], source, sizeof(struct a) );}
GENERIC_TABLE
#undef X
#undef SEP
/*run, code, run!*/
int main()
{
struct A a_copy;
struct B b_copy;
struct C c_copy;
get_A(&a_copy);
get_B(&b_copy);
get_C(&c_copy);
printf("STRUCTURE IN ARRAY BEFORE INITIALIZATIONn");
printf("A = %dn", a_copy.a1);
printf("B = %fn", b_copy.b1);
printf("C = %xn", c_copy.c1);
a_copy.a1 = -1;
b_copy.b1 = 2.3;
c_copy.c1 = 3;
printf("STRUCTURE CHANGED TOn");
printf("A = %dn", a_copy.a1);
printf("B = %fn", b_copy.b1);
printf("C = %xn", c_copy.c1);
printf("STRUCTURE SAVEDn");
put_A(&a_copy);
put_B(&b_copy);
put_C(&c_copy);
get_A(&a_copy);
get_B(&b_copy);
get_C(&c_copy);
printf("STRUCTURE LOADEDn");
printf("A = %dn", a_copy.a1);
printf("B = %fn", b_copy.b1);
printf("C = %xn", c_copy.c1);
a_copy.a1 = 1000;
b_copy.b1 = -50.576;
c_copy.c1 = 700;
printf("STRUCTURE CHANGED TOn");
printf("A = %dn", a_copy.a1);
printf("B = %fn", b_copy.b1);
printf("C = %xn", c_copy.c1);
get_A(&a_copy);
get_B(&b_copy);
get_C(&c_copy);
printf("STRUCTURE RELOADED WITHOUT SAVINGn");
printf("A = %dn", a_copy.a1);
printf("B = %fn", b_copy.b1);
printf("C = %xn", c_copy.c1);
return 0;
}
我会将结构的大小作为每个结构中的第一个元素。所以你有
struct a
{
size_t size;
int a1;
double a2;
// ...
};
struct b
{
size_t size;
char b1[20];
float b2;
// ...
};
任何时候创建结构时,都要确保初始化大小,例如
struct a *aptr = malloc( sizeof(struct a) );
aptr->size = sizeof(struct a);
aptr->a1 = 3;
aptr->a2 = 100.5;
struct b someB = { sizeof(struct b), "hello", 1.0 };
然后,在复制例程中,您可以将void *
强制转换为size_t *
以获得结构的大小,因为您知道每个结构的大小都将作为第一个元素。