mcpField
H
header-optimization

header-optimization

github.com/openharmonyinsight開発3582 回インストール11 週間前に更新
リポジトリ

header-optimization について

header-optimizationは、ace_engineのC++ヘッダーのコンパイル負荷を抑えるスキルです。実装の移動、依存関係の整理、前方宣言の活用を案内し、分析のみのモードも備えています。

主な機能

  1. 01長いインライン実装をCPPファイルへ移動
  2. 02未使用のヘッダー依存を削除
  3. 03適用可能な箇所で前方宣言を使用
  4. 04定数や列挙型を別ヘッダーに分割
  5. 05分析のみのモードで改善案を提示

活用シーン

  1. 01特定ヘッダーのコンパイル負荷を軽減
  2. 02test_header.cppの依存分析結果を確認
  3. 03ファイルを変更せずに最適化案を作成

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/openharmonyinsight/openharmony-skills --skill header-optimization

インストール後、Claude Code で/header-optimizationを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

README.md
examples/
README.md
examples/before-after/
forward_declaration_example.md
inline_extraction_example.md
references/
case-click_event-forward-declaration.md
case-drag-drop-forward-declaration.md
case-drag-event-include-reduction.md
case-split-enums.md
forward-declaration.md
patterns.md
pimp-guide.md
scripts/
analyze-includes.sh
extract-includes.py

詳細

ACE Engine のヘッダーファイル最適化

依存関係の体系的な削減と実装の再構成により、ace_engine における C++ ヘッダーファイルのコンパイル効率を最適化します。このスキルでは、ヘッダーの依存関係を最小限に抑え、実装をヘッダーの外に移すことで、コンパイル時間とメモリ使用量を削減します。

このスキルを使用するタイミング

次の場合にこのスキルを呼び出します:

  • 特定のヘッダーファイルのコンパイル効率を最適化するとき
  • ヘッダーの依存関係を分析するために test_header.cpp を使用するとき
  • ace_engine のヘッダーファイルの依存関係を削減するとき
  • 特定のヘッダーのコンパイルパフォーマンスを分析するとき
  • ユーザーが「只分析不进行修改」(分析のみ、変更なし)と指定したとき

重要なルール

最適化中は、次の重要な制約を守ってください。

  1. ビジネスロジックを維持する - 変更するのは構造のみとし、機能は変更しません
  2. ファイルの作成を最小限に抑える - 現在のヘッダー/cpp を直接編集し、ヘッダー分割が必要な場合にのみ新しいファイルを作成します
  3. 対象範囲を限定する - 重要な手順で定義された作業のみを行い、変更範囲を広げません
  4. 単独でのコンパイル - 個別ファイルのコンパイル検証には compile-analysis スキルを使用し、フルビルドは行いません
  5. test_header.cpp は読み取り専用 - test_header.cpp は変更しません。依存関係の統計にのみ使用します

最適化ワークフロー

Step 1: 入力とモードを分析する

最適化対象とモードを特定します。

  • 入力がヘッダーファイル(.h)の場合:そのヘッダーを直接最適化します
  • 入力が test_header.cpp の場合:その内容からヘッダーファイルを特定し、そのヘッダーを最適化します
  • ユーザーが「只分析不进行修改」(分析のみのモード)を指定しているか確認します

分析のみのモードでは:

  • すべての分析手順を実行します
  • 最適化の提案を作成します
  • ファイルは変更しません
  • 分析結果と推奨される変更を提示し、ユーザーの承認を求めます
Step 2: インライン実装を CPP に移動する

目的:3 行を超えるすべてのメソッド実装をヘッダーから cpp ファイルに移動します。

手順:

  1. 対象のヘッダーファイルを読み込みます
  2. 実装が 3 行を超えるすべてのメソッド(静的メソッドを含む)を特定します
  3. 対応する cpp ファイルが存在しない場合は作成します
  4. メソッド実装を cpp ファイルに移動します
  5. ヘッダーファイルには宣言のみを残します
  6. テンプレートメソッドについては、Step 6 を参照してください

3 行として数えるもの:

// Less than 3 lines - Keep in header
int GetValue() const { return value_; }

// 3 lines or more - Move to cpp
void ProcessData() {
    auto result = Calculate();
    Validate(result);
    cache_.Store(result);
}

例外:単純なゲッター/セッターは、通常、パフォーマンス上の理由からヘッダーに残します。

Step 3: 不要なヘッダーインクルードを削除する

目的:使用されていないヘッダー依存関係を取り除きます。

手順:

  1. ヘッダーファイル内のすべての #include ディレクティブを列挙します
  2. インクルードされたヘッダーごとに、実際に使用されているか確認します:
    • そのヘッダーの型がファイル内で使用されているか検索します
    • メンバー変数、関数パラメーター、または戻り値の型でその型が使われているか確認します
    • 基底クラスの継承を確認します
  3. 実際に使用されていないインクルードを削除します
  4. 分析レポートに削除理由を記録します

例:

// Before
#include "base/memory/ace_type.h"
#include "core/components/common/layout.h"
#include "core/pipeline/base/element.h"  // Not used - REMOVE

// After
#include "base/memory/ace_type.h"
#include "core/components/common/layout.h"
Step 4: インクルードを前方宣言に置き換える

目的:可能な限り、完全なヘッダーのインクルードを前方宣言に置き換えます。

手順:

  1. 残っている各ヘッダーインクルードについて、何が必要かを分析します:
    • 型名のみ(ポインター、参照、関数パラメーター)
    • 型定義(完全なクラス定義が必要)
    • 静的定数または列挙型
  2. 型名のみが必要な場合は、前方宣言に置き換えます:
    // Before
    #include "core/components_ng/base/frame_node.h"
    
    // After
    namespace OHOS::Ace::NG {
    class FrameNode;
    }  // namespace OHOS::Ace::NG
    
  3. 次の場合に限り、完全なインクルードを残します:
    • クラスを継承している
    • クラスにその型のメンバー変数がある
    • テンプレートのインスタンス化に完全な定義が必要
    • 静的定数またはインラインメソッドにアクセスしている

前方宣言のテンプレート:

namespace OHOS::Ace {
namespace NG {
class FrameNode;
class UINode;
}  // namespace NG
}  // namespace OHOS
Step 4.1: 高度な前方宣言のケース

重要:このセクションでは、ace_engine の実際の最適化事例に基づいた、高度な前方宣言のパターンを扱います。

シナリオ 1:スマートポインターのテンプレートパラメーター(RefPtr/WeakPtr)

前方宣言を使用する場合:

  • 型が RefPtr<T> または WeakPtr<T> のテンプレートパラメーターとしてのみ使用されている
  • 型がスマートポインターを使ったメンバー変数として使用されている
  • 型がスマートポインターを使った関数の引数/戻り値として使用されている

ルール:スマートポインターのテンプレート(RefPtr、WeakPtr、std::unique_ptr、std::shared_ptr)では、ヘッダーファイル内に完全な型定義は必要ありません。

最適化前の例:

// click_event.h (BEFORE)
#include "core/components_ng/gestures/recognizers/click_recognizer.h"  // ❌ Full include

class ClickEventActuator {
private:
    RefPtr<ClickRecognizer> clickRecognizer_;  // Only needs forward declaration

    const RefPtr<ClickRecognizer>& GetClickRecognizer();  // Also OK with forward decl
};

最適化後の例:

// click_event.h (AFTER)
// Removed: #include "click_recognizer.h"
namespace OHOS::Ace::NG {
class ClickRecognizer;  // ✅ Forward declaration only
}

class ClickEventActuator {
private:
    RefPtr<ClickRecognizer> clickRecognizer_;  // Works with forward decl
};

// click_event.cpp
#include "core/components_ng/gestures/recognizers/click_recognizer.h"  // Full include here

const RefPtr<ClickRecognizer>& ClickEventActuator::GetClickRecognizer() {
    if (!clickRecognizer_) {
        clickRecognizer_ = MakeRefPtr<ClickRecognizer>();  // Needs full definition
    }
    return clickRecognizer_;
}

主なポイント:

  • ✅ RefPtr<T> を使ったメンバー変数は前方宣言で使用できます
  • ✅ 戻り値の型 const RefPtr<T>& は前方宣言で使用できます
  • ❌ 実際のインスタンス化 MakeRefPtr<T>() には .cpp で完全な型定義が必要です

確認:未定義の型に関するエラーがないことを確認するため、ヘッダーと cpp の両方をコンパイルしてください。


シナリオ 2:名前空間をまたぐ前方宣言

使用する場合:ある名前空間の型を、別の名前空間のヘッダーファイルで使用する必要がある場合。

パターン:適切な名前空間スコープに前方宣言を追加します。

例 - gesture_event_hub.h で ClickInfo が必要な場合:

// click_event.h
#include "base/memory/ace_type.h"
#include "core/components_ng/event/gesture_event_actuator.h"
#include "ui/gestures/gesture_event.h"         // Provides GestureEvent, GestureEventFunc
#include "core/components_ng/event/target_component.h"  // Provides GestureJudgeFunc

// Cross-namespace forward declaration for gesture_event_hub.h
namespace OHOS::Ace {
class ClickInfo;  // Type defined in OHOS::Ace, needed by gesture_event_hub.h
}

namespace OHOS::Ace::NG {
class GestureEventHub;   // Forward declaration in target namespace
class ClickRecognizer;   // Forward declaration in target namespace
}

class ClickEventActuator : public GestureEventActuator {
    // ... implementation
};

これが機能する理由:

  • gesture_event_hub.h が frame_node.h → click_event.h のチェーンを通じてインクルードされる場合
  • ClickInfo の前方宣言はすでに利用可能です
  • 循環依存を回避できます
  • 名前空間間の結合を弱められます

シナリオ 3:間接的な依存関係を適切なインクルードに置き換える

問題:click_recognizer.h のような大きなインクルードを削除すると、ほかの型が間接的にインクルードされていたため、コンパイルに失敗します。

解決策:必要な型定義ヘッダーだけを特定し、直接インクルードします。

例 - click_recognizer.h を削除する場合:

// BEFORE: Heavy indirect dependencies
#include "core/components_ng/gestures/recognizers/click_recognizer.h"
// This indirectly brought in:
// - tap_gesture.h (GestureEventFunc)
// - gesture_recognizer.h
// - multi_fingers_recognizer.h
// - And many more...

// AFTER: Precise includes
#include "ui/gestures/gesture_event.h"         // Provides GestureEvent, GestureEventFunc
#include "core/components_ng/event/target_component.h"  // Provides GestureJudgeFunc

// Forward declarations
namespace OHOS::Ace::NG {
class ClickRecognizer;  // Only name needed for RefPtr<ClickRecognizer>
}

分析手順:

  1. ヘッダー内で型が使用されている箇所を検索します(例:GestureEventFunc、GestureJudgeFunc)
  2. grep/検索ツールを使って、それらの型の定義場所を探します
  3. その型を定義しているヘッダーを直接インクルードします
  4. 大きなインクルードを削除し、RefPtr/WeakPtr 型については前方宣言に置き換えます

シナリオ 4:前方宣言に関する完全な判断表
使用パターン前方宣言を使用可能か完全なインクルードが必要か
T* メンバー変数✅ はい❌ いいえ
T& 引数/戻り値✅ はい❌ いいえ
RefPtr<T> メンバー✅ はい❌ いいえ
RefPtr<T>& 戻り値✅ はい❌ いいえ
WeakPtr<T> メンバー✅ はい❌ いいえ
std::unique_ptr<T>✅ はい❌ いいえ
std::shared_ptr<T>✅ はい❌ いいえ
std::vector<T>❌ いいえ*✅ はい
std::vector<T*>✅ はい❌ いいえ
クラスの継承❌ いいえ✅ はい
T メンバー変数❌ いいえ✅ はい
T 値渡しの引数❌ いいえ*✅ はい
テンプレートのインスタンス化❌ いいえ✅ はい
static メンバーへのアクセス❌ いいえ✅ はい
インラインメソッドへのアクセス❌ いいえ✅ はい

* extern テンプレートを使う例外があります


よくある落とし穴と解決策

落とし穴 1:インクルードを削除すると依存先のヘッダーが壊れる

症状:

error: unknown type name 'ClickInfo' in gesture_event_hub.h

根本原因:gesture_event_hub.h は click_recognizer.h から ClickInfo を間接的に取得していました

解決策:適切な名前空間に前方宣言を追加します

namespace OHOS::Ace {
class ClickInfo;  // Forward declaration for gesture_event_hub.h
}

落とし穴 2:インクルードを削除した後に型定義が不足する

症状:

error: unknown type name 'GestureEventFunc'
error: unknown type name 'GestureJudgeFunc'

根本原因:これらの型は click_recognizer.h を通じて間接的にインクルードされていました

解決策:これらの型を定義しているヘッダーを直接インクルードします

#include "ui/gestures/gesture_event.h"         // GestureEvent, GestureEventFunc
#include "core/components_ng/event/target_component.h"  // GestureJudgeFunc

落とし穴 3:前方宣言で名前空間が一致しない

症状:

error: no type named 'ClickInfo' in namespace 'OHOS::Ace::NG'

根本原因:ClickInfo は OHOS::Ace::NG ではなく OHOS::Ace にあります

解決策:前方宣言には正しい名前空間を使用します

namespace OHOS::Ace {        // Correct namespace
class ClickInfo;
}

namespace OHOS::Ace::NG {    // Different namespace
class ClickRecognizer;
}

実際のケーススタディ:click_event.h の最適化

最適化前:

// 214 lines, 6 includes
#include <list>
#include "base/memory/ace_type.h"
#include "base/memory/referenced.h"              // Redundant
#include "base/utils/noncopyable.h"
#include "core/components_ng/event/gesture_event_actuator.h"
#include "core/components_ng/gestures/recognizers/click_recognizer.h"  // Heavy include

namespace OHOS::Ace::NG {
class GestureEventHub;  // Only 1 forward declaration
}

// 15 inline method implementations

最適化後:

// 139 lines (-35%), 5 includes (-16.7%)
#include <list>
#include "base/memory/ace_type.h"
#include "base/utils/noncopyable.h"
#include "core/components_ng/event/gesture_event_actuator.h"
#include "ui/gestures/gesture_event.h"         // Precise include
#include "core/components_ng/event/target_component.h"  // Precise include

namespace OHOS::Ace {
class ClickInfo;  // Cross-namespace forward declaration
}

namespace OHOS::Ace::NG {
class GestureEventHub;    // Existing forward declaration
class ClickRecognizer;    // ✨ New forward declaration (RefPtr<T> optimization)
}

// 5 inline method implementations (moved 12 to cpp)

結果:

  • ✅ ヘッダーの行数が214行から139行に削減されました(-35.0%)
  • ✅ インクルードが6件から5件に削減されました(-16.7%)
  • ✅ 前方宣言が1件から3件に増加しました(+200%)
  • ✅ インライン実装が15件から5件に削減されました(-66.7%)
  • ✅ コンパイルが正常に成功することを確認しました
  • ✅ test_header.cpp の間接依存を確認しました

適用した主な手法:

  1. #include "click_recognizer.h" を前方宣言に置き換えました
  2. 型定義(GestureEventFunc、GestureJudgeFunc)に必要なインクルードを追加しました
  3. 名前空間をまたぐ ClickInfo の前方宣言を追加しました
  4. 12件のインライン実装を cpp ファイルに移動しました

ステップ 5:定数と列挙型のヘッダーを分割する

目的:頻繁に使用される定数や列挙型を別のヘッダーに切り出し、依存関係を減らします。

適用するタイミング:

  • 元のヘッダーが、少数の定数や列挙値のためだけにインクルードされている
  • 複数のファイルがこれらの定数に依存しているものの、ヘッダー全体には依存していない
  • 分割によって測定可能なコンパイル上のメリットが得られる

手順:

  1. 他のファイルの依存関係となっている定数や列挙型を特定します
  2. 新しいヘッダーファイル(例:original_types.h、original_constants.h)を作成します
  3. 切り出した定義を新しいヘッダーに移動します
  4. 元のヘッダーで、分割したヘッダーをインクルードするように更新します
  5. 依存するファイルでは、より小さなヘッダーだけをインクルードするように更新します

例:

// original.h (before)
#pragma once
namespace Constants {
    constexpr int MAX_VALUE = 100;
    enum class Type { A, B, C };
}

class BigImplementation {
    // ... hundreds of lines
};

// original_types.h (after split)
#pragma once
namespace Constants {
    constexpr int MAX_VALUE = 100;
    enum class Type { A, B, C };
}

// original.h (after split)
#pragma once
#include "original_types.h"
class BigImplementation {
    // ... implementation
};

確認:明確なコンパイル上のメリットが得られる場合に限り、分割してください。compile-analysis スキルを使って、実施前後を測定します。

ステップ 6:PIMPL パターンを適用する(該当する場合)

目的:実装の詳細を隠し、コンパイル時の依存関係を減らします。

適用するタイミング:

  • ヘッダーにプライベートメンバー由来の重い依存関係が多数ある
  • パブリック API は安定しているものの、実装は頻繁に変更される
  • ヘッダーの依存関係を減らすことで、コンパイル時間が大幅に改善される

手順:

  1. Impl クラスを作成します(ヘッダー内で前方宣言します)
  2. プライベートメンバーと重い依存関係を cpp 内の Impl クラスに移動します
  3. プライベートメンバーを std::unique_ptr に置き換えます
  4. コンストラクターとデストラクターを更新し、Impl のライフサイクルを管理します

PIMPL テンプレート:

// header.h
#pragma once
#include <memory>
namespace OHOS::Ace {
class MyClass {
public:
    MyClass();
    ~MyClass();

    // Public interface
    void PublicMethod();

private:
    class Impl;
    std::unique_ptr<Impl> impl_;
};

// cpp.cpp
#include "header.h"
#include "heavy_dependency.h"

class MyClass::Impl {
public:
    HeavyDependency dep_;
    // Private implementation details
};

MyClass::MyClass() : impl_(std::make_unique<Impl>()) {}
MyClass::~MyClass() = default;

void MyClass::PublicMethod() {
    impl_->dep_.DoSomething();
}

注意:PIMPL は実行時のオーバーヘッド(追加の割り当て、間接参照)を伴います。コンパイル上のメリットがコストに見合う場合に限り使用してください。

ステップ 7:テンプレートメソッドを最適化する

目的:明示的なインスタンス化によって、テンプレートのコンパイル時のオーバーヘッドを減らします。

手順:

  1. ヘッダー内のテンプレートメソッドを特定します
  2. 明示的なインスタンス化を使い、テンプレートを通常のメソッドに変換できるか確認します
  3. テンプレートの実装を cpp ファイルに移動します
  4. よく使用される型に対する明示的なインスタンス化の宣言を追加します

例:

// header.h (before)
template<typename T>
void Process(T value) {
    // Implementation
}

// header.h (after)
template<typename T>
void Process(T value);

extern template void Process<int>(int);
extern template void Process<float>(float);

// cpp.cpp (after)
template<typename T>
void Process(T value) {
    // Implementation
}

template void Process<int>(int);
template void Process<float>(float);

制限事項:テンプレートパラメーターの型が既知で、種類が限られている場合にのみ機能します。

ステップ 8:単独でのコンパイルを確認する

目的:最適化したコードが正しくコンパイルされることを確認します。

手順:

  1. compile-analysis スキルを使い、cpp ファイルのコンパイルコマンドを抽出します
  2. cpp ファイルを単独でコンパイルできることを確認します
  3. test_header.cpp の解析では、最適化したヘッダーを使ってコンパイルできることを確認します
  4. フルビルドは実行しないでください。単独でのコンパイル確認のみ行います

エラーへの対処:

  • コンパイルに失敗した場合は、エラーを分析します
  • 不足しているインクルードや前方宣言を修正します
  • コンパイルが成功するまで再確認します
  • 適用した修正をすべて記録します
ステップ 9:最適化の結果を測定する

目的:最適化の効果を定量化します。

収集する指標:

  1. ヘッダーの依存関係数:

    • 最適化前のインクルード数を数えます
    • 最適化後のインクルード数を数えます
    • 削減率を計算します
  2. コンパイル時間:

    • compile-analysis スキルを使って、実施前後を測定します
    • 短縮した時間を報告します
  3. メモリフットプリント:

    • 最適化前後のヘッダーファイルサイズを測定します
    • 削減率を報告します
  4. コード行数:

    • ヘッダーファイルの LOC 削減数
    • cpp ファイルに追加された新しい LOC

結果テンプレート:

## Optimization Results

### Header: frameworks/path/to/header.h

**Before Optimization:**
- Includes: 15
- Header size: 45.2 KB
- Estimated compile impact: High

**After Optimization:**
- Includes: 6
- Header size: 12.8 KB
- Reduction: 60% includes, 72% size

**Changes Made:**
- Moved 12 method implementations to cpp
- Converted 8 includes to forward declarations
- Removed 3 unused includes
- Split constants into separate header
- Applied PIMPL pattern (if applicable)

**Compilation Status:** ✅ Verified standalone compilation
ステップ 10:Git で変更をステージする

目的:最適化したファイルを Git に保存します。

手順:

  1. git add を使用して、変更したファイルをステージします:
    • 最適化したヘッダーファイル
    • 変更または作成した cpp ファイル
    • 新たに作成した分割ヘッダー
  2. test_header.cpp は参照用のみのため、ステージしないでください
  3. ステージした変更をユーザーに提示します

汎用的な最適化戦略

このセクションでは、ace_engine における実際の最適化事例から得られた汎用的なガイダンスを紹介します。状況に応じて、適切な最適化アプローチを選択するために、これらの戦略を活用してください。

戦略選択の判断ツリー
┌─────────────────────────────────────────────────────────────┐
│               What type of dependency problem?              │
└─────────────────────────────────────────────────────────────┘
                            │
        ┌───────────────────┼───────────────────┐
        │                   │                   │
   Heavy include        Value type        Cross-namespace
   within same        member from        include with
   namespace          heavy include    only enum usage
        │                   │                   │
        │                   │                   │
        ▼                   ▼                   ▼
┌───────────────┐  ┌───────────────┐  ┌───────────────┐
│  RefPtr<T>    │  │ unique_ptr<T> │  │ Light Include │
│  Forward Decl │  │  + Forward    │  │  Replacement │
│  (Scenario 1) │  │  Declaration  │  │  (Scenario 3) │
└───────────────┘  └───────────────┘  └───────────────┘
シナリオ 1:RefPtr の前方宣言(最も軽量)

使用する場面:

  • メンバーがすでに RefPtr<T> または WeakPtr<T> である
  • ヘッダーで必要なのは型名だけである
  • 値セマンティクスが必要ない

パターン:

// Header
namespace OHOS::Ace::NG {
class ClickRecognizer;  // Forward declaration
}

class ClickEventActuator {
private:
    RefPtr<ClickRecognizer> clickRecognizer_;  // ✅ Works with forward decl
};

// CPP
#include "core/components_ng/gestures/recognizers/click_recognizer.h"

const RefPtr<ClickRecognizer>& ClickEventActuator::GetClickRecognizer() {
    if (!clickRecognizer_) {
        clickRecognizer_ = MakeRefPtr<ClickRecognizer>();  // Full definition needed here
    }
    return clickRecognizer_;
}

⚠️ 重要:RefPtr メンバーでは前方宣言を使用できます

よくある誤解:「RefPtr メンバーには完全な型定義が必要」

✅ 正しい理解:RefPtr メンバーは前方宣言で問題なく機能します。ただし、次の条件を満たす必要があります。

  1. RefPtr メンバーを使用するインラインメソッドはすべて .cpp に移動する

    • member_->Method() を呼び出すメソッドはすべて .cpp に置く必要があります
    • MakeRefPtr<T>() を呼び出すメソッドはすべて .cpp に置く必要があります
    • RefPtr<T> を引数または戻り値の型に持つメソッドはすべて .cpp に置く必要があります
  2. ヘッダーには宣言のみを残す

    • RefPtr<T> member_; ✅ 前方宣言で動作します
    • RefPtr<T> GetMember(); ✅ 宣言のみ
    • void SetMember(const RefPtr<T>& member); ✅ 宣言のみ
  3. ヘッダーでインラインのインスタンス化を行わない

    • ❌ ヘッダー内の MakeRefPtr<T>() 呼び出し
    • ❌ ヘッダー内の member_->Method() 呼び出し
    • ✅ どちらも .cpp の実装に置く必要があります

gesture_recognizer.h の最適化例:

// gesture_recognizer.h - BEFORE (inline implementations)
class NGGestureRecognizer {
    bool IsSystemGesture() const {  // ❌ Inline - needs full GestureInfo definition
        if (!gestureInfo_) return false;
        return gestureInfo_->IsSystemGesture();
    }

    RefPtr<GestureInfo> GetGestureInfo() {  // ❌ Inline - returns RefPtr<GestureInfo>
        return gestureInfo_;
    }

private:
    RefPtr<GestureInfo> gestureInfo_;  // ❌ Requires full definition due to inline methods
};

// gesture_recognizer.h - AFTER (forward declaration + declarations)
namespace OHOS::Ace::NG {
class GestureInfo;  // ✅ Forward declaration only
}

class NGGestureRecognizer {
    bool IsSystemGesture() const;  // ✅ Declaration only

    RefPtr<GestureInfo> GetGestureInfo();  // ✅ Declaration only

private:
    RefPtr<GestureInfo> gestureInfo_;  // ✅ Works with forward declaration!
};

// gesture_recognizer.cpp - Full implementations
#include "core/components_ng/event/gesture_info.h"  // ✅ Full include here

bool NGGestureRecognizer::IsSystemGesture() const {
    if (!gestureInfo_) return false;
    return gestureInfo_->IsSystemGesture();  // ✅ Full definition available
}

RefPtr<GestureInfo> NGGestureRecognizer::GetGestureInfo() {
    return gestureInfo_;  // ✅ Can return RefPtr with full definition
}

重要な原則:RefPtr メンバー自体は、ヘッダー内に完全な型定義を必要としません。必要となるのはメンバー自体ではなく、そのメンバーを使用するインラインメソッドです。

利点:

  • ✅ デストラクターを分離する必要がない(RefPtr がライフサイクルを管理します)
  • ✅ コード変更を最小限に抑えられる
  • ✅ 実行時オーバーヘッドがゼロ
  • ✅ ace_engine で既に使われているパターン

実例:case-click_event-forward-declaration.md

複雑さ:簡単


シナリオ 2:値型から unique_ptr への変換

使用する場面:

  • 値型メンバーが重いインクルードに由来する
  • メンバーは実装(.cpp)でのみ使用される
  • 重いインクルードへの依存を取り除きたい
  • 小さな実行時オーバーヘッド(ヒープ割り当て)を許容できる

パターン:

// Before
#include "core/components_ng/gestures/gesture_info.h"  // Heavy include

class DragDropRelatedConfigurations {
private:
    DragPreviewOption previewOption_;  // ❌ Value type - needs full definition
};

// After
namespace OHOS::Ace::NG {
struct DragPreviewOption;  // Forward declaration
}

class DragDropRelatedConfigurations {
public:
    ~DragDropRelatedConfigurations() override;  // ✅ Declaration only

    ACE_FORCE_EXPORT const DragPreviewOption& GetOrCreateDragPreviewOption();  // ✅ Return const&

private:
    std::unique_ptr<DragPreviewOption> previewOption_;  // ✅ Smart pointer
};

// CPP
#include "core/components_ng/gestures/gesture_info.h"  // Full include

DragDropRelatedConfigurations::~DragDropRelatedConfigurations() = default;

const DragPreviewOption& DragDropRelatedConfigurations::GetOrCreateDragPreviewOption()
{
    if (!previewOption_) {
        previewOption_ = std::make_unique<DragPreviewOption>();
    }
    if (!previewOption_) {
        static DragPreviewOption defaultInstance;  // ✅ Non-const fallback
        return defaultInstance;
    }
    return *previewOption_;  // ✅ Zero-copy return
}

必要な変更:

  1. ✅ 前方宣言を追加する
  2. ✅ メンバーを std::unique_ptr<T> に変換する
  3. ✅ デストラクターを cpp に移動する(前方宣言を使う unique_ptr<T> では必須)
  4. ✅ 戻り値を const T& に変更する(コピーを行わない最適化)
  5. ✅ cpp を更新し、スマートポインターをデリファレンスする

利点:

  • ✅ 重いインクルードへの依存を取り除く
  • ✅ コピーを行わない戻り値の最適化
  • ✅ const の正しさを維持する
  • ✅ カプセル化を改善する

コスト:

  • ⚠️ ヒープ割り当てのオーバーヘッド(通常は無視できる程度)
  • ⚠️ デストラクターを cpp で管理する必要がある

実例:case-drag-drop-forward-declaration.md

複雑さ:中程度


シナリオ 3:インクルードの置き換え(軽量インクルード戦略)

使用するケース:

  • 名前空間をまたぐ重いインクルードがある
  • そのヘッダーから必要なのは列挙型/定数のみ
  • 新しい軽量ヘッダーファイルを作成できる
  • ほとんどの型を前方宣言できる

パターン:

// Before - Heavy include
#include "core/gestures/drag_event.h"  // ~200 lines, full implementations

class DragEventActuator {
private:
    OptionsAfterApplied optionsAfterApplied_;  // Value type
};

// After - Light include + forward declarations
#include "core/gestures/drag_constants.h"  // ✅ Only enums (~15 lines)

namespace OHOS::Ace {
class DragEvent;  // Forward declaration
}

namespace OHOS::Ace::NG {
struct OptionsAfterApplied;  // Forward declaration
}

class DragEventActuator {
public:
    const OptionsAfterApplied& GetOptionsAfterApplied();  // Declaration only

private:
    std::unique_ptr<OptionsAfterApplied> optionsAfterApplied_;  // ✅ Smart pointer
};

// Create light header: drag_constants.h
namespace OHOS::Ace {
enum class PreDragStatus : int32_t {
    READY = 0,
    PROCESSING = 1,
    FAILED = 2,
};
}

必要な変更:

  1. ✅ 新しい軽量ヘッダー(*_constants.h)を作成する
  2. ✅ 列挙型/定数を軽量ヘッダーに抽出する
  3. ✅ 重いインクルードを軽量インクルードに置き換える
  4. ✅ 複雑な型の前方宣言を追加する
  5. ✅ 値メンバーをスマートポインターに変換する(該当する場合)
  6. ✅ 実装を cpp に移動する

メリット:

  • ✅ 依存関係を大幅に削減(90%以上)
  • ✅ 名前空間の境界がより明確になる
  • ✅ コード構成が改善される
  • ✅ 他のファイルでも再利用できる軽量ヘッダー

コスト:

  • ⚠️ 新しいファイル(軽量ヘッダー)が作成される
  • ⚠️ シナリオ 1 より変更が多い

実例:case-drag-event-include-reduction.md

複雑さ:中


戦略の比較表
項目シナリオ 1:RefPtrシナリオ 2:unique_ptrシナリオ 3:軽量インクルード
主な使用ケースすでにスマートポインターを使用している値型メンバー名前空間をまたぐ重いインクルード
新しいファイルを作成するか?いいえいいえはい(軽量ヘッダー)
デストラクターの分離不要必須必須
戻り値の型の変更RefPtr<T>& が必要になる場合がある値 → const T&必要な場合と不要な場合がある
実行時のオーバーヘッドなし小(ヒープ割り当て)小(ヒープ割り当て)
依存関係の削減大大非常に大(90%以上)
コードの複雑さ低中中
最適な対象RefPtr/WeakPtr メンバー値型メンバー名前空間をまたぐ依存関係

一般的な実装パターン
パターン 1:デストラクターの分離

使用するケース:スマートポインターメンバーで前方宣言を使用している場合

理由:スマートポインターのデストラクターには完全な型定義が必要です

テンプレート:

// Header
class MyClass {
public:
    ~MyClass();  // ✅ Declaration only

private:
    std::unique_ptr<ForwardDeclaredType> member_;
};

// CPP
MyClass::~MyClass() = default;  // ✅ Implementation here

パターン 2:ゼロコピーの戻り値

使用するケース:スマートポインターが管理するオブジェクトを返す場合

理由:不要なオブジェクトのコピーを避けるため

テンプレート:

// ❌ Before - Returns by value (creates copy)
DragPreviewOption GetOption() {
    return *previewOption_;
}

// ✅ After - Returns const reference (zero copy)
const DragPreviewOption& GetOption() {
    if (!previewOption_) {
        static DragPreviewOption defaultInstance;
        return defaultInstance;
    }
    return *previewOption_;
}

パターン 3:静的なフォールバックインスタンス

使用するケース:スマートポインターが null の可能性があり、安全なデフォルト値が必要な場合

テンプレート:

const OptionsAfterApplied& GetOptionsAfterApplied()
{
    if (!optionsAfterApplied_) {
        static OptionsAfterApplied defaultInstance;  // ✅ Non-const
        return defaultInstance;
    }
    return *optionsAfterApplied_;
}

const ではない理由:複雑な型の const static 初期化は、コンストラクターを持つ型では失敗する場合があります。


パターン 4:条件付きの間接参照

使用するケース:値型を期待する関数にスマートポインターの値を渡す場合

テンプレート:

// When calling: void SetOptionsAfterApplied(const OptionsAfterApplied& options)

// ❌ Wrong - Compile error
frameNode->SetOptionsAfterApplied(optionsAfterApplied_);

// ✅ Correct - Conditional dereference
frameNode->SetOptionsAfterApplied(
    optionsAfterApplied_ ? *optionsAfterApplied_ : OptionsAfterApplied()
);

汎用的な判断チェックリスト

このチェックリストを使って、適用する最適化戦略を判断します。

ステップ 1:依存関係を分析する
  • 問題の原因となっている重いインクルードを特定する
  • ヘッダー内で、そのインクルードに含まれる型がどこで使われているかすべて検索する
  • 使用箇所を分類する:
    • メンバー変数(値型またはスマートポインター)
    • 関数のパラメーター/戻り値
    • 基底クラスの継承
    • テンプレートのインスタンス化
ステップ 2:戦略を選ぶ

すでに RefPtr/WeakPtr を使用している場合 → シナリオ 1

  • ✅ 前方宣言を追加するだけ
  • ✅ ほかの変更は不要

同じ名前空間の値型メンバーの場合 → シナリオ 2

  • ✅ unique_ptr に変換する
  • ✅ デストラクターを cpp に移動する
  • ✅ 値渡しではなく const& を返す

列挙型のみを使用する名前空間をまたぐインクルードの場合 → シナリオ 3

  • ✅ 軽量ヘッダー(*_constants.h)を作成する
  • ✅ 重いインクルードを軽量インクルードに置き換える
  • ✅ 複雑な型の前方宣言を追加する
  • ✅ 値メンバーをスマートポインターに変換する(該当する場合)

上記のいずれにも該当しない場合 → PIMPL を検討する

  • 詳細なガイダンスは references/pimp-guide.md を参照してください
ステップ 3:変更を実装する
  • 前方宣言を追加する
  • メンバーをスマートポインターに変換する(該当する場合)
  • デストラクターを cpp に移動する(unique_ptr を使用する場合)
  • インライン実装を cpp に移動する
  • 戻り値の型を const& に更新する
  • cpp でスマートポインターを間接参照するように更新する
  • cpp に完全なインクルードを追加する
  • 単独でコンパイルできることを確認する
ステップ 4:結果を検証する
  • ヘッダーが単独でコンパイルできることを確認します
  • cpp がすべてのインクルードを含めてコンパイルできることを確認します
  • 依存ファイルにコンパイルエラーがないことを確認します
  • 依存関係の削減量を測定します

よくある落とし穴
落とし穴 1:デストラクターの分離を忘れる

症状:

error: invalid application of 'sizeof' to an incomplete type

原因:前方宣言を使用した unique_ptr では、デストラクターを cpp に記述する必要があります

解決策:デストラクターの実装を cpp ファイルに移します


落とし穴 2:unique_ptr への変換後に値で返す

症状:不要なオブジェクトコピーが発生し、パフォーマンスが低下する可能性があります

原因:メンバーを unique_ptr に変更したものの、値で返したままになっています

解決策:戻り値の型を const T& に変更します


落とし穴 3:デリファレンスの漏れ

症状:スマートポインターを関数に渡すとコンパイルエラーになります

原因:値型が必要な箇所で unique_ptr のデリファレンスを忘れています

解決策:条件付きデリファレンスのパターンを使用します:

function(ptr ? *ptr : Type());

落とし穴 4:const 静的初期化の失敗

症状:複雑な型の静的初期化に失敗します

原因:複雑なコンストラクターを持つ型に static const T を使用しています

解決策:代替策として static T(非 const)を使用します


追加リソース

参照ファイル

詳しい手法や例については、以下を参照してください:

  • references/patterns.md - ヘッダー最適化に関する一般的なリファクタリングパターン
  • references/pimp-guide.md - ace_engine の例を含む PIMPL パターンの詳細ガイド
  • references/forward-declaration.md - 前方宣言のベストプラクティス
  • references/case-split-enums.md - 実際の事例:drag_event.h から drag_constants.h へ列挙型を分割(依存関係を 90% 以上削減)
  • references/case-click_event-forward-declaration.md - 実際の事例:click_event.h の RefPtr 前方宣言最適化(サイズを 35% 削減、スマートポインター最適化パターン)
  • references/case-drag-drop-forward-declaration.md - ✨ 新着:実際の事例:drag_drop_related_configuration.h の値型メンバーを unique_ptr に変換
  • references/case-drag-event-include-reduction.md - ✨ 新着:実際の事例:drag_event.h の名前空間をまたぐ依存関係に対する軽量なインクルード置換戦略
例

examples/ にある実例:

  • before-after/ - 最適化前後の比較
  • pimpl-example/ - PIMPL の完全な実装例
  • split-header/ - ヘッダー分割の例
スクリプト

/scripts/ にあるユーティリティスクリプト:

  • analyze-includes.sh - ヘッダーのインクルード依存関係を分析します
  • extract-includes.py - ヘッダーからインクルード統計を抽出します

よくある落とし穴

次のことはしないでください:

  • いかなる理由でも test_header.cpp を変更する
  • 最適化中に全体ビルドを実行する
  • ビジネスロジックを変更したり、動作を変えたりする
  • (分割シナリオ以外で)不要な追加ファイルを作成する
  • 最適化の範囲を定義された手順を超えて広げる
  • 単独コンパイルの検証を忘れる

必ず行ってください:

  • 各最適化を単独コンパイルで検証する
  • 変更内容とその理由を記録する
  • コンパイルコマンドの抽出には compile-analysis スキルを使用する
  • ヘッダーの依存関係削減に注力する
  • 機能的な同等性を維持する
  • 最適化が成功したら git add で変更をステージする

分析のみのモード

ユーザーが「分析のみで変更はしない」と指定した場合:

  1. すべての分析手順(1~9)を実行します
  2. 以下を含む詳細なレポートを作成します:
    • 現在のヘッダー依存関係の分析
    • 推奨する最適化とその理由
    • 期待される改善
    • 提案する具体的なコード変更
  3. レポートをユーザーに提示します
  4. 変更を行う前に承認を求めます
  5. ユーザーが承認するまで、ファイルを一切変更しないでください

compile-analysis スキルとの連携

このスキルは、コンパイル効率を測定するために compile-analysis スキルと連携します。

  1. compile-analysis を使用して、コンパイルコマンドを抽出します
  2. 最適化前のコンパイル時間を測定します
  3. 最適化を適用します
  4. 最適化後のコンパイル時間を測定します
  5. 結果を比較して報告します

コンパイルコマンドの抽出が必要な場合は、次を呼び出します。

Use compile-analysis skill to extract compilation command for [file.cpp]

コメント