Files
FESADev/docs/CODINGSTYLE.md
T
2026-08-16 01:54:41 +09:00

12 KiB

FESA C++ Coding Style

목적

이 문서는 FESA production 및 test C++의 코드 스타일, 객체 설계, 문서화와 검증 규칙을 정의하는 project-local source of truth다. 새 C++를 작성하거나 기존 C++를 리팩터링하는 사람과 Implementation Agent는 작업 전에 이 문서를 읽어야 한다.

Google C++ Style Guide를 baseline으로 사용한다. 이 문서, AGENTS.md, 승인된 feature contract와 architecture/ADR이 Google guide의 일반 규칙보다 우선한다.

적용 범위와 우선순위

규칙 충돌 시 다음 순서로 해석한다.

  1. 승인된 feature requirements, formulation, numerical-review, I/O와 reference contract
  2. AGENTS.md, docs/ARCHITECTURE.mddocs/ADR.md
  3. 이 문서의 FESA-specific rule과 exception
  4. Google C++ Style Guide
  5. 기존 local style

새 코드는 이 문서를 즉시 준수한다. 기존 코드는 승인된 refactoring plan의 module slice 단위로 전환한다. 요청 범위 밖의 file을 style-only 이유로 함께 수정하지 않는다.

Language와 Toolchain

  • Production language는 C++17 이상이며 MSVC x64를 지원해야 한다.
  • 승인된 build 기준은 CMake, Visual Studio generator와 Debug configuration이다.
  • C++ compiler extension에 의존하지 않는다.
  • MKL, TBB, HDF5와 Win32 type은 public solver core header에 노출하지 않는다.
  • Standard library와 RAII를 manual lifetime management보다 우선한다.
  • Google guide가 현재 권장하는 C++20 language target은 FESA의 C++17 contract를 바꾸지 않는다.

File 이름과 Header

  • File 이름은 소문자 snake_case를 사용한다.
  • Production 및 test source extension은 기존 FESA/CMake 관례인 .cpp를 유지한다.
  • Header extension은 .h를 사용한다. 기존 .hpp는 승인된 migration slice에서 .h로 바꾼다.
  • Header는 self-contained여야 하며 include consumer의 transitive include에 의존하지 않는다.
  • Header는 #pragma once 대신 full repository path 기반 include guard를 사용한다.

예:

#ifndef FESA_MATH_VECTOR3_H_
#define FESA_MATH_VECTOR3_H_

namespace fesa {

class Vector3 {};

}  // namespace fesa

#endif  // FESA_MATH_VECTOR3_H_

Include 순서는 다음과 같다.

  1. 대응하는 header
  2. C system header
  3. C++ standard library header
  4. Third-party header
  5. FESA project header

각 non-empty group 사이에는 빈 줄을 두고 group 안에서는 알파벳순으로 정렬한다. 사용하는 symbol의 declaration을 제공하는 header를 직접 include한다.

이름 규칙

대상 규칙
class, struct, enum, alias PascalCase ElementProperty, EntityIndex
function, method, accessor PascalCase ComputeStiffness(), NodeCount()
local variable, parameter snake_case element_index, source_order
class data member snake_case + trailing _ youngs_modulus_
struct data member snake_case source_id
compile-time/static constant kPascalCase kNodeCount
enumerator kPascalCase FailureCategory::kModel
namespace snake_case fesa::hdf5_internal
macro UPPER_SNAKE_CASE FESA_MATH_VECTOR3_H_

Google guide는 accessor의 snake_case를 허용하지만 FESA는 사용자 승인에 따라 production 및 test 호출부를 포함한 모든 function name에 PascalCase를 적용한다. Constructor, destructor와 operator 이름은 C++ language 규칙을 따른다.

이름은 물리 및 수치 의미를 드러내야 한다. value, data, handler, manager처럼 문맥이 없는 generic name을 넓은 scope에서 사용하지 않는다. Source label, internal entity index와 equation index를 이름에서 구분한다.

Formatting

  • .clang-formatBasedOnStyle: Google을 사용한다.
  • 들여쓰기는 space 2개이며 tab을 사용하지 않는다.
  • 최대 line length는 80자다. Include, guard, URL과 분할할 수 없는 contract string은 Google guide의 예외를 따른다.
  • Opening brace는 declaration/control statement의 마지막 줄에 둔다.
  • Namespace body는 들여쓰지 않고 closing namespace comment를 작성한다.
  • 한 statement에 한 declaration만 둔다.
  • Variable은 가능한 가장 좁은 scope에서 선언과 동시에 초기화한다.
  • constconstexpr를 의미가 허용하는 범위에서 사용한다.
  • override, final, explicit, noexcept[[nodiscard]]는 실제 contract를 표현할 때 사용한다.

Formatting-only 변경과 behavior/architecture 변경은 같은 commit에 섞지 않는다.

Class와 Interface 설계

  • Class는 하나의 명확한 책임과 invariant를 가져야 한다.
  • Polymorphic base는 public virtual destructor를 가져야 한다.
  • Abstract interface에는 현재 concrete 구현이 공유하지 않는 future method를 추가하지 않는다.
  • 단독 ownership은 std::unique_ptr로 표현한다. 실제 shared lifetime이 없는 std::shared_ptr는 사용하지 않는다.
  • Non-owning pointer/reference의 lifetime은 Doxygen contract에 기록한다.
  • Downcast와 type switch를 주요 확장 mechanism으로 사용하지 않는다.
  • State가 없는 함수를 묶기 위한 static-only class를 만들지 않는다. Internal namespace와 focused module을 사용한다.
  • Base class에 optional field와 no-op method를 누적하지 않는다. Material density, plastic state와 anisotropic constitutive law처럼 독립적인 의미는 별도 capability 또는 구성 객체로 설계한다.
  • Public header가 implementation/vendor dependency를 역으로 끌어오지 않도록 한다.

Data-only record는 struct를 사용할 수 있다. Invariant, encapsulation, lifetime 또는 behavior가 있으면 class를 사용한다.

FEM Module 책임

  • model: immutable semantic definition과 stable source/internal identity
  • elements: element numerical kernel, local contribution과 element recovery
  • properties: element property identity와 concrete section data
  • materials: constitutive capability와 concrete material behavior
  • fem: DOF/equation numbering, scatter와 sparse pattern
  • assembly: deterministic element/load contribution reduction
  • constraints: constraint definition 적용과 equation policy
  • analysis: procedure-specific lifecycle과 backend orchestration
  • results: backend-neutral recovery record와 physical result identity
  • io: Abaqus syntax/semantic mapping과 HDF5 schema implementation
  • math: backend-neutral value/storage type와 private numerical adapter

한 module의 class가 다른 module의 owner 책임을 가져가지 않는다. Element가 global CSR을 직접 쓰거나 Node가 equation ID를 저장하거나 Material이 analysis state를 임의로 소유하면 안 된다.

중복과 공통화

같은 의미, units, coordinate, failure policy와 ownership을 가진 logic을 한 번만 구현한다. 현재 승인된 공통화 방향은 다음과 같다.

  • 3D coordinate/axis/director 연산은 Vector3 value class
  • source label/set/instance 해석은 SourceTargetResolver
  • full/free/constrained invariant는 DofManager owner validation
  • MKL size/copy helper는 private dense-BLAS adapter
  • ASCII case-insensitive comparison과 label parsing은 focused core utility

두 코드 block이 비슷해 보여도 formulation sign, result location, source identity 또는 tolerance가 다르면 공통화하지 않는다. 단 한 번 사용하는 logic을 future flexibility만을 위해 framework로 만들지 않는다.

Error와 Ownership

  • Expected failure는 Status 또는 Result<T>로 반환한다.
  • Public solver API를 통해 backend exception이 그대로 새지 않게 한다.
  • Unknown type/property/material 조합은 structured diagnostic으로 fail-closed 처리한다.
  • Invalid input을 silent default, clamp, average 또는 fallback으로 숨기지 않는다.
  • Candidate를 완성하고 검증한 뒤 Domain state, AnalysisState 또는 final HDF5에 commit한다.
  • Stable ordering과 failure atomicity는 optimization option이 아니라 correctness contract다.

Doxygen

Doxygen coverage는 production code에만 요구한다. Test function과 test helper에는 Doxygen를 요구하지 않는다.

Public/protected class와 function declaration은 다음 내용을 필요한 만큼 기록한다.

  • @brief: 무엇을 하는지 동사형 한 문장
  • @param: 이름만으로 드러나지 않는 units, coordinates, ownership 또는 valid range
  • @return: success value와 failure 의미
  • @throws: 실제로 경계를 넘어가는 exception
  • @pre: caller가 보장해야 하는 invariant
  • @note: deterministic order, lifetime 또는 backend constraint
  • @warning: sign, physical/numerical distinction 또는 destructive side effect

예:

/// @brief Computes the element stiffness in stable global DOF order.
/// @return A finite symmetric contribution or a structured model failure.
/// @note The returned matrix does not include nonphysical result terms.
virtual Result<Matrix> ComputeStiffness() const = 0;

Private/internal production function은 declaration 또는 definition에 목적을 기록한다. 수식, sign, coordinate transform, lifetime, ordered reduction이나 failure preservation이 비자명하면 그 이유를 설명한다. Header declaration의 사용법을 .cpp definition에서 그대로 반복하지 않는다.

Comment는 코드 한 줄을 한국어 또는 영어로 번역하는 방식으로 작성하지 않는다. Public API Doxygen는 일관된 tool output을 위해 영어를 기본으로 한다. Diagnostic message와 existing contract language는 현재 외부 계약을 유지한다.

Determinism과 수치 코드

  • Element contribution은 stable source/internal index 순서로 생성한다.
  • Parallel worker는 index-owned output만 수정한다.
  • Floating-point reduction 순서는 명시적으로 고정한다.
  • Refactoring 중 expression/reduction 순서를 편의상 바꾸지 않는다.
  • Arbitrary max(1, ...), zero clamp 또는 missing-row ignore를 추가하지 않는다.
  • End action, section resultant, generalized result와 stress의 identity/sign을 구분한다.
  • Reference mapping은 row order가 아니라 승인된 source identity와 component를 사용한다.

수치식을 공통화할 때는 현재 formulation과 test가 정의한 operation order 및 tolerance를 먼저 확인한다.

Test와 변경 관리

  • Production C++ 변경은 관련 C++ test와 같은 patch에 있어야 한다.
  • Behavior 또는 interface 변경은 RED -> observed failure -> minimal GREEN -> VERIFY를 따른다.
  • Refactoring test는 base interface 사용, ownership/lifetime, invalid combination, deterministic order와 current numerical result preservation을 검증한다.
  • Focused test 뒤에 full MSVC x64 Debug build와 CTest를 실행한다.
  • B33/MITC4 output 경계를 건드린 변경은 승인된 reference comparison을 다시 실행한다.
  • Reference artifact, input path와 tolerance를 리팩터링에 맞춰 수정하지 않는다.
  • Commit은 review 가능한 module slice로 제한하고 Conventional Commits를 사용한다.

Tooling

Repository가 제공하는 설정을 우선한다.

clang-format --dry-run --Werror <changed-cpp-and-header-files>
clang-tidy <changed-cpp-files> -- -std=c++17
doxygen Doxyfile

실제 build/test command는 .harness/config.json이 있으면 그 설정을 우선하고, 없으면 AGENTS.md와 Harness의 MSVC/CMake/CTest entry point를 따른다. 필요한 tool이 설치되지 않아 검증을 실행할 수 없으면 성공으로 간주하지 않고 environment limitation을 보고한다.

Generated Doxygen HTML과 tool cache/build output은 source control에 넣지 않는다.

Implementation Agent Checklist

Implementation Agent는 C++ Step을 시작하기 전에 다음을 확인한다.

  • 이 문서와 feature implementation plan을 읽었다.
  • 변경할 base/concrete/module owner가 승인 설계와 일치한다.
  • 관련 test file과 RED condition이 Step에 명시되어 있다.
  • Public API naming과 production Doxygen가 이 문서에 맞는다.
  • Vendor dependency와 ownership direction이 역전되지 않는다.
  • Stable identity, numerical order, HDF5와 reference contract가 보존된다.
  • Formatting, Doxygen, focused/full MSVC/CTest acceptance command가 계획되어 있다.