Search code examples
c++serializationcmakecereal

I'm getting a strange error when I try to do serialization with cereal in C++


I keep getting this strange error

/usr/bin/ld: CMakeFiles/libdungeon.dir/src/main.cpp.o: in function `decltype (({parm#2}.serialize)({parm#1})) cereal::access::member_serialize<cereal::BinaryOutputArchive, Character>(cereal
::BinaryOutputArchive&, Character&)':
main.cpp:(.text._ZN6cereal6access16member_serializeINS_19BinaryOutputArchiveE9CharacterEEDTcldtfp0_9serializefp_EERT_RT0_[_ZN6cereal6access16member_serializeINS_19BinaryOutputArchiveE9Chara
cterEEDTcldtfp0_9serializefp_EERT_RT0_]+0x1f): undefined reference to `void Character::serialize<cereal::BinaryOutputArchive>(cereal::BinaryOutputArchive&)'
collect2: error: ld returned 1 exit status
make[2]: *** [CMakeFiles/libdungeon.dir/build.make:119: bin/libdungeon] Error 1
make[1]: *** [CMakeFiles/Makefile2:96: CMakeFiles/libdungeon.dir/all] Error 2
make: *** [Makefile:104: all] Error 2

I made my classes super bare-bones but I'm still getting the error.

character.hpp:

#pragma once

#include <string>

#include <cereal/archives/binary.hpp>

  class Character {

    public:

      Character(int t_level);

    private:

      friend class cereal::access;

      template <class Archive>
      void serialize(Archive & ar);

      int m_level = 0;

  };

character.cpp:

#include "character.hpp"

using namespace std;

  //Generic constructor
  Character::Character(int t_level){

    m_level = t_level;

  }


  template<class Archive>
  void Character::serialize(Archive & ar) {
    ar (m_level);
    }

main.cpp:


#include "character.hpp"
#include <fstream>
#include <cereal/archives/binary.hpp>

using namespace std;
int main() {
  Character test = Character(5);

  std::ofstream ofs("archive", std::ios::binary);
  cereal::BinaryOutputArchive archive(ofs);

  archive(test);

  return 0;

}

CMakeLists.txt:

# Basic setup
cmake_minimum_required(VERSION 3.5)
project (libdungeon LANGUAGES CXX VERSION 0.0.1)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)

# Directories with header files
include_directories ("include")

# Setting compile flags
SET(GCC_COVERAGE_COMPILE_FLAGS "-Wall -Wextra -Wshadow -Wnon-virtual-dtor -Wpedantic")
add_definitions(${GCC_COVERAGE_COMPILE_FLAGS})
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

#conan
include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
conan_basic_setup()

# Libraries
add_executable(libdungeon src/character.cpp src/main.cpp)

target_link_libraries(libdungeon ${CONAN_LIBS})

Any ideas? When I use a example file like the one found here https://uscilab.github.io/cereal/stl_support.html, with a similar cmake file everything works fine.


Solution

  • You can't separate the declaration and definition for function templates to a header (.h) and implementation (.cpp) file in C++. To fix, move the function template definition for Character::serialize to the header file:

    //in character.hpp
    
    ...
    
    template <class Archive>
    void serialize(Archive & ar){ //function definition moved to header file
        ar (m_level);
    }
    
    ...
    
    

    This is a substantial limitation of templates in C++. See this answer for a more thorough explanation of why function template definitions have to be in header files: https://stackoverflow.com/a/495056/13375552