728x90
반응형
728x90
반응형
728x90
반응형

C++ 문자열(string, array)과 실수(float, double) 간 변환 방법들

  • 실전 C++ 프로그래밍에서는 문자열과 실수(float, double) 간의 변환이 자주 필요합니다.
  • 본 글에서는 C++ 표준 라이브러리에서 제공하는 방법들 과 함께, WindowsLinux에서 지원하는 비표준적 방식들까지 모두 종합 하여 설명합니다.
  • 각 방식의 장단점과 사용 환경도 함께 정리하였습니다.


1. 문자열 → 실수(float, double) 변환

1.1 C++ 표준 방식

1.1.1 std::stof, std::stod (C++11)

  • cpp

      std::string str = "3.14";
      float f = std::stof(str);
      double d = std::stod(str);
    
  • 예외 처리 가능 (std::invalid_argument, std::out_of_range)
  • 간결하지만 성능은 중간 수준

1.1.2 std::stringstream

  • cpp

      std::stringstream ss("2.718");
      double d;
      ss >> d;
    
  • 입력 실패 시 검사 필요 (ss.fail())
  • 느리지만 직관적이며 다양한 타입 지원

1.1.3 std::strtof, std::strtod (C 스타일)

  • c

      char* end;
      float f = std::strtof("1.23", &end);
      double d = std::strtod("1.23", &end);
    
  • 문자열 파싱 위치 추적 가능
  • 로케일에 영향을 받음

1.1.4 std::from_chars (C++17)

  • cpp

      std::string str = "123.456";
      float f;
      auto result = std::from_chars(str.data(), str.data() + str.size(), f);
    
  • 빠르고 예외를 발생시키지 않음
  • 실패 여부는 result.ec 검사


1.2 비표준 방식

1.2.1 Windows: _atof_l, _strtod_l

  • cpp, windows

      _locale_t loc = _create_locale(LC_NUMERIC, "C");
      double d = _atof_l("3.14", loc);
    
  • 명시적 로케일 지정 가능
  • 로케일에 따른 파싱 정확도 확보

1.2.2 Linux (glibc): strtod_l

  • cpp, linux

      locale_t loc = newlocale(LC_NUMERIC_MASK, "C", nullptr);
      char* end;
      double d = strtod_l("2.718", &end, loc);
    
  • POSIX 확장
  • 스레드 안전하고 다국어 환경에 적합


2. 실수(float, double) → 문자열 변환

2.1 C++ 표준 방식

2.1.1 std::to_string

  • cpp

      double d = 3.14159;
      std::string str = std::to_string(d);
    
  • 소수점 6자리까지 출력
  • 과도한 자릿수 출력 시 후처리 필요

2.1.2 std::ostringstream

  • cpp

      std::ostringstream oss;
      oss << 2.71828;
      std::string str = oss.str();
    
  • 포맷 제어 가능 (std::fixed, std::setprecision)
  • 느리지만 유연함

2.1.3 std::snprintf

  • cpp

      char buffer[64];
      std::snprintf(buffer, sizeof(buffer), "%.3f", 1.23);
      std::string str = buffer;
    
  • 고정 자릿수 출력에 적합
  • 플랫폼 간 일관성 높음

2.1.4 std::to_chars (C++17)

  • cpp

      std::array<char, 64> buf;
      auto result = std::to_chars(buf.data(), buf.data() + buf.size(), 123.456);
      std::string str(buf.data(), result.ptr);
    
  • 빠르며 예외 없음
  • 출력 형식 제한적 (소수점 제어 불가)


2.2 비표준 방식

2.2.1 Windows: _gcvt, _fcvt, _ecvt

  • cpp, windows

      char buffer[64];
      _gcvt(123.456, 6, buffer);  // 최대 6자리까지 변환
    
  • 포맷 자동 결정(_gcvt), 고정 소수점(_fcvt)
  • 오래된 API지만 여전히 사용됨

2.2.2 Windows: _sprintf_l

  • cpp, windows

      char buffer[64];
      _locale_t loc = _create_locale(LC_NUMERIC, "C");
      _sprintf_l(buffer, "%f", loc, 3.14);
    
  • 로케일 기반 문자열 출력 가능

2.2.3 Linux: setlocale + sprintf

  • cpp

      setlocale(LC_NUMERIC, "C");
      char buffer[64];
      sprintf(buffer, "%.3f", 1.23);
    
  • 시스템 전역 로케일 변경
  • 멀티스레드 환경에서는 주의 필요


3. 비교

  • 목적 방법 플랫폼 버전 특징
    문자열 → 실수 std::stof, std::stod 전역 C++11~ 간편, 예외 처리
    문자열 → 실수 std::from_chars 전역 C++17~ 빠름, 예외 없음
    문자열 → 실수 std::strtod 전역 C 고전적, 위치 추적
    문자열 → 실수 strtod_l Linux glibc 로케일 지정
    문자열 → 실수 _strtod_l Windows MSVC 로케일 지정
    실수 → 문자열 std::to_string 전역 C++11~ 간편, 자릿수 고정
    실수 → 문자열 std::to_chars 전역 C++17~ 고속, 포맷 제한
    실수 → 문자열 snprintf, ostringstream 전역 C/C++ 유연한 포맷
    실수 → 문자열 _gcvt Windows MSVC 포맷 자동 결정
    실수 → 문자열 setlocale + sprintf Linux glibc 글로벌 설정 필요


4. 정리

  • C++17 이상을 사용하는 경우 : std::from_chars / std::to_chars 조합은 빠르고 예외 없이 안전합니다.

  • 로케일 의존 파싱/출력을 할 경우 : strtod_l, _strtod_l, _sprintf_l과 같은 로케일 지정 가능한 함수들이 유리합니다.

  • 정밀한 포맷 제어가 필요한 경우 : ostringstream, snprintf를 사용하는 것이 좋습니다.

  • ⚠️ setlocale은 스레드 안전하지 않으므로 멀티스레드 환경에서는 사용 주의 가 필요합니다.




  • 도움이 되셨으면 하단의 ❤️ 공감 버튼 부탁 드립니다. 감사합니다! 😄

728x90
반응형
728x90
반응형

C++ 최소 min, 최대 max 함수 정리

  • C++에서는 값 비교와 제한을 위해 다양한 min, max 관련 함수를 제공합니다.
  • STL 함수, 매크로(macro), 범위 제한 함수, 커스텀 비교자까지 폭넓게 활용할 수 있으며, 실전에서는 인덱스 추출, 컨테이너 전체 비교, 구조체 비교 등 여러 상황에 맞게 적용됩니다.

  • 이 글에서는 C++min, max 관련 기능을 종류별, 사용법별, 주의사항별 로 정리하고, 성능 비교와 실용 예제까지 함께 소개합니다.


1. 두 값 비교 (std::min / std::max)

  • 가장 기본적인 형태로 두 값 중 작은 값 또는 큰 값을 반환합니다.

  • cpp

      #include <algorithm>
      
      int a = 5, b = 10;
      int small = std::min(a, b); // 5
      int big   = std::max(a, b); // 10
    

1.1. 비교 함수 사용

  • cpp

      std::min(a, b, [](int x, int y) { return x > y; }); // 큰 값 반환
    


2. 다중 값 비교 (std::initializer_list, C++11)

  • C++11부터는 {} 리스트로 여러 값을 비교할 수 있습니다.

  • cpp

      std::min({a, b, c, d});
      std::max({x, y, z});
    
  • 리턴값의 타입이 일치하지 않으면 명시적 타입 지정이 필요할 수 있습니다.



3. 동시 반환 (std::minmax / std::minmax_element, C++11)

3.1. std::minmax (C++11)

  • 두 값 중 작은 값과 큰 값을 std::pair로 반환합니다.

  • cpp

      auto [min_val, max_val] = std::minmax(a, b);
    

3.2. std::minmax_element (C++11)

  • 컨테이너 전체에서 최솟값과 최댓값의 반복자를 동시에 반환합니다.

  • 반복자는 단 한 번만 순회하므로 성능상 이점이 있습니다.

  • cpp

      std::vector<int> v = {3, 1, 4, 1, 5};
      auto [min_it, max_it] = std::minmax_element(v.begin(), v.end());
    


4. 값 범위 제한 (std::clamp, C++17)

  • clamp는 주어진 값이 최소/최대 범위를 벗어나지 않도록 제한합니다.

  • cpp

      int x = std::clamp(15, 0, 10); // 결과: 10
      // std::clamp(value, low, high)
    
  • xlo보다 작으면 lo, hi보다 크면 hi, 아니면 그대로 반환됩니다.



5. Windows 매크로 충돌 및 회피 방법

  • <windows.h>를 포함하면 다음과 같은 매크로가 정의됩니다:

  • cpp

      #define min(a,b) ((a) < (b) ? (a) : (b))
      #define max(a,b) ((a) > (b) ? (a) : (b))
    
  • 이 매크로는 std::min, std::max충돌 할 수 있고, 동시 사용시 반드시 비활성화 해야 합니다.

    • cpp

      #include <windows.h>
      using namespace std; 
      min(1,2); // Windows 매크로 버전의 min이 호출
      

5.1. 방법 1: NOMINMAX 정의

  • cpp

      #define NOMINMAX
      #include <windows.h>
    

5.2. 방법 2: #undef 사용

  • cpp

      #include <windows.h>
      #undef min
      #undef max
    


6. 벡터(vector) 전체에서 최솟값/최댓값 + 인덱스 추출

6.1. std::min_element / std::max_element (C++98)

  • cpp

      std::vector<int> data = {50, 20, 40, 90, 10, 80};
      
      auto min_it = std::min_element(data.begin(), data.end());
      int min_val = *min_it;
      int min_idx = std::distance(data.begin(), min_it);
      
      auto max_it = std::max_element(data.begin(), data.end());
      int max_val = *max_it;
      int max_idx = std::distance(data.begin(), max_it);
    

6.2. std::minmax_element로 한 번에 추출 (C++11)

  • cpp

      auto [min_it, max_it] = std::minmax_element(data.begin(), data.end());
      int min_idx = std::distance(data.begin(), min_it);
      int max_idx = std::distance(data.begin(), max_it);
    


7. 사용자 정의 타입 비교 (operator< 또는 비교자 사용)

  • 구조체 등 사용자 정의 타입에서도 std::min, std::max는 사용 가능합니다.

7.1. operator< 기반

  • cpp

      struct Data {
          int value;
          bool operator<(const Data& other) const {
              return value < other.value;
          }
      };
      
      Data a{10}, b{20};
      Data result = std::min(a, b); // value 작은 쪽 반환
    

7.2. 비교자 함수 사용

  • cpp

      Item best = std::min(a, b, [](const Item& lhs, const Item& rhs) {
          return lhs.score < rhs.score;
      });
    


8. 동일 최솟값/최댓값이 여러 개인 경우

  • 특정 값과 같은 모든 인덱스를 추출하려면 수동으로 순회합니다:

  • cpp

      std::vector<int> v = {3, 1, 4, 1, 5};
      int min_val = *std::min_element(v.begin(), v.end());
      
      std::vector<int> min_indices;
      for (size_t i = 0; i < v.size(); ++i) {
          if (v[i] == min_val) {
              min_indices.push_back(i);
          }
      }
    


9. 성능 비교 및 권장 사항

  • 구분 성능 안전성 추천 용도
    std::min, std::max 최적화됨 ✅ 매우 좋음 일반 비교
    std::minmax_element 1회 순회 컨테이너 전체 비교
    매크로 min/max 빠르지만 위험 ❌ 낮음 ❌ 권장하지 않음
    clamp 최적화됨 값 제한

일반적으로는 템플릿 STL 함수 (std::min 등) 사용이 가장 안전하고 유지보수에 유리합니다.




  • 도움이 되셨으면 하단의 ❤️ 공감 버튼 부탁 드립니다. 감사합니다! 😄

728x90
반응형
728x90
반응형

VTKOpenCV 비교: 목적에 따른 라이브러리 선택 가이드


  • 과학적 데이터의 시각화나 실시간 영상 처리와 같은 작업을 수행할 때, 어떤 라이브러리를 선택할지 고민하게 됩니다.

  • 특히 그래픽 처리와 컴퓨터 비전 분야에서 많이 언급되는 VTK(Visualization Toolkit)OpenCV(Open Source Computer Vision) 는 각각 강력한 기능을 제공하지만, 목적과 사용 방식이 크게 다릅니다.

  • 이 글에서는 두 라이브러리의 공통점, 차이점, 적합한 활용 분야 를 정리하여, 상황에 맞는 올바른 선택을 할 수 있도록 도와드립니다.



1. 공통점

  • 항목 설명
    오픈 소스 둘 다 무료로 사용할 수 있는 오픈 소스 라이브러리입니다.
    C++ 기반 모두 C++로 개발되었으며, Python 등 다양한 언어 바인딩을 지원합니다.
    시각화 기능 모두 데이터를 화면에 시각화할 수 있는 기능을 제공합니다.
    이미지 처리 가능 둘 다 2D 이미지 처리가 가능하지만, 범위와 목적이 다릅니다.


2. 차이점

  • 항목 VTK (Visualization Toolkit) OpenCV (Open Source Computer Vision)
    주요 목적 과학 데이터의 3D 시각화 실시간 이미지 및 비디오 처리, 컴퓨터 비전
    시각화 방식 3D 시각화 중심 (볼륨 렌더링, 등고선 등) 2D 이미지/영상 중심 시각화 (카메라, 필터, 트래킹 등)
    주요 활용 분야 의료 영상, 유체역학, 구조해석 결과 시각화 등 얼굴 인식, 객체 추적, 증강현실, 딥러닝 추론 등
    렌더링 엔진 고급 렌더링 기능 내장 (OpenGL 기반) 시각화는 단순 디버깅용 수준 (주로 OpenCVimshow)
    GUI 연동 Qt, wxWidgets 등과 통합하여 3D 뷰어 제작 가능 Qt와 연동 가능하나 GUI보다는 비전 처리 중심
    딥러닝 지원 거의 없음 DNN 모듈을 통한 딥러닝 모델 추론 지원


3. 정리 및 선택 기준

  • VTK"과학적 3D 시각화" 가 목적이며, 메쉬/볼륨/필드 데이터를 렌더링 하고 분석 하는 데 강력합니다.
  • OpenCV"컴퓨터 비전 및 실시간 영상 처리" 가 목적이며, 카메라 입력, 필터, 추적, 인식 등에 적합합니다.


4. 사용 예시 가이드

  • 사용 목적 추천 라이브러리
    CT/MRI3D로 렌더링 VTK
    얼굴을 실시간으로 인식 및 추적 OpenCV
    유체 시뮬레이션 결과 시각화 VTK
    웹캠으로 손동작 추적 OpenCV



  • 도움이 되셨으면 하단의 ❤️ 공감 버튼 부탁 드립니다. 감사합니다! 😄

728x90
반응형
728x90
반응형

Visual Studio에서 OpenCV를 간편 설치 및 사용

  • OpenCV는 영상 및 이미지 처리에 널리 사용되는 강력한 라이브러리입니다.

  • 특히 Visual Studio 환경에서 OpenCV를 사용하는 경우, 처음 설치와 설정이 복잡하게 느껴질 수 있습니다.

  • 이 문서는 Windows에서 Visual Studio를 이용해 OpenCV를 쉽게 설치하고, 환경 변수를 설정하며, 간단한 예제 프로그램을 빌드하고 실행하는 전 과정을 상세히 안내합니다.

  • 초보자도 따라하기 쉬운 단계별 가이드로 구성되어 있으며, 예제를 통해 설치가 제대로 되었는지 검증할 수 있습니다.



OpenCV 설치 및 설정 가이드

1. OpenCV 릴리즈 다운로드



2. OpenCV 환경 변수 설정

  • 2.1. 시스템 환경 변수에 OpenCV_DIR을 추가합니다.
    • 변수 이름: OpenCV_DIR
    • 변수 값: C:\opencv\build\x64\vc16\lib
  • 2.2. 해당 경로에 OpenCVConfig.cmake 파일이 있는지 확인합니다.
    • 존재하지 않는 경우, 해당 파일이 있는 실제 경로를 찾아 변수 값으로 설정합니다.


3. 예제 프로젝트 생성

3.1 CMake 프로젝트 생성

  • 3.1.1 CMakeLists.txt 파일에 다음 내용을 작성합니다.

  • cmake

      cmake_minimum_required(VERSION 3.16) # OpenCV 4.11 기준 최소 버전
      
      # 프로젝트 명 설정
      project(MyOpenCVApp)
      
      # OpenCV 설정 찾기
      find_package(OpenCV REQUIRED)
      
      # 실행 파일 만들기
      add_executable(my_app main.cpp)
      
      # OpenCV 라이브러리 연결
      target_link_libraries(my_app ${OpenCV_LIBS})
      
      # 헤더 경로 설정 (OpenCV_INCLUDE_DIRS는 자동 포함되는 경우도 있음)
      target_include_directories(my_app PRIVATE ${OpenCV_INCLUDE_DIRS})
    

3.2. main.cpp 예제 작성

  • 3.2.1 main.cpp 파일에 다음 예제 코드를 작성합니다.

    • 녹색 정사각형을 만드는 예제 입니다.

  • cpp

      #include <opencv2/opencv.hpp>
      
      int main() {
          cv::Mat image(300, 300, CV_8UC3, cv::Scalar(0, 255, 0));
          cv::imshow("Green", image);
          cv::waitKey(0);
          return 0;
      }
    


4. 프로젝트 빌드

  • 4.1. Developer Command Prompt for VS 2022를 실행합니다. (Visual Studio 2022 인 경우)
  • 4.2. 디버그 모드로 빌드하려면 다음 명령어를 실행합니다.
    • cmd

        ...\hello\build>msbuild MyOpenCVApp.sln /p:Configuration=Debug
      
  • 4.3. 릴리즈 모드로 빌드하려면 다음 명령어를 실행합니다.
    • cmd

        ...\hello\build>msbuild MyOpenCVApp.sln /p:Configuration=Release
      
  • 4.4. 또는 Visual Studio IDE에서 .sln 파일을 열어 직접 빌드할 수도 있습니다.
    • 비주얼 스튜디오를 사용하는 경우 include 경로, lib 경로, 실행파일 및 OpenCV DLL 파일들 경로를 점검하여야 한다.


5. 예제 실행

  • 5.1. 아래 명령어를 입력하여 예제를 실행합니다.

    • cmd

      ...\hello\build\Release>my_app.exe
    
  • 5.2. 녹색 정사각형이 표시되면 OpenCV 설정이 정상적으로 완료된 것입니다.




  • 도움이 되셨으면 하단의 ❤️ 공감 버튼 부탁 드립니다. 감사합니다! 😄

728x90
반응형
728x90
반응형

QuickCppLib : 고성능 경량 C++ 유틸리티 모음


  • quickcpplibC++ 표준화 제안자 Niall Douglas가 개발한 고성능 C++ 유틸리티 모음 라이브러리 입니다.
  • 이 프로젝트는 Boost 등의 라이브러리들과 유사하거나 대체 가능한 기능들을 훨씬 더 경량 으로 제공하며, 시스템 프로그래밍, 파일시스템, 네트워크, 임베디드 분야에서 실용적이고 빠르게 쓸 수 있도록 설계되었습니다.
  • 모두 헤더 전용(header-only) 으로 구성되어 있어 설치 없이 바로 사용할 수 있습니다.

  • 이 문서에서는 quickcpplib의 다양한 모듈을 실제 예제를 통해 하나씩 살펴봅니다.


1. status_code – 에러 상태 코드 처리

  • cpp

      #include "quickcpplib/status_code/status_code.hpp"
      #include <iostream>
      
      // 네임스페이스 이름이 긴 경우 단축하여 사용.
      namespace sc = quickcpplib::status_code;
      
      sc::system_code do_work(bool fail) {
          if (fail)
              return sc::errc::invalid_argument; // 실패 (인자 오류)
          else
              return {};  // 성공
      }
      
      int main() {
          auto result = do_work(true);
      
          if (result) {
              std::cout << "에러 발생: " << result.message().c_str() << std::endl;
          } else {
              std::cout << "성공!" << std::endl;
          }
      }
    


2. expected – 성공/실패 값을 담는 타입

  • cpp

      #include "quickcpplib/expected.hpp"
      #include <iostream>
      #include <string>
      
      using quickcpplib::expected;
      using quickcpplib::make_unexpected;
      
      expected<int, std::string> divide(int a, int b) {
          if (b == 0)
              return make_unexpected("0으로 나눌 수 없습니다."); // std::string 타입
     		 
          return a / b; // 성공한 경우의 반환값 (int 타입)
      }
      
      int main() {
          auto result = divide(10, 0);
      
          if (result.has_value()) {
              std::cout << "결과: " << result.value() << std::endl;
          } else {
              std::cout << "에러: " << result.error() << std::endl;
          }
      }
    


3. tribool – 세 가지 상태: true, false, unknown 상태 표현

  • cpp

      #include "quickcpplib/tribool.hpp"
      #include <iostream>
      
      using quickcpplib::tribool;
      using quickcpplib::indeterminate;
      
      tribool check_something(int x) {
          if (x > 0) return true;
          else if (x < 0) return false;
          else return indeterminate; // true/false 이외의 결과
      }
      
      int main() {
          tribool t = check_something(0);
      
          if (indeterminate(t)) {
              std::cout << "결과를 알 수 없습니다." << std::endl;
          } else if (t) {
              std::cout << "true입니다." << std::endl;
          } else {
              std::cout << "false입니다." << std::endl;
          }
      }
    


4. ringbuffer – 고속 순환 버퍼

  • cpp

      #include "quickcpplib/ringbuffer.hpp"
      #include <iostream>
      
      int main() {
          // 최대 4개의 정수를 저장하는 버퍼
          quickcpplib::ringbuffer<int, 4> rb;
      
          rb.push_back(10);
          rb.push_back(20);
          rb.push_back(30);
          rb.push_back(40);
     	 
     	 // 버퍼가 가득 찼기 때문에 다음 push는 덮어쓰기 됨
          rb.push_back(50);
      
          while (!rb.empty()) {
              std::cout << rb.front() << " ";
              rb.pop_front();
          }
          std::cout << std::endl;
     	 
     	 // 출력: 20 30 40 50
          // (처음 값 10은 덮어쓰기됨) 
      }
    


5. bitfield – 비트( bit) 구조체 관리

  • cpp

      #include "quickcpplib/bitfield.hpp"
      #include <iostream>
      
      using namespace quickcpplib::bitfield;
      
      struct MyFlags {
          // BITFIELD_MEMBER(필드 타입, 이름, 비트수);
     	 
          BITFIELD_MEMBER(bool, enabled, 1); // 1비트 : true/false 설정 가능
          BITFIELD_MEMBER(uint8_t, mode, 3); // 3비트 : 0~7 값 설정 가능
          BITFIELD_MEMBER(uint8_t, count, 4); // 4비트 : 0~15 값 설정 가능
      };
      
      int main() {
          MyFlags flags;
          flags.enabled() = true;
          flags.mode() = 5;
          flags.count() = 12;
      
          std::cout << "enabled: " << flags.enabled() << std::endl;
          std::cout << "mode: " << static_cast<int>(flags.mode()) << std::endl;
          std::cout << "count: " << static_cast<int>(flags.count()) << std::endl;
     	 
     	 // enabled: 1
          // mode: 5
          // count: 12
      }
    


6. byte – 바이트 단위 처리

  • cpp

      #include "quickcpplib/byte.hpp"
      #include <iostream>
      
      using namespace quickcpplib;
      
      int main() {
          byte b = byte{0x01}; // b = 0000 0001 (2진수 표시)
          b <<= 1;             // b = 0000 0010 (왼쪽으로 1비트 이동)
     	 
          if (b == byte{0x02}) {
              std::cout << "비트 연산 성공" << std::endl; 
          }
      
          uint8_t raw = to_integer<uint8_t>(b); // raw 는 2 
          std::cout << "정수로 변환: " << static_cast<int>(raw) << std::endl;
      }
    


7. span – 배열 뷰 구조

  • C++20std::span과 유사한 기능 제공.

    • C++20보다 하위 버전에서 span 활용 가능.
  • cpp

      #include "quickcpplib/span.hpp"
      #include <iostream>
      
      // span은 컨테이너처럼 동작하므로 range-based for 문으로 순회 가능.
      // std::vector처럼 begin(), end() 메서드를 갖고 있어 반복문에 적합.
      void print_span(quickcpplib::span<int> s) {
          for (auto val : s) {
              std::cout << val << " ";
          }
          std::cout << std::endl;
      }
      
      int main() {
          int arr[] = {1, 2, 3, 4, 5};
     	 
          quickcpplib::span<int> s(arr, 5);
     	 // arr 배열의 포인터와 길이를 이용해 span 객체를 생성.
          // span<int>은 int 타입 요소들의 연속된 메모리 블록에 대한 비소유 참조.
     	 // 배열을 복사하지 않고 읽기/쓰기 가능. (가볍고 빠름)
     	 
          print_span(s); // 출력 결과 : 1 2 3 4 5
      }
    

  • span 활용 시 장점
    • 복사 없음 : 원본 배열/컨테이너 참조만 하며, 성능 향상.
    • 유형 안전 : 포인터보다 안전하게 타입 추론 가능.
    • 크기 추적 : 배열 크기를 함께 저장하므로 범위 오류 방지.
    • 일관된 인터페이스 : std::vector, std::array, 원시 배열 모두 동일 방식으로 처리 가능.


8. intrusive – 포인터 삽입 연결 리스트

  • cpp

      #include "quickcpplib/intrusive/list.hpp"
      #include <iostream>
      
      using namespace quickcpplib::intrusive;
      
      struct MyNode : list_node<MyNode> {
          int value;
          MyNode(int v) : value(v) {}
      };
      // 상속을 통해 노드로서의 연결 기능을 포함.
      // list_node<MyNode>는 노드가 스스로 다음/이전 포인터를 보유하도록 구성됨.
      // 이 구조 덕분에 리스트 객체는 단순히 포인터들을 연결만 하면 됨 → 매우 경량.
      
      int main() {
      
          list<MyNode> my_list;
     	 // MyNode 타입을 구성하는 intrusive 리스트.
     	 // 이 리스트는 각 노드가 자체적으로 연결 정보를 갖기 때문에 
     	 // 별도의 메모리 할당 없이 동작.
      
          MyNode a(10), b(20), c(30);
     	 
          my_list.push_back(&a);
     	 // 노드 인스턴스를 포인터로 리스트에 추가.
     	 // 노드 객체는 이미 외부에서 생성되었기 때문에 리스트는 그 메모리만 참조.
     	 
          my_list.push_back(&b);
          my_list.push_back(&c);
      
          for (auto& node : my_list) {
              std::cout << node.value << " ";
          }
     	 // intrusive::list는 반복 가능한 인터페이스를 제공.
     	 // 노드를 직접 순회하며 .value 필드에 접근 가능.
     	 
          std::cout << std::endl;
      }
    

  • intrusive list의 장점

    • 메모리 효율 : 노드 자체에 포인터 내장 → 메모리 추가 할당 없음
    • 빠른 성능 : 생성/삭제가 매우 빠름 (malloc/free 없음)
    • 안정적 순회 : 포인터만 조작하므로 주소 안정성 ↑
    • 시스템/커널 코드 적합 : Linux 커널도 비슷한 구조 사용 (list_head)
  • 주의할 점

    • 노드 는 반드시 수명 관리가 외부 에서 되어야 함.
      • 즉, 리스트가 노드의 메모리를 소유하지 않음.
    • 하나의 노드 는 여러 intrusive 리스트에 동시에 포함될 수 없음.
      • 동시 포함되면 포인터 충돌 발생.


9. bit_cast – 타입 간 비트 이동

  • cpp

      #include "quickcpplib/bit_cast.hpp"
      #include <iostream>
      
      int main() {
          float f = 3.1415926f;
     	 // float 실수형 값을 정의.
     	 // 내부적으로는 IEEE 754 형식으로 저장되며, 이는 32비트로 구성.
     	 
          uint32_t bits = quickcpplib::bit_cast<uint32_t>(f);
     	 // float 값을 uint32_t(32비트 무부호 정수)로 비트 단위 그대로 복사.
     	 // reinterpret_cast와는 달리 정적으로 타입 안전하며, 복사 기반으로 동작.
     	 // 결과: f의 내부 32비트 이진 표현을 그대로 uint32_t로 해석. 
      
          std::cout << "float의 비트 표현: 0x" << std::hex << bits << std::endl;
     	 // float의 비트 표현: 0x40490fdb
      
          float recovered = quickcpplib::bit_cast<float>(bits);
     	 // 앞에서 얻은 uint32_t를 다시 float으로 복원.
     	 // 실제 비트 수준 복사를 통해 값이 손실 없이 정확히 되돌아옴.
     	 
          std::cout << "복원된 float: " << recovered << std::endl;
     	 // 복원된 float: 3.14159
      }
    
  • bit_cast 활용 시 장점

    • reinterpret_cast 위험성 제거 : 타입 충돌, 정렬 문제 발생 가능
    • memcpy() 활용 시, 저수준의 번거롭운 코드 가독성 발생
  • 사용 조건

    • 원본대상 타입크기가 같아야 합니다 (sizeof(T) == sizeof(U))
    • POD(Plain Old Data) 타입이어야 하며, trivial copy 가능해야 함.
      • Trivial Type (평범한 타입)
        • 기본 생성자, 복사 생성자, 대입 연산자, 소멸자가 모두 컴파일러가 자동 생성하는 것.
        • 사용자 정의 생성자/소멸자가 없음.
      • Standard Layout (표준 레이아웃 타입)
        • 멤버 변수의 메모리 배치가 예측 가능. (C 구조체처럼)
        • 상속, 접근 제한자(public/private) 등을 복잡하게 사용하지 않음.


10. spinlock – 경량 락 기반 동시성 제어

  • cpp

      #include "quickcpplib/spinlock.hpp"
      #include <iostream>
      #include <thread>
      #include <vector>
      
      quickcpplib::spinlock lock;
      // 경량 lock 객체를 하나 선언.
      // std::mutex보다 훨씬 간단하고 빠르며, 짧은 락 구간에서 효율적.
      // 락이 걸려 있는 동안 다른 스레드는 계속 루프를 돌며(lock free가 아님) 대기.
      
      int counter = 0;
      
      void increment() {
          for (int i = 0; i < 10000; ++i) {
     	 
              quickcpplib::spinlock_guard guard(lock);
     		 // RAII 방식으로 lock을 잠금.
     		 // guard가 생성되면 lock.lock()이 호출되고, 
     		 //  guard가 스코프를 벗어나면 자동으로 lock.unlock()이 호출.
     		 // std::lock_guard<std::mutex>와 유사한 사용 방식.
     		 
              ++counter;
          }
      }
      
      int main() {
          std::vector<std::thread> threads;
     	 
          for (int i = 0; i < 4; ++i)
              threads.emplace_back(increment);
      
          for (auto& t : threads)
              t.join();
      
          std::cout << "최종 카운터 값: " << counter << std::endl;
     	 // 최종 카운터 값: 40000
      }
    

  • Spinlock vs Mutex

    • 항목 spinlock mutex (std::mutex)
      대기 방식 루프를 돌며 계속 락 확인 OS 커널 개입으로 대기/슬립 발생
      성능 매우 빠름 (짧은 락에 적합) 컨텍스트 스위칭 발생 → 느림
      락 유지 시간 짧을수록 좋음 상대적으로 긴 구간도 무방
      사용 환경 실시간 시스템, 경량 스레드 일반 어플리케이션, I/O 블록
  • 주의할 점

    • spinlock락이 길어지면 CPU 자원을 낭비 할 수 있습니다.
    • 적절한 사용처는 다음과 같습니다:
      • 짧고 빈번한 연산 (예: 카운터 증가)
      • 멀티스레드 프로파일링용 간단한 락
      • 락을 걸 때 context switching이 오히려 느린 환경


11. algorithmSTL 유사 알고리즘

  • cpp

#include "quickcpplib/algorithm.hpp"
#include <iostream>
#include <vector>

int main() {
    std::vector<int> v = {1, 2, 3, 4, 5};

    auto found = quickcpplib::algorithm::find(v.begin(), v.end(), 3);
	// v.begin()부터 v.end()까지 순회하면서 3을 찾음.
	// std::find와 사용 방식이 동일.
	// 반환값은 3이 위치한 반복자(iterator).
	
    if (found != v.end()) {
        std::cout << "3을 찾았습니다." << std::endl;
    }
}
  • quickcpplib::algorithm 장점

    • 경량 구현 : 불필요한 최적화/검사 제거로 속도 개선.
    • 헤더 전용 : 설치 없이 사용 가능.
    • STL 대체 : std::find, std::copy, std::equal 등과 거의 동일한 인터페이스.
    • POD 특화 : 단순한 타입의 연산을 더 빠르게 수행할 수 있음.
  • 주의할 점

    • STL과 완전한 호환성을 전제로 하지 않으며, 고성능이 필요한 코드에서 선택적으로 사용하는 것이 좋습니다.
    • iterator 기반이므로 raw 배열에도 적용 가능하지만, 범위 체크는 직접 해주어야 할 수 있습니다.


  • 위 예제들은 모두 quickcpplib 라이브러리가 지닌 저비용, 고성능, 실용성의 강점을 잘 보여줍니다.
  • 실제 시스템, 네트워크, 임베디드, 게임, 서버 백엔드 같은 분야에서도 충분히 활용 가능한 수준의 기능들이며, Boost 등보다 훨씬 가볍게 사용할 수 있는 것이 큰 장점입니다. 🚀 👽



  • 도움이 되셨으면 하단의 ❤️ 공감 버튼 부탁 드립니다. 감사합니다! 😄

728x90
반응형
728x90
반응형

CPM.cmake (CMake Package Manager) : 설정이 필요 없는 크로스 플랫폼, 재현 가능한 종속성 관리를 위한 CMake용 패키지 관리자


1. 개요

  • CPM.cmake(CMake Package Manager)는 CMakeFetchContent 모듈을 확장하여, 프로젝트 외부의 라이브러리를 간편하게 가져오고 캐싱할 수 있도록 돕는 경량 의존성 관리 도구 입니다.

  • 일반적으로 GitHub 저장소에서 패키지를 받아오는 용도로 사용되지만, 사설 Git 서버(예: 내부 IP 기반 서버)에서도 충분히 활용 가능합니다.

  • 이 문서에서는 사설 Git 서버를 구축하고, CPM.cmake를 이용해 해당 서버에서 라이브러리를 자동으로 다운로드하여 사용하는 방법 을 자세히 소개합니다.



2. CPM.cmake 사용법

2.1. CPM.cmake 가져오기

  • CPM.cmake는 프로젝트 내에 직접 포함하는 것이 일반적입니다:

  • bash

      mkdir -p cmake
      wget -O cmake/CPM.cmake https://github.com/cpm-cmake/CPM.cmake/releases/latest/download/get_cpm.cmake
    

2.2. CMakeLists.txt 구성 예시 (GitHub 저장소)

  • 아래는 fmt, nlohmann/json, Catch2CPM(CMake Package Manager)을 통해 GitHub에서 받아 사용하는 예시입니다:

  • cmake

      cmake_minimum_required(VERSION 3.14 FATAL_ERROR)
      
      # create project
      project(MyProject)
      
      # add executable
      add_executable(main main.cpp)
      
      # add dependencies
      include(cmake/CPM.cmake)
      
      CPMAddPackage("gh:fmtlib/fmt#7.1.3")
      CPMAddPackage("gh:nlohmann/json@3.10.5")
      CPMAddPackage("gh:catchorg/Catch2@3.4.0")
      
      # link dependencies
      target_link_libraries(main
          fmt::fmt
          nlohmann_json::nlohmann_json
          Catch2::Catch2WithMain
      )
    

  • #버전Git 태그/브랜치/커밋을 직접 지정하는 방식이며,
    @버전CPM 내부 버전 추적용 이름을 포함하는 방식입니다.
    일반적으로 @버전이 캐시 충돌을 줄이고 관리에 더 유리합니다.



2.3. CMakeLists.txt 구성 예시 (사설 저장소)

  • 다음은 사설 Git 저장소에서 라이브러리를 받아와 사용하는 예시입니다:

  • cmake

      cmake_minimum_required(VERSION 3.14)
      
      project(MyProject)
      
      include(cmake/CPM.cmake)
      
      CPMAddPackage(
        NAME MyLib
        GIT_REPOSITORY git@192.168.0.10:mylib.git
        GIT_TAG main   # 또는 특정 커밋 해시, 태그
      )
      
      add_executable(main main.cpp)
      target_link_libraries(main MyLib)
    
  • GIT_TAG는 반드시 명시해야 하며, 브랜치명, 태그명, 또는 커밋 해시를 사용할 수 있습니다.



2.4. URL에서 아카이브 직접 다운로드 예시

  • CPM(CMake Package Manager)은 Git 저장소뿐만 아니라 압축 파일(.zip 등) 형식의 패키지URL을 통해 직접 가져올 수 있습니다.

  • cmake

      # 버전은 자동 추정 (예: 파일명에서 추출)
      CPMAddPackage("https://example.com/my-package-1.2.3.zip")
      
      # MD5 해시값을 명시하여 무결성 검증
      CPMAddPackage("https://example.com/my-package-1.2.3.zip#MD5=68e20f674a48be38d60e129f600faf7d")
      
      # 명시적으로 버전 지정
      CPMAddPackage("https://example.com/my-package.zip@1.2.3")
    
  • 이 방식은 Git을 사용하지 않는 사내 배포 시스템이나, 외부 공개 라이브러리를 간단히 가져오고자 할 때 유용합니다.



3. 캐싱 및 오프라인 빌드

  • CPM.cmake는 다운로드된 의존성을 자동으로 캐싱합니다. 기본 캐시 위치는 다음과 같습니다:

    • $HOME/.cache/CPM
    • ${CMAKE_BINARY_DIR}/_deps (빌드 디렉토리 내)
  • 이 디렉토리를 보존하거나 다른 머신에 복사하면 오프라인에서도 동일하게 사용할 수 있습니다.



4. Gitea 또는 GitLab 등 도구를 활용한 구성 방안

  • 단순한 Git 베어 저장소 외에도, 다음과 같은 웹 기반 Git 서버를 설치하여 사용자 인터페이스와 프로젝트 관리 기능을 강화할 수 있습니다:

    • Gitea : 경량 오픈소스 Git 서비스
    • GitLab CE : 보다 강력한 CI/CD, 권한 관리 등 제공
  • 이런 도구들은 SSH/HTTPS 모두를 지원하며, 여러 개발자와 협업 시 매우 유용합니다.



5. 사설 Git 저장소 준비

5.1 Git 설치 및 저장소 생성

  • 사설 서버(IP: 192.168.0.10 등)에 Git을 설치합니다:

    • bash

        sudo apt update
        sudo apt install git
      
  • 베어 저장소를 생성합니다:

    • bash

        mkdir -p /srv/git/mylib.git
        cd /srv/git/mylib.git
        git init --bare
      

5.2 SSH 접근 설정

  • 클라이언트에서 해당 서버로 접속할 수 있도록 SSH 인증을 설정합니다:

    • (1) 서버에 git 사용자 생성 (또는 기존 사용자 사용)
    • (2) 클라이언트의 공개키를 서버의 ~/.ssh/authorized_keys에 등록
    • (3) 클라이언트에서 SSH 접속 확인:
      • bash

        ssh git@192.168.0.10
        
  • CPM 사용을 위하여 비밀번호 없이 접근되도록 설정하는 것이 중요합니다.


5.3 HTTPS 저장소 사용 시 참고사항

  • 사설 Git 서버가 HTTPS로 동작하고 있고, 자체 서명된 SSL 인증서를 사용하는 경우 다음 설정으로 인증서 검증을 건너뛸 수 있습니다:

  • bash

    git config --global http.sslVerify false
    
  • ⚠️ 단, 이 설정은 보안상 위험하므로, 내부 개발 환경 에서만 사용하는 것이 좋습니다.



6. 정리

  • 사설 Git 서버와 CPM.cmake를 조합하면, 외부 네트워크에 의존하지 않고도 안정적으로 CMake 의존성을 관리할 수 있습니다.
  • 특히, 보안이 중요한 기업 내 네트워크나, 오프라인 환경에서도 CMake 프로젝트를 유연하게 운영할 수 있는 강력한 방법입니다.



  • 도움이 되셨으면 하단의 ❤️ 공감 버튼 부탁 드립니다. 감사합니다! 😄

728x90
반응형
728x90
반응형

설정 파일 비교 및 활용: .toml.ini


1. 설정 파일 개요

  • .toml 파일과 .ini 파일은 모두 소프트웨어에서 설정 정보를 저장하는 데 널리 사용되는 텍스트 기반 구성 파일 입니다.

  • 사람(man)과 기계(machine)가 모두 읽기 쉽도록 설계되어 있으며, 다양한 운영체제와 프로그래밍 언어에서 활용됩니다.



2. 공통점과 차이점

2.1. 공통점

  • 모두 키-값 쌍 (key-value pair) 형태로 데이터를 저장합니다.

  • 섹션 구분자를 사용해 설정 항목을 그룹화할 수 있습니다.

  • 텍스트 기반이므로 사람이 직접 읽고 편집 할 수 있습니다.


2.2. 차이점

  • 항목 .ini 파일 .toml 파일
    문법 규격 비공식, 다양한 변형 존재 엄격한 공식 명세 존재 (TOML 공식 문서)
    자료형 지원 대부분 문자열 문자열, 숫자, 날짜, 배열, 불리언 등 지원
    중첩 구조 단순 섹션만 가능 테이블, 중첩 테이블, 배열 테이블 가능
    주석 ;, # 모두 가능 #만 가능
    사용 예시 윈도우 설정 파일, 오래된 소프트웨어 Rust의 Cargo.toml, Python의 pyproject.toml


3. .ini vs .toml 예제 비교

3.1. .ini 예제

  • ini

      ; 설정 파일 예제
      [database]
      user = root
      password = 1234
      
      [server]
      host = localhost
      port = 8080
    

3.2. .toml 예제

  • toml

      # 설정 파일 예제
      title = "TOML 예제"
      count = 1000
      enabled = true
      
      [database]
      user = "root"
      password = "1234"
      
      [server]
      host = "localhost"
      port = 8080
      
      [[products]]
      name = "Apple"
      price = 1.2
      
      [[products]]
      name = "Orange"
      price = 0.8
    


4. .toml에서 지원하는 데이터 타입

  • .toml은 다양한 데이터 타입을 명확하게 지원합니다.

  • 이는 구조화된 설정이 필요한 프로젝트에서 매우 유리합니다.

  • 타입 예시 설명
    문자열 "Hello" / 'World' 큰따옴표 또는 작은따옴표 사용 가능
    멀티라인 문자열 """여러 줄""", '''여러 줄''' 여러 줄 문자열 지원
    정수 42, -7, 1_000 밑줄 _로 가독성 향상 가능
    실수 3.14, -0.01, 1e6 부동소수점 및 지수 표기
    불리언 true, false 소문자만 허용
    날짜/시간 1979-05-27T07:32:00Z RFC 3339 형식 사용
    배열 [1, 2, 3], ["a", "b", "c"] 혼합 타입은 허용되지 않음
    테이블 [section] 키-값 그룹 정의
    중첩 테이블 [parent.child] 점(.)으로 중첩 표현
    배열 테이블 [[products]] 동일 형식의 객체 배열 표현


5. Python에서 .toml 활용 예제

  • Python에서는 tomllib(3.11 이상) 또는 tomli(3.10 이하 설치 필요) 모듈을 사용하여 TOML 파일을 파싱할 수 있습니다.

5.1. TOML 파일 예 (config.toml)

  • toml

      title = "TOML 예제"
      count = 1000
      enabled = true
      
      [database]
      user = "admin"
      port = 5432
      
      [[products]]
      name = "Apple"
      price = 1.2
      
      [[products]]
      name = "Orange"
      price = 0.8
    

5.2. Python 코드

  • python

      import tomllib
      
      with open("config.toml", "rb") as f:
          data = tomllib.load(f)
      
      print(data["title"])               # TOML 예제
      print(data["database"]["user"])    # admin
      print(data["products"][0]["name"]) # Apple
    
  • 💡 Python 3.10 이하는 pip install tomli로 설치하여 사용해야 합니다.



6. Rust에서 .toml 활용 예제

  • Rust에서는 toml 크레이트와 serde를 함께 사용하여 구조체에 TOML 데이터를 직접 매핑할 수 있습니다.

6.1. Cargo.toml에 추가

  • toml

      [dependencies]
      toml = "0.8"
      serde = { version = "1.0", features = ["derive"] }
    

6.2. Rust 코드

  • rust

      use serde::Deserialize;
      
      #[derive(Debug, Deserialize)]
      struct Config {
          title: String,
          count: i64,
          enabled: bool,
          database: Database,
          products: Vec<Product>,
      }
      
      #[derive(Debug, Deserialize)]
      struct Database {
          user: String,
          port: u16,
      }
      
      #[derive(Debug, Deserialize)]
      struct Product {
          name: String,
          price: f64,
      }
      
      fn main() {
          let toml_str = std::fs::read_to_string("config.toml").unwrap();
          let config: Config = toml::from_str(&toml_str).unwrap();
      
          println!("Title: {}", config.title);
          println!("DB User: {}", config.database.user);
          println!("First product: {}", config.products[0].name);
      }
    


7. 정리

.ini는 단순한 구조의 설정 파일에 적합하고, .toml은 타입 명시성과 구조화가 필요한 복잡한 설정에 적합합니다.

  • 특히 Python, Rust 같은 언어에서는 .toml을 표준 설정 형식으로 채택하고 있어 앞으로의 사용 빈도는 더욱 높아질 것입니다.



  • 도움이 되셨으면 하단의 ❤️ 공감 버튼 부탁 드립니다. 감사합니다! 😄

728x90
반응형
728x90
반응형

meojson : C++17 고성능 JSON 직렬화 라이브러리


1. 개요

  • meojsonC++17을 기반으로 한 차세대 JSON/JSON5 직렬화 및 역직렬화 라이브러리 입니다.

  • 헤더 전용(header-only) 구조로 구현되어 있어 별도의 빌드 과정이나 종속성이 없으며, 직관적이고 유연한 JSON 처리를 지원합니다.



2. 주요 특징

2.1. 간편한 JSON 생성 및 조작

  • JSON 객체를 직관적인 문법으로 생성하고 수정할 수 있습니다.

  • cpp

      json::value j;
      
      j["pi"] = 3.14;
      j["happy"] = true;
      j["answer"]["everything"] = 42;
      j["object"] = { {"currency", "USD"}, {"value", 42.99} };
    

2.2. 다양한 표준 컨테이너 지원

  • std::set, std::unordered_map 등 다양한 표준 C++ 컨테이너(container)를 자동으로 JSON으로 변환하거나 역변환할 수 있습니다.

  • cpp

      std::set<int> set { 1, 2, 3 };
      
      j["set"] = set;
    

2.3. 사용자 정의 타입 직렬화

  • 사용자 정의 구조체에 대해 매크로 MEO_JSONIZATION을 활용하여 손쉽게 직렬화 및 역직렬화를 구현할 수 있습니다.

  • cpp

    struct MyStruct {
        int x = 0;
        std::vector<double> vec;
        
        MEO_JSONIZATION(x, vec);
    };
    

2.4. JSON5 포맷 지원**

  • json5.hpp를 포함하면 JSON5 형식의 문법(주석, 싱글쿼트, 끝 쉼표 등)도 지원합니다.

  • cpp

      #include "json5.hpp"
    

2.5. 안전한 타입 변환

  • 타입 검사 기능을 통해 JSON 데이터를 안전하게 다룰 수 있으며, 런타임 에러를 줄일 수 있습니다.

  • cpp

      if (j["happy"].is<std::vector<int>>()) {
          std::vector<int> vec = (std::vector<int>)j["happy"];
      } else {
          std::cout << "j[\"happy\"]는 배열이 아닙니다!" << std::endl;
      }
    


3. 정리

  • meojson은 복잡한 JSON 데이터를 손쉽게 처리할 수 있는 C++ 라이브러리로, 성능과 간결한 문법을 모두 추구하는 개발자에게 적합합니다.
  • 더 많은 정보와 예제는 GitHub 저장소 에서 확인하실 수 있습니다.



  • 도움이 되셨으면 하단의 ❤️ 공감 버튼 부탁 드립니다. 감사합니다! 😄

728x90
반응형
728x90
반응형

cpp-ipc: 고성능 C++ 프로세스 간 통신 라이브러리

1. 개요

  • cpp-ipcC++17 이상을 기반으로 하는 고성능 프로세스 간 통신(IPC : Inter-Process Communication) 라이브러리 로, 공유 메모리 를 기반으로 하여 WindowsLinux에서 동작합니다.

  • 이 라이브러리는 가볍고 빠르며 락-프리(lock-free) 또는 경량 스핀락(spin-lock) 기법을 통해 높은 성능을 보장합니다.

  • 추가적인 외부 의존성 없이 표준 템플릿 라이브러리(STL) 만으로 구성되어 있으며, Vcpkg를 이용한 간단한 설치도 가능합니다.

    • cmd

        vcpkg install cpp-ipc
      
  • 지원 컴파일러는 다음과 같습니다:

    • MSVC 2017 이상
    • GCC 7 이상
    • Clang 4 이상

1.1. 테스트 환경

  • CPU: Intel Core i7-9700K 3.6GHz
  • RAM: 32GB
  • OS: Windows 10 64bit / Ubuntu 20.04
  • 빌드 구성: Release 모드 / MSVC or GCC 9+

1.2. 주요 성능 지표 (메시지 처리 속도)

  • ipc::route: 약 380 만 회/초
  • ipc::channel: 약 360 만 회/초
  • 메시지 크기: 일반적으로 64바이트 또는 문자열 기준

1.3. CPU 사용률

  • 초기에 spin-wait 동안 약간의 CPU 사용이 있지만, 이후 세마포어 대기 모드로 전환되면 CPU 사용률이 매우 낮아짐.
  • 실제 테스트에서는 CPU 코어 1개 기준 평균 5~10% 이내로 측정됨.


2. 핵심 구성 요소

  • cpp-ipc는 크게 두 가지 핵심 클래스인 ipc::routeipc::channel을 제공합니다:

    • ipc::route : 단일 송신자(single writer)다중 수신자(multiple readers) 를 지원
    • ipc::channel : 다중 송신자(multiple writer)다중 수신자(multiple readers) 를 지원
  • 모두 순환 배열(circular array)을 기반으로 동작하며, 브로드캐스트 방식으로 데이터를 전달합니다.

  • 내부적으로 재시도 횟수를 초과하면 세마포어(semaphore)를 이용해 효율적인 대기 상태를 유지합니다.



3. 예제(Tutorial) 코드 설명


3.1 ipc::route 예제

  • 이 예제는 단일 생산자(송신자) - 단일 소비자(수신자) 구조를 이용한 기본 통신 흐름을 보여줍니다.

  • cpp

      std::vector<char const *> const datas = {
          "hello!",
          "foo",
          "bar",
          "ISO/IEC",
          "14882:2011",
          "ISO/IEC 14882:2017 Information technology - Programming languages - C++",
          "ISO/IEC 14882:2020",
          "Modern C++ Design: Generic Programming and Design Patterns Applied"
      };
      
      // 생산자(송신자) 스레드
      std::thread t1 {[&] {
          ipc::route cc { "my-ipc-route" }; // ipc::route 생산자 생성
          cc.wait_for_recv(1); // 최소 1개의 수신자 연결까지 블로킹 대기
          for (auto str : datas) cc.send(str); // 데이터 전송 (소비자 없어도 전송 가능)
          cc.send(ipc::buff_t('\0')); // 종료 신호 전송
      }};
      
      // 소비자(수신자) 스레드
      std::thread t2 {[&] {
          ipc::route cc { "my-ipc-route", ipc::receiver }; // ipc::route 소비자 생성
          while (1) {
              auto buf = cc.recv(); // ipc 수신될 때까지 대기
              auto str = static_cast<char*>(buf.data());
              if (str == nullptr || str[0] == '\0') return; // 종료 조건 확인
              std::printf("recv: %s\n", str); // 데이터 수신 및 출력
          }
      }};
      
      t1.join();
      t2.join();
    


3.2 ipc::channel 예제

  • 다중 송신자-수신자를 허용하는 구조입니다.

  • 이 예제는 1초 대기 후 데이터를 전송하는 구조를 포함합니다.

  • cpp

      using namespace std::literals;
      
      std::vector<char const*> const datas = {
          "hello!",
          "foo",
          "bar",
          "ISO/IEC",
          "14882:2011",
          "ISO/IEC 14882:2017 Information technology - Programming languages - C++",
          "ISO/IEC 14882:2020",
          "Modern C++ Design: Generic Programming and Design Patterns Applied"
      };
      
      // 생산자(송신자) 스레드
      std::thread t1{ [&] {
          ipc::channel cc { "my-ipc-channel" }; // ipc::channel 생산자 생성
          std::this_thread::sleep_for(1s); // 소비자 연결 대기
          for (auto str : datas) cc.send(str); // 데이터 전송
          cc.send(ipc::buff_t('\0')); // 종료 신호 전송
      }};
      
      // 소비자(수신자) 스레드
      std::thread t2{ [&] {
          ipc::channel cc { "my-ipc-channel", ipc::receiver }; // ipc::channel 소비자 생성
          while (1) {
              auto buf = cc.recv(); // ipc 수신될 때까지 대기
              auto str = static_cast<char*>(buf.data());
              if (str == nullptr || str[0] == '\0') return; // 종료 조건 확인
              std::printf("recv: %s\n", str); // 데이터 수신 및 출력
          }
      }};
      
      t1.join();
      t2.join();
    


4. demo 디렉토리 예제


4.1 chat

  • 여러 프로세스 간 채팅을 시뮬레이션합니다.
  • 각 프로세스는 메시지를 주고받으며 ipc::channel을 활용한 다중 송수신 통신 방식을 보여줍니다.

4.2 linux_service

  • Linux 환경에서의 백그라운드 서비스(데몬)를 구현하는 예제입니다.
  • 클라이언트는 IPC를 통해 데몬과 통신하며, 데몬 프로세스는 별도로 관리됩니다.

4.3 msg_que

  • 생산자-소비자 구조를 메시지 큐 형태로 구현한 예제입니다.
  • 한쪽에서 데이터를 계속 생산하고, 다른 쪽에서 수신하여 처리합니다.

4.4 send_recv

  • 기본적인 송신-수신 흐름을 보여주는 단순 예제입니다.
  • ipc::route를 활용하여 한 프로세스가 데이터를 전송하고, 다른 프로세스가 이를 받아 출력합니다.

4.5 win_service

  • Windows 환경에서 실행되는 서비스 애플리케이션입니다.
  • cpp-ipc를 통해 외부 클라이언트와의 통신을 처리하며, 서비스 관리 기능과 통합되어 있습니다.


5. 정리

  • cpp-ipc는 단순하고 빠른 IPC를 요구하는 시스템에서 매우 유용한 도구입니다.
  • 다양한 운영 체제에서 활용 가능한 데모와 튜토리얼이 잘 정리되어 있어, 실무에 빠르게 적용할 수 있습니다.
  • 공유 메모리 기반의 고성능 IPC를 구현하고자 한다면, 이 라이브러리는 매우 훌륭한 선택이 될 수 있습니다.



  • 도움이 되셨으면 하단의 ❤️ 공감 버튼 부탁 드립니다. 감사합니다! 😄

728x90
반응형
728x90
반응형

C++ 템플릿(template) 문법: typenametemplate 키워드 사용 요약

C++ 템플릿 문법, 왜 헷갈릴까요?

  • C++ 템플릿을 사용하다 보면 typenametemplate 키워드의 정확한 사용법에서 자주 혼란을 겪게 됩니다.
  • 특히 중첩된 템플릿이나 종속 타입을 다룰 때는 문법적인 실수로 인해 컴파일 에러가 발생하기 쉬습니다.
  • 이 문서에서는 그런 혼동을 줄이기 위해 typenametemplate 키워드가 각각 언제 필요한지 간결한 표와 실습 예제를 통해 정리하였습니다.
  • 실전 코드 작성 중 만나는 다양한 상황에 대비해 유용하게 참고하시기 바랍니다.


1. typename vs template 요약표

  • 상황 예시 typename 필요 여부 template 키워드 필요 여부 설명
    종속 타입 이름 사용 typename T::value_type ✅ 필요 ❌ 불필요 T에 따라 달라지는 타입이므로 typename 필요
    비종속 타입 이름 사용 std::vector<int>::value_type ❌ 불필요 ❌ 불필요 T 같은 템플릿 인자에 의존하지 않음
    중첩된 템플릿 사용 T::template rebind<U>::other ✅ 필요 ✅ 필요 T가 템플릿이며, 내부에 또 다른 템플릿이 있을 경우
    템플릿이 아닌 멤버 접근 T::some_function() ❌ 불필요 ❌ 불필요 함수 호출이므로 typename, template 모두 불필요
    템플릿 멤버 함수 호출 ptr->template foo<T>() ❌ 불필요 ✅ 필요 foo가 템플릿 함수일 때 template 필요


2. 예시 비교

2.1. 올바른 사용

  • cpp

      template <typename T>
      void func() {
        typename T::value_type x;                  // 종속 타입 이름 → typename 필요
        typename T::template rebind<int>::other y; // 중첩 템플릿 → typename + template 필요
      }
    

2.2. 잘못된 사용

  • cpp

      template <typename T>
      void func() {
          T::value_type x;         // ❌ 컴파일 오류 → typename 필요
          T::rebind<int>::other y; // ❌ 컴파일 오류 → template 키워드 필요
      }
    


3. 기억 공식

  • T::X타입(type) 이면 → typename 필요

  • T::template Y<...> 처럼 템플릿 안의 템플릿(template)이면 → template 키워드 필요



4. 실습 예제 1: typename이 필요한 경우

  • cpp

      #include <iostream>
      #include <vector>
      
      template <typename T>
      void show_value_type() {
          // T::value_type은 종속 타입 이름 → typename 필요
          typename T::value_type val = 0;
     	 
          std::cout << "value_type: " << val << "\n";
      }
      
      int main() {
          show_value_type< std::vector<int> >();  // int로 출력됨
      }
    

4.1. typename을 생략한 잘못된 예

  • cpp

      // ❌ 컴파일 에러: 'T::value_type' is not a type
      T::value_type val = 0;
    


5. 실습 예제 2: template 키워드가 필요한 경우

  • cpp

      #include <iostream>
      
      template <typename T>
      struct Wrapper {
          template <typename U>
          struct Rebind {
              using type = Wrapper<U>;
          };
      };
      
      template <typename T>
      void use_rebind() {
          // T::template Rebind<U>::type → 중첩 템플릿 사용 → template 키워드 필요
          typename T::template Rebind<int>::type obj;
     	 
          std::cout << "Rebind succeeded\n";
      }
      
      int main() {
          use_rebind<Wrapper<double>>();
      }
    

5.1. template 키워드를 생략한 잘못된 예

  • cpp

      // ❌ 컴파일 에러: 'Rebind' is not a template
      typename T::Rebind<int>::type obj;
    


6. 실습 예제 3: 템플릿 멤버 함수 호출 시 template 필요

  • cpp

      #include <iostream>
      
      struct Example {
          template <typename T>
          void foo() {
              std::cout << "foo<T>() called\n";
          }
      };
      
      template <typename T>
      void call_foo(T* obj) {
          // 템플릿 멤버 함수 호출 시 → template 필요
          obj->template foo<int>();
      }
      
      int main() {
          Example e;
          call_foo(&e);
      }
    

6.1. template 키워드를 생략한 잘못된 예

  • cpp

      // ❌ 컴파일 에러: ‘foo’ is not a template
      obj->foo<int>();
    


7. 컴파일 실습 팁

  • 위 예제들은 하나씩 복사해서 주석을 바꿔보며 실습하면 효과적입니다.
  • GCC, Clang, MSVC 모두에서 동일한 문법 규칙이 적용됩니다.
  • C++ 표준은 typenametemplate을 생략하지 않도록 명시하고 있습니다.



  • 도움이 되셨으면 하단의 ❤️ 공감 버튼 부탁 드립니다. 감사합니다! 😄

728x90
반응형
728x90
반응형

고성능 C++ 해시맵(HashMap) 라이브러리 : emhash


1. ✅ 개요

  • emhashC++용 고성능 헤더 전용 해시맵/해시셋 라이브러리입니다.

  • std::unordered_map 보다 빠르고 메모리 효율적인 대안을 제공하며, 다양한 최적화 구현체가 포함되어 있어 성능 및 용도에 따라 선택할 수 있습니다.

  • 주요 특징:

    • 헤더 전용 (header-only)
    • 고속 삽입/탐색/삭제
    • 낮은 메모리 사용량
    • 높은 부하 계수 (load factor) 지원
    • 다양한 환경(Windows/Linux/Mac, GCC/Clang/MSVC)에서의 호환성


2. 🚀 대표 기능

  • insert_unique: 중복 확인 없이 삽입
  • shrink_to_fit: 사용하지 않는 버킷 제거로 메모리 절약
  • reserve: 초기 해시맵 크기 설정으로 리사이징 최소화
  • 다양한 버전(emhash1~emhash8)의 해시맵 구현 제공


3. 🧪 예제 코드

  • 아래는 emhash8::HashMap을 사용하는 간단한 예제입니다:

  • cpp

      // 헤더 파일 포함
      #include "emhash8.h" // 또는 "emhash7.h" 등 다른 버전 사용 가능
      #include <iostream>
      
      int main() {
          emhash8::HashMap<int, std::string> map;
      
          // 값 삽입
          map[1] = "apple";
          map[2] = "banana";
          map[3] = "cherry";
      
          // 값 접근
          std::cout << "map[2] = " << map[2] << std::endl;
      
          // 반복문을 통한 전체 출력
          for (const auto& kv : map) {
              std::cout << kv.first << ": " << kv.second << std::endl;
          }
      
          // 요소 존재 확인
          if (map.find(3) != map.end()) {
              std::cout << "키 3은 존재합니다." << std::endl;
          }
      
          // 요소 삭제
          map.erase(1);
      
          // 전체 요소 수 출력
          std::cout << "남은 요소 수: " << map.size() << std::endl;
      
          return 0;
      }
    


4. 📊 성능

  • 외부 벤치마크에 따르면 emhashGoogledense_hash_map, absl::flat_hash_map, robin_hood::unordered_flat_map 등과 비교해 삽입/탐색/삭제 성능에서 우수한 결과 를 보였습니다.

  • 특히 반복(iteration) 속도와 부하 계수(load factor) 면에서 탁월한 성능을 자랑합니다.

  • 참고: 공식 벤치마크 결과



5. 🧩 설치 및 사용

  • (1) emhash GitHub 저장소에서 소스 다운로드
  • (2) 원하는 구현 버전의 헤더 파일 포함
    • 예: #include "emhash8.h"
  • (3) 컴파일 시 최적화 플래그를 적용하면 성능이 향상됨
    • -O2, -O3, -march=native


6. ✅ 정리

  • emhash는 고성능을 요구하는 C++ 애플리케이션에서 std::unordered_map의 대체재로 고려할 수 있습니다.
  • 헤더 전용이므로 프로젝트에 쉽게 통합할 수 있으며, 다양한 환경에서 안정적인 작동을 보장합니다.
  • 필요에 따라 emhash1부터 emhash8까지 구현체를 시험해보며 가장 적합한 성능 조합을 선택해보시길 권장드립니다.



  • 도움이 되셨으면 하단의 ❤️ 공감 버튼 부탁 드립니다. 감사합니다! 😄

728x90
반응형
728x90
반응형

VSCode 확장(extensions) 개발 : TypeScript.vsix 만들기 가이드


1. 개발 환경 준비

  • VS Code 확장을 개발하려면 다음 도구가 필요합니다:
    • Node.js 설치
    • Visual Studio Code 설치
    • 확장 개발 도구 설치:
      • bash

          npm install -g yo generator-code @vscode/vsce
        
      • ⚠️ 오류 발생 시 --force 옵션을 사용할 수 있습니다:

          npm install -g typescript --force
        


2. TypeScript 설치 및 설정

2.1. 전역 설치 (Global)

  • bash

      npm install -g typescript
    

  • 설치 확인:
    • bash

      tsc --version
    

2.2. 로컬 설치 (프로젝트용)

  • bash

      npm install --save-dev typescript
    

2.3. tsconfig.json 초기화

  • bash

      npx tsc --init
    

  • 샘플 설정:
  • json

      {
        "compilerOptions": {
          "target": "ES2020",
          "module": "commonjs",
          "outDir": "dist",
          "rootDir": "src",
          "strict": true
        }
      }
    


3. 확장 템플릿 생성

  • bash

      npx yo code
    

  • 언어: TypeScript 선택
  • 확장 이름, 설명 등 입력
  • 생성된 디렉터리로 이동 후 의존성 설치:
    • bash

        npm install
      


4. 확장 코드 작성 예시

4.1. 디렉터리 구조

  • dir

      my-extension/
      ├── package.json
      ├── tsconfig.json
      ├── src/
      │   └── extension.ts
    

4.2. package.json

  • json

      {
        "name": "my-extension",
        "displayName": "My Extension",
        "publisher": "your-name",
        "version": "0.0.1",
        "engines": {
          "vscode": "^1.70.0"
        },
        "activationEvents": ["onCommand:my-extension.hello"],
        "main": "./dist/extension.js",
        "contributes": {
          "commands": [
            {
              "command": "my-extension.hello",
              "title": "Hello World"
            }
          ]
        },
        "scripts": {
          "vscode:prepublish": "npm run compile",
          "compile": "tsc -p ./",
          "watch": "tsc -w -p ./"
        },
        "devDependencies": {
          "@types/vscode": "^1.70.0",
          "typescript": "^4.7.4",
          "vscode-test": "^1.6.2"
        }
      }
    

4.3. src/extension.ts

  • ts

      import * as vscode from 'vscode';
      
      function sayHello() {
        vscode.window.showInformationMessage('👋 안녕하세요!');
      }
      
      function sayBye() {
        vscode.window.showInformationMessage('👋 안녕히 가세요!');
      }
      
      export function activate(context: vscode.ExtensionContext) {
        const helloCmd = vscode.commands.registerCommand('my-extension.hello', sayHello);
        const byeCmd = vscode.commands.registerCommand('my-extension.bye', sayBye);
        context.subscriptions.push(helloCmd, byeCmd);
      
        return {
          greet: sayHello,
          farewell: sayBye
        };
      }
      
      export function deactivate() {
        console.log("🛑 확장이 비활성화되었습니다.");
      }
    

  • ⚠️ main 항목은 반드시 컴파일된 JS 경로(dist/extension.js) 를 가리켜야 합니다.
    .ts 파일을 직접 실행할 수는 없습니다.



5. 빌드 및 패키징

5.1 컴파일

  • bash

      npm run compile
    
  • src/extension.tsdist/extension.js로 변환됩니다.


5.2 .vsix 패키지 만들기

  • bash

      vsce package
    
  • my-extension-0.0.1.vsix 파일 생성


5.3 로컬 설치 (테스트용)

  • bash

      code --install-extension my-extension-0.0.1.vsix
    
  • 또는 VS Code에서 Extensions: Install from VSIX... 명령을 사용하여 설치합니다.



6. 외부 확장에서 API 호출

  • activate() 함수에서 객체를 반환하면, 다른 확장에서 다음과 같이 사용할 수 있습니다:

  • ts

      const ext = vscode.extensions.getExtension('your-name.my-extension');
      const api = await ext.activate();
      
      api.greet();    // sayHello() 호출
      api.farewell(); // sayBye() 호출
    


7. 정리

  • 이 가이드를 통해 TypeScript 기반의 VS Code 확장을 생성하고 .vsix 파일로 패키징하는 과정을 간단히 소개하였습니다.
  • 직접 확장을 배포하거나, 팀 내에서 배포 전 테스트용으로 사용하실 수 있습니다. 🚀 👽



  • 도움이 되셨으면 하단의 ❤️ 공감 버튼 부탁 드립니다. 감사합니다! 😄

728x90
반응형

+ Recent posts