[ROS] multiple definition of ... 오류

Gyuwon Choi
10 min readNov 11, 2020

--

=======================================

‘22년 08월 16일 업데이트

내용이 두서 없어서 하기의 링크로 다시 정리했습니다.

좀 더 읽기 편하니 참고 부탁 드립니다.

[ROS] multiple definition of … 오류 (tistory.com)

=======================================

문제상황

  • 변수나 함수, 쓰레드 등을 헤더에 선언하였을 때, 다음과 같은 오류 메세지를 출력함.
error msg :multiple definition of …
  • class 나 struct 내에 변수와 함수를 선언하면 오류가 생기지 않음.

원인

  • 특정 헤더파일(.hpp 또는 .h) 가 서로 다른 소스 (.cpp 또는 .c)에서 여러번 include 되었을 때 나타나는 에러이다.
  • 여러 번 include 되면서 multiple definition을 정의하게 된다.
  • 함수나 변수를 헤더파일에 선언하는 것은 "header가 include 되는 각각의 translation(by compiler) 마다 각각의 definition이 정의되는 것"과 같다.
  • 하나의 ROS 패키지에서는 한번의 definition만이 허용된다 : 정의는 반드시 한번만 이뤄져야 한다. (One Definition Rule)

해결방법

  • 크게 세 가지의 해결법이 있다.

(1) CMakeLists.txt 수정

(2) 선언된 변수 및 함수의 선언 방식 수정 (묵시적 inline 화)

(3) inline 함수로 선언하여 해결한다.

(1) CMakeLists.txt 수정

  • 여러번 include 되는 헤더를 하나의 library로 CMakeLists.txt에 명시하여 multi-define 되지 않도록한다.
  • 단, 상호 간 dependency 를 주의해야한다. circle이 되지 않도록 항상 상위관계를 명확하게 선언해야한다.

Leishen 사의 lslidar c16 코드를 예시로 들어보자.

해당 소스의 주요 패키지는 크게 decoder, driver로 나누어 생각해볼 수 있다.

이중 decoder 패키지의 소스는

  • lslidar_c16_decoder.cpp
  • lslidar_c16_decoder_node.cpp
  • lslidar_c16_decoder_nodelet.cpp

으로 나눌 수 있다.

해당 패키지의 헤더 중 `lslidar_c16_decoder.h`는 src/lslidar_c16_decoder_node.cpp와 src/lslidar_c16_decoder.cpp 모두의 선언부에

#include <lslidar_c16_decoder/lslidar_c16_decoder.h>

로 include 되는 것을 확인할 수 있다.

그러나, `lslidar_c16_decoder.h`내의 namespace lslidar_c16_decoder에서 class 또는 struct 밖에서 변수와 함수를 선언해도 문제가 되지 않는다.

해당 이유는 lslidar_c16_decoder 패키지의 CMakeLists.txt에서 확인할 수 있다.

...중략...
# Lslidar C16 Decoderadd_library(lslidar_c16_decodersrc/lslidar_c16_decoder.cpp)target_link_libraries(lslidar_c16_decoder${catkin_LIBRARIES})add_dependencies(lslidar_c16_decoder${${PROJECT_NAME}_EXPORTED_TARGETS}${catkin_EXPORTED_TARGETS})# Lslidar C16 Decoder nodeadd_executable(lslidar_c16_decoder_nodesrc/lslidar_c16_decoder_node.cpp)target_link_libraries(lslidar_c16_decoder_nodelslidar_c16_decoder ${catkin_LIBRARIES})add_dependencies(lslidar_c16_decoder_node${${PROJECT_NAME}_EXPORTED_TARGETS}${catkin_EXPORTED_TARGETS})# Lslidar C16 Decoder nodeletadd_library(lslidar_c16_decoder_nodeletsrc/lslidar_c16_decoder_nodelet.cpp)target_link_libraries(lslidar_c16_decoder_nodeletlslidar_c16_decoder${catkin_LIBRARIES})add_dependencies(lslidar_c16_decoder_nodelet${${PROJECT_NAME}_EXPORTED_TARGETS}${catkin_EXPORTED_TARGETS})... 중략 ...

위의 ./lslidar_c16_decoder/CMakeLits.txt 는 각각의 cpp 파일에 대한 라이브러리와 dependency를 각각 명시하고 있다.

add_library는

add_library (<사용자가_지정한_library_이름>
소스1_경로/소스1.cpp
소스2_경로/소스2.cpp
..중략..
)

로 사용할 수 있기 때문에, 위의

# Lslidar C16 Decoderadd_library(lslidar_c16_decodersrc/lslidar_c16_decoder.cpp)

는 'lslidar_c16_decoder' 라는 라이브러리명을 지정하고 해당 라이브러리는src/lslidar_c16_decoder.cpp 내에 선언된 정의를 지칭하는 것이다.

따라서,

# Lslidar C16 Decoder nodeadd_executable(lslidar_c16_decoder_nodesrc/lslidar_c16_decoder_node.cpp)target_link_libraries(lslidar_c16_decoder_nodelslidar_c16_decoder${catkin_LIBRARIES})add_dependencies(lslidar_c16_decoder_node${${PROJECT_NAME}_EXPORTED_TARGETS}${catkin_EXPORTED_TARGETS})# Lslidar C16 Decoder nodeletadd_library(lslidar_c16_decoder_nodeletsrc/lslidar_c16_decoder_nodelet.cpp)target_link_libraries(lslidar_c16_decoder_nodeletlslidar_c16_decoder${catkin_LIBRARIES})

해당 부분은 decoder_node와 decoder_nodelet이 모두 src/ lslidar_ c16_ decoder.cpp 를 `target_link_libraries`를 통해 내부 라이브러리를 동일하게 링크하는 것으로 명시하여 각각의 .cpp 파일에서

#include <lslidar_c16_decoder/lslidar_c16_decoder.h>

를 중복으로 include하여도 multiple definition 오류가 나지 않는 것이다.

(2) 오류가 발생하는 변수나 함수를 struct 또는 class 내에 선언한다.

  • 오류가 발생한 변수 또는 함수의 선언을 struct와 class 내부로 옮긴다.
  • struct, class 내에 선언되는 함수는 자동적으로 inline화 된다.
  • inline화 되면 그 자체가 내용이 되므로, 호출을 통해 생기는 multiple definition 에러를 잡아줄 수 있다.
  • Declaring the variables and functions in the ‘struct’ or ‘class’ makes them appear in only one translation unit.

<오류 메세지>

CMakeFiles/lidar_vision_node.dir/src/lidar_vision.cpp.o:(.bss+0x0): multiple definition of `lidar_vision::topic_list'CMakeFiles/lidar_vision_node.dir/nodes/lidar_vision_node.cpp.o:(.bss+0x0): first defined herecollect2: error: ld returned 1 exit status
  • main 함수가 있는 ./lidar_vision_node.cpp 가 다음과 같을 때,
#include "lidar_vision_manager.h"
int main (int argc, char** argv){
ros::init(argc, argv, "lidar_vision_pkg"); lidar_vision::Lidar_Vision_Node lidarVisionNode; lidarVisionNode.RunVision(); return EXIT_SUCCESS;}

./include/lidar_vision_manager.h 는 다음과 같이 작성되는 것이 바람직함.

#include "ros/ros.h"#include "topic_list.h"namespace lidar_vision{   class Lidar_Vision_Node {      private:      //Node handle for ROS      ros::NodeHandle nh_;
//create ROS I/O bool createRosIO();
TOPIC_LIST topic_list;
ros::Publisher lidar_vision_pub; ros::Subscriber point_cloud_sub;
public:
Lidar_Vision_Node(); ~Lidar_Vision_Node(); void RunVision(void); };
} //end namespace lidar_vision

즉, 모든 함수와 변수를 class 내에 선언한다.

(3) inline 함수로 선언하여 해결한다.

  • ‘multiple definition of…’ 오류가 나는 함수나 변수를 직접 inline 선언한다.
  • 하지만 (2)의 방법이 좀 더 일반적이므로 (2)를 최종적으로 추천.

참조 :
https://stackoverflow.com/questions/9959563/why-do-i-get-a-multiple-definition-error-while-linking

https://github.com/tongsky723/lslidar_C16

--

--