为什么声明出现在一个混乱的符号名中

Why is declval present in a mangled symbol name?

本文关键字:混乱 一个 符号 声明 为什么      更新时间:2023-10-16

我正在使用GCC (4.9.2) abi::__cxa_demangle,我遇到了一个无法demangle特定符号名的情况。

符号是:

_ZNK12DebugWrapperIR5TestClsE5getIdIIEEEDTcldtcl7declvalIS1_EEL_ZNKS0_5getIdEvEspfp_EEDpOT_ 

我很惊讶在这里看到declval

这个函数是用这个宏定义的:

template <typename WrappedType>
class DebugWrapper
{
private:
WrappedType VALUE;
std::string NAME;
mutable std::string CALLER;
public:
...
#define WRAP_CONST_FUNCTION(name, enterLogging, exitLogging)                                    
        template <typename ...Args>                                                             
        auto name(Args&&... args) const -> decltype(std::declval<WrappedType>().name(args...))  
        {                                                                                       
                                                                                            
            struct dummy                                                                        
            {                                                                                   
                const char* const FUNCTION = nullptr;                                           
                const std::string& CALLER;                                                      
                const WrappedType& VALUE;                                                       
                                                                                                
                dummy(  const char* const input,                                                
                        const std::string& caller,                                              
                        const WrappedType& value):                                              
                    FUNCTION(input), CALLER(caller), VALUE(value)                               
                {                                                                               
                    enterLogging;                                                               
                }                                                                               
                                                                                                
                ~dummy()                                                                        
                {                                                                               
                    exitLogging;                                                                
                }                                                                               
            }dummy(#name, CALLER, VALUE);                                                       
                                                                                                
            return VALUE.name(args...);                                                         
        }
        WRAP_CONST_FUNCTION(getId, <whatever>, <whatever>)
...
};

我也做了一个快速搜索,通过itanc++ abi规范声明,但没有结果。

它为什么在那里?为什么我不能这样做呢?

模板函数需要将其返回类型出现在修改后的名称中,因为模板函数可以仅在返回类型上重载。一个更简单的例子是

template <typename T> void f() { }
template <typename T> auto g() -> decltype(f<T>()) { }
inline void h() { }
int main() { g<int>(); h(); }

编译并检查输出,我看到:

<>之前$ g++ -c h.cc -std=c++11 && nm h.o | c++filt0000000000000000 T主0000000000000000 W decltype ((f)()) g()0000000000000000 W h()之前

您可以在那里看到g的返回类型,但h没有。事实上,h已经不是一个模板函数,这意味着在相同的命名空间中不可能有另一个具有相同参数的函数h

这也是为什么getId在你的名字中出现了两次。其中一个是名称本身,另一个来自它在返回类型中的外观。

declval在这里并不特殊,这就是为什么它没有在c++ ABI中调用的原因。任何函数,库或用户,都以同样的方式处理。

至于它为什么不动,很难说。在您的问题中生成混乱的名称的真实代码示例将在这里有所帮助,但是5TestCls看起来错误,因为名称前面的数字表示长度,并且我确实得到了TestCls应该是全名的印象。如果这确实是名称,则请求失败,因为修改后的名称无效。

根据您在评论中发布的完整示例,我可以提出一个简化的程序,似乎是成员访问操作符没有被demangler正确处理:

extern struct A { void f(); static void g(); } a;
template <typename...T> auto f(T...t) -> decltype(a.f(t...));
template <typename...T> auto g(T...t) -> decltype(A::g(t...));
int main() { f(); g(); }
<>以前g++化c++ 11美元迂腐- c h.cc & & h.o & & nm - c h.o0000000000000000 T主U _Z1fIJEEDTcldtL_Z1aEL_ZN1A1fEvEspfp_EEDpT_U _Z1gIJEEDTclL_ZN1A1gEvEspfp_EEDpT_0000000000000000 T主U _Z1fIJEEDTcldtL_Z1aEL_ZN1A1fEvEspfp_EEDpT_U decltype (A::g({parm#1}…))g<>()

_Z1fIJEEDTcldtL_Z1aEL_ZN1A1fEvEspfp_EEDpT__Z1gIJEEDTclL_ZN1A1gEvEspfp_EEDpT_之间的差异在一些额外的空格中更加明显:

<>以前_Z1fIJEEDTcldtL_Z1aEL_ZN1A1fEvEspfp_EEDpT__Z1gIJEEDTcl L_ZN1A1gEvEspfp_EEDpT_ 之前

唯一的区别是fgdtL_Z1aE

Itanium c++ ABI指定x.y被修改为dt <expression> <unresolved-name>。这里,<expression>L_Z1aE。这部分看起来不错。这表示a全局变量的"混乱"(不是真正的)名称。但是<unresolved-name>就是_ZN1A1fEvE。这是错误的。这对于decltype(A::g(t...))版本是正确的,其中函数调用操作符的最左边的操作数可以通过<expr-primary> ::= L <mangled-name> E表示为一个混乱的名称,但对于decltype(a.f(t...))版本则不是这样。它可能意味着类似A::f()的东西,但<unresolved-name>不应该有名称空间限定符,除非它们实际出现在源代码中,即使这样,也只能使用特殊的前缀(sr)。它也不应该有参数信息。<unresolved-name>应该是1f

如果使用了正确的名称,则需方可以处理:

$ c++filt <<_Z1fIJEEDTcldtL_Z1aE1fspfp_EEDpT_Decltype ((a.f)({parm#1}…))f<>()

clang确实会生成正确的名称,在Coliru上可以看到,只需在命令调用中将g++更改为clang++。您可以将此作为bug报告给GCC开发人员。