我目前正在创建一组随机函数,我将在未来的 C 项目中将其用作库(我只需要头文件并链接库)。
一切都运行良好,但我发现我的
.h
文件非常不可读,并且由于具有相同基本名称的函数数量较多,它在 VSCode 智能感知中造成了混乱。
例如,这是我在头文件中实现的
isPrime()
函数:
/** @brief Type-generic primality checker using O(sqrt(n)) algorithm */
#define isPrime(n) \
_Generic((n), \
u8 : isPrimeu8, \
u16: isPrimeu16, \
u32: isPrimeu32, \
u64: isPrimeu64, \
i8 : isPrimei8, \
i16: isPrimei16, \
i32: isPrimei32, \
i64: isPrimei64, \
default: isPrimeu64 \
)(n)
bool isPrimeu8(u8 n);
bool isPrimeu16(u16 n);
bool isPrimeu32(u32 n);
bool isPrimeu64(u64 n);
bool isPrimei8(i8 n);
bool isPrimei16(i16 n);
bool isPrimei32(i32 n);
bool isPrimei64(i64 n);
(我使用我自己的类型,只是 typedef : u -> 无符号整数,i -> 有符号整数,f -> 浮点数。然后后面的数字是位大小)
如您所见,代码中有很多冗余,因为我希望我的函数是类型通用的(这在 C 中是一个痛苦)。这是我的目标的一个例子:
/** @brief Type-generic primality checker using O(sqrt(n)) algorithm */
#define isPrime(n) __my_function_with_all_the_type_generic_mess(n)
那么,有办法做到这一点吗?将通用命令提取到另一个文件中,以便我的函数
isPrime()
在导入我的库时仍然有效?
注意:我目前正在使用 C23 的最新功能,所以即使是实验性的东西对我来说也有好处,我有 GCC 13.2,并且在可用时肯定会有 GCC 14。
编辑:
确实,
isPrime()
是一个坏例子,因为它可以通过在更大的类型上使用隐式转换来轻松缩短(我完全忘记了这是可能的)。这是另一个在我看来无法简化的例子:
/** @brief Type-generic sum on a whole array */
#define arrayAdd(array, length) \
_Generic((array), \
uint8_t* : arrayAddu8 , \
uint16_t*: arrayAddu16, \
uint32_t*: arrayAddu32, \
uint64_t*: arrayAddu64, \
int8_t* : arrayAddi8 , \
int16_t* : arrayAddi16, \
int32_t* : arrayAddi32, \
int64_t* : arrayAddi64, \
float* : arrayAddf32, \
double* : arrayAddf64, \
default : arrayAddf64 \
)((array), (length))
uint64_t arrayAddu8 (uint8_t* array, size_t length);
uint64_t arrayAddu16(uint16_t* array, size_t length);
uint64_t arrayAddu32(uint32_t* array, size_t length);
uint64_t arrayAddu64(uint64_t* array, size_t length);
int64_t arrayAddi8 (int8_t* array, size_t length);
int64_t arrayAddi16(int16_t* array, size_t length);
int64_t arrayAddi32(int32_t* array, size_t length);
int64_t arrayAddi64(int64_t* array, size_t length);
double arrayAddf32(float* array, size_t length);
double arrayAddf64(double* array, size_t length);
即使语法可能不同(例如,我不知道我是否希望特定于类型的函数具有相同的基本名称+类型前缀形式,但我们现在可以忽略它),我不明白一种简化此操作的方法,因为数组索引和操作需要类型的大小。
根据您的所有答案,似乎没有像 C++ 中的模板一样紧凑的解决方案,除了尝试通过使用隐式转换和类似的方法来最小化所需的特定于类型的函数的数量。如果是的话
如果唯一的问题是 VSCode 智能感知,只需更改基本名称即可。
除此之外,如果您单独定义它们,并且问题是代码重复,则一种简化方法是使用宏生成它们:
#define gen_isprime(type) \
bool isPrime##type(type n) { \
// Add the code here \
} \
// And now:
gen_isprime(u8)
gen_isprime(u16)
gen_isprime(u32)
gen_isprime(u64)
gen_isprime(i8)
gen_isprime(i16)
gen_isprime(i32)
gen_isprime(i64)
虽然我不明白为什么你不能只有两个
isPrimeu64()
和 isPrimei64()
函数,或者不使用 stdint.h
中的类型(uint64_t
、int32_t
等)。
我将结束我的问题并将其标记为答案,因为收集到的所有信息似乎只是“不可能”做我想做的事。 我仍然会展示最终结果的示例,因为即使这不是我最初想要的,至少它在代码方面更具可读性,并且特定于类型的函数现在对 VSCode 的智能感知隐藏了。 如果您认为有更好的方法,请随时给我任何意见。
array_functions.h :
(注意:impl_array_functions_error()
定义明确)
/** @brief Type-generic function doing a operation specified by the user on a whole array. */
#define arrayDoOperation(array, length, neutral_value, operator) \
_Generic((array), \
uint8_t* : impl_DoOperation_uint8_t , \
uint16_t*: impl_DoOperation_uint16_t, \
uint32_t*: impl_DoOperation_uint32_t, \
uint64_t*: impl_DoOperation_uint64_t, \
int8_t* : impl_DoOperation_int8_t , \
int16_t* : impl_DoOperation_int16_t , \
int32_t* : impl_DoOperation_int32_t , \
int64_t* : impl_DoOperation_int64_t , \
float* : impl_DoOperation_float , \
double* : impl_DoOperation_double , \
default : impl_array_functions_error \
)((array), (length), (neutral_value), (operator))
uint64_t impl_DoOperation_uint8_t (uint8_t* array, size_t length, uint64_t neutral_value, uint64_t (*operator)(uint64_t, uint64_t));
uint64_t impl_DoOperation_uint16_t(uint16_t* array, size_t length, uint64_t neutral_value, uint64_t (*operator)(uint64_t, uint64_t));
uint64_t impl_DoOperation_uint32_t(uint32_t* array, size_t length, uint64_t neutral_value, uint64_t (*operator)(uint64_t, uint64_t));
uint64_t impl_DoOperation_uint64_t(uint64_t* array, size_t length, uint64_t neutral_value, uint64_t (*operator)(uint64_t, uint64_t));
int64_t impl_DoOperation_int8_t (int8_t* array, size_t length, int64_t neutral_value, int64_t (*operator)(int64_t , int64_t ));
int64_t impl_DoOperation_int16_t (int16_t* array, size_t length, int64_t neutral_value, int64_t (*operator)(int64_t , int64_t ));
int64_t impl_DoOperation_int32_t (int32_t* array, size_t length, int64_t neutral_value, int64_t (*operator)(int64_t , int64_t ));
int64_t impl_DoOperation_int64_t (int64_t* array, size_t length, int64_t neutral_value, int64_t (*operator)(int64_t , int64_t ));
double impl_DoOperation_float (float* array, size_t length, double neutral_value, double (*operator)(double , double ));
double impl_DoOperation_double (double* array, size_t length, double neutral_value, double (*operator)(double , double ));
array_functions/arrayDoOperation.c
(注意:头文件位于完全不同的目录中,因为它就像公共 api,第一个文件是您应该包含的内容,“array_functions”文件夹是我所有实现的位置)
#include <stdint.h>
#include <stddef.h>
#define GEN_FUNC_ARRAY_DO_OPERATION_UNSIGNED(bitsize) \
uint64_t impl_DoOperation_uint##bitsize##_t(uint##bitsize##_t* array, size_t length, uint64_t neutral_value, uint64_t (*operator)(uint64_t, uint64_t)) { \
uint64_t retval = neutral_value; \
\
for (size_t i = 0 ; i < length ; ++i) { \
retval = (*operator)(retval, array[i]); \
} \
\
return retval; \
}
GEN_FUNC_ARRAY_DO_OPERATION_UNSIGNED(8)
GEN_FUNC_ARRAY_DO_OPERATION_UNSIGNED(16)
GEN_FUNC_ARRAY_DO_OPERATION_UNSIGNED(32)
GEN_FUNC_ARRAY_DO_OPERATION_UNSIGNED(64)
#define GEN_FUNC_ARRAY_DO_OPERATION_SIGNED(bitsize) \
int64_t impl_DoOperation_int##bitsize##_t(int##bitsize##_t* array, size_t length, int64_t neutral_value, int64_t (*operator)(int64_t, int64_t)) { \
int64_t retval = neutral_value; \
\
for (size_t i = 0 ; i < length ; ++i) { \
retval = (*operator)(retval, array[i]); \
} \
\
return retval; \
}
GEN_FUNC_ARRAY_DO_OPERATION_SIGNED(8)
GEN_FUNC_ARRAY_DO_OPERATION_SIGNED(16)
GEN_FUNC_ARRAY_DO_OPERATION_SIGNED(32)
GEN_FUNC_ARRAY_DO_OPERATION_SIGNED(64)
#define GEN_FUNC_ARRAY_DO_OPERATION_FLOAT(type_name) \
double impl_DoOperation_##type_name(type_name* array, size_t length, double neutral_value, double (*operator)(double, double)) { \
double retval = neutral_value; \
\
for (size_t i = 0 ; i < length ; ++i) { \
retval = (*operator)(retval, array[i]); \
} \
\
return retval; \
}
GEN_FUNC_ARRAY_DO_OPERATION_FLOAT(float)
GEN_FUNC_ARRAY_DO_OPERATION_FLOAT(double)