본문 바로가기

c++

[c++] visibility

공부를 하는 입장이기 때문에, 내용에 오류가 있을 수 있습니다. 오류가 있다면 적극적으로 알려주시면 감사합니다!

 

Visibility

 visibility란 공개 api 다룰 때 사용하는 기능이다. 어떤 클래스와 함수등 특정 심볼을 보여줄지 아닐지를 정해 다른 api와 충돌을 최소화하고 공개 라이브러리의 성능을 향상할 수 있다. gcc wiki에 이와 관련된 글이 있다.

 

Visibility - GCC Wiki

Note: the text on this page was almost entirely written by Niall Douglas, the original author of the patch, and placed on nedprod.com. This copy in the GCC wiki has some corrections that may not be present on Niall's site. Why is the new C++ visibility sup

gcc.gnu.org

 

 오늘 내용은 예시 코드와 같이 open source에서 어떻게 사용하는지만 확인하고 넘어가고자 한다. 이건 ros2에서 사용하는 rclcpp라이브러리 속 rclcpp/include/rclcpp/visibility_control.hpp에 위치한 파일을 내가 이해한 내용의 주석을 추가한 코드이다.

 

 

rclcpp/rclcpp/include/rclcpp/visibility_control.hpp at rolling · ros2/rclcpp

rclcpp (ROS Client Library for C++). Contribute to ros2/rclcpp development by creating an account on GitHub.

github.com

#ifndef RCLCPP__VISIBILITY_CONTROL_HPP_
#define RCLCPP__VISIBILITY_CONTROL_HPP_

// This logic was borrowed (then namespaced) from the examples on the gcc wiki:
//     https://gcc.gnu.org/wiki/Visibility

#if defined _WIN32 || defined __CYGWIN__ //window나 cygwin(window에서 linux인걸로 기억)
  #ifdef __GNUC__          // GNU compiler and you can use GNU extensions. => CYGWIN
    #define RCLCPP_EXPORT __attribute__ ((dllexport))    //라이브러리 빌드
    #define RCLCPP_IMPORT __attribute__ ((dllimport))    //라이브러리 사용 
  #else
    #define RCLCPP_EXPORT __declspec(dllexport)          //라이브러리 빌드
    #define RCLCPP_IMPORT __declspec(dllimport)          //라이브러리 사용 
  #endif
  #ifdef RCLCPP_BUILDING_LIBRARY                         //라이브러리 빌드 
    #define RCLCPP_PUBLIC RCLCPP_EXPORT
  #else
    #define RCLCPP_PUBLIC RCLCPP_IMPORT
  #endif
  #define RCLCPP_PUBLIC_TYPE RCLCPP_PUBLIC
  #define RCLCPP_LOCAL
#else                                                  //gnu(linux도 포함됨)으로 하는거
  #define RCLCPP_EXPORT __attribute__ ((visibility("default"))) 
  #define RCLCPP_IMPORT
  #if __GNUC__ >= 4                                    //gcc 버전이 4 이상
    #define RCLCPP_PUBLIC __attribute__ ((visibility("default")))
    #define RCLCPP_LOCAL  __attribute__ ((visibility("hidden")))
  #else
    #define RCLCPP_PUBLIC
    #define RCLCPP_LOCAL
  #endif
  #define RCLCPP_PUBLIC_TYPE
#endif

#endif  // RCLCPP__VISIBILITY_CONTROL_HPP_

 

Gnu

 GNU는 운영 체제의 하나이자 컴퓨터 소프트웨어의 모음집이다. 나는 간단하게 자유롭게 사용할 수 있도록 만든 os개념이고, 더 간단하게 linux와 같은 os라고 이해했다.(맞는 개념인지는 모르겠지만, 오늘 이해하는 데는 상관없는 것 같다. linux는 사실 gnu/linux라고 한다. gnu의 개념을 바탕으로 만들어진 것 같다.)

 

dllexport, dllimport

 dllexport, dllimport이 두 지시어는 __declspec() 함수와 __attribute__() 함수 인자로 사용된다. 함수에 dllexport로 선언하게 된다면 컴파일러에게 "이 함수는 외부에 공개되어야 한다! "라고 알려주는 것이다. 즉 외부 dll에서 사용할 수 있도록 한다. 반대로  dllimport의 경우는 컴파일러에게 "이 함수(심볼)은 외부 dll에 존재해서, 그걸 참조해라!"라는 걸 알려주는 것이다. 조금 더 간단하게 사용 상황과 같이 말하자면

 

dllexport : dll을 만들 때 사용해, 심볼들을 외부에서 사용할 수 있도록 만들음. >> 심볼을 외부에 공개 (라이브러리를 만들때)

 

dllimport : 외부에서 dll을 사용할 때, 그 심볼의 링크를 연결해 준다. >> 외부 DLL 심볼을 가져다 씀 (나의 프로젝트에서 라이브러리를 사용할 때)

 

로 정리할 수 있을 것 같다.

 

 추가로 __declspec()는 윈도우 환경에서 사용할 때, 사용되는 함수이고 __attribute__()는 gnu 컴파일러(gcc)에서 사용된다.

 

visibility 

  visibility("default")는 gcc컴파일러에서 확장되는 기능으로 즉, __attribute__ ()와 사용할 수 있다. gnu에서는 기본 값으로 모든 심볼이 외부에 노출되어 있도록 되어있다. 여기서 __attribute__ ((visibility("hidden")))함수를 사용하여 특정 심볼을 라이브러리 안에서만 사용하고 노출을 막아 공유라이브러리의 성능을 향상한다.

 

예제 해석

 첫 줄인 #if defined _WIN32 || defined __CYGWIN__ ... #else 부분은 윈도우와 gnu를 나눠준 부분이다. __CYGWIN__는 윈도우에서 특정 프로그램으로 리눅스를 실행시키는 상황이라고 생각하면 편할 것 같다.

 

#ifdef __GNUC__는 gcc컴파일러를 사용하는가?라는 구문이고 앞에서 __CYGWIN__상황이라고 유추할 수 있다.

#define RCLCPP_EXPORT __attribute__ ((dllexport)), #define RCLCPP_IMPORT __attribute__ ((dllimport)) 부분은 라이브러리를 만들 때와 실제로 프로젝트로 사용할 때를 나눈 것으로 생각할 수 있다.

 

 #ifdef RCLCPP_BUILDING_LIBRARY는 위에서 나눈 상황을 판단하는 구문으로 라이브러리를 빌드할 때(라이브러리를 만들 때)인가?라는 구문이다. RCLCPP_BUILDING_LIBRARY는 cmake file에서 정의를 확인할 수 있다.

# Causes the visibility macros to use dllexport rather than dllimport,
# which is appropriate when building the dll but not consuming it.
target_compile_definitions(${PROJECT_NAME}
  PRIVATE "RCLCPP_BUILDING_LIBRARY")

 

 

그래서 만약 RCLCPP_BUILDING_LIBRARY가 true이면 라이브러리를 빌드 중이므로 export구문이 RCLCPP_PUBLIC 이 되는 것이고, 없으면 다른 파일에서 사용 중인 곳 이므로 RCLCPP_PUBLIC가 import가 된다.

 

#define RCLCPP_EXPORT __attribute__ ((visibility("default")))
#define RCLCPP_IMPORT

이 두 부분은 사실 gcc 즉 gnu(리눅스)는 기본적으로 외부에 노출이 기본값이기 때문에 그냥 명시해 준 것이다. 그리고  #if __GNUC__ >= 4 이 부분은 gcc의 버전이 4 이상인가? 를 묻는 부분이다.

 

사용 방법

 사용 방법은 간단하니깐 예제 하나만 보면, 

__declspec(dllimport) void 외부함수();

 

이런 식으로 앞에다 붙여주기만 하면 끝이다. 그리고 이를 매크로로 지정했기 때문에 위에 예시를 사용하면

......

  RCLCPP_PUBLIC
  explicit Node(
    const std::string & node_name,
    const std::string & namespace_,
    const NodeOptions & options = NodeOptions());

  RCLCPP_PUBLIC
  virtual ~Node();

  /// Get the name of the node.
  /** \return The name of the node. */
  RCLCPP_PUBLIC
  const char *
  get_name() const;
  
......

 

이런 식으로 심볼 앞에다 붙여주면 된다.

'c++' 카테고리의 다른 글

[c++] __VA_ARGS__, parameter pack  (0) 2024.04.30
[c++] 복사 생성자와 복사 대입 연산자  (1) 2024.04.29
[c++] Range-based for loop (범위 기반 for 문)  (1) 2024.04.25
04. C++만의 특징 2  (0) 2024.01.14
03. C언어 복습  (0) 2024.01.04