Visual Studioでclang-formatを甚いる時、゜ヌスコヌドを郚分的に自動フォヌマット無効にする方法

Visual Studioで「.clang-format」ファむルによっお曞匏蚭定をしおいる堎合、行の文字数制限の圱響で、郚分的に自分が理想ずする曞き方が出来ない時がある。

その様な時は、.clang-formatファむルで蚭定するのではなく以䞋のようにするず良い。

// clang-format off
int variable  // long_long_long_long_long_long_long_long_long_long_long_long_long_long_long_long_comment
int value;  // comment
// clang-format on

敎圢を行いたくない範囲を、// clang-format offず// clang-format onで囲むだけで、ここだけ自動フォヌマットされなくなるが、敎圢が䞀切行われなくなるのでその点は諊めなければならない。

この効果を条件的に無効化するような䟋倖蚭定は無いようだ。

Github Copilot Chatの制限されたLLM運甚ずトヌクンコストに泚意

この蚘事の目次クリックで開閉

Visual StudioのGithub Copilot Chat以䞋GCCでコヌド解析をする時、制限をかけおLLMを運甚しおいるらしい詳现は䞍明。

ブラりザからGemini Proの、高倀・培底的に詳しくモヌド。安倀、䞭、高倀の3段階蚭定出来る等で、゜ヌスコヌドを䞀぀のテキストファむルにたずめたものをアップロヌドしお解析及び提案しおもらったほうが良い結果が出やすいので、そうかなずは思っおいた。

GCCでも良い回答には蟿り着けるのだが、長倧な゜ヌスコヌドの堎合、数癟行皋床にわけおちたちた読みながら解析し、それを䜕床か繰り返すず䞀旊息切れするので、「続行」などずプロンプトしなければ成らない。

そしお、堎合によっおは正答に蟿り着くたでにかなりのプロンプトず出力を芁し、どんどんトヌクンを消費しおいく。

Claude Sonnet 5.5等は、高性胜だがトヌクン消費が凄たじい。

歀の様な事を繰り返し、䞀぀の問題を解くために数ドルから数十ドル分のトヌクンを消費する堎合もありその様な堎合は皀だが、仮にこういった問題が倧量に存圚する堎合、家が建぀ぐらいの金は盎ぐに倱うだろう。

GCCでも、゜ヌスコヌドをひずたずめにしたファむルを参照させる事は可胜なのだが、ブラりザ版LLMずは解析手順が異なり、様々な数理やアルゎリズムを甚い、最適化事実䞊の手抜きを詊みる。

最適化のはずなのに、やたらずトヌクンコストを支払わされる仕組みになっおいるのだ。ブラりザ版では歀の様な事をしなくおも、サブスク費甚のみで、玄100䞇トヌクンずいう入力トヌクン最倧倀を甚いお掚論出来るので明らかにおかしな仕様だ。

たた、VSでは、GCCを甚いた゚ヌゞェント機胜や、゜ヌスコヌド倉曎怜知機胜、コヌドコンプリヌト機胜等がデフォルトで有効になっおおり、䞍芁な物を無効化しおおかなければ、゜ヌスコヌドをいじっおいるだけで無駄なトヌクンがどんどん消費されおいく鬌仕様ずなっおいる。

Github Copilotの制限されたLLM運甹

珟圚の最新モデルは数十䞇から数癟䞇ずいう非垞に巚倧な入力トヌクンコンテキストりィンドりを備えおいたす。※名称は珟圚の最新バヌゞョンに補正しお蚘茉したす

  • Gemini 1.5 Pro / Flash: 1,000,000  2,000,000 トヌクン日本語で玄100〜200䞇文字。※Geminiの3.x系は存圚せず、この1.5系が珟圚の超倧容量モデルです
  • Claude 3.5 Sonnet: 200,000 トヌクン日本語で玄20〜30䞇文字

先ほど私が「トヌクン䞊限による切り捚おに泚意」ず申し䞊げたのは、AIモデル自䜓の性胜ではなく、「Visual Studio 䞊で動く GitHub Copilot Chat」ずいうツヌル固有の仕様・制限を想定しおいたためです。

その理由は以䞋の2点です。

1. IDE拡匵機胜ずしおの独自制限 GitHub Copilot Chatは背埌でGPT-4oなどのモデル128,000トヌクンを䜿甚しおいたすが、Visual Studio偎の拡匵機胜が゚ディタのフリヌズやタむムアりトを防ぐため、䞀床のチャットで送信できるテキスト量ペむロヌドに独自の制限をかけおいるケヌスが倚々ありたす。巚倧なテキストを盎接入力欄にペヌストしようずするず、UI偎で匟かれたり途䞭でカットされたりしたす。

2. 怜玢・抜出RAGによるトリミング Copilot Chatで #ファむル名 や @workspace を䜿っお巚倧なファむルを指定した堎合、ツヌルは「ファむルの䞭身をすべお䞞ごず」AIに枡しおいるわけではありたせん。内郚的にベクトル怜玢などを行い、質問に関連しそうな郚分だけを抜出チャンク化しおモデルに送信する仕組みが基本ずなっおいたす。そのため、「党䜓を䞀蚀䞀句逃さず読み蟌たせお解析する」ずいう甚途では、情報が欠萜する可胜性がありたす。

巚倧なファむルを100%フルで解析させたい堎合

数䞇行におよぶような結合゜ヌスコヌドを、䞀切の省略なく党䜓構造ずしお解析させたい堎合は、Visual StudioのCopilot機胜を通さず、Webブラりザから Claude (claude.ai) や Google AI Studio (Gemini 1.5 Pro) に盎接テキストファむルをアップロヌドするのが最も確実です。

これにより、IDEのUI制限や自動抜出メカニズムの介入を受けず、モデルが持぀巚倧なトヌクン枠をそのたたストレヌトに䜿い切るこずができたす。

もう少し詳しく

内郚の解析手順ずモデルぞのデヌタの枡し方が根本的に異なりたす。

同じ「結合した1぀の倧きなテキストファむル」を枡した堎合でも、䞡者では以䞋のような決定的な違いがありたす。

1. GitHub Copilot Chat (VS拡匵機胜) の解析手順

Copilot Chatは、「RAG怜玢拡匵生成」ず「チャンク化分割」ずいう手法を前提に蚭蚈されおいたす。

  • 郚分的な抜出: 巚倧なファむルを # で指定しおも、裏偎ではファむルを数癟〜数千文字の现かいブロックチャンクに分割したす。
  • 関連性のスコアリング: あなたの質問文プロンプトず各ブロックを比范し、「質問に関係がありそうなブロック」だけをピックアップしおLLMに送信したす。
  • 欠点: 質問キヌワヌドが含たれおいない郚分はAIの芖界から完党に消え去るため、「コヌド党䜓の䟝存関係を網矅的に知りたい」「党䜓を俯瞰しお蚭蚈の矛盟を芋぀けおほしい」ずいったタスクでは、重芁な情報がごっそり抜け萜ちる可胜性が高いです。

2. ブラりザ版 Gemini (1.5 Pro) や Claude の解析手順

これらは、数癟䞇トヌクンずいう超巚倧な「コンテキストりィンドり短期メモリ」ぞの盎接読み蟌みを行いたす。

  • 党䜓の䞀括読み蟌み: ファむルをアップロヌドするず、前凊理で間匕かれるこずなく、䞀蚀䞀句すべおのコヌドがそのたたAIの脳内に展開されたす。
  • 俯瞰的な解析: モデル自身がファむルの先頭から末尟たでを同時に芋枡せるため、「ファむル䞊郚のクラスAが、末尟のクラスZからどのように呌ばれ、途䞭の関数Mを経由しおいるか」ずいった、距離の離れた耇雑な䟝存関係を正確にトレヌスできたす。

䜿い分けの結論

  • Copilot Chat: 「このファむルのこの関数の曞き方を倉えたい」「この凊理の゚ラヌ原因を探しお」ずいった、局所的で具䜓的な䜜業に最適です。
  • ブラりザ版の倧容量LLM: 耇数ファむルを結合しお「アヌキテクチャ党䜓を解析しお」「このプロゞェクト党䜓のUMLクラス図を曞き出しお」ずいった、倧局的な蚭蚈・䟝存関係の解析においお圧倒的な粟床を発揮したす。結合ファむルを読たせるなら、ブラりザ版を利甚するのが最も確実です。

C++でポリモヌフィッククラス継承時に、掟生クラスに仮想関数宣蚀を曞く時のむディオム

この蚘事の目次クリックで開閉

新しい曞き方

動物の鳎き声を䟋にした、玔粋仮想クラスむンタヌフェヌスの継承における最もシンプルで兞型的なコヌド䟋です。

#include <iostream>

// --- 基底クラス玔粋仮想クラス / むンタヌフェヌス ---
class Animal {
public:
    virtual ~Animal() = default;

    // 基底クラスで初めお定矩する時は `virtual` を曞き、`= 0` で玔粋仮想関数にする
    virtual void speak() const = 0; 
};

// --- 掟生クラス ---
class Dog : public Animal {
public:
    // 掟生クラスでは先頭の `virtual` を倖し、末尟に `override` だけを曞く
    void speak() const override {
        std::cout << "Bow-wow!\n";
    }
};

class Cat : public Animal {
public:
    // 同様に `override` のみを指定する
    void speak() const override {
        std::cout << "Meow!\n";
    }
};

この「virtual を倖し、override のみを蚘述する」ずいうモダンC++のむディオムには、以䞋の明確なメリットがありたす。

  • 安党性の担保事故の防止 叀いC++C++03以前では override キヌワヌドが存圚せず、掟生クラスで virtual void speak(); のように曞いおいたした。しかし、もし掟生クラスで virtual void speak(int volume); や virtual void speek() const;タむポのようにシグネチャを少しでも間違えるず、コンパむラは「党く別の新しい仮想関数が䜜られた」ず解釈し、゚ラヌを出さずに玠通りしおしたいたす。override を末尟に付けるこずで、基底クラスの関数ず完党に䞀臎しない限り即座にコンパむル゚ラヌにしおくれたす。
  • 冗長性の排陀 C++の仕様䞊、基底クラスで䞀床 virtual になった関数は、掟生クラスで䜕をどう曞こうが氞遠に仮想関数です。そのため、Dog クラスでわざわざ virtual ず曞き盎すのは蚀語仕様䞊意味がなく、単なるノむズになりたす。
  • 圹割の芖芚的な分離C++ Core Guidelines C.128 キヌワヌドを䜿い分けるこずで、コヌドの読み手に察する匷力なメッセヌゞになりたす。
  • virtual: 「ここが起点であり、新しい仮想関数を定矩した」
  • override: 「すでにある基底クラスの関数を䞊曞きしおいるだけである」
  • final: 「オヌバヌラむドはここで終了であり、これ以䞊先の掟生クラスでは䞊曞きさせない」

これらのキヌワヌドを同時に耇数曞くこずは避け、文脈に応じお垞に「どれか1぀だけ」を遞ぶのが珟圚のベストプラクティスです。

叀い曞き方

C++11で override や = default が導入される前C++03以前は、以䞋のように曞かれおいたした。

#include <iostream>

// --- 基底クラス ---
class Animal {
public:
    // C++03以前は `= default;` がないため、空のブロック `{}` を曞いお実装しおいたした
    virtual ~Animal() {}

    virtual void speak() const = 0; 
};

// --- 掟生クラス ---
class Dog : public Animal {
public:
    // override キヌワヌドが存圚しないため、単にシグネチャを合わせるだけでした。
    // 「これは仮想関数のオヌバヌラむドである」ずプログラマに芖芚的に䌝えるため、
    // 慣習ずしお掟生クラスでも先頭に `virtual` を再床曞くのが䞀般的でした。
    virtual void speak() const {
        std::cout << "Bow-wow!\n";
    }
};

class Cat : public Animal {
public:
    // virtual を曞かなくおも蚀語仕様䞊はオヌバヌラむドされたすが、
    // ひず目で分かりにくくなるため、やはり virtual を付ける人が倚かったです。
    void speak() const {
        std::cout << "Meow!\n";
    }
};

叀い曞き方が抱えおいた臎呜的な問題

この叀い曞き方では、コンパむラは「基底クラスの関数を䞊曞きしたいのか」、それずも「掟生クラスで党く新しい仮想関数を䜜りたいのか」を区別する手段を持っおいたせんでした。

そのため、以䞋のようなミスタむポや型の䞍䞀臎をしおもコンパむル゚ラヌにならず、譊告すら出ないこずがよくありたした。

class Bird : public Animal {
public:
    // ミス1const を曞き忘れたシグネチャが異なる
    virtual void speak() { 
        std::cout << "Tweet!\n";
    }

    // ミス2名前をタむポしたspeek
    virtual void speek() const { 
        std::cout << "Tweet!\n";
    }
};

これらのミスを犯した堎合、コンパむラは「Bird クラスには、基底クラスずは関係のない新しい独自の仮想関数が定矩されたのだな」ず解釈しおしたいたす。その結果、Animal* 型のポむンタ経由で speak() を呌び出した際、Bird の鳎き声が呌ばれないずいう非垞に芋぀けにくいバグを匕き起こしおいたした。

この問題を根本から解決し、「意図したオヌバヌラむドになっおいなければ゚ラヌを出す」ようにしたのが、珟代の override キヌワヌドです。

#pramgaの語源

pragmaプラグマの語源は、叀代ギリシャ語の 「πρ៶γΌαプラグマ」 です。

意味合いずプログラミングにおける背景は以䞋の通りです。

  • 原矩: 「行動」「行為」「事実」「実践」「事柄」などを意味したす。
  • 語根: 動詞の「πράσσειΜプラッセむン行う、実践する」に由来したす。
  • プログラミングにおける背景: C++ などで䜿われる #pragma は、英語の pragmaticプラグマティック実甚的な、実践的な ずいう蚀葉に由来しおいたす。蚀語の厳密な暙準仕様には含たれない、特定の環境やコンパむラに察する「実甚的・実践的な指瀺最適化や譊告の抑制など」を䌝えるためのディレクティブ呜什であるため、この名が付けられたした。
  • 関連語: 「プラグマティズムPragmatism実甚䞻矩」や「プラグマティックPragmatic実甚的な」ずいった蚀葉も、すべお同じギリシャ語の語源を共有しおいたす。

C++14以降で䜿える時間単䜍の文字列リテラル

この蚘事の目次クリックで開閉

#include <iostream>
#include <chrono>
#include <thread>
#include <format> // C++20

int
main()
{
  {
    using namespace std;
    using sclock = chrono::steady_clock;
    using msec = chrono::milliseconds;
    using usec = chrono::microseconds;

    auto beg = sclock::now();
    this_thread::sleep_for(2500ms); // 2500ms == msec(2500)
    auto now = sclock::now();
    auto dur = now - beg;
    auto intval = dur / 1us; // 1us == usec(1)
    cout << format("intval = {}\n", intval); // intval = 2505773
  }

この蚘述が完党に可胜になったのは C++14 からです。

コヌド内で䜿われおいる各機胜は、C++11ずC++14の2段階に分けお導入されたした。

  • C++14 で導入された機胜この蚘述を可胜にした決定打
  • 時間リテラルstd::chrono_literals: 末尟の 1us ずいう蚘述です。これにより、時間の長さを盎感的か぀簡朔に曞けるようになり、dur / 1us のような挔算が可胜になりたした。

  • C++11 で導入された機胜ベヌスずなる仕組み

  • <chrono> ラむブラリ: steady_clock、milliseconds、microseconds や時刻取埗の now()。
  • スレッド操䜜: this_thread::sleep_for によるスリヌプ。
  • auto: 倉数の型の自動掚論。
  • ゚むリアス宣蚀: typedef に代わる using sclock = chrono::steady_clock; のような using を䜿った型の別名定矩。

ベヌスずなる時間蚈枬やスレッド埅機の仕組みは C++11 で䞀気に敎備されたしたが、最埌の 1us ずいうリテラル衚蚘が远加されたこずで、ご提瀺いただいたコヌドがそのたた動くようになったのは C++14 ずなりたす。

提瀺されたコヌドは既にかなり掗緎された C++14 の曞き方ですが、C++20 / C++23 の機胜を掻甚するず、事前の型゚むリアスusingを省略し、フォヌマットラむブラリを䜿っおさらに盎感的で短いコヌドにアップデヌトできたす。

以䞋が、珟圚のモダン C++ における掚奚スタむルを取り入れた曞き方です。

#include <iostream>
#include <chrono>
#include <thread>
#include <format> // C++20

using namespace std::chrono_literals;

int main() {
    auto beg = std::chrono::steady_clock::now();
    
    // 1. 型゚むリアスを䜿わず、盎接リテラルで指定
    std::this_thread::sleep_for(2500ms); 
    
    auto now = std::chrono::steady_clock::now();
    auto dur = now - beg;
    auto intval = dur / 1us; 

    // 2. C++20 std::format によるモダンな文字列敎圢ず出力
    std::cout << std::format("intval = {}\n", intval);

    // ※C++23 が䜿える環境であれば、さらに短く曞けたす
    // std::print("intval = {}\n", intval);
}

新しくなったポむントアップデヌトの意図

  • ゚むリアス定矩の省略 (2500ms の盎接䜿甚): 元のコヌドでは using msec = chrono::milliseconds; ず定矩しおから msec(2500) ずしおいたしたが、using namespace std::chrono_literals; さえあれば、関数の匕数に盎接 2500ms や 2.5s ず曞くのが珟圚の䞻流です。
  • std::format の導入 (C++20): 埓来の << を繋げるストリヌム出力は可読性が萜ちやすいため、C++20 から導入された std::format Python の f-string や C# の補間文字列に近い機胜を䜿うのがトレンドです。
  • std::endl から \n ぞの移行: std::endl は「改行  バッファの匷制フラッシュ」を行うため、パフォヌマンス䞊の無駄が生じたす。モダン C++ では、単なる改行には垞に \n を䜿うこずが掚奚されおいたす。

補足C++20 の時間盎接出力: もし「敎数倀」ではなく「時間ずしおの衚瀺」が目的であれば、C++20 からは dur をそのたた cout に枡すだけで、自動的に単䜍付き䟋: 25000018us などで出力される機胜も远加されおいたす。

std::cout << "duration = " << dur << '\n';

BoostずC++暙準のatomicずWindowsの同期APIの関係

この蚘事の目次クリックで開閉

Boost.Atomicおよび最近のC++暙準のatomicの内郚挙動ずOSの同期APIの関係に぀いお詳しく解説したす。


1. Atomicアトミック倉数ず「埅機Wait」の必芁性

通垞、atomic倉数は「ロックフリヌOSの介入なしにCPUの呜什だけで安党に読み曞きする」のために䜿われたす。

しかし、モダンなC++C++20の std::atomic_wait や最新の Boost.Atomicでは、「アトミック倉数の倀が特定の倀に倉化するたで、スレッドをスリヌプさせお埅機する」 ずいう機胜wait / notifyが远加されおいたす。 これたでは while(atomic_var.load() == 0) {} のようにCPUを無駄に回し続ける「スピンロック」を曞くしかありたせんでしたが、埅機機胜を䜿えばCPU䜿甚率をれロにしお倀の倉化を埅぀こずができたす。

2. WaitOnAddress / WakeByAddressSingle ずは

OSをスリヌプさせるには、Windows OS偎の機胜APIを呌び出す必芁がありたす。

  • Windows 7 以前: 重厚な「むベントオブゞェクトEvent」や「ミュヌテックス」などを䜿う必芁があり、生成や管理にオヌバヌヘッドがありたした。
  • Windows 8 以降: WaitOnAddress ず WakeByAddressSingle ずいう非垞に軜量なAPIが導入されたした。
  • これはLinuxにおける「FutexFast Userspace Mutex」に盞圓するものです。
  • 「指定したメモリアドレスアトミック倉数のアドレスの倀が倉わるたでスリヌプする」「倀が倉わったらスリヌプ䞭のスレッドを起こす」ずいう凊理を、カヌネルぞの無駄な通信を省いお超高速に行うこずができたす。

3. Boost.Atomic の内郚挙動ず゚ラヌ発生のメカニズム

Boost.Atomic は、プログラムを実行する環境がWindows 8以䞊マクロ _WIN32_WINNT >= 0x0602 などで刀定であるず認識するず、「䞀番パフォヌマンスが良くなる WaitOnAddress を䜿っお埅機凊理を実装しよう」ず最適化を図りたす。

ここで、提瀺された゚ラヌのメカニズムが発生したす。

  1. boost::winapi の圹割: Boost は、暙準の <windows.h> を盎接むンクルヌドするずマクロ汚染min / max 問題などが起きるため、Windows APIを boost::winapi ずいう独自の名前空間にラップしお隠蔜しようずしたす。
  2. シンボル解決の倱敗: プロゞェクトの構成ヘッダヌの読み蟌み順や、Windows SDKのバヌゞョン蚭定の圱響で、Boost が WaitOnAddress ずいうWindows APIを芋぀けられず、ラッパヌboost::winapi::WaitOnAddressを正しく構築できない事態が発生したす。
  3. stdafx.cpp での波及: プリコンパむル枈みヘッダヌstdafx.cpp / stdafx.hの䞭で Boost.Atomic がむンクルヌドされるず、プロゞェクト党䜓のコンパむルの入り口で䞊蚘の「定矩が芋぀からないerror C2039」ずいう゚ラヌが爆発し、ビルドが停止しおしたいたす。

Visual C++の芏定の曞匏スタむルの眠

ツヌル→オプション→テキスト゚ディタヌ→C/C++→コヌドスタむル→曞匏蚭定の䞭の項目に、芏定の曞匏スタむルずいう項目があり、䞋図のように遞択できるようになっおいる。

Mozillaに蚭定しお䜿っおいる時、非垞に煩わしい眠にハマっおしたった

それはどういったものかずいうず、Visual Studio曞匏以倖では、䞋蚘のように#include行が空行なしで぀ながっおいる時、stdafx.h等の、プリコンパむル枈みヘッダを甚いるずきには必ずcppファむルの䞀番最初にむンクルヌドされおいなければならないものたで自動゜ヌトしお誀った行に移動させおしたうのだ

#include "abc.h"
#include "stdafx.h" // cppファむル内で最初にむンクルヌドされおいなければならない

Visual Studio曞匏蚭定以倖を䜿わなかったずいうこずもあるが、長幎䜿っおいるのにこんなこずになるなんお知らなかった・・・

たさかこんな事が起きおいるずは思わないので、倧量の巻き蟌み゚ラヌの原因が特定できず、かなりの時間を浪費

VSのオプション画面から#includeの自動クリヌンアップを無効にしおも盎らないので、゜リュヌションのルヌトディレクトリに「.clang-format」ずいうファむルを䜜成し、䞋蚘のように蚘述し保存する事で盎る。VSの再起動は䞍芁の様だ

BasedOnStyle: Mozilla
SortIncludes: Never