Search code examples
c++11inlinemultiple-definition-errorheader-only

Multiple definition error (collect2: error: ld returned 1 exit status)


I was using https://github.com/jdduke/three_cpp as a header-only mode but faced some issues while compiling the same with my project.

The issue happens when I include the following matrix4.hpp header (only necessary part is included) in more than one C++ files

three/core/matrix4.hpp (removed unwanted parts)

#ifndef THREE_MATRIX4_HPP
#define THREE_MATRIX4_HPP

#include <three/common.hpp>

#include <three/core/math.hpp>
#include <three/core/quaternion.hpp>
#include <three/core/vector3.hpp>
#include <three/core/vector4.hpp>

namespace three {

class Matrix4 {
public:
  // Function without any erros
  THREE_DECL Matrix4();
  THREE_DECL Vector3    getScale() const;

  // Error causing functions
  Vector3 getColumnX() const;
  Vector3 getColumnY() const;
  Vector3 getColumnZ() const;
  Matrix4& setPosition( const Vector3& v );
};

} // namespace three

#if defined(THREE_HEADER_ONLY)
# include <three/core/impl/matrix4.ipp> // This is where all this functions is defined.
#endif // defined(THREE_HEADER_ONLY)

#endif // THREE_MATRIX4_HPP

three/core/impl/matrix4.ipp (removed unwanted parts)

#ifndef THREE_MATRIX4_IPP
#define THREE_MATRIX4_IPP

#include <three/core/matrix4.hpp>

namespace three {

Matrix4::Matrix4() {
  identity();
}

Matrix4::Matrix4( const Matrix4& other ) {
  copy( other );
}

Vector3 Matrix4::getScale() const {
  auto sx = Vector3( te[0], te[1], te[2] ).length();
  auto sy = Vector3( te[4], te[5], te[6] ).length();
  auto sz = Vector3( te[8], te[9], te[10] ).length();
  return Vector3( sx, sy, sz );
}

Vector3 Matrix4::getColumnX() const {
  return Vector3( te[0], te[1], te[2] );
}

Vector3 Matrix4::getColumnY() const {
  return Vector3( te[4], te[5], te[6] );
}

Vector3 Matrix4::getColumnZ() const {
  return Vector3( te[8], te[9], te[10] );
}

Vector3 Matrix4::getPosition() const {
  return Vector3( te[12], te[13], te[14] );
}

} // namespace three

#endif // THREE_MATRIX4_IPP

When three/core/matrix4.hpp is included from multiple C++ files following multiple definition error is generated

/usr/bin/ld: /tmp/ccmYWwMM.o: in function `std::_Rb_tree_const_iterator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, bool> >::_Rb_tree_const_iterator(std::_Rb_tree_iterator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, bool> > const&)':
/home/three/core/impl/matrix4.ipp:617: multiple definition of `three::Matrix4::setPosition(three::Vector3 const&)'; /tmp/ccSaHLVI.o:/home/three/three/core/impl/matrix4.ipp:617: first defined here
/usr/bin/ld: /tmp/ccmYWwMM.o: in function `three::Vector3::subSelf(three::Vector3 const&)':
/home/three/three/core/impl/matrix4.ipp:627: multiple definition of `three::Matrix4::getColumnX() const'; /tmp/ccSaHLVI.o:/home/three/three/core/impl/matrix4.ipp:627: first defined here
/usr/bin/ld: /tmp/ccmYWwMM.o: in function `three::Matrix4::getColumnY() const':
/home/three/three/core/impl/matrix4.ipp:631: multiple definition of `three::Matrix4::getColumnY() const'; /tmp/ccSaHLVI.o:/home/three/three/core/impl/matrix4.ipp:631: first defined here
/usr/bin/ld: /tmp/ccmYWwMM.o: in function `three::Matrix4::getColumnZ() const':
/home/three/three/core/impl/matrix4.ipp:635: multiple definition of `three::Matrix4::getColumnZ() const'; /tmp/ccSaHLVI.o:/home/three/three/core/impl/matrix4.ipp:635: first defined here
collect2: error: ld returned 1 exit status

So based on our tryouts we noted that THREE_DECL is not mentioned for the following functions in matrix4.hpp

  • Vector3 getColumnX() const;
  • Vector3 getColumnY() const;
  • Vector3 getColumnZ() const;
  • Matrix4& setPosition( const Vector3& v );

So after modifying these functions like follows errors are fixed

  • THREE_DECL Vector3 getColumnX() const;
  • THREE_DECL Vector3 getColumnY() const;
  • THREE_DECL Vector3 getColumnZ() const;
  • THREE_DECL Matrix4& setPosition( const Vector3& v );

Based on three/config.hpp in case of using a header-only version THREE_DECL is defined as inline.

What I didn't understand is

  1. Why multiple definition error has occurred even if the same header is included in different compilation units?
  2. What will be the significance of adding THREE_DECL for resolving these issues?

Any help will be appreciated.


Solution

  • This is due to the restriction based on ODR.

    Why multiple definition error has occurred even if the same header is included in different compilation units?

    As mentioned in the comments this is due to restrictions in the One Definition Rule(ODR).

    The One Definition Rule (ODR) is an important rule of the C++ programming language that prescribes that objects and non-inline functions cannot have more than one definition in the entire program and template and types cannot have more than one definition by translation unit. It is defined in the ISO C++ Standard (ISO/IEC 14882) 2003, at section 3.2.

    So it also answers your second query.

    What will be the significance of adding THREE_DECL for resolving these issues?

    If you check pros of using inline functions it gives you significance for the usage of inline keyword.

    By marking it as inline, you can put a function definition in a header file (i.e. it can be included in multiple compilation unit, without the linker complaining).

    So if you change your functions to inline, multiple definitions will not occur.