使用库中定义的模板时未找到符号 [英] Symbol not found when using template defined in a library

查看:415
本文介绍了使用库中定义的模板时未找到符号的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图在iOS应用程序中使用adobe xmp库,但我收到链接错误。我在我的路径中有适当的标题和库,但我收到链接错误。我仔细检查,以确保标题和库在我的路径。我检查了方法的名称,但他们不在库(我使用 nm 命令检查)。我做错了什么?



图书馆标题:

  #if (TXMP_STRING_TYPE)

#includeTXMPMeta.hpp
#includeTXMPIterator.hpp
#includeTXMPUtils.hpp
typedef class TXMPMeta< TXMP_STRING_TYPE> SXMPMeta; //为了客户方便。
typedef class TXMPIterator< TXMP_STRING_TYPE> SXMPIterator;
typedef class TXMPUtils< TXMP_STRING_TYPE> SXMPUtils;

.mm文件:

  #include< string> 
using namespace std;
#define IOS_ENV
#define TXMP_STRING_TYPE string
#importXMP.hpp

void DoStuff()
{
SXMPMeta meta;
string returnValue;
meta.SetProperty(kXMP_NS_PDF,test,{formId:{guid}});
meta.DumpObject(DumpToString,& returnValue);
}



链接错误:

 (null):TXMPMeta< std :: __ 1 :: basic_string< char,std :: __ 1 :: char_traits< char>,std :: __ 1 :: allocator< char> ;> :: DumpObject(int(*)(void *,char const *,unsigned int),void *)const,引用自:
(null):TXMPMeta< std :: _ 1 :: basic_string ; char,std :: __ 1 :: char_traits< char>,std :: __ 1 :: allocator< char>>> :: TXMPMeta(),引用自:
(null):TXMPMeta< std :: __ 1 :: basic_string< char,std :: __ 1 :: char_traits< char> ;, std :: __ 1 :: allocator< char>>> :: SetProperty(char const *,char const *,char const * unsigned int),引用自:
(null):TXMPMeta< std :: __ 1 :: basic_string< char,std :: __ 1 :: char_traits< char> ;, std :: __ 1 :: allocator< char> >> ::〜TXMPMeta(),引用自:
(null):链接器命令失败并且退出代码1(使用-v查看调用)
/ pre>

解决方案

基本上发生了什么是你只有头文件中的定义
如果我说



模板< class T> T something(T); 在某处,告诉编译器trust me bro,it exists,leave it to the linker



将符号添加到对象文件,就好像它存在一样。因为它可以看到原型它知道多少堆栈空间,它返回什么类型等等,所以它只是设置它,所以链接器可以只是来和放置的函数的地址。



但在你的情况下没有地址。你/必须/在同一个文件中有模板定义(不只是声明),所以编译器可以创建一个(使用弱链接),所以这里假设它们存在,但没有从模板实际上从哪里删除这个类,所以链接器没有找到它,因此错误。



现在会毛茸茸我的答案,希望这有帮助。



附录1:

 模板< class T& void output(T&); 

int main(int,char **){
int x = 5;
output(x);
return 0;
}

这将编译,但 p>

输出:

  if! g ++ -Isrc -Wall -Wextra -O3 -std = c ++ 11 -g -gdwarf-2 -Wno-write-strings -MM src / main.cpp> build / main.o.d;然后rm build / main.o.d;出口1; fi 
g ++ -Wall -Wextra -O3 -std = c ++ 11 -g -gdwarf-2 -Wno-write-strings -Isrc -c src / main.cpp -o build / main.o
g ++ build / main.o -o a.out
build / main.o:在main函数中:
(my home)/src/main.cpp:13:undefined引用`void输出< int>(int&)'
collect2:错误:ld返回1退出状态
make:*** [a.out]错误1
/ pre>

(我劫持了一个开放的项目,因此命名)



命令工作正常(以-o build / main.o结尾),因为我们告诉它看这个函数存在



所以在目标文件中,链接器(在某些名称管理形式保留模板)将位置放在void输出(int&);这里的位置链接器找不到它。



编译和链接

  #include< iostream& 
template< class T> void output(T&);

int main(int,char **){
int x = 5;
output(x);
return 0;
}

template< class T> void output(T& what){
std :: cout<< what<<\\\
;
std :: cout.flush();
}

注意第2行,我们告诉它有一个函数, T调用的输出,不返回任何东西,并采取一个T引用,这意味着它可以使用它在main函数(记住,当它解析主函数,它还没有看到输出的定义,它刚刚被告知存在),链接器然后修复它。 '虽然现代编译器是非常聪明(因为我们有更多的ram :))和强奸你的代码的结构,链接时间优化这更多,但这是它如何使用,以及如何可以被考虑



输出:

  make all 
if! g ++ -Isrc -Wall -Wextra -O3 -std = c ++ 11 -g -gdwarf-2 -Wno-write-strings -MM src / main.cpp> build / main.o.d;然后rm build / main.o.d;出口1; fi
g ++ -Wall -Wextra -O3 -std = c ++ 11 -g -gdwarf-2 -Wno-write-strings -Isrc -c src / main.cpp -o build / main.o
g ++ build / main.o -o a.out

正如你所看到的那样,



多个文件不包含此的证明

main.cpp

  #include< iostream> 

int TrustMeCompilerIExist();

int main(int,char **){
std :: cout<< TrustMeCompilerIExist();
std :: cout.flush();
return 0;
}

proof.cpp
$ b

  int TrustMeCompilerIExist(){
return 5;
}

编译和链接
$ b

  make all 
if! g ++ -Isrc -Wall -Wextra -O3 -std = c ++ 11 -g -gdwarf-2 -Wno-write-strings -MM src / main.cpp> build / main.o.d;然后rm build / main.o.d;出口1; fi
g ++ -Wall -Wextra -O3 -std = c ++ 11 -g -gdwarf-2 -Wno-write-strings -Isrc -c src / main.cpp -o build / main.o
if! g ++ -Isrc -Wall -Wextra -O3 -std = c ++ 11 -g -gdwarf-2 -Wno-write-strings -MM src / proof.cpp> build / proof.o.d;然后rm build / proof.o.d;出口1; fi
g ++ -Wall -Wextra -O3 -std = c ++ 11 -g -gdwarf-2 -Wno-write-strings -Isrc -c src / proof.cpp -o build / proof.o
g ++ build / main.o build / proof.o -o a.out

(输出5)



记住#include LITERALLY转储一个文件,其中显示#include(+一些其他调整行号的宏),这被称为翻译单元。而不是使用头文件包含int TrustMeCompilerIExist();它声明函数存在(但编译器再次不知道它在哪里,它内部的代码,只是它存在)我重复自己。



查看proof.o



命令

  objdump proof.o -t 

输出 b
$ b

  proof.o:文件格式elf64-x86-64 

符号表:
0000000000000000 l df * ABS * 0000000000000000 proof.cpp
LD 0000000000000000 0000000000000000的.text .text段
LD 0000000000000000 0000000000000000。数据。数据
0000000000000000 LD的.bss 0000000000000000的.bss
LD 0000000000000000 0000000000000000 .debug_info $ .debug_info b $ b LD 0000000000000000 0000000000000000 .debug_abbrev .debug_abbrev
LD 0000000000000000 0000000000000000 .debug_aranges .debug_aranges
LD 0000000000000000 0000000000000000 .debug_line .debug_line
LD 0000000000000000 0000000000000000 .debug_str .debug_str
0000000000000000 LD。 note.GNU堆栈0000000000000000 .note.GNU堆栈
LD 0000000000000000 0000000000000000 .eh_frame .eh_frame
LD 0000000000000000 0000000000000000的.comment的.comment
0000000000000000 G + F的.text 0000000000000006 _Z21TrustMeCompilerIExistv

右键底部在那里,有一个功能,偏移量6成文件,用调试信息,(对G是全球性的虽然)你可以看到它被称为_Z(这就是为什么_保留一些东西,我忘了究竟是什么...但它与此有关),Z是整数,21名长度和名称后,v是void返回类型。



开始btw处的零是节号,记住二进制可以是巨大的。



反汇编
运行:



objdump proof.o -S 给予

  proof.o:文件格式elf64-x86-64 

.text段的
拆卸:

0000000000000000< _Z21TrustMeCompilerIExistv计算值:


INT TrustMeCompilerIExist(){
返回5;
}
0:b8 05 00 00 00 mov $ 0x5,%eax
5:c3 retq

由于我有-g,你可以看到它把大会涉及的代码(它更有意义与作用更大,它可以显示什么,直到下一个代码块下面的说明实际上做)这通常不会在那里。



main.o



这是符号表,以上:

  objdump main.o -t 

main.o:文件格式elf64-x86 -64

符号表:
00000000亿升DF * ABS * 0000000000000000的main.cpp
LD 0000000000000000 0000000000000000的.text .text段
LD 0000000000000000 0000000000000000。数据。数据
0000000000000000 LD的.bss 0000000000000000的.bss
LD 0000000000000000 0000000000000000 .text.startup .text.startup
0000000000000030升˚F.text.startup 0000000000000026 _GLOBAL__sub_I_main
0000000000000000 1。·.bss中0000000000000001 _ZStL8__ioinit
LD 0000000000000000 0000000000000000 .init_array .init_array
LD 0000000000000000 0000000000000000 .debug_info .debug_info
LD 0000000000000000 0000000000000000 .debug_abbrev .debug_abbrev
LD 0000000000000000 0000000000000000 .debug_loc .debug_loc
0000000000000000 LD。 debug_aranges 0000000000000000 .debug_aranges
LD 0000000000000000 0000000000000000 .debug_ranges .debug_ranges
LD 0000000000000000 0000000000000000 .debug_line .debug_line
LD 0000000000000000 0000000000000000 .debug_str .debug_str
0000000000000000 LD .note.GNU堆栈0000000000000000 .note.GNU堆栈
LD 0000000000000000 0000000000000000 .eh_frame .eh_frame
LD 0000000000000000 0000000000000000的.comment的.comment
0000000000000000 G + F .text.startup 0000000000000026主要
0000000000000000 * UND * 0000000000000000 _Z21TrustMeCompilerIExistv
0000000000000000 * UND * 0000000000000000 _ZSt4cout
0000000000000000 * UND * 0000000000000000 _ZNSolsEi
0000000000000000 * UND * 0000000000000000 _ZNSo5flushEv
0000000000000000 * UND * 0000000000000000 _ZNSt8ios_base4InitC1Ev
0000000000000000 * UND * 0000000000000000 .hidden __dso_handle
0000000000000000 * UND * 0000000000000000 _ZNSt8ios_base4InitD1Ev
0000000000000000 * UND * 0000000000000000 __cxa_atexit

看看它是如何说不确定的,这是因为它不知道它在哪里,它只是知道它的存在。


$ b $(与标准库的东西,该连接器将发现自己一起) b

在结束
使用头警卫和使用模板把#包括file.cpp底部的收盘头后卫了。那样你可以照常包括头文件:)


I'm trying to use the adobe xmp library in an iOS application but I'm getting link errors. I have the appropriate headers and libraries in my path, but I'm getting link errors. I double-checked to make sure the headers and library are on my path. I checked the mangled names of the methods, but they aren't in the library (I checked using the nm command). What am I doing wrong?

Library Header:

#if defined ( TXMP_STRING_TYPE )

    #include "TXMPMeta.hpp"
    #include "TXMPIterator.hpp"
    #include "TXMPUtils.hpp"
    typedef class TXMPMeta <TXMP_STRING_TYPE>     SXMPMeta;       // For client convenience.
    typedef class TXMPIterator <TXMP_STRING_TYPE> SXMPIterator;
    typedef class TXMPUtils <TXMP_STRING_TYPE>    SXMPUtils;

.mm file:

#include <string>
using namespace std;
#define IOS_ENV
#define TXMP_STRING_TYPE string
#import "XMP.hpp"

void DoStuff()
{    
    SXMPMeta meta;
    string returnValue;
    meta.SetProperty ( kXMP_NS_PDF, "test", "{ formId: {guid} }" );
    meta.DumpObject(DumpToString, &returnValue);
}

Link Errors:

(null): "TXMPMeta<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >::DumpObject(int (*)(void*, char const*, unsigned int), void*) const", referenced from:
(null): "TXMPMeta<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >::TXMPMeta()", referenced from:
(null): "TXMPMeta<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >::SetProperty(char const*, char const*, char const*, unsigned int)", referenced from:
(null): "TXMPMeta<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >::~TXMPMeta()", referenced from:
(null): Linker command failed with exit code 1 (use -v to see invocation)

解决方案

Basically what's happened is that you only have the definitions in the headers if I say

template<class T> T something(T); somewhere, that tells the compiler "trust me bro, it exists, leave it to the linker"

and it adds the symbol to the object file as if it did exist. Because it can see the prototype it knows how much stack space, what type it returns and such, so it just sets it up so the linker can just come along and put the address of the function in.

BUT in your case there is no address. You /MUST/ have the template definition (not just declaration) in the same file so the compiler can create one (with weak linkage) so here it's assumed they exist, but no where does it actually stamp out this class from a template, so the linker doesn't find it, hence the error.

Will fluff out my answer now, hope this helps.

Addendum 1:

template<class T> void output(T&);

int main(int,char**) {
    int x = 5;
    output(x);
    return 0;
}

This will compile but NOT link.

Output:

if ! g++ -Isrc -Wall -Wextra -O3 -std=c++11 -g -gdwarf-2 -Wno-write-strings  -MM src/main.cpp >> build/main.o.d ; then rm build/main.o.d ; exit 1 ; fi
g++ -Wall -Wextra -O3 -std=c++11 -g -gdwarf-2 -Wno-write-strings  -Isrc -c src/main.cpp -o build/main.o
g++  build/main.o  -o a.out
build/main.o: In function `main':
(my home)/src/main.cpp:13: undefined reference to `void output<int>(int&)'
collect2: error: ld returned 1 exit status
make: *** [a.out] Error 1

(I hijacked an open projet for this hence the names)

As you can see the compile command works fine (the one that ends in -o build/main.o) because we tell it "look this function exists"

So in the object file it says to the linker (in some "name managled form" to keep the templates) "put the location in memory of void output(int&); here" the linker can't find it.

Compiles and links

#include <iostream>
template<class T> void output(T&);

int main(int,char**) {
    int x = 5;
    output(x);
    return 0;
}

template<class T> void output(T& what) {
    std::cout<<what<<"\n";
    std::cout.flush();
}

Notice line 2, we tell it "there exists a function, a template in T called output, that returns nothing and takes a T reference", that means it can use it in the main function (remember when it's parsing the main function it hasn't seen the definition of output yet, it has just been told it exists), the linker then fixes that. 'though modern compilers are much much smarter (because we have more ram :) ) and rape the structure of your code, link-time-optimisation does this even more, but this is how it used to work, and how it can be considered to work these days.

Output:

make all 
if ! g++ -Isrc -Wall -Wextra -O3 -std=c++11 -g -gdwarf-2 -Wno-write-strings  -MM src/main.cpp >> build/main.o.d ; then rm build/main.o.d ; exit 1 ; fi
g++ -Wall -Wextra -O3 -std=c++11 -g -gdwarf-2 -Wno-write-strings  -Isrc -c src/main.cpp -o build/main.o
g++  build/main.o  -o a.out

As you can see it compiled fine and linked fine.

Multiple files without include as proof of this

main.cpp

#include <iostream>

int TrustMeCompilerIExist();

int main(int,char**) {
    std::cout<<TrustMeCompilerIExist();
    std::cout.flush();
    return 0;
}

proof.cpp

int TrustMeCompilerIExist() {
    return 5;
}

Compile and link

make all 
if ! g++ -Isrc -Wall -Wextra -O3 -std=c++11 -g -gdwarf-2 -Wno-write-strings  -MM src/main.cpp >> build/main.o.d ; then rm build/main.o.d ; exit 1 ; fi
g++ -Wall -Wextra -O3 -std=c++11 -g -gdwarf-2 -Wno-write-strings  -Isrc -c src/main.cpp -o build/main.o
if ! g++ -Isrc -Wall -Wextra -O3 -std=c++11 -g -gdwarf-2 -Wno-write-strings  -MM src/proof.cpp >> build/proof.o.d ; then rm build/proof.o.d ; exit 1 ; fi
g++ -Wall -Wextra -O3 -std=c++11 -g -gdwarf-2 -Wno-write-strings  -Isrc -c src/proof.cpp -o build/proof.o
g++  build/main.o build/proof.o  -o a.out

(Outputs 5)

Remember #include LITERALLY dumps a file where it says "#include" (+ some other macros that adjust line numbers) this is called a translation unit. Rather than using a header file to contain "int TrustMeCompilerIExist();" which declares that the function exists (but the compiler again doesn't know where it is, the code inside of it, just that it exists) I repeated myself.

Lets look at proof.o

command

objdump proof.o -t

output

proof.o:     file format elf64-x86-64

SYMBOL TABLE:
0000000000000000 l    df *ABS*  0000000000000000 proof.cpp
0000000000000000 l    d  .text  0000000000000000 .text
0000000000000000 l    d  .data  0000000000000000 .data
0000000000000000 l    d  .bss   0000000000000000 .bss
0000000000000000 l    d  .debug_info    0000000000000000 .debug_info
0000000000000000 l    d  .debug_abbrev  0000000000000000 .debug_abbrev
0000000000000000 l    d  .debug_aranges 0000000000000000 .debug_aranges
0000000000000000 l    d  .debug_line    0000000000000000 .debug_line
0000000000000000 l    d  .debug_str 0000000000000000 .debug_str
0000000000000000 l    d  .note.GNU-stack    0000000000000000 .note.GNU-stack
0000000000000000 l    d  .eh_frame  0000000000000000 .eh_frame
0000000000000000 l    d  .comment   0000000000000000 .comment
0000000000000000 g     F .text  0000000000000006 _Z21TrustMeCompilerIExistv

Right at the bottom there, there's a function, at offset 6 into the file, with debugging information, (the g is global though) you can see it's called _Z (this is why _ is reserved for some things, I forget what exactly... but it's to do with this) and Z is "integer", 21 is the name length, and after the name, the v is "void" the return type.

The zeros at the start btw are the section number, remember binaries can be HUGE.

Disassembly running:

objdump proof.o -S gives

proof.o:     file format elf64-x86-64


Disassembly of section .text:

0000000000000000 <_Z21TrustMeCompilerIExistv>:


int TrustMeCompilerIExist() {
    return 5;
}
   0:   b8 05 00 00 00          mov    $0x5,%eax
   5:   c3                      retq   

Because I have -g you can see it put the code that the assembly relates to (it makes more sense with bigger functions, it shows you what the following instructions until the next code block actually do) that wouldn't normally be there.

main.o

Here's the symbol table, obtained the same way as the above:

objdump main.o -t

main.o:     file format elf64-x86-64

SYMBOL TABLE:
0000000000000000 l    df *ABS*  0000000000000000 main.cpp
0000000000000000 l    d  .text  0000000000000000 .text
0000000000000000 l    d  .data  0000000000000000 .data
0000000000000000 l    d  .bss   0000000000000000 .bss
0000000000000000 l    d  .text.startup  0000000000000000 .text.startup
0000000000000030 l     F .text.startup  0000000000000026 _GLOBAL__sub_I_main
0000000000000000 l     O .bss   0000000000000001 _ZStL8__ioinit
0000000000000000 l    d  .init_array    0000000000000000 .init_array
0000000000000000 l    d  .debug_info    0000000000000000 .debug_info
0000000000000000 l    d  .debug_abbrev  0000000000000000 .debug_abbrev
0000000000000000 l    d  .debug_loc 0000000000000000 .debug_loc
0000000000000000 l    d  .debug_aranges 0000000000000000 .debug_aranges
0000000000000000 l    d  .debug_ranges  0000000000000000 .debug_ranges
0000000000000000 l    d  .debug_line    0000000000000000 .debug_line
0000000000000000 l    d  .debug_str 0000000000000000 .debug_str
0000000000000000 l    d  .note.GNU-stack    0000000000000000 .note.GNU-stack
0000000000000000 l    d  .eh_frame  0000000000000000 .eh_frame
0000000000000000 l    d  .comment   0000000000000000 .comment
0000000000000000 g     F .text.startup  0000000000000026 main
0000000000000000         *UND*  0000000000000000 _Z21TrustMeCompilerIExistv
0000000000000000         *UND*  0000000000000000 _ZSt4cout
0000000000000000         *UND*  0000000000000000 _ZNSolsEi
0000000000000000         *UND*  0000000000000000 _ZNSo5flushEv
0000000000000000         *UND*  0000000000000000 _ZNSt8ios_base4InitC1Ev
0000000000000000         *UND*  0000000000000000 .hidden __dso_handle
0000000000000000         *UND*  0000000000000000 _ZNSt8ios_base4InitD1Ev
0000000000000000         *UND*  0000000000000000 __cxa_atexit

See how it says undefined, that's because it doesn't know where it is, it just knows it exists (along with the standard lib stuff, which the linker will find itself)

In closing USE HEADER GUARDS and with templates put #include file.cpp at the bottom BEFORE the closing header guard. that way you can include header files as usual :)

这篇关于使用库中定义的模板时未找到符号的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆