在 C 中采用未知类型参数的泛型函数
Generic functions taking unknown type parameters in C
我正在尝试制作一些函数,采用未知类型参数,以通用地应用函数。
让我们以一个可以对数组的每个元素应用 close(int fd)
的函数为例:
void for_each(void *array[], void (*func)(void *))
{
for (size_t i = 0; array[i] != NULL; ++i)
func(array[i]);
}
如果我想将此函数与 close(int fd)
一起使用,我必须像这样创建一个包装函数:
void close_fd(void *fd)
{
close(*(int *)fd);
}
我想将此 for_each
函数与 字符串、浮点数和其他所有内容一起使用。
- Isn't there something that could achieve it, without the wrapping part ?
- I know that C++ have a lot of way to do it, lambdas, templates etc., but is there a good way in C ? A compromise ?
嗯,很明显你想针对不同的数据多次调用一个函数(同一个函数)。但是这些数据是在一个数组中传递的,根据定义是存储多个相同类型的数据项。所以最后没有特殊的构造来做到这一点,只需在普通 C:
typedef .... MY_TYPE;
/* define a pointer to represent the type of callback pointer you are using */
typedef void (*callback_ptr)(MY_TYPE param);
/* this is the real callback function you are going to use */
void my_func1(MY_TYPE param){
/* process a single MY_TYPE */
}
void my_funcN(MY_TYPE param) {
/* another process function */
}
/* function to apply the callback to an array of MY_TYPE */
void for_each( MY_TYPE array[], size_t array_sz, callback_ptr f )
{
int i;
for (i = 0; i < array_sz; i++)
f(array[i]);
}
....
MY_TYPE my_array[100] = { ... };
size_t my_array_sz = sizeof my_array / sizeof my_array[0];
for_each(my_array, my_array_sz, my_func1); /* process all by my_func1 */
for_each(my_array, my_array_sz, my_funcN); /* process all by my_funcN */
尽量避免在非绝对必要时使用 void *
。可能在早期的设计阶段,你并不知道它要处理的数据的实际类型,但很明显,一旦你编写了实际的调用语句,你就必须放入参数并使用 return 值,所以你坚持使用实际类型......这给了你如何声明回调指针和过程函数的必要提示。将实际类型放在函数调用中可以让编译器检查正确性,这有助于您避免犯错误。
泛型函数
C 不支持泛型函数,但是如果你想要某种安排,允许你编写函数,其中某些数据类型是泛型的,你可以用宏来模拟它(你必须非常小心地编写宏可以工作,但可以实现非常好的近似值)
您可以使用 cpp
宏为每种类型定义不同的函数,如:
func_def.h
#define FUNC(TYPE) \
void my_func_##TYPE( TYPE param ) \
{ \
/* body of function */ \
}
然后包含在文件范围内,函数如下:
program.c
FUNC(int)
FUNC(double)
这将扩展为:
void my_func_int( int param ) \
{ \
/* body of function */ \
}
void my_func_double( double param ) \
{ \
/* body of function */ \
}
无论如何,您都会在参数中进行类型检查。如您所见,您必须在函数名称中使用类型,因为 C 不支持函数重载(具有相同名称和不同参数列表的函数)在这种情况下,my_func_*
回调和for_each_*
必须定义函数,编译器才能完全进行类型检查。
如评论中所述,您可以使用宏来实现。此外,在 C 语言中更习惯于将数组的大小传递给使用它的函数,而不是依赖标记值(字符串除外)。
#define for_each(array, array_size, func) do { \
for (size_t i = 0; i < (array_size); ++i) { \
func((array)[i]); \
} \
} while(0)
用法:
#define ARRAY_SIZE(array) (sizeof((array)) / sizeof(*(array)))
void int_func(int arg)
{
}
void string_func(const char *arg)
{
}
int int_array[5];
const char *string_array[5];
void calling_func(void)
{
for_each(int_array, ARRAY_SIZE(int_array), string_func); // Warning: pointer from integer without a cast
for_each(string_array, ARRAY_SIZE(string_array), int_func); // Warning: integer from pointer without a cast
}
我正在尝试制作一些函数,采用未知类型参数,以通用地应用函数。
让我们以一个可以对数组的每个元素应用 close(int fd)
的函数为例:
void for_each(void *array[], void (*func)(void *))
{
for (size_t i = 0; array[i] != NULL; ++i)
func(array[i]);
}
如果我想将此函数与 close(int fd)
一起使用,我必须像这样创建一个包装函数:
void close_fd(void *fd)
{
close(*(int *)fd);
}
我想将此 for_each
函数与 字符串、浮点数和其他所有内容一起使用。
- Isn't there something that could achieve it, without the wrapping part ?
- I know that C++ have a lot of way to do it, lambdas, templates etc., but is there a good way in C ? A compromise ?
嗯,很明显你想针对不同的数据多次调用一个函数(同一个函数)。但是这些数据是在一个数组中传递的,根据定义是存储多个相同类型的数据项。所以最后没有特殊的构造来做到这一点,只需在普通 C:
typedef .... MY_TYPE;
/* define a pointer to represent the type of callback pointer you are using */
typedef void (*callback_ptr)(MY_TYPE param);
/* this is the real callback function you are going to use */
void my_func1(MY_TYPE param){
/* process a single MY_TYPE */
}
void my_funcN(MY_TYPE param) {
/* another process function */
}
/* function to apply the callback to an array of MY_TYPE */
void for_each( MY_TYPE array[], size_t array_sz, callback_ptr f )
{
int i;
for (i = 0; i < array_sz; i++)
f(array[i]);
}
....
MY_TYPE my_array[100] = { ... };
size_t my_array_sz = sizeof my_array / sizeof my_array[0];
for_each(my_array, my_array_sz, my_func1); /* process all by my_func1 */
for_each(my_array, my_array_sz, my_funcN); /* process all by my_funcN */
尽量避免在非绝对必要时使用 void *
。可能在早期的设计阶段,你并不知道它要处理的数据的实际类型,但很明显,一旦你编写了实际的调用语句,你就必须放入参数并使用 return 值,所以你坚持使用实际类型......这给了你如何声明回调指针和过程函数的必要提示。将实际类型放在函数调用中可以让编译器检查正确性,这有助于您避免犯错误。
泛型函数
C 不支持泛型函数,但是如果你想要某种安排,允许你编写函数,其中某些数据类型是泛型的,你可以用宏来模拟它(你必须非常小心地编写宏可以工作,但可以实现非常好的近似值)
您可以使用 cpp
宏为每种类型定义不同的函数,如:
func_def.h
#define FUNC(TYPE) \
void my_func_##TYPE( TYPE param ) \
{ \
/* body of function */ \
}
然后包含在文件范围内,函数如下:
program.c
FUNC(int)
FUNC(double)
这将扩展为:
void my_func_int( int param ) \
{ \
/* body of function */ \
}
void my_func_double( double param ) \
{ \
/* body of function */ \
}
无论如何,您都会在参数中进行类型检查。如您所见,您必须在函数名称中使用类型,因为 C 不支持函数重载(具有相同名称和不同参数列表的函数)在这种情况下,my_func_*
回调和for_each_*
必须定义函数,编译器才能完全进行类型检查。
如评论中所述,您可以使用宏来实现。此外,在 C 语言中更习惯于将数组的大小传递给使用它的函数,而不是依赖标记值(字符串除外)。
#define for_each(array, array_size, func) do { \
for (size_t i = 0; i < (array_size); ++i) { \
func((array)[i]); \
} \
} while(0)
用法:
#define ARRAY_SIZE(array) (sizeof((array)) / sizeof(*(array)))
void int_func(int arg)
{
}
void string_func(const char *arg)
{
}
int int_array[5];
const char *string_array[5];
void calling_func(void)
{
for_each(int_array, ARRAY_SIZE(int_array), string_func); // Warning: pointer from integer without a cast
for_each(string_array, ARRAY_SIZE(string_array), int_func); // Warning: integer from pointer without a cast
}