简体   繁体   中英

c++ undefined reference to a class member function

I encountered the following linking issues when trying to use librbd.

The following is my code snippet.

  • main.cc
#include <iostream>
#include <rados/librados.hpp>
#include <rbd/librbd.hpp>

int main(){
    // Initialize and open an rbd image
    std::string pool = "xxx";
    std::string image_name = "xxxx";
    int r;
    librados::Rados cluster;
    librados::IoCtx io_ctx;
    librbd::Image image;
    librbd::RBD rbd;
    r = cluster.init("cinder-ctest");
    r = cluster.connect();
    r = cluster.ioctx_create(pool.c_str(), io_ctx);
    r = rbd.open_read_only(io_ctx, image, image_name.c_str(), NULL);

    std::string id;
    image.get_id(&id);   // <- Where the problem occurs
    std::cerr << id << std::endl;
    return 0;
}

An error occurred when I compiled using the following command

$ g++ main.cc -o info -lrbd -lrados 
/tmp/ccOpSFrv.o: In function `main':
main.cc:(.text+0x12b): undefined reference to `librbd::Image::get_id(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*)'
collect2: error: ld returned 1 exit status

but I use nm to see that get_id exists:

$ nm -D /usr/lib64/librbd.so | grep get_id
0000000000083d00 T rbd_get_id
000000000008de10 T _ZN6librbd5Image6get_idEPSs
                 U _ZN8librados7v14_2_05IoCtx6get_idEv

and it is globally visible:

$ readelf -s /usr/lib64/librbd.so | grep get_id
   498: 0000000000083d00    70 FUNC    GLOBAL DEFAULT   11 rbd_get_id
   559: 000000000008de10    54 FUNC    GLOBAL DEFAULT   11 _ZN6librbd5Image6get_idEP

why do I get an error when compiling: undefined reference to librbd::Image::get_id . It clearly exists, which makes me wonder.

Some background: C++11 changed the std::string interface slightly by adding noexcept specifiers to a few member functions, but it turns out that slight change meant that libstdc++ had to re-write their std::string implementation in a non-ABI-compatible way. For backwards compatibility, they retained the old version and put the new one in an inline namespace so it's named std::__cxx11::basic_string as far as the linker is concerned. See this page for more info.

That's where you're running into trouble. _ZN6librbd5Image6get_idEPSs demangles to

librbd::Image::get_id(
    std::basic_string<
        char,
        std::char_traits<char>,
        std::allocator<char>
    >*
)

That function accepts the old version of std::string , but you're passing it a pointer to the new version of std::string . Presumably the version of librbd you have was either built with an old version of GCC, or was purposely built against the old ABI.

You have a few options to work around this:

  1. Find a version of librbd that was built for libstdc++'s new ABI.
    • If the version you're using is from your distro's package manager, you may need to look elsewhere (like Conan or vcpkg or something).
  2. Build librbd yourself against the new ABI.
    • I'm not familiar with that library, so I don't know how hard this would be. It seems that on some distros their toolchain prevents it .
  3. Build your application against the old ABI.
    • As the page I linked above says, you can define the _GLIBCXX_USE_CXX11_ABI preprocessor macro to 0 to tell libstdc++ to use the old implementation of std::string . It doesn't technically fully comply with C++11 and later standard revisions, but it's mostly the same.

Seems like you use a C++11-compatible compiler, that generates _ZN6librbd5Image6get_idEPNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE names, but your library was compiled using C++03, C++98 or something like that, before C++11.

If you have access to the library sources, recompile it with C++11, if you don't, compile your program in the C++03 compatibility mode.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM