2

I can't seem to get the c++ bindings for the HDF5 library to compile when using gcc.

The test program is the example found at: https://support.hdfgroup.org/ftp/HDF5/current/src/unpacked/c++/examples/h5tutr_crtdat.cpp

My cmake file reads as follows:

cmake_minimum_required(VERSION 3.1.0)

project(readhdf5 C CXX)

find_package(HDF5 COMPONENTS C CXX HL REQUIRED)

link_directories(${HDF5_INCLUDE_DIRS})
include_directories(${HDF5_INCLUDE_DIRS})

add_executable(readdata ../src/main.cpp)

target_link_libraries(readdata ${HDF5_LIBRARIES})
target_link_libraries(readdata ${HDF5_CXX_LIBRARIES})

Which I am using with the command:

cmake -DCMAKE_C_COMPILER=gcc-8 -DCMAKE_CXX_COMPILER=g++-8 ..

The error I recieve is:

me@comp:~/Downloads/hdf5-cmake-example-master/build$ make
[ 50%] Linking CXX executable readdata
Undefined symbols for architecture x86_64:
  "H5::H5File::H5File(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, unsigned int, H5::FileCreatPropList const&, H5::FileAccPropList const&)", referenced from:
      _main in main.cpp.o
  "H5::H5Location::createDataSet(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, H5::DataType const&, H5::DataSpace const&, H5::DSetCreatPropList const&) const", referenced from:
      _main in main.cpp.o
ld: symbol(s) not found for architecture x86_64
collect2: error: ld returned 1 exit status
make[2]: *** [readdata] Error 1
make[1]: *** [CMakeFiles/readdata.dir/all] Error 2
make: *** [all] Error 2

I'm having difficulty working out the cause of the error. If I do not specify the compiler it defaults to clang/clang++ which compiles fine. If I use only the C bindings then it compiles fine. If I specify the compilers as h5cc and h5c++ wrappers, then it compiles fine.

I'm not sure why the gcc/g++ compiler can't find the source code where clang/clang++ can.

I'm using macOS High Sierra. HDF5 was installed using Homebrew.

Thanks very much for your suggestions.

I understand what an undefined symbol is and how they're usually fixed. However the HDF5 library doesn't seem to follow the typical pattern of how an external library is linked. The question is not about how to fix undefined symbols in general but why they are not found only when using gcc in this case.

I suspect the answer requires specific knowledge of this library rather than general c++.

user7119460
  • 1,451
  • 10
  • 20
  • 1
    For those thinking this is a trivial dupe of unresolved symbol: the problems is not *what is an unresolved symbol and how do I fix it?*, but *what is wrong with the C++ hdf5 bindings that cause this unresolved symbol?*. – Walter Jul 19 '18 at 20:53

1 Answers1

4

I encountered very similar problems with HDF5 and C++, which ultimately where related to this ABI issue, which also affects clang (when it cannot be fixed via resorting to an older ABI). If your problem is caused by the same issue, read on.

Ultimately, I solved this by avoiding std::string in the interface to the HDF5 library. This implies

  • not using any HDF5 functions taking/returning std::string
  • not using any HDF5 structures using std::string

For most cases you can simply use the version of the HDF5 routines taking const char* and pass string::c_str(). However, there are two more difficult cases.

  1. HDF5 exceptions are based on std::string, so you cannot use them, i.e. catch other than via catch(...)

  2. Reading a std::string via HDF5. To this end I actually re-implemented the corresponding routine (stolen from the HDF5 source code) to compile it with the correct ABI for std::string. Here it is:

    //
    // reading a std::string, see H5DataSet.cpp
    //
    std::string read_string(H5::DataSet const&data_set,
                            H5::DataType const&mem_type,
                            H5::DataSpace const&mem_space,
                            H5::DataSpace const&file_space,
                            H5::DSetMemXferPropList const&xfer_plist
                          = H5::DSetMemXferPropList::DEFAULT)
    {
        const auto is_variable_len = H5Tis_variable_str(mem_type.getId());
        if(is_variable_len<0)
            throw std::runtime_error("read_string: H5Tis_variable_str failed");
        std::string strg;
        if(!is_variable_len) {
            const auto data_size = data_set.getInMemDataSize();
            if(data_size>0) {
                std::unique_ptr<char[]> strg_C{new char[data_size+1]};
                std::memset(strg_C.get(), 0, data_size+1);
                if(0>H5Dread(data_set.getId(), mem_type.getId(), mem_space.getId(),
                             file_space.getId(), xfer_plist.getId(), strg_C.get()))
                    throw std::runtime_error("read_string: H5Dread failed for fixed length string");
                strg = strg_C.get();
            }
        } else {
            char*strg_C;
            if(0>H5Dread(data_set.getId(), mem_type.getId(), mem_space.getId(),
                         file_space.getId(), xfer_plist.getId(), &strg_C))
                throw std::runtime_error("read_string: H5Dread failed for variable length string");
            strg = strg_C;
            std::free(strg_C);
        }
        return strg;
    }
    
Walter
  • 44,150
  • 20
  • 113
  • 196
  • Thank you so much for taking the time to explain this. I'd never considered that it could be something like this but your explanation makes a lot of sense. Removing usages of std::string and try/catch fixes it all. Thanks again! – user7119460 Jul 19 '18 at 21:35
  • 1
    You're most welcome. I remember well all the hassle and effort it took me to understand this problem in the first place and to fix it. I'm only too happy to help others avoiding this. – Walter Jul 19 '18 at 21:56