**現代のプログラミングで実践しているC++習慣**

1. **説明的な変数名を使う**  
   - 単一文字の識別子は避け、意図が伝わる名前を選ぶ。

2. **不変データには `const` と `static const` を優先する**  
   - 変更されないことを保証し、コンパイラ最適化を促進する。

3. **RAII(Resource Acquisition Is Initialization)パターンを採用する**  
   - リソースの取得と解放をオブジェクトに閉じ込めることでリークを防止。

4. **必要に応じてヘッダーオンリ―ライブラリを利用する**  
   - コンパイル依存性が減り、ビルド時間が短縮される。

5. **型推論には `auto` を使う**  
   - 複雑なイテレータ型を簡潔にしつつ可読性を保つ。

6. **モダンなコンテナ初期化子を活用する**  
   - `std::vector<int> v{1, 2, 3};` は手動の `push_back` よりも明確で簡潔。

7. **Catch2 や Google Test のようなフレームワークで単体テストを書く**  
   - コードの正しさを保証し、リファクタリングを容易にする。

8. **関数は短く、目的を絞る**  
   - 単一責任原則を目指すことで保守性が向上する。

9. **Doxygen コメントでドキュメント化する**  
   - クリーンで検索可能な API ドキュメントを自動生成できる。

10. **最適化はプロファイル後に行う**  
    - ホットスポットを測定し、実際のパフォーマンスボトルネックに対処する。

2026/01/19 17:03

**現代のプログラミングで実践しているC++習慣** 1. **説明的な変数名を使う** - 単一文字の識別子は避け、意図が伝わる名前を選ぶ。 2. **不変データには `const` と `static const` を優先する** - 変更されないことを保証し、コンパイラ最適化を促進する。 3. **RAII(Resource Acquisition Is Initialization)パターンを採用する** - リソースの取得と解放をオブジェクトに閉じ込めることでリークを防止。 4. **必要に応じてヘッダーオンリ―ライブラリを利用する** - コンパイル依存性が減り、ビルド時間が短縮される。 5. **型推論には `auto` を使う** - 複雑なイテレータ型を簡潔にしつつ可読性を保つ。 6. **モダンなコンテナ初期化子を活用する** - `std::vector<int> v{1, 2, 3};` は手動の `push_back` よりも明確で簡潔。 7. **Catch2 や Google Test のようなフレームワークで単体テストを書く** - コードの正しさを保証し、リファクタリングを容易にする。 8. **関数は短く、目的を絞る** - 単一責任原則を目指すことで保守性が向上する。 9. **Doxygen コメントでドキュメント化する** - クリーンで検索可能な API ドキュメントを自動生成できる。 10. **最適化はプロファイル後に行う** - ホットスポットを測定し、実際のパフォーマンスボトルネックに対処する。

RSS: https://news.ycombinator.com/rss

要約

Japanese Translation:

作者は主にC#とPythonで作業していますが、バインディングやニッチなタスクのために依然としてC(またはC++)を使用します。これは細粒度の制御を提供するからです。Cには公式のスタイルガイドラインがないため、ブログ、Rust、および完璧主義的マインドセットから引き出した個人的な習慣を構築しています。

新しいプロジェクトでは、GCC/Clang/MSVCサポート付きC23を好み、

#if CHAR_BIT != 8 #error
を強制して8ビットの
char
を保証します。彼らは簡潔な typedef のセット(
u8
,
i8
,
i16
,
u16
,
i32
,
u32
,
u64
,
f32
,
f64
,
uptr
,
isize
,
usize
)を採用し、<stdbool.h> からの C23 の
bool
をブール値に使用します。

ヌル終端文字列を避けるために、彼らは 長さ+データ構造

String
u8 *data; isize len
)を使用します。「parse, don’t validate」に触発されてオープックタイプと信頼できるコンストラクタ(Lelenthran のブログ参照)を作成しています。C23 のタグ互換性により、マクロ (
Tuple2(T1,T2)
) を使って単純なタプルを定義できますが、名前付き構造体は必要です。

エラーハンドリングは sum types でモデル化されています:列挙型とそれに伴う構造体(

ErrorCode
,
SafeBuffer
,
MaybeBuffer
)が戻り値に成功または失敗を符号化します。作者は純粋な C では動的メモリ割り当てを意図的に避け、ヒープ重視のコードには Rust または C# を好みます;アレーナアロケータも言及されますが使用されません。

標準ライブラリの使用は最小限です。文字列関数はほとんど使わず、代わりに生の

mem*
呼び出しを優先します。また、OS API はエルゴノミクスが悪いため再実装されることがよくあります。作者は外部関数のドキュメントを注意深く読むことを強調し、将来的により安全なメモリ取り扱いのために 「slice」タイプ を追加することを検討しています。

全体として、この記事は読者が自分自身の C スタイルガイドラインを作成するよう奨励しつつ、言語の強みと挫折の両方を認めています。

本文

投稿日: 2026‑01‑17T21:02:00Z
最終更新: 2026‑01‑17T23:20:00Z


はじめに

「本格的な」プログラミング言語として学んだのは、K&R を連続で読んだ時の C です。今ではあまり頻繁に C を書くことはありません。Resonite をプレイするうちに、ゲームをモッド化するために大量の C# が書かれるようになり、日常的に行っている作業のほとんどはコンピュータ上の退屈な作業を自動化することであり、その多くは既存インフラのおかげでシェルや Python へ委譲されています。

とはいえ、時折「何か C(あるいは C++)を書かなければならない」「書きたくなる」場面が出てきます。ライブラリのバインディングを作るときもあれば、言語・アーキテクチャ上のギャップを埋めるためでもあります。さらに、私はまだ C を試作品を書く際の好きな言語だと言えます(その理由ははっきりしませんが)。

C は「スタイル」や「慣行」に関して標準化がほとんどない興味深い言語です。他の多くの言語は、構文自体に微妙に埋め込まれているか、あるいは「公式」ドキュメントで「これが最適な使い方だ」と明示しています。C には公式ドキュメントチャネルもなく、構文や標準ライブラリの構成要素も特定の書き方を奨励するものではありません。その結果、人々の書き方に不整合が生じ、言語と標準ライブラリが初期段階だった頃は、全体的な慣行がエラーを招きやすい状態でした。そこで私はブログ記事や C# / Rust の経験、完璧主義的思考から自分自身の習慣を作り上げました。

このスタイルで書くことを勧めているわけではありませんし、常に最良の方法だとも言い切っていません。組み込みシステム向けや極限まで高速化したい場合は、これらの慣行を破ることもあります。しかし、多くのプロジェクトで私はこのベースラインから始め、書かないと一貫性が保てません。


基本設定

新しい C プロジェクトでは通常 C23 を採用しています。別プロジェクトに参加する際は、そのプロジェクトのバージョンを尊重しますが、C23 ならこの投稿で紹介している多くの機能が利用可能ですので、絶対最大限のポータビリティや組み込みアーキテクチャ(例:GCC、clang、MSVC など)に依存しないプロジェクトでは C23 を選択しています。

ほぼすべてのプラットフォーム(POSIX 対応を含む)は

CHAR_BIT
が 8 に設定されているため、以下のように明示しておくとプロジェクトが想定するビット幅をはっきりさせることができます。

#if CHAR_BIT != 8
    #error "CHAR_BIT != 8"
#endif

Rust を学んだ際に好きだった「固定長型」を簡潔に参照できる方法と、Chris Wellon の他の typedef を組み合わせて、プロジェクト内で次のような型エイリアスを使用しています。

typedef uint8_t   u8;
typedef int8_t    i8;
typedef int16_t   i16;
typedef uint16_t  u16;
typedef int32_t   i32;
typedef uint32_t  u32;
typedef uint64_t  u64;
typedef float     f32;
typedef double    f64;
typedef uintptr_t uptr;
typedef ptrdiff_t isize;
typedef size_t    usize;

b32
(Wellon の投稿で紹介されている)は使わず、C23 の
_Bool
型を採用しています。C99 以降なら
<stdbool.h>
bool
を使用します。ブール値に関してはすでに慣れ親しんだ構文があるので、より自然に思えます。

また長さ+データ文字列構造体(null 終端文字列の削除を望むなら)も長年使っています。C の歴史的な欠点として「ヌル終端文字列」を排除できればと願っているので、以下のように定義しています。

typedef struct {
    /* null terminator を含む(悪い関数向け) */
    u8 *data;
    /* null terminator を除く(メモリコピー用) */
    isize len;
} String;

既存 C 文字列やバッファから初期化したり、安全にコピーしたりするユーティリティも併せて実装しています。


パース、検証ではなく「パース」だけ

プログラミングを始めた頃に読んだ中で最も目が覚めるブログ記事の一つは、「parse, don’t validate」という永遠のテーマです。ご自身でお読みいただけていないなら、ぜひ読むことをおすすめします。TL;DR は、言語の型システムを最大限に活用し、関数署名に厳密な型を要求することで、信頼できるインターフェースからのみ生成される型だけを使用するというものです。その結果、非常に堅牢で明確な API が得られ、エラーはコンパイル時に明示的に示されます。

C の弱い型システムの中でも最も柔軟かつ強力なのは「構造体」です。しかし、「信頼できるインターフェースからのみ作成可能」という点をどう実装するかがわからず、当初は C でこの哲学を適用しようと試みませんでした。数年後、ウェブ上で再び関連記事に触れ、Lelenthran のブログ投稿で「任意の不透明型(opaque type)を作成できる」ことが示されているのを知り、実際に適用可能だと悟りました。

現在は非プレイグラウンドプロジェクトでこの手法を使用しています。過去に書いたコードを書き換えようとして失敗した経験もありますが、今では実践的です。


タプル(Tuple)!

C23 のもっともエキサイティングな変更点の一つは、「同じ名前と内容でタグ付き型(struct, union, enum)が完全に互換性を持つ」という標準化です。関数から複数値を返したい場合、構造体やメンバに明示的な名前を付ける必要がないケースも多々あります。そのような場面で役立つのが タプル です。

#define Tuple2(T1, T2)           \
    struct Tuple2_##T1##_##T2 {  \
        T1 a;                    \
        T2 b;                    \
    }

メンバ名は

_0
,
_1
など別のパターンもありますが、私は英字(a, b, …)を好みます。Rust のセマンティクスに近い名前付けを模倣したい場合は少し不自然に感じるかもしれません。

ただし、この機能は匿名タグ付き型には適用されないため、実際に使用するたびに明示的な名前が必要です。ポインタ型で使おうとすると以下のようなエラーになります。

$ cc kmp.c
In file included from kmp.c:5:
kmp.c: In function 'main':
kmp.c:29:20: error: pasting "*" and "_" does not give a valid preprocessing token
   29 |         Tuple2(char*, int) buffer;
      |                    ^
../common/tuple.h:5:25: note: in definition of macro 'Tuple2'
    5 |         struct Tuple2_##T1##_##T2 {  \
      |                         ^~

対処法としては、ポインタに typedef を作るか、構造体の名前を明示的に指定する必要があります。どちらもエルゴノミクスが最適とは言えないため、実際にはあまり頻繁に使用していません。

一般的にタプルはポインタを格納せず、実質的な構造体で意味合いを持たせるケースが多いです。


結果(Result)型の返却

できるだけ型システム内に情報を埋め込みたいという考えから、sum type を活用することに大きな関心があります。C には正式な sum type がないため、構造体とディシプリン(discipline)で実装します。主に公開 API 用や意味的に自律した形で使われるので、typedef 化して扱います。

以下は典型例です。

typedef enum {
    /* ... */
} ErrorCode;

typedef struct {
    char *val;
} SafeBuffer;

typedef struct {
    bool ok;
    union {
        SafeBuffer *val;
        ErrorCode err;
    };
} MaybeBuffer;

エラーコードごとにメッセージを印字する関数なども用意しています。呼び出し側は必ず

ok
を確認すべきです。実際に「parse, don’t validate」のアプローチと組み合わせることで、C でのエラーハンドリングが苦痛ではなくなることを発見しました。

これにより冗長性(C は関数型言語ほど簡潔な構文糖衣は持たない)が多少増えるものの、フローと安全性は大幅に向上します。


動的メモリ管理

私は動的メモリ管理を頻繁に扱うわけではありません。Wellon らが推奨する arena は好きですが、実際にヒープを多用しない限りあまり使いません。もし大量の動的割当とライフタイム管理が必要になった場合は、Rust や C# を選択します。現時点ではそのようなケースは稀であり、まだ十分に探求している段階です。


その他小さな習慣

  • string.h
    の関数はほとんど避け、メモリ操作を行いたいときだけ
    mem*
    系を使用します。
  • 標準ライブラリは OS 関連の機能が必要な場合のみ利用し、多くの場合自前で実装しています。
  • 外部関数を呼び出す際は必ずドキュメント(マニュアル)を確認し、潜在的な問題を洗い出します。将来的に「スライス」型のような安全な抽象化を導入する可能性もありますが、現時点ではまだ実装していません。

まとめ

この投稿が皆さん自身の C スタイルを見直すきっかけになれば幸いです。C は好きで好きでないと同時に、制限や課題があるからこそ面白くなる言語だと思っています。これらの制約は解決すべき楽しい問題を提供し、安全性を高めるためのガードとなります。

同じ日のほかのニュース

一覧に戻る →

2026/01/24 10:00

**27ブランドから325車種へ対応したオープンソース自動運転**

## Japanese Translation: 提供された要約は正確で網羅的かつ明瞭であるため、変更の必要はありません。 --- **元の要約:** Comma Four は、オープンパイロットプラットフォームを利用した高度なドライバーアシスタンス機能を車両に提供する AI 主導型アップグレードです。トヨタ・ヒュンダイ・フォードなど 27 社の 325 台以上の車に追加でき、広範なモデルでテストされ、人間の入力を最小限に抑えて数時間動作可能であることが示されています。オープンパイロットの実証済み自律走行機能を基盤とし、Comma Four は自動運転技術の業界全体への普及へ向けた一歩を表します。同社はユーザーにコミュニティ参加と将来の自律システム形成への貢献を呼びかけつつ、プロダクト開発・自律工学・運用部門での採用も積極的に行っています。広く展開されれば、このアップグレードは多ブランドのドライバーアシスト機能の導入を加速し、自動車技術分野で新たなキャリアパスを創出する可能性があります。

2026/01/20 0:06

**Go言語が1万5000行を削減** --- ### 概要 Goプログラミング言語は、最近の更新で約 **150万行(LOC)** のコードを削除し、コードベースの大幅な縮小を実現しました。これはコミュニティが言語をシンプルに保ち、保守性を向上させるために継続的に取り組んでいる結果です。 ### 主なポイント - **削減規模** - コアパッケージとツール全体で約1,500,000行が削除されました。 - **動機** - 現在の使用状況に合わなくなった重複コードやレガシーコードを排除する。 - 保守性を簡素化し、コンパイル時間を短縮し、可読性を向上させる。 - **開発者への影響** - 廃止予定の機能に対してわずかなAPI変更が加えられました。 - よりシンプルになったコードベースを反映したドキュメントが更新されました。 - **今後の展望** - ミニマリズムとパフォーマンスへの継続的な注力。 - 言語をさらに洗練させるため、コミュニティからの貢献を奨励しています。 ### 結論 Goプロジェクトが半百万行に及ぶ削減を意図的に実施したことは、世界中の開発者に対して明瞭性・効率性・長期的持続可能性へのコミットメントを示すものです。

## Japanese Translation: ``` ## Summary 著者はQuaminaにUnicode文字プロパティ正規表現の堅牢なサポートを構築し、`[~p{L}~p{Zs}~p{Nd}]`という構文を使用しました。 Goの標準ライブラリが最新のUnicodeバージョン(15.0対17.0)に追いついていないため、Quaminaは独自のデータを維持する必要がありました。著者は `UnicodeData.txt` を取得し、フィールド1と3を解析してすべての37カテゴリとそれらの補集合の範囲をリスト化したコードを生成しました—結果として従来の775K行アプローチに比べ5,122行のGoコードのみで済みました。 初期は、すべてのオートマタを事前計算しコードへ直列化すると約12Mのデータが生成され、起動時に長時間停止したりIDEがクラッシュする問題が発生しました。実行時キャッシュ戦略に切り替えることで、Quaminaは初回使用時にUnicodeプロパティオートマタを計算し保持できるようになりました。この変更で追加速度が135/秒から4,330/秒へ(30倍)向上しました。マッチング性能も高いままであり、UTF‑8の短さと浅いオートマタのおかげで数十万〜百万メッセージ/秒を処理できます。 著者は日常的な作業にGenAIツールを使用することを検討しましたが、ツール不足・時間制約・そのようなサービスのビジネス実現性への懐疑心から控えています。次の主要機能は数値量指定子サポート(例:`a{2-5}`)であり、これによりQuaminaの正規表現機能が完結します。この成功を受けてQuamina 2.0の安定リリースが計画されています。生活上の誘惑が勢いを鈍らせましたが、不確実性があるものの今後の開発は奨励されます。 ```

2026/01/24 1:19

**ガスタウンのエージェントパターン、設計ボトルネック、および大規模時のビベコーディング** - **エージェントパターン** - パターンA:高頻度インタラクション - パターンB:低レイテンシ応答 - パターンC:分散調整 - **設計ボトルネック** - データスループット制限 - 共有モジュール内のメモリ競合 - シャード間での同期オーバーヘッド - **大規模時のビベコーディング** - スケーラブルなメッセージバス実装 - 適応型負荷分散アルゴリズム - リアルタイム監視と自動チューニングフレームワーク

## Japanese Translation: *(No content was provided for translation.)*

**現代のプログラミングで実践しているC++習慣** 1. **説明的な変数名を使う** - 単一文字の識別子は避け、意図が伝わる名前を選ぶ。 2. **不変データには `const` と `static const` を優先する** - 変更されないことを保証し、コンパイラ最適化を促進する。 3. **RAII(Resource Acquisition Is Initialization)パターンを採用する** - リソースの取得と解放をオブジェクトに閉じ込めることでリークを防止。 4. **必要に応じてヘッダーオンリ―ライブラリを利用する** - コンパイル依存性が減り、ビルド時間が短縮される。 5. **型推論には `auto` を使う** - 複雑なイテレータ型を簡潔にしつつ可読性を保つ。 6. **モダンなコンテナ初期化子を活用する** - `std::vector<int> v{1, 2, 3};` は手動の `push_back` よりも明確で簡潔。 7. **Catch2 や Google Test のようなフレームワークで単体テストを書く** - コードの正しさを保証し、リファクタリングを容易にする。 8. **関数は短く、目的を絞る** - 単一責任原則を目指すことで保守性が向上する。 9. **Doxygen コメントでドキュメント化する** - クリーンで検索可能な API ドキュメントを自動生成できる。 10. **最適化はプロファイル後に行う** - ホットスポットを測定し、実際のパフォーマンスボトルネックに対処する。 | そっか~ニュース