英文:
_Generic function overloading with same parameters in C
问题
以下是您要翻译的内容:
我目前正在制作自己的动态数组,并且我有一个函数可以将内容附加到它,但我想要附加我的结构或指针,所以我需要修改函数的参数而不是函数名称。
这是我的函数,它将项目附加到ph_arr中。
#define ph_array_(type, name) \
struct name { \
type* data; size_t used_size, alloc_size; \
} __attribute__((__packed__))
void ph_array_append_(struct ph_array_void_* ph_arr, size_t data_size, void* items, size_t count);
我想要像这样使用宏(ph_array_append
):
ph_array_append(ph_arr1, ph_arr2);
// 或者
const int data[] = { 1, 2, 3, 4 };
ph_array_append(ph_arr1, data, 4);
我无法调用该函数,因为我需要为相同的函数创建不同的参数,所以我尝试添加具有调整后参数的函数,但它不起作用,同时我尝试仅添加调整后的参数,但它也不起作用。
是否可能做到这一点,还是我需要全部更改?如果可以,您能推荐一些解决方案吗?
英文:
I am currently making my own dynamic array and I have function that will append to it but I want to append my structure or pointer so I need to modify function arguments and not the function name.
This is my function that will append items into ph_arr.
#define ph_array_(type, name) \
struct name { \
type* data; size_t used_size, alloc_size; \
} __attribute__((__packed__))
void ph_array_append_(struct ph_array_void_* ph_arr, size_t data_size, void* items, size_t count);
I want to use the macro (ph_array_append
) like this
ph_array_append(ph_arr1, ph_arr2);
// or
const int data[] = { 1, 2, 3, 4 };
ph_array_append(ph_arr1, data, 4);
I can't call the function because I need to make different arguments for the same function, so I tried to add the function with tweaked arguments but it didn't work, also I tried to add only the tweaked arguments but it didn't work either.<br><br>Is it possible to do this or do I have to change it all? If yes can you recommend me some solution?
答案1
得分: 1
这里是一个关于如何使用 _Generic
调用不同函数的想法,具体取决于第二个参数,该参数可以是指向 struct ph_array_##name
的指针,也可以是指向结构体所包含类型的指针。
请注意:我在使用gcc扩展 __VA_OPT__
。
#include <stdio.h>
#define ph_array_(type, name) \
struct ph_array_##name { \
type* data; size_t used_size, alloc_size; \
} __attribute__((__packed__));
// 定义两个结构体
ph_array_(void,void)
ph_array_(int,int)
// 每个结构体的2个函数:
void ph_array_append_void_void(struct ph_array_void *a1, void* items, size_t count, size_t data_size) {
puts("ph_array_append_void_void");
}
void ph_array_append_void_ph_array_void(struct ph_array_void *a1, struct ph_array_void *a2) {
puts("ph_array_append_void_ph_array_void");
}
void ph_array_append_int_int(struct ph_array_int *a1, int* items, size_t count) {
puts("ph_array_append_int_int");
}
void ph_array_append_int_ph_array_int(struct ph_array_int *a1, struct ph_array_int *a2) {
puts("ph_array_append_int_ph_array_int");
}
// 这是用于选择调用哪个函数的 _Generic:
#define ph_array_append(A, S, ...) \
_Generic((S), \
void* : ph_array_append_void_void, \
struct ph_array_void* : ph_array_append_void_ph_array_void, \
int* : ph_array_append_int_int, \
struct ph_array_int* : ph_array_append_int_ph_array_int) \
((A),(S) __VA_OPT__(,) __VA_ARGS__)
int main(void) {
struct ph_array_int foo;
struct ph_array_void bar;
ph_array_append(&foo, &foo);
ph_array_append(&foo, (int*)NULL, 0); // 没有 data_size
ph_array_append(&bar, &bar);
ph_array_append(&bar, (void*)NULL, 0, 10); // 需要 data_size
}
输出:
ph_array_append_int_ph_array_int
ph_array_append_int_int
ph_array_append_void_ph_array_void
ph_array_append_void_void
英文:
Here's one idea for how you could use a _Generic
to call different functions depending on the second argument, which is either a pointer to a struct ph_array_##name
or a pointer to the type the struct holds.
Note: I'm using the gcc extension __VA_OPT__
.
#include <stdio.h>
#define ph_array_(type, name) \
struct ph_array_##name { \
type* data; size_t used_size, alloc_size; \
} __attribute__((__packed__));
// define two structs
ph_array_(void,void)
ph_array_(int,int)
// 2 functions for each struct:
void ph_array_append_void_void(struct ph_array_void *a1, void* items, size_t count, size_t data_size) {
puts("ph_array_append_void_void");
}
void ph_array_append_void_ph_array_void(struct ph_array_void *a1, struct ph_array_void *a2) {
puts("ph_array_append_void_ph_array_void");
}
void ph_array_append_int_int(struct ph_array_int *a1, int* items, size_t count) {
puts("ph_array_append_int_int");
}
void ph_array_append_int_ph_array_int(struct ph_array_int *a1, struct ph_array_int *a2) {
puts("ph_array_append_int_ph_array_int");
}
// and the _Generic that selects which function to call:
#define ph_array_append(A, S, ...) \
_Generic((S), \
void* : ph_array_append_void_void, \
struct ph_array_void* : ph_array_append_void_ph_array_void, \
int* : ph_array_append_int_int, \
struct ph_array_int* : ph_array_append_int_ph_array_int) \
((A),(S) __VA_OPT__(,) __VA_ARGS__)
int main(void) {
struct ph_array_int foo;
struct ph_array_void bar;
ph_array_append(&foo, &foo);
ph_array_append(&foo, (int*)NULL, 0); // no data_size
ph_array_append(&bar, &bar);
ph_array_append(&bar, (void*)NULL, 0, 10); //data_size needed
}
Output:
ph_array_append_int_ph_array_int
ph_array_append_int_int
ph_array_append_void_ph_array_void
ph_array_append_void_void
答案2
得分: 0
在C语言中,最通用的编程方式仍然是使用void*
(尽管_Generic
也有其用途,但不要把它看作唯一的工具,其他方法也同样有效)。
例如:
struct array_t {
void *data; //通用数据指针
size_t type_size; //sizeof(type)
size_t size; //data中的元素数量
size_t capacity; //分配的容量(以元素数量表示)
};
然后,通过新增的type_size
成员,您可以获得重新分配和复制各种数据数组所需的所有信息。您还可以通过添加许多函数指针来扩展通用数组,以指定适当的函数来分配、释放、复制或移动数据(默认为malloc
、realloc
、free
、memcpy
、memmove
)。
简而言之,放弃类型安全(使用宏)或使用一堆函数,它们都执行相同的操作,但在名称和签名上有所不同,或者只实现一个通用版本,通过使用void*
作为类型参数,并通过字段type_size
来决定要分配或移动多少字节。
例如:
array_t* array_append(array_t *dst, const array_t *src)
{
if (dst->type_size != src->type_size) { //不兼容的类型
return NULL;
}
if (dst->capacity < dst->size + src->size) {
dst->data = realloc(dst->data, (dst->capacity + src->size) * dst->type_size);
dst->capacity += src->size;
}
memcpy(
dst->data + (dst->size * dst->type_size),
src->data,
src->size * src->type_size
);
dst->size += src->size;
return dst;
}
或者
array_t* array_append(array_t *dst, const void *src, size_t num);
这里唯一有用的宏是数组初始化程序或成员访问(下标、解引用等),除此之外,不需要其他宏、内联函数或_Generic
魔法(记住,有人说宏是邪恶的)。
更复杂的版本:
//每种类型都有自己的类
struct array_class_t {
size_t type_size;
//以下大小,可以解释为字节数或元素数量,
//但要保持一致性,不要混合使用!
void* (*allocate)(size_t);
void* (*reallocate)(void*, size_t);
void (*free)(void*);
void (*copy)(void*, const void*, size_t);
void (*move)(void*, const void*, size_t);
};
struct array_t {
struct array_class_t *class_;
void *data;
size_t size;
size_t capacity;
};
注意: 上面的代码(指针算术)不符合C标准,它使用了GCC的扩展功能,详见:Arithmetic on void- and Function-Pointers。为了符合C标准,指针必须转换为unsigned char*
(或者一开始就使用它,而不是void*
)。
英文:
The most generic way to program in C is still: void*
(albeit _Generic
, which undoubtly has its uses, but do not see it as a hammer and everything else as a nail).
e.g.
struct array_t {
void *data; //generic data pointer
size_t type_size; //sizeof(type)
size_t size; //number of elements in data
size_t capacity; //allocated size (in number of elements)
};
Then, with the newly introduced member type_size
, you have every bit of information you need to reallocate and copy the various data arrays. You could also extend the generic array with numerous function pointers to specify the appropriate functions to allocate, free, copy or move the data (default would be malloc
, realloc
, free
, memcpy
, memmove
).
Simply put, give up type safety (use macros) or use a bunch of functions which do all the same thing but differ in name and signature or implement only a generic version, by using void*
as type parameter and decide via the field type_size
on how many bytes to allocate or move around.
e.g.
array_t* array_append(array_t *dst, const array_t *src)
{
if (dst->type_size != src->type_size) { //incompatible types
return NULL;
}
if (dst->capacity < dst->size + src->size) {
dst->data = realloc(dst->data, (dst->capacity + src->size) * type_size);
dst->capacity += src->size;
}
memcpy(
dst->data + (dst->size * dst->type_size),
src->data,
src->size * src->type_size
);
dst->size += src->size;
return dst;
}
or
//param 'num' interpreted as number of elements in 'src',
//therefore the length in bytes of src would be: num * sizeof(type)
//num bytes could also be used here,
//but then, num would have to be a multiple of dst->type_size,
//therefore num elements recommended
array_t* array_append(array_t *dst, const void *src, size_t num);
The only useful macros here would be for an array initializer or member access (subscript, dereference, ...), other than that no other macro or inline function or _Generic
voodoo needed (remember, it's being said that macros are evil).
More sophisticated version:
//each type has its own class
struct array_class_t {
size_t type_size;
//following sizes, either interpreted as num bytes or num elements,
//but be consistent, do not mix!
void* (*allocate)(size_t);
void* (*reallocate)(void*, size_t);
void (*free)(void*);
void (*copy)(void*, const void*, size_t);
void (*move)(void*, const void*, size_t);
};
struct array_t {
struct array_class_t *class_;
void *data;
size_t size;
size_t capacity;
};
Note: above code (pointer arithmetic) does not conform to the C Standard, it uses an GCC extension, see: Arithmetic on void- and Function-Pointers. To conform to the C Standard, the pointers would have to be cast to unsigned char*
(or to be used in the first place, e.g. instead of void*
).
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论