728x90
반응형
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
반응형
728x90
반응형

GitHub CLI (gh) 설치 가이드


  • (1) Windows에서 설치
    • 1.1. Winget 사용 (추천)
      • 명령 프롬프트(cmd)나 PowerShell에서 다음 명령어 실행:
      • bash

          winget install --id GitHub.cli
        
    • 1.2. Scoop 사용
      • bash

          scoop install gh
        
    • 1.3. 설치 파일 수동 다운로드


  • (2) macOS에서 설치
    • 2.1. Homebrew 사용 (추천)
      • bash

          brew install gh
        
    • 2.2. MacPorts 사용
      • bash

          sudo port install gh
        


  • (3) Linux에서 설치
    • 3.1. Ubuntu / Debian 계열
      • bash

          sudo apt update  
          sudo apt install gh
        
    • 3.2. Fedora
      • bash

          sudo dnf install gh
        
    • 3.3. CentOS / RHEL
      • bash

          sudo dnf install 'https://github.com/cli/cli/releases/download/v2.0.0/gh_2.0.0_linux_amd64.rpm'
        
      • ※ 위 URL은 예시이며, 실제 버전은 최신 릴리즈 페이지에서 확인하여 교체해 주세요.


  • (4) 설치 확인 방법
    • 설치가 완료되면 터미널에서 다음 명령어로 정상 설치 여부를 확인할 수 있습니다:
    • bash

        gh --version
      



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

728x90
반응형
728x90
반응형

C++ 버전 별 템플릿 타입 유추(Deduction Guide) 기능 구현 비교


1. 개요

  • 이 글에서는 C++98부터 C++20까지 다양한 C++ 표준 버전에서 템플릿 타입 유추(Deduction Guide) 기능을 어떻게 구현할 수 있는지를 비교합니다.
  • 특히 "const char*"이 입력되었을 때 내부적으로 std::string으로 처리되도록 하는 Holder 클래스 예제를 기준으로 각 버전별 코드를 설명합니다.


2. C++98: 템플릿 특수화와 오버로딩으로 구현

  • cpp

      #include <string>
      #include <iostream>
      
      template<typename T>
      class Holder {
      public:
          Holder(T value) {
              std::cout << "Generic Holder: " << value << std::endl;
          }
      };
      
      template<>
      class Holder<std::string> {
      public:
          Holder(const char* value) {
              std::cout << "Specialized Holder<std::string>: " << value << std::endl;
          }
      };
      
      int main() {
          Holder<int> h1(42);
          Holder<std::string> h2("hello");  // 직접 타입 명시 필요
      }
    

  • 자동 유추 기능이 없기 때문에 사용자가 명시적으로 타입을 지정해야 합니다.


3. C++11 / C++14: 헬퍼 함수로 타입 유추 흉내내기

  • cpp

      #include <string>
      #include <iostream>
      
      template<typename T>
      class Holder {
      public:
          Holder(T value) {
              std::cout << "Generic Holder: " << value << std::endl;
          }
      };
      
      // 헬퍼 함수 제작
      
      template<typename T>
      Holder<T> make_holder(T value) {
          return Holder<T>(value);
      }
      
      Holder<std::string> make_holder(const char* value) {
          return Holder<std::string>(std::string(value));
      }
      
      int main() {
          auto h1 = make_holder(42);         // Holder<int>
          auto h2 = make_holder("hello");    // Holder<std::string>
      }
    

  • make_holder()와 같은 헬퍼 함수를 통해 Deduction Guide와 유사한 효과를 낼 수 있습니다.
  • 단, 일반 생성자 방식(Holder h(...))은 사용할 수 없습니다.


4. C++17: 공식 Deduction Guide 도입

  • cpp

      #include <string>
      #include <iostream>
      
      template<typename T>
      class Holder {
      public:
          Holder(T value) {
              std::cout << "Holder<" << typeid(T).name() << ">: " << value << std::endl;
          }
      };
      
      Holder(const char*) -> Holder<std::string>; // Deduction Guide 
      
      int main() {
          Holder h1(42);         // Holder<int>
          Holder h2("hello");    // Holder<std::string>
      }
    

  • 생성자 인자를 기반으로 자동 템플릿 타입 유추가 가능합니다.


5. C++20: requiresconcepts를 통한 조건 기반 유도

  • cpp

      #include <string>
      #include <iostream>
      #include <concepts>
      
      template<typename T>
      class Holder {
      public:
          Holder(T value) {
              std::cout << "Holder<" << typeid(T).name() << ">: " << value << std::endl;
          }
      };
      
      Holder(const char*) -> Holder<std::string>;
      
      template<std::integral T>
      Holder(T) -> Holder<int>;
      
      template<std::floating_point T>
      Holder(T) -> Holder<double>;
      
      int main() {
          Holder h1("hello");   // Holder<std::string>
          Holder h2(10);        // Holder<int>
          Holder h3(3.14f);     // Holder<double>
      }
    

  • requires 또는 concepts를 활용하여 조건에 따라 다른 타입으로 유도할 수 있습니다.


6. 정리

  • 기능 C++98 C++11/14 C++17 C++20
    템플릿 자동 유추 🔸 함수 통해 우회 ✅ 생성자 인자 기반 유도 ✅ + 조건 분기 가능
    타입 유추 (const char*std::string) 수동 특수화 make_* 함수 Deduction Guide Deduction Guide + requires
    코드 간결성 ❌ 복잡 🔸 함수 우회 ✅ 깔끔 ✅ 더 정교함
  • C++ 버전 별로 타입 유추 기능은 계속 진화 중입니다. 😕 😵 🤔 🚀 👽




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

728x90
반응형
728x90
반응형

C++20 컴파일 타임 함수 : constevalconstinit


1. consteval — 컴파일 타임 전용 함수

1.1 정의

  • consteval함수가 반드시 컴파일 타임에만 실행되어야 함 을 나타내는 키워드입니다.
  • constexpr보다 더 엄격하며, 런타임 호출은 허용되지 않습니다.

1.2 사용 예시

  • cpp

      consteval int square(int x) { // 컴파일 타임 함수
          return x * x;
      }
      
      constexpr int value = square(5); // ✅ 가능: 컴파일 타임 평가
      int runtime = square(5);         // ❌ 오류: 런타임 호출 불가
    


2. constinit — 정적 변수의 컴파일 타임 초기화 보장

2.1 정의

  • constinit은 **정적 저장 기간(static storage duration)**을 가진 변수에 대해, 컴파일 타임에 초기화가 반드시 일어나야 함을 보장합니다.

  • 런타임 초기화를 막아 **정적 초기화 순서 문제(static initialization order fiasco)**를 방지할 수 있습니다.


2.2 사용 예시

  • cpp

      constinit int global_value = 42; // ✅ 컴파일 타임 초기화됨
      
      // 아래는 오류
      // constinit int another = std::rand(); // ❌ rand()는 런타임 평가
    


3. C++20 consteval vs constinit 비교

  • 항목 consteval constinit
    용도 컴파일 타임 전용 함수 정의 정적 변수의 초기화 시점 명시
    적용 대상 함수 정적 저장 기간을 가진 변수
    평가 시점 컴파일 타임에 반드시 호출 컴파일 타임에 반드시 초기화
    런타임 사용 ❌ 불가 ✅ 변수 사용은 가능 (단 초기화는 컴파일 타임)
    대표 장점 컴파일 타임 계산 강제 초기화 순서 오류 방지



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

728x90
반응형
728x90
반응형

C++ SFINAE(Substitution Failure Is Not An Error) : 템플릿 조건부 제어를 위한 핵심 기법

  • C++SFINAE(Substitution Failure Is Not An Error)템플릿 메타프로그래밍(template meta programming)의 핵심 개념 중 하나 입니다.
  • 이는 C++에서 템플릿 인자 치환(substitution) 과정에서 오류가 발생하더라도 그 자체로 컴파일 에러가 되지 않도록 허용 하는 규칙입니다.
  • 대신 컴파일러는 다른 오버로드 후보를 탐색하거나, 템플릿 인스턴스화를 포기하게 됩니다.


1. 핵심 개념 요약

  • 템플릿 인자 대체 시 오류가 발생하면, 컴파일러는 해당 함수 템플릿을 무시하고 다른 후보를 계속 찾는다.

  • 이 규칙 덕분에 다양한 조건부 템플릿 코드 작성이 가능해졌습니다.



2. 간단한 예제

  • cpp

      #include <type_traits>
      #include <iostream>
      
      template<typename T>
      typename std::enable_if<std::is_integral<T>::value, void>::type
      func(T t) {
          std::cout << "정수 타입입니다: " << t << "\n";
      }
      
      template<typename T>
      typename std::enable_if<std::is_floating_point<T>::value, void>::type
      func(T t) {
          std::cout << "실수 타입입니다: " << t << "\n";
      }
    

2.1. 사용 예:

  • cpp

      func(42);      // 정수 타입입니다
      func(3.14);    // 실수 타입입니다
      // func("hello"); → 컴파일 에러 (문자열에 대한 적절한 템플릿 없음)
    

  • 여기서 std::enable_ifbool 조건이 true일 경우에만 타입을 생성하게 하며, false이면 치환 실패(Substitution Failure) 가 발생해 그 오버로드는 무시됩니다.


3. SFINAE 없이 발생할 수 있는 문제

  • 템플릿 인자에 따라 조건부 코드 작성을 하고 싶지만, 타입이 맞지 않아 에러가 나버리면 전체 템플릿 인스턴스가 실패하게 됩니다.
  • SFINAE는 이러한 조건부 인스턴스화를 가능하게 해줍니다.


4. C++20 이후에는 Concepts로 대체 가능

  • C++20부터는 SFINAE 대신 Conceptsrequires 표현식 을 사용하는 것이 더 명확하고 간결합니다.

  • cpp

      template<typename T>
      requires std::integral<T>
      void func(T t) {
          std::cout << "정수 타입입니다: " << t << "\n";
      }
    


5. 자주 사용하는 SFINAE 도구

  • 도구 설명
    std::enable_if 조건에 따라 타입을 정의/배제
    std::is_same 두 타입이 같은지 비교
    std::is_base_of 상속 관계 확인
    decltype(...) + void_t 표현식이 유효한지 검증하는 패턴



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

728x90
반응형
728x90
반응형

AIQt 개발을 더 스마트하게 – QodeAssist 소개 및 설치 가이드


  • 대규모 언어 모델(LLM)을 활용하여 코드 자동 완성, 생성, 리팩토링, 문서화 등의 기능을 제공함으로써 Qt 개발자들의 생산성을 대폭 향상시켜 줍니다.
  • 현재는 OpenAI API를 지원하며, 앞으로 다양한 AI 모델과의 통합도 계획하고 있습니다.


1. 주요 기능

  • 코드 자동 완성 : 현재 작성 중인 코드의 맥락을 파악하여 자연스럽고 유용한 코드 제안을 제공합니다.
  • 코드 생성 : 주석 또는 자연어 설명을 기반으로 코드 스니펫을 자동 생성합니다.
  • 코드 리팩토링 : 기존 코드의 구조를 분석하고 개선 방향을 제안하거나 자동 리팩토링을 실행합니다.
  • 자동 문서화 : 함수나 클래스에 대한 주석을 생성하여 코드 가독성과 유지 보수성을 높입니다.


2. 설치 방법

2.1. QodeAssist 소스 코드 다운로드

  • bash

      git clone https://github.com/Palm1r/QodeAssist.git
    

2.2. Qt Creator에서 플러그인 빌드 및 설치

  • Qt CreatorQodeAssist 프로젝트를 엽니다.

  • 프로젝트를 빌드한 후, 생성된 플러그인 파일(.so, .dll 등)을 Qt Creator의 플러그인 디렉토리에 복사합니다.

  • Qt Creator를 재시작합니다.


2.3. 플러그인 활성화

  • 메뉴에서 [도구] → [플러그인] 으로 이동한 후, QodeAssist 플러그인을 활성화합니다.


3. 지원 및 권장 버전

  • QodeAssistQt Creator의 버전에 따라 호환되는 플러그인 버전이 다릅니다:

  • Qt Creator 버전 QodeAssist 호환 버전
    16.0.0 0.5.2 이상
    15.0.1 0.4.8 ~ 0.5.1
    15.0.0 0.4.0 ~ 0.4.7
    14.0.2 0.2.3 ~ 0.3.x
    14.0.1 이하 0.2.2 이하





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

728x90
반응형

+ Recent posts