Skip to content

Latest commit

 

History

History
428 lines (324 loc) · 24.6 KB

File metadata and controls

428 lines (324 loc) · 24.6 KB

AGENTS.md - CCTL (C Template Library) 개발 및 유지보수 가이드

이 문서는 CCTL(C Template Library) 프로젝트의 아키텍처, 설계 의도, 매크로 메타프로그래밍 규칙, 코딩 컨벤션 및 개발 지침을 AI 에이전트와 기여자를 위해 정리한 기술 가이드입니다.


1. 프로젝트 개요 및 설계 철학

1.1 프로젝트 목적

CCTL최신 C 언어 표준(C23 지향) 환경에서 C++의 STL(Standard Template Library)과 유사한 타입 안전(Type-Safe) 제네릭 자료구조 및 컨테이너를 제공하는 템플릿 라이브러리입니다.

1.2 C 언어 표준 정책 (C Standard Policy: Modern C & C23+ Focused)

  • 최신 C 표준 지향 (Targeting C23 & Future Standards): CCTL은 ISO/IEC 9899:2024(C23)을 기본 설계 및 빌드 타깃으로 지향하며, 향후 제정되는 최신 C 표준 사양을 적극 수용합니다.
  • 최소 요구 표준 및 컴파일러 플래그:
    • C11 _Generic 디스패처, <uchar.h>, <stdbool.h>, <stdint.h>를 기반으로 동작하므로 최소 C11 이상의 컴파일러가 필요하며, 기본 권장 표준은 **C23 (-std=c23 / /std:clatest)**입니다.
    • C23 미지원 컴파일러 대응 (-std=c2x): 사용 중인 컴파일러 버전(예: GCC 913, Clang 917 등)이 아직 -std=c23 옵션을 인식하지 못할 경우, C23 드래프트 임시 명칭인 -std=c2x 플래그를 대체 사용해야 합니다.
  • 레거시 C 문법 지양: 타입 안전성이 결여된 구형 C90/C99 방식이나 void* 만능 캐스팅 대신, 최신 C 표준이 제공하는 강력한 컴파일 타임 기능과 매크로 메타프로그래밍을 조합하여 제로 오버헤드를 달성합니다.

1.3 핵심 설계 원칙

  1. 순수 모던 C 기반 제네릭 구현 (Pure Modern C Generic Metaprogramming)
    • C++ 컴파일러나 런타임 의존성 없이 표준 C 전처리기(Preprocessor)와 _Generic 문법만을 활용하여 컴파일 타임 타입 검사 및 전용 함수를 생성합니다.
  2. 헤더/소스 분리형 인스턴스화 모델 (Separation of Declaration & Implementation)
    • 단순 매크로 치환 방식의 중복 심볼(ODR 위반) 및 코드 블로트(Code Bloat)를 방지하기 위해, 전방 선언(_fd), 헤더 선언(_imp_h), 소스 구현(_imp_c)으로 템플릿 인스턴스화 단계를 엄격히 분리합니다.
  3. 제로 런타임 오버헤드 (Zero Runtime Overhead & Inline Optimization)
    • void* 간접 참조를 최소화하고, 타입별 전용 구조체와 인라인 함수(static inline)를 생성하여 최적의 성능과 캐시 지역성을 보장합니다.
  4. 일관된 API 및 이터레이터 인터페이스 (Uniform Container & Iterator Protocol)
    • 모든 컨테이너는 공통된 수명주기 함수(init, free, clear)와 표준 이터레이터(begin, rbegin, next, prev, foreach, rforeach) 인터페이스를 따릅니다.

2. 프로젝트 아키텍처 및 파일 구조

2.1 디렉터리 구성

cctl/
├── cctl.h          # 핵심 전처리기 매크로 (토큰 연결, 이름 맹글링, 포인터/문자열 기본 타입 정의)
├── utils.h         # C11/C23 _Generic 기반 기본 비교/복사/해제 함수 디스패처 및 메모리 유틸리티
├── hasher.h        # 32비트 FNV-1a, MurmurHash3, SipHash 알고리즘 및 _Generic 해시 디스패처
├── vector.h        # 동적 배열 (Dynamic Array)
├── deque.h         # 청크 기반 양방향 큐 (Double-Ended Queue)
├── list.h          # 이중 연결 리스트 (Doubly Linked List)
├── heapq.h         # 2진 힙 기반 우선순위 큐 (Priority Queue: Min/Max Heap)
├── rbt.h           # 레드-블랙 트리 (Red-Black Tree, 정렬된 Key-Value 맵)
├── hashmap.h       # Open Addressing 선형 탐색 해시맵 (Key-Value Map)
├── trie.h          # Radix-256 트라이 (문자열 Prefix 트리)
├── critbit.h       # DJB Crit-Bit 트리 (문자열 Key-Value 맵)
├── optional.h      # 널 가능 값 래퍼 (Optional/Nullable Value Container)
├── result.h        # 성공/실패 결과 래퍼 (Result/Expected Value Container)
├── str.h           # 동적 기본 문자열 컨테이너 (basic_string)
├── test/           # 단위 테스트 및 검증 코드 디렉터리
├── LICENSE.txt     # MIT 라이선스
├── README.md       # 사용자 매뉴얼 및 컨테이너 API 명세
└── AGENTS.md       # AI 에이전트 및 기여자용 아키텍처/개발 가이드

2.2 모듈 간 의존성 관계

graph TD
	cctl_h["cctl.h (Base Macros & Types)"] --> utils_h["utils.h (Memory & Generic Dispatch)"]
	cctl_h --> vector_h["vector.h"]
	cctl_h --> deque_h["deque.h"]
	cctl_h --> list_h["list.h"]
	cctl_h --> optional_h["optional.h"]
	cctl_h --> result_h["result.h"]
	cctl_h --> str_h["str.h"]
	
	utils_h --> hasher_h["hasher.h (Hash Algorithms)"]
	utils_h --> heapq_h["heapq.h"]
	utils_h --> rbt_h["rbt.h"]
	
	hasher_h --> hashmap_h["hashmap.h"]
	cctl_h --> trie_h["trie.h"]
	cctl_h --> critbit_h["critbit.h"]
Loading

3. 매크로 메타프로그래밍 및 인스턴스화 규칙

3.1 식별자 이름 맹글링 (Name Mangling)

CCTL은 전처리기 토큰 연결(##)을 통해 타입별 전용 심볼을 생성합니다.

  • 단일 타입 컨테이너: cctl_join(T, NAME)<T>_<NAME>
    • 예: vector(int)int_vector
    • 예: vector_iterator(int)int_vector_iterator
    • 예: vector_func(push_back, int)int_vector_push_back
    • 예: critbit(int)int_critbit
    • 예: critbit_func(insert, int)int_critbit_insert
  • 다중 타입(Key-Value) 컨테이너: cctl_join3(K, V, NAME)<K>_<V>_<NAME>
    • 예: hashmap(cstring, int)cstring_int_hashmap
    • 예: rbt(int, double)int_double_rbt
    • 예: hashmap_func(insert, cstring, int)cstring_int_hashmap_insert

3.2 3단계 인스턴스화 모델 (Three-Stage Instantiation Pattern)

다중 파일 프로젝트에서 컴파일 오류와 중복 정의(Symbol Duplication)를 피하기 위해 다음 패턴을 준수해야 합니다.

매크로 단계 역할 위치 예시
*_fd(...) 불완전 타입/구조체 전방 선언 (Forward Declaration) 헤더 (.h) vector_fd(int);
*_imp_h(...) 구조체 정의 + 함수 프로토타입 선언 헤더 (.h) vector_imp_h(int);
*_imp_c(...) 함수 본문 구현 (1개 번역 단위에만 작성) 소스 (.c) vector_imp_c(int);

사용 예시 (CCTL 표준 패턴)

  • cctl_define.h (헤더 파일)

     #pragma once
     #include "cctl/vector.h"
     #include "cctl/hashmap.h"
    
     vector_fd(int);
     vector_imp_h(int);
    
     hashmap_fd(cstring, int);
     hashmap_imp_h(cstring, int);
  • cctl_define.c (구현 소스 파일)

     #include "cctl_define.h"
    
     vector_imp_c(int);
     hashmap_imp_c(cstring, int);
  • main.c (사용자 코드)

     #include <stdio.h>
     #include "cctl_define.h"
    
     int main(void) {
     	vector(int) v;
     	vector_init(int, &v);
     	vector_push_back(int, &v, 42);
     	printf("Value: %d\n", *vector_at(int, &v, 0));
     	vector_free(int, &v);
     	return 0;
     }

3.3 포인터 및 특수 타입 처리 규약

C 전처리기는 토큰 연결 시 * 문자를 사용할 수 없습니다 (int*##_vector 불가). 포인터 타입을 컨테이너에 담으려면 반드시 사전 typedef를 거쳐야 합니다.

  • 포인터 타입 정의 매크로:

     // cctl_ptr_def(T) -> typedef T* T_ptr;
     cctl_ptr_def(MyStruct); 
     
     vector_fd(cctl_ptr(MyStruct));
     vector_imp_h(cctl_ptr(MyStruct));
  • 기본 제공 문자열 타입 별칭:

    • cstringchar* (Null-terminated C String)
    • u16cstringchar16_t* (UTF-16 String)
    • u32cstringchar32_t* (UTF-32 String)

3.4 컨테이너 생성, 초기화 및 가변 인자 디스패치 패턴

CCTL은 다양한 선언 및 초기화 방식을 통일된 패턴으로 제공합니다.

  1. 기본 초기화 (*_init):
    • 포인터와 크기 필드를 0/NULL 상태로 리셋하는 순수 void 함수입니다.
    • 힙 메모리를 할당하지 않으므로 항상 성공하며 free 내부에서도 재호출되어 자원을 리셋합니다.
    • 예: vector_init(int, &v);
  2. 값 반환형 생성자 (*_new):
    • 초기화된 컨테이너 구조체를 값으로 직접 반환하여 선언과 동시에 대입할 수 있습니다.
    • 시퀀스 컨테이너(vector, deque, list, str)는 cctl_dispatch를 통해 크기 인자(size)를 선택적으로 전달할 수 있습니다.
    • 예: vector(int) v1 = vector_new(int); (빈 벡터 생성)
    • 예: vector(int) v2 = vector_new(int, 10); (10개 원소가 0으로 초기화된 벡터 생성)
  3. 리터럴 초기화 및 확장 (*_from_items, *_extend_items):
    • C99 복합 리터럴(Compound Literal)과 sizeof를 활용하여 가변 인자로 항목을 전달받아 컨테이너를 생성하거나 벌크 추가합니다.
    • 예: vector(int) v = vector_from_items(int, 1, 2, 3, 4, 5);
    • 예: vector_extend_items(int, &v, 6, 7, 8); (추가 성공 시 true, 실패 시 false 반환)
  4. OOM 할당 실패 시 안전 롤백 (Rollback to Empty State):
    • new(size)from_items(...) 실행 중 내부 메모리 할당(resize, extend_items 등)이 실패하면, 이미 부분 할당되었던 메모리를 즉시 내부에서 free하고 **완전한 빈 상태(size = 0, NULL)**로 롤백하여 반환합니다. 이를 통해 메모리 누수를 100% 방지하고 더블 프리로부터 안전합니다.
  5. 가변 인자 매크로 디스패처 (cctl_va_count, cctl_dispatch):
    • cctl.h에 내장된 0~64개 가변 인자 카운터 매크로를 통해 인자 개수에 따라 적절한 접미사 매크로(BASE_N)로 컴파일 타임 분기합니다.

4. 제네릭 디스패치 및 메모리 수명주기 (utils.h, hasher.h)

4.1 C11/C23 _Generic 기본 함수 디스패치

CCTL은 내장 기본 타입 및 문자열 타입에 대해 컴파일 타임에 자동으로 기본 함수 포인터를 바인딩합니다.

  • 지원되는 기본 타입: int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t, uint64_t, float, double, cstring, u16cstring, u32cstring.
  • 제공되는 디스패처 매크로:
    • cctl_get_default_compare_func(T): <> 연산 기반 비교 함수 (문자열은 strcmp / 메모리 비교).
    • cctl_get_default_copy_func(T): 기본 값 복사 (문자열은 힙 동적 할당 기반 Deep Copy).
    • cctl_get_default_free_func(T): 기본 타입은 no-op, 문자열은 free() 호출.
    • cctl_get_default_hash_func(T): 32비트 MurmurHash3 기반 해시 함수 반환.

4.2 커스텀 타입 훅 (Custom Type Callbacks)

사용자 정의 구조체나 복합 포인터 타입을 hashmap, rbt, heapq 등에서 사용할 때는 초기화 후 명시적으로 함수 포인터를 등록해야 합니다:

hashmap_init(MyKey, MyVal, &hm);
hashmap_set_hash_func(MyKey, MyVal, &hm, my_key_hash_fn);
hashmap_set_compare_func(MyKey, MyVal, &hm, my_key_compare_fn);
hashmap_set_copy_func(MyKey, MyVal, &hm, my_key_copy_fn);
hashmap_set_free_func(MyKey, MyVal, &hm, my_key_free_fn);

5. 공통 이터레이터 프로토콜 (Iterator Protocol)

모든 이터레이터 지원 컨테이너(vector, deque, list, rbt, hashmap, trie, critbit)는 표준 이터레이터 규격을 준수합니다.

5.1 이터레이터 함수 및 매크로

  • *_begin(T, p_c): 첫 번째 유효 요소를 가리키는 이터레이터 반환.
  • *_end(T, p_c): 마지막 요소의 다음(past-the-end / 센티넬)을 가리키는 이터레이터 반환.
  • *_rbegin(T, p_c): 역방향 첫 번째(마지막) 유효 요소를 가리키는 이터레이터 반환.
  • *_rend(T, p_c): 역방향 마지막 요소의 이전(before-the-first / 센티넬)을 가리키는 이터레이터 반환.
  • *_iterator_is_valid(T, p_it): 현재 이터레이터가 유효한 요소를 가리키는지 확인 (bool).
  • *_iterator_is_equal(T, p_it1, p_it2): 두 이터레이터가 동일한 컨테이너 및 위치/노드를 가리키는지 확인 (bool).
  • *_iterator_get(T, p_it): 현재 요소의 포인터 반환 (T* 또는 pair*).
  • *_iterator_get_key(T, p_it): 현재 요소의 키를 새로 동적 할당(malloc) 및 복사하여 반환 (호출자가 free).
  • *_iterator_get_key_ref(T, p_it): 내부적으로 키를 보관하는 컨테이너(critbit, rbt, hashmap)에서 현재 요소의 키에 대한 직접 참조/포인터 반환.
  • *_iterator_next(T, p_it): 다음 요소로 전진.
  • *_iterator_prev(T, p_it): 이전 요소로 후진.

5.2 순회 매크로 및 구간 순회 (Traversal & Range Queries)

// 정방향 전체 순회
vector_foreach(int, &v, it) {
	int value = *vector_iterator_get(int, &it);
	printf("%d\n", value);
}

// 역방향 전체 순회
vector_rforeach(int, &v, it) {
	int value = *vector_iterator_get(int, &it);
	printf("%d\n", value);
}

// [first, last) 반열린 구간(Range) 순회 및 lower_bound/upper_bound 활용 (RBT 예시)
rbt_iterator(int, int) first = rbt_lower_bound(int, int, &tree, 20); // key >= 20
rbt_iterator(int, int) last  = rbt_upper_bound(int, int, &tree, 40); // key > 40

for (rbt_iterator(int, int) it = first;
     !rbt_iterator_is_equal(int, int, &it, &last);
     rbt_iterator_next(int, int, &it)) {
	printf("Key: %d, Val: %d\n", it.p_node->data.key, it.p_node->data.value);
}

6. 주요 자료구조 상세 명세 및 불변식 (Container Specs & Invariants)

컨테이너 설명 시간 복잡도 (접근 / 삽입 / 삭제) 주요 특이사항
vector(T) 연속 메모리 기반 동적 배열 $O(1)$ / $O(1)$ amortized / $O(N)$ 지수적(2배) 용량 증가, vector_at, reserve, resize 지원
deque(T) 고정 청크(512단위) 기반 양방향 큐 $O(1)$ / $O(1)$ (front/back) / $O(1)$ 잦은 앞/뒤 삽입 시 메모리 재할당 최소화
list(T) 이중 연결 리스트 $O(N)$ / $O(1)$ / $O(1)$ 노드 단위 힙 할당, 노드 캡슐화 및 이터레이터/포인터 기반 삽입/삭제 지원
heapq(T) 2진 힙 기반 우선순위 큐 $O(1)$ (top) / $O(\log N)$ / $O(\log N)$ is_min_heap 플래그로 Min-Heap / Max-Heap 전환 가능
rbt(K, V) 레드-블랙 트리 (정렬 Key-Value) $O(\log N)$ / $O(\log N)$ / $O(\log N)$ 센티넬 노드 p_nil을 공유하여 경계 검사 최적화, In-Order 정렬 순회
hashmap(K, V) Open Addressing 선형 탐색 해시맵 $O(1)$ avg / $O(1)$ avg / $O(1)$ avg 톰스톤(Tombstone) 삭제 상태(CCTL_HASHMAP_DELETED) 관리, 부하율 50% 시 리사이즈
trie(T) 256-ary Radix 문자열 트라이 $O(L)$ / $O(L)$ / $O(L)$ ($L$ = 키 길이) Prefix 탐색, 사전순 정렬 순회, trie_iterator_get_key로 키 역추적
critbit(V) DJB Crit-Bit 트리 (문자열 Key-Value) $O(L)$ / $O(L)$ / $O(L)$ ($L$ = 키 길이) Tagged Pointer 노드, Prefix 탐색, $O(1)$ 메모리 사전순 양방향 정렬 순회
optional(T) 값의 존재 유무 래퍼 $O(1)$ / $O(1)$ / $O(1)$ has_value, value_or, some, none 제공
result(T, E) 성공/실패 결과 래퍼 $O(1)$ / $O(1)$ / $O(1)$ is_ok, is_err, unwrap, unwrap_err, unwrap_or, ok, err 제공
str.h 가변 길이 동적 문자열 $O(1)$ / $O(1)$ amortized / $O(1)$ basic_string(char) 기반, 문자열 버퍼 관리

7. 코딩 컨벤션 및 에이전트 수정 지침

새로운 컨테이너를 추가하거나 기존 코드를 수정할 때 AI 에이전트는 다음 규칙을 엄격히 준수해야 합니다.

7.1 네이밍 컨벤션 (Naming Conventions)

7.1.1 변수 및 매개변수 접두사 규약 (Variable & Parameter Prefix Rules)

함수의 매개변수(Parameter)와 내부 지역변수(Local Variable)는 타입과 참조 형태에 따라 다음 접두사를 일관되게 적용합니다.

접두사 타입 및 메모리 참조 형태 실제 C 타입 예시 변수명 예시
(없음) 일반 값 / 구조체 (Value) int, size_t, pair index, count, capacity, item
p_ 단일 포인터 (Pointer) int*, vector*, node* p_v, p_hm, p_node, p_data, p_it
pp_ 이중 포인터 (Pointer to Pointer) void**, node** pp_parent, pp_root, pp_node
a_ 값의 고정 배열 (Array) int[10], char[256] a_items, a_buffer, a_bytes, a_slots
ap_ 포인터의 배열 (Array of Pointers) char*[10], node*[32] ap_strings, ap_nodes
fp_ 함수 포인터 (Function Pointer) int (*)(int, int) fp_comp_func, fp_hash_func, fp_copy_func

7.1.2 줄임말 및 어휘 표준 정책 (Abbreviation & Vocabulary Policy)

임의의 자음 축약이나 불명확한 줄임말을 지양하고, 공인된 표준 관용 축약어와 **온전한 영단어(Full Word)**를 명확히 구분하여 사용합니다.

  1. 공인 표준 단축어 (Established Standard Abbreviations)

    • 순회/위치 (4글자 대칭): prev (previous 지양), curr (current 지양), next
    • C 언어 및 시스템 핵심: char (character 지양), ptr (pointer 지양), func (function/fn 지양), temp (temporary/tmp 지양)
    • C 언어 및 시스템 핵심: char (character 지양), ptr (pointer 지양), func (function, fn 지양), temp (temporary, tmp 지양)
    • 수학 및 극값: min (minimum 지양), max (maximum 지양)
    • I/O 및 시스템 동작: src (source 지양), dest (destination/dst 지양), dir (direction 지양), init (initialize 지양), alloc (allocate 지양)
    • 역방향 접두사: r* (rbegin, rforeach / reversed_* 지양)
    • 컨테이너 주체 포인터: p_v, p_hm, p_d, p_l, p_r, p_t, p_opt, p_it (1~2글자 이니셜 허용)
    • I/O 및 시스템 동작: src (source 지양), dest (destination, dst 지양), dir (direction 지양), init (initialize 지양), alloc (allocate 지양)
    • 역방향 접두사: r* (예: rbegin, rforeach / reversed_* 지양)
    • 컨테이너 주체 변수 및 포인터: 컨테이너를 다루는 주체 포인터(p_v, p_hm, p_d, p_l, p_r, p_t, p_opt, p_it)뿐만 아니라, 해당 인스턴스를 직접 생성하여 다루는 지역변수명(v, hm, d, l, r, t, opt, it)에는 1~2글자 이니셜을 허용합니다 (예: vector(int) v = vector_new(int);).
  2. 완전한 단어 유지 (Full Word Mandate)

    • 마땅한 표준 줄임말이 없는 명사는 임의로 자음만 따거나 2~3글자로 줄이지 않고 온전한 영단어를 유지합니다.
    • 데이터 및 값: data (❌ dat), value (❌ val), item (❌ elem, element)
    • 수치 및 용량: capacity (❌ caps, cap), index (❌ idx), count (❌ cnt), length (❌ len)
    • 결과 및 버퍼: result (❌ res, ret), buffer (❌ buf)

7.1.3 매크로 및 구조체 명명 규약

  1. 매크로 인자: 대문자를 사용하며, 단일 타입은 T, Key-Value 컨테이너는 K, V로 통일합니다 (T, K, V, FUNC).
  2. 매크로 분류 및 대소문자 규약:
    • 매크로 상수 (Macro Constants): 모두 대문자(ALL_CAPS)를 사용합니다 (CCTL_HASHMAP_EMPTY, CCTL_RBT_RED, CCTL_RBT_BLACK).
    • 함수형 매크로 (Function-like Macros): 반환형이 존재하거나 특정 동작/연산을 수행하는 매크로로, **반드시 소문자(lowercase_with_underscores)**로 작성합니다 (vector_init, critbit_insert, cctl_critbit_is_branch_node, cctl_critbit_to_branch_node, cctl_critbit_tag_branch_node, cctl_critbit_to_leaf_node).
    • 치환형 매크로 (Token Replacement Macros): 타입명 정의나 식별자 이름 생성/토큰 맹글링 등 단순 치환을 수행하는 매크로로, **반드시 소문자(lowercase_with_underscores)**로 작성합니다 (vector, vector_func, vector_iterator, critbit, critbit_func, cctl_join, cctl_ptr).
  3. 네이밍 시 역할 중심 직관성 규칙:
    • 내부 연산에 쓰이는 매크로라도 모호한 internal 등의 단어 대신 그 역할과 동작이 이름에서 명확히 드러나도록 작성해야 합니다 (is_internal 지양 $\to$ is_branch_node 지향).
  4. 구조체 태그: cctl_join(container(T), struct) 규칙에 따라 <T>_<container>_struct (또는 <K>_<V>_<container>_struct)로 선언합니다.
  5. 헤더 가드: __CCTL_<MODULE>_H__ 형식을 준수합니다.

7.2 포맷팅 및 스타일 (Formatting & Indentation)

  • 들여쓰기 (Tab Indentation Everywhere): C 소스/헤더 파일뿐만 아니라 마크다운(.md) 문서를 포함한 모든 파일에서 들여쓰기 시 스페이스 대신 반드시 **탭(Tab, \t)**을 사용합니다.

  • 문서화 주석:

    • 함수형 매크로: 반환형과 매개변수 타입을 포함한 가상 함수 시그니처(`/// ```c <return_type> <func_name>(...) ````) 및 동작 설명을 반드시 작성합니다.
    • 치환형 매크로: /// name(ARGS)... 형식으로 치환 결과 및 역할을 설명합니다.
    • 헤더 내 static inline 비-매크로 함수: IDE/LSP 툴팁에서 C 정의가 직접 표시되므로, 별도의 가상 시그니처 블록 없이 함수의 목적과 역할을 간결하게 삼중 슬래시(///) 주석으로 설명합니다.
  • 라이선스 헤더: 모든 헤더 파일 상단에 MIT 라이선스 식별자를 유지합니다:

     // SPDX-License-Identifier: MIT
     // Copyright (c) 2021 Michael Bäck <mhcoma@gmail.com>

7.3 메모리 안전성 및 방어적 프로그래밍 규칙

  1. NULL 검사: 컨테이너 포인터(p_v, p_hm 등) 및 이터레이터 포인터(p_it)는 함수 진입 시 항상 유효성을 검사해야 합니다.
  2. 할당 실패 처리: malloc, calloc, realloc 호출 실패 시 이전 메모리를 온전히 유지하고 false를 반환해야 합니다.
  3. 초기화 및 리셋: free 호출 후 구조체 내부 포인터를 반드시 NULL로 초기화(memset 또는 init 호출)하여 더블 프리(Double Free) 및 댕글링 포인터를 방지해야 합니다.
  4. API 서명 불변성: 기존 매크로의 인수 순서 및 반환 형식을 파괴하는 변경은 지양합니다.

8. 테스트 및 빌드 검증 가이드

8.1 테스트 작성 방법

test/ 디렉터리에 각 자료구조별 테스트 코드를 작성할 때, *_fd, *_imp_h, *_imp_c가 정상 작동하는지 분리 컴파일 및 단일 컴파일 모두 검증해야 합니다.

예시: test/test_vector.c

#include <assert.h>
#include <stdio.h>
#include "../vector.h"

vector_fd(int);
vector_imp_h(int);
vector_imp_c(int);

int main(void) {
	vector(int) v;
	vector_init(int, &v);
	assert(vector_is_empty(int, &v));

	for (int i = 0; i < 100; i++) {
		assert(vector_push_back(int, &v, i));
	}
	assert(vector_size(int, &v) == 100);
	assert(*vector_at(int, &v, 50) == 50);

	int count = 0;
	vector_foreach(int, &v, it) {
		assert(*vector_iterator_get(int, &it) == count++);
	}

	vector_free(int, &v);
	printf("vector test passed successfully!\n");
	return 0;
}

8.2 컴파일 및 실행 검증 명령어

최신 C 표준(C23 기본 타깃)으로 컴파일을 수행합니다. 컴파일러가 -std=c23을 지원하지 않는 경우 -std=c2x를 사용합니다:

# GCC 최신 C23 컴파일 (c23 미지원 시 -std=c2x 대체)
gcc -std=c23 -Wall -Wextra -g test/test_vector.c -I. -o test/test_vector.exe
# (대체) gcc -std=c2x -Wall -Wextra -g test/test_vector.c -I. -o test/test_vector.exe
./test/test_vector.exe

# Clang 최신 C23 컴파일 (AddressSanitizer 포함, c23 미지원 시 -std=c2x 대체)
clang -std=c23 -Wall -Wextra -fsanitize=address -g test/test_vector.c -I. -o test/test_vector.exe
# (대체) clang -std=c2x -Wall -Wextra -fsanitize=address -g test/test_vector.c -I. -o test/test_vector.exe
./test/test_vector.exe

# MSVC (최신 C 표준 타깃)
cl.exe /std:clatest /W4 test\test_vector.c /I. /Fe:test\test_vector.exe
.\test\test_vector.exe

9. Git 커밋 메시지 컨벤션

프로젝트의 기존 커밋 히스토리를 반영하여 일관된 접두사를 사용합니다:

  • Add : <기능 설명> - 새로운 자료구조, 매크로, 테스트 추가
  • Fix : <버그 설명> - 버그 수정, 오탈자 수정, 주석 보완
  • Refactor : <리팩토링 내용> - 구조 개선 및 알고리즘 최적화