在C(不是C ++)中是否可以使用带有泛型值(不是指针)的函数指针,并设置-pedantic和-wall -werror标志。
注意:我无法更改参数类型。代码必须支持uint8_t,uint16_t等...类型作为参数
目标:用代码解决问题。
问题
有没有办法将uint8_t(和/或uint16_t)参数强制转换为void *(Approach1)? 特别是将非指针类型值传递给void *值。
有没有办法设置一个适用于所有不同值的通用类型(方法2)?
最后的手段 有没有办法在代码中设置特定的编译器Exception?(this question has been answer)
方法1(导致从uint8_t无效转换为void *)
typedef struct
{
void (*set_func)(void*);
} SetFunction;
void setValue(uint8_t byteValue)//Not a pointer parameter
{
byteValue++;
}
void setShortValue(uint16_t byteValue)//Not a pointer parameter
{
byteValue++;
}
int main()
{
uint8_t a = 123;
uint16_t b = 321;
SetFunction pointerFuncion;
SetFunction pointerFuncionShort;
//Typecast the setValue to appease compiler warning
pointerFunction.set_func = (void(*)(void*))&setValue;
pointerFuncionShort.set_func = (void(*)(void*))&setShortValue;
//use the function pointer with non-pointer parameter
// Compile ERROR thrown invalid conversion from uint8_t to void*
pointerFunction.set_func(a);
pointerFuncionShort.set_func(b);
}
Aprroach 2(导致参数编译错误太多)
typedef struct
{
void (*set_func)();//Blank parameter to allow multiple args
} SetFunction;
void setValue(uint8_t byteValue)//Not a pointer parameter
{
byteValue++;
}
void setShortValue(uint16_t byteValue)//Not a pointer parameter
{
byteValue++;
}
int main()
{
uint8_t a = 123;
uint16_t b = 321;
SetFunction pointerFuncion;
SetFunction pointerFuncionShort;
//Typecast the setValue to appease compiler warning
pointerFunction.set_func = (void(*)())&setValue;
pointerFuncionShort.set_func = (void(*)())&setShortValue;
//use the function pointer with non-pointer parameter
pointerFunction.set_func(a);// Compile ERROR thrown "Too many Args"
pointerFuncionShort.set_func(b);// Compile ERROR thrown "Too many Args"
}
更新
为问题增加清晰度。 我有100个具有1个参数的函数。 函数的1参数是不同的类型。 我无法更改任何函数,但我想为任何函数提供1个函数指针类型(或更多基于类型)。 我可以将与函数指针和类型相关联的任何类型更改为函数指针,但不能更改它指向的类型。
答案 0 :(得分:3)
不,不是。
简单回答:被调用的函数不知道如何获取参数。
详细信息:调用(执行)功能代码时已经修复。因此它包含访问参数的代码,这取决于arguemtn的类型(例如,对于uint32_t,需要32位加载/ soter,对于uint8_t是8位加载/存储)。因此它甚至无法正确处理值。 与C ++和Python等高级语言不同,C没有内置运行时类型识别的概念。
但是,您可以将union
传递给函数并单独处理函数中的每个变体。这将产生所有可能的访问。但是,您必须指定要传递的实际类型。这通常由第二个参数完成,该参数指定实际类型。
该联合也可以是由类型标识符和实际值组成的结构。但这只是一个信封,一切都是明确的。
typedef union {
int i;
float f;
} MyValue;
typedef enum {
MY_VALUE_int,
MY_VALUE_float
} MyValueType;
void func(MyValueType type, MyValue value)
{
switch ( type ) {
...
}
}
int main(void)
{
func(MY_VALUE_int, (MyValueType){ .i=1 });
}
复合文字参数仅适用于常量,否则您必须先将值分配给联合(或者只使用该联合)。 gcc有一个扩展来避免这种情况,因此你可以拥有一个带有这种联合的函数,但是调用者可以使用简单的强制转换,而不是复合文字。这也适用于变量:
func(MY_VALUE_float, (MyValueType)1.0);
另一种方法是传递const void *
和内部投射。然而,这比工会方法更具风险。
所有方法都需要明确地设置实际类型(例如使用枚举)。
C11允许根据使用新_Generic构造的参数类型创建一个评估不同表达式的宏。有了这个原始方法可以模拟(使用gcc扩展,正常方式是可能的,但更复杂):
// use the code of the first block here
#define func_generic(val) _Generic((val), \
int : func(MY_VALUE_int, (MyValueType)(val)), \
int : func(MY_VALUE_int, (MyValueType)(val)) )
// and call like:
func_generic(1);
func_generic(1.0);
但请注意_Generic
的限制:对于uint16_t和uint32_t,类型选择器不允许使用两种兼容类型(即const int
和int
都不允许)但是。
请注意,gcc(您显然使用)支持使用-std=c11
或std=gnu11
的C11。后者还支持GNU扩展。
答案 1 :(得分:1)
请注意,两个示例都表示未定义行为,因为您通过另一个(函数)类型的指针调用函数。
我找到了一个解决方案,它依赖于事先已知的有限数量的函数类型这一事实。不过我认为这太麻烦了。只需调用原始函数。
SecondaryTile t2 = new SecondaryTile();
t2.TileId = muss.ID.ToString();
t2.Arguments = muss.ID.ToString();
string nome = "Open ARTE";
t2.DisplayName = nome;
t2.VisualElements.Square150x150Logo = new Uri(string.Format("ms-appdata:///local/{0}", fileName));
t2.VisualElements.ShowNameOnSquare150x150Logo = true;
await t2.RequestCreateAsync();
CreateLiveTileSecondary();
请注意
函数指针可以自由转换为任何其他函数 函数指针类型再返回,你会得到原始的 指针。
这就是我们使用的。我们甚至不能使用enum GFType {
GF_UINT8,
GF_UINT16 // etc
};
struct GenericFunction {
void (*func)(void);
GFType type;
};
void callGenericFunction(GenericFunction func, uint64_t p) // largest type
{
switch (func.type) {
case GF_UINT8:
((void (*)(uint8_t))func.func)(p);
return;
case GF_UINT16:
((void (*)(uint16_t))func.func)(p);
return;
default:
assert(1); // unimplemented function type
}
}
void setValue(uint8_t byteValue) // Not a pointer parameter
{
byteValue++;
}
void setShortValue(uint16_t byteValue) // Not a pointer parameter
{
byteValue++;
}
int main() {
uint8_t a = 123;
uint16_t b = 321;
GenericFunction pointerFunction;
GenericFunction pointerFunctionShort;
pointerFunction.func = (void (*)(void))setValue;
pointerFunction.type = GF_UINT8;
pointerFunctionShort.func = (void (*)(void))setShortValue;
pointerFunction.type = GF_UINT16;
callGenericFunction(pointerFunction, a);
callGenericFunction(pointerFunctionShort, b);
return 1;
}
(因为它是一个数据指针,而不是函数指针)来存储函数指针。所以我使用void *
来存储函数指针。枚举告诉我们在需要调用它时我们必须转换它的功能。
答案 2 :(得分:1)
如果您可以使用C11,则可以使用_Generic
:
#include <stdio.h>
#include <inttypes.h>
#define setvalue_generic(x) _Generic((x), \
uint8_t: setValue, \
uint16_t: setShortValue \
)(x)
void setValue(uint8_t byteValue)
{
printf("setValue: %" PRIu8 "\n", byteValue);
byteValue++;
}
void setShortValue(uint16_t byteValue)
{
printf("setValue: %" PRIu16 "\n", byteValue);
byteValue++;
}
int main(void)
{
uint8_t a = 123;
uint16_t b = 321;
setvalue_generic(a);
setvalue_generic(b);
return 0;
}
似乎与gcc -std=c11 -pedantic -Wextra -Wall
配合良好。
答案 3 :(得分:0)
简短的回答是否定的。
你有几个问题:
1)不同的函数都必须具有相同的签名,以允许函数指针指向它们。
2)函数按值进行args,这意味着将传入一个副本,对值进行的任何操作都不会在函数调用之外产生任何影响。由于你不允许指针,我无法看到任何方式。
如果你没有对问题2感到烦恼,那么你可以尝试声明一个可以接受任何类型args的可变函数。
e.g。
void doSomethingWithValue(enum MyType type ...)
{
va_list args;
va_start( args, type);
switch( type)
{
case Uint8Type:
{
uint8_t value = va_arg(args, uint8_t);
//doSomething to value
}
break;
.
.
.
}
va_end(args);
}
其中MyType是枚举设置,用于标识传入的类型。 使用方式如下:
uint8_t value = 7;
doSomethingWithValue(Uint8Type, value);
//note that value is still 7
答案 4 :(得分:0)
@bolov答案适用于处理不同类型,这只是处理同一问题的一种不同方式,但有1个参数。
这种方法的缺点是main中的类型必须是GENERAL_TYPE。在我的应用程序中,我可以更改参数的类型,但我可以更改我指向的函数的类型。
(void(*)(GENERAL_TYPE))&
处理函数的参数类型,Union处理所有不同大小的类型。
另一种选择是为每种类型提供函数指针。
typedef union generalType
{
uint8_t byteData;
uint16_t shortData;
uint32_t intData;
int integerData;
uint64_t longData;
void * voidData;
//Add any type
} GENERAL_TYPE;
typedef struct
{
void (*set_func)(GENERAL_TYPE);
} SetFunction;
void setValue(uint8_t byteValue)//Not a pointer parameter
{
byteValue++;
}
void setShortValue(uint16_t byteValue)//Not a pointer parameter
{
byteValue++;
}
int main()
{
GENERAL_TYPE a.byteData = 123;//restricted to use GENERAL_TYPE here
GENERAL_TYPE b.shortData = 321;
SetFunction pointerFuncion;
SetFunction pointerFuncionShort;
//Typecast the setValue parameter to be a general type will
//Allow it to send the data of whatever type.
pointerFunction.set_func = (void(*)(GENERAL_TYPE))&setValue;
pointerFuncionShort.set_func = (void(*)(GENERAL_TYPE))&setShortValue;
//use the function pointer with non-pointer parameter
pointerFunction.set_func(a);
pointerFuncionShort.set_func(b);
}