在gcc中不兼容的函数类型之间进行强制转换
我有一个代码生成脚本,该脚本是在2008年左右由其他人编写的,此后几乎没有发生任何变化.最近,我尝试使用gcc9进行编译,并且在生成的代码中看到7300警告在不兼容的函数类型之间进行转换".
I have a code generation script that was written by someone else around 2008 and has worked fine mostly unchanged since then. Just recently I tried compiling with gcc9 and I see 7300 warnings for "cast between incompatible function types" in the generated code.
该代码采用一组函数指针和各种类型标识符,并将所有内容插入到大映射中,以供以后进行选项序列化,打印等.许多函数和变量都以void *的形式进行转换和存储.否则,该代码可以编译而不会出错,并且可以正常运行.
The code takes a set of function pointers and various type identifiers and inserts everything into a big map that's used later for option serialization, printing, etc. Many functions and variables are cast and stored as some form of void *. The code otherwise compiles with no errors and works properly.
我尝试了各种C风格的强制转换,reinterpret_cast和将函数指针强制转换为void(*)(void),但是它们都没有删除警告.为避免此警告,将函数强制转换为通用类型的正确方法是什么?我唯一能想到的解决方案是禁用-Wcast-function-type.
I tried various C-style casts, reinterpret_cast, and casting the function pointer to void(*)(void), but none of them remove the warnings. What's the correct way to cast the function to a generic type to avoid this warning? The only solution I can come up with is disabling -Wcast-function-type.
以下是生成警告的示例行:
Here is one example line that generates warnings:
pim.must_find("input_fn")->set_introspect_info( sizeof(filename_t), ((char *)(&p_cn->input_fn)) - ((char *)p_cn), 0,invalid_offset, (str_from_base_t *)str_from_filename_t, (send_base_t *)send_filename_t, (recv_base_t *)recv_filename_t, (val_from_param_t *)val_from_param_filename_t, 0);
警告与以下两个功能有关:
The warning is related to these two functions:
typedef std::string str_from_base_t( void const * );
std::string str_from_filename_t( filename_t const & v ) { return v; }
filename_t是从std :: string插入的类.
Where filename_t is a class that interits from std::string.
请注意,有数十种不同的str_from _ ***()函数具有不同的类作为参数.
Note that there are dozens of different str_from_***() functions that have different classes as arguments.
我得到的警告是:
../src/gen/DEFReader_PostParam.cc: In function 'void croix::DEFReaderCLI_introspect_pim_init()':
../src/gen/DEFReader_PostParam.cc:25:153: warning: cast between incompatible function types from 'std::string (*)(const croix::filename_t&)' {aka 'std::basic_string<char> (*)(const croix::filename_t&)'} to 'std::string (*)(const void*)' {aka 'std::basic_string<char> (*)(const void*)'} [-Wcast-function-type]
25 | pim.must_find("input_fn")->set_introspect_info( sizeof(filename_t), ((char *)(&p_cn->input_fn)) - ((char *)p_cn), 0,invalid_offset, (str_from_base_t *)str_from_filename_t, (send_base_t *)send_filename_t, (recv_base_t *)recv_filename_t, (val_from_param_t *)val_from_param_filename_t, 0);
将函数转换为泛型的正确方法是什么?
What's the correct way to cast the function to a generic type?
答案是没有正确的方法.该警告是完全合理的.
The answer is that there is no correct way. The warning is absolutely justified.
重点是您拥有一个类型安全的函数,该函数只能传递 filename_t
,并将其强制转换为可以实际使用任何东西的东西.不会遇到未定义行为的唯一方法是,即使您现在可以传递其他任何内容,仍然要传递一个 filename_t
.
The point is that you have a typesafe function that you can pass only filename_t
and you cast it to something that can take literally anything. The only way to not run into undefined bahavior is to still pass it a filename_t
even though you now could pass anything else.
这产生了一个问题,为什么您需要将其强制转换为泛型?您说这些函数被存储在带有某些类型标识符的映射中.现在,由于信息有限,很难对此进行判断,但是对我来说,这听起来像是人为地构建了一种机制,可以为您选择合适的功能,而为此目的,有一个完善的内置机制:函数重载解析.
That yields the question of why you need to cast it to a generic type? You say that those functions get stored in a map with some type identifiers. Now, due to limited information it is hard to judge on this, but to me this sounds a little like artificially building a mechanism that choses the right function for you, while there is a perfectly working built in mechanism for this: function overload resolution.
因此,在代码中的某些点上,您必须有一个 filename_t
对象.然后(我假设)您从中推断出一些类型标识符,并使用该类型标识符在映射中查找适当的函数.然后,将 filename_t
作为 void *
传递到此处,知道它会起作用.
So, at some point in the code you must have a filename_t
object at hands. Then (I'm assuming) you infer some type identifier from it and use that to look up the proper function in a map. Then you pass your filename_t
as a void*
there, knowing that it will work.
我敢肯定它比这要复杂得多,但是也许您可以识别出逻辑的这一部分并加以摆脱:摆脱地图(至少将类型标识符映射到函数的那一部分),然后调用在您仍然知道类型的时候直接使用该函数(因为重载解析将为您解决问题).
I'm sure it's much more complex than this, but maybe you can identify this part of the logic and get rid of it: get rid of the map (at least that part that maps type identifiers to functions) and just call the function directly at a time where you still know the type (because then overload resolution will do the trick for you).
模板可以帮助您保留类型,而不是使所有内容都变为 void *
.
Templates can help you to keep the types instead of making everything void*
.