header-optimization について
header-optimizationは、ace_engineのC++ヘッダーのコンパイル負荷を抑えるスキルです。実装の移動、依存関係の整理、前方宣言の活用を案内し、分析のみのモードも備えています。
主な機能
- 01長いインライン実装をCPPファイルへ移動
- 02未使用のヘッダー依存を削除
- 03適用可能な箇所で前方宣言を使用
- 04定数や列挙型を別ヘッダーに分割
- 05分析のみのモードで改善案を提示
活用シーン
- 01特定ヘッダーのコンパイル負荷を軽減
- 02test_header.cppの依存分析結果を確認
- 03ファイルを変更せずに最適化案を作成
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ openharmonyinsight/ openharmony-skills --skill header-optimizationインストール後、Claude Code で/header-optimizationを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
README.mdexamples/README.mdexamples/before-after/forward_declaration_example.mdinline_extraction_example.mdreferences/case-click_event-forward-declaration.mdcase-drag-drop-forward-declaration.mdcase-drag-event-include-reduction.mdcase-split-enums.mdforward-declaration.mdpatterns.mdpimp-guide.mdscripts/analyze-includes.shextract-includes.py詳細
ACE Engine のヘッダーファイル最適化
依存関係の体系的な削減と実装の再構成により、ace_engine における C++ ヘッダーファイルのコンパイル効率を最適化します。このスキルでは、ヘッダーの依存関係を最小限に抑え、実装をヘッダーの外に移すことで、コンパイル時間とメモリ使用量を削減します。
このスキルを使用するタイミング
次の場合にこのスキルを呼び出します:
- 特定のヘッダーファイルのコンパイル効率を最適化するとき
- ヘッダーの依存関係を分析するために test_header.cpp を使用するとき
- ace_engine のヘッダーファイルの依存関係を削減するとき
- 特定のヘッダーのコンパイルパフォーマンスを分析するとき
- ユーザーが「只分析不进行修改」(分析のみ、変更なし)と指定したとき
重要なルール
最適化中は、次の重要な制約を守ってください。
- ビジネスロジックを維持する - 変更するのは構造のみとし、機能は変更しません
- ファイルの作成を最小限に抑える - 現在のヘッダー/cpp を直接編集し、ヘッダー分割が必要な場合にのみ新しいファイルを作成します
- 対象範囲を限定する - 重要な手順で定義された作業のみを行い、変更範囲を広げません
- 単独でのコンパイル - 個別ファイルのコンパイル検証には compile-analysis スキルを使用し、フルビルドは行いません
- test_header.cpp は読み取り専用 - test_header.cpp は変更しません。依存関係の統計にのみ使用します
最適化ワークフロー
Step 1: 入力とモードを分析する
最適化対象とモードを特定します。
- 入力がヘッダーファイル(
.h)の場合:そのヘッダーを直接最適化します - 入力が
test_header.cppの場合:その内容からヘッダーファイルを特定し、そのヘッダーを最適化します - ユーザーが「只分析不进行修改」(分析のみのモード)を指定しているか確認します
分析のみのモードでは:
- すべての分析手順を実行します
- 最適化の提案を作成します
- ファイルは変更しません
- 分析結果と推奨される変更を提示し、ユーザーの承認を求めます
Step 2: インライン実装を CPP に移動する
目的:3 行を超えるすべてのメソッド実装をヘッダーから cpp ファイルに移動します。
手順:
- 対象のヘッダーファイルを読み込みます
- 実装が 3 行を超えるすべてのメソッド(静的メソッドを含む)を特定します
- 対応する cpp ファイルが存在しない場合は作成します
- メソッド実装を cpp ファイルに移動します
- ヘッダーファイルには宣言のみを残します
- テンプレートメソッドについては、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: 不要なヘッダーインクルードを削除する
目的:使用されていないヘッダー依存関係を取り除きます。
手順:
- ヘッダーファイル内のすべての
#includeディレクティブを列挙します - インクルードされたヘッダーごとに、実際に使用されているか確認します:
- そのヘッダーの型がファイル内で使用されているか検索します
- メンバー変数、関数パラメーター、または戻り値の型でその型が使われているか確認します
- 基底クラスの継承を確認します
- 実際に使用されていないインクルードを削除します
- 分析レポートに削除理由を記録します
例:
// 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: インクルードを前方宣言に置き換える
目的:可能な限り、完全なヘッダーのインクルードを前方宣言に置き換えます。
手順:
- 残っている各ヘッダーインクルードについて、何が必要かを分析します:
- 型名のみ(ポインター、参照、関数パラメーター)
- 型定義(完全なクラス定義が必要)
- 静的定数または列挙型
- 型名のみが必要な場合は、前方宣言に置き換えます:
// Before #include "core/components_ng/base/frame_node.h" // After namespace OHOS::Ace::NG { class FrameNode; } // namespace OHOS::Ace::NG - 次の場合に限り、完全なインクルードを残します:
- クラスを継承している
- クラスにその型のメンバー変数がある
- テンプレートのインスタンス化に完全な定義が必要
- 静的定数またはインラインメソッドにアクセスしている
前方宣言のテンプレート:
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>
}
分析手順:
- ヘッダー内で型が使用されている箇所を検索します(例:
GestureEventFunc、GestureJudgeFunc) - grep/検索ツールを使って、それらの型の定義場所を探します
- その型を定義しているヘッダーを直接インクルードします
- 大きなインクルードを削除し、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 の間接依存を確認しました
適用した主な手法:
#include "click_recognizer.h"を前方宣言に置き換えました- 型定義(
GestureEventFunc、GestureJudgeFunc)に必要なインクルードを追加しました - 名前空間をまたぐ
ClickInfoの前方宣言を追加しました - 12件のインライン実装を cpp ファイルに移動しました
ステップ 5:定数と列挙型のヘッダーを分割する
目的:頻繁に使用される定数や列挙型を別のヘッダーに切り出し、依存関係を減らします。
適用するタイミング:
- 元のヘッダーが、少数の定数や列挙値のためだけにインクルードされている
- 複数のファイルがこれらの定数に依存しているものの、ヘッダー全体には依存していない
- 分割によって測定可能なコンパイル上のメリットが得られる
手順:
- 他のファイルの依存関係となっている定数や列挙型を特定します
- 新しいヘッダーファイル(例:
original_types.h、original_constants.h)を作成します - 切り出した定義を新しいヘッダーに移動します
- 元のヘッダーで、分割したヘッダーをインクルードするように更新します
- 依存するファイルでは、より小さなヘッダーだけをインクルードするように更新します
例:
// 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 は安定しているものの、実装は頻繁に変更される
- ヘッダーの依存関係を減らすことで、コンパイル時間が大幅に改善される
手順:
- Impl クラスを作成します(ヘッダー内で前方宣言します)
- プライベートメンバーと重い依存関係を cpp 内の Impl クラスに移動します
- プライベートメンバーを std::unique_ptr に置き換えます
- コンストラクターとデストラクターを更新し、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:テンプレートメソッドを最適化する
目的:明示的なインスタンス化によって、テンプレートのコンパイル時のオーバーヘッドを減らします。
手順:
- ヘッダー内のテンプレートメソッドを特定します
- 明示的なインスタンス化を使い、テンプレートを通常のメソッドに変換できるか確認します
- テンプレートの実装を cpp ファイルに移動します
- よく使用される型に対する明示的なインスタンス化の宣言を追加します
例:
// 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:単独でのコンパイルを確認する
目的:最適化したコードが正しくコンパイルされることを確認します。
手順:
- compile-analysis スキルを使い、cpp ファイルのコンパイルコマンドを抽出します
- cpp ファイルを単独でコンパイルできることを確認します
- test_header.cpp の解析では、最適化したヘッダーを使ってコンパイルできることを確認します
- フルビルドは実行しないでください。単独でのコンパイル確認のみ行います
エラーへの対処:
- コンパイルに失敗した場合は、エラーを分析します
- 不足しているインクルードや前方宣言を修正します
- コンパイルが成功するまで再確認します
- 適用した修正をすべて記録します
ステップ 9:最適化の結果を測定する
目的:最適化の効果を定量化します。
収集する指標:
-
ヘッダーの依存関係数:
- 最適化前のインクルード数を数えます
- 最適化後のインクルード数を数えます
- 削減率を計算します
-
コンパイル時間:
- compile-analysis スキルを使って、実施前後を測定します
- 短縮した時間を報告します
-
メモリフットプリント:
- 最適化前後のヘッダーファイルサイズを測定します
- 削減率を報告します
-
コード行数:
- ヘッダーファイルの 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 に保存します。
手順:
git addを使用して、変更したファイルをステージします:- 最適化したヘッダーファイル
- 変更または作成した cpp ファイル
- 新たに作成した分割ヘッダー
- test_header.cpp は参照用のみのため、ステージしないでください
- ステージした変更をユーザーに提示します
汎用的な最適化戦略
このセクションでは、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 メンバーは前方宣言で問題なく機能します。ただし、次の条件を満たす必要があります。
-
RefPtr メンバーを使用するインラインメソッドはすべて .cpp に移動する
member_->Method()を呼び出すメソッドはすべて .cpp に置く必要がありますMakeRefPtr<T>()を呼び出すメソッドはすべて .cpp に置く必要がありますRefPtr<T>を引数または戻り値の型に持つメソッドはすべて .cpp に置く必要があります
-
ヘッダーには宣言のみを残す
RefPtr<T> member_;✅ 前方宣言で動作しますRefPtr<T> GetMember();✅ 宣言のみvoid SetMember(const RefPtr<T>& member);✅ 宣言のみ
-
ヘッダーでインラインのインスタンス化を行わない
- ❌ ヘッダー内の
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
}
必要な変更:
- ✅ 前方宣言を追加する
- ✅ メンバーを
std::unique_ptr<T>に変換する - ✅ デストラクターを cpp に移動する(前方宣言を使う
unique_ptr<T>では必須) - ✅ 戻り値を
const T&に変更する(コピーを行わない最適化) - ✅ 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,
};
}
必要な変更:
- ✅ 新しい軽量ヘッダー(*_constants.h)を作成する
- ✅ 列挙型/定数を軽量ヘッダーに抽出する
- ✅ 重いインクルードを軽量インクルードに置き換える
- ✅ 複雑な型の前方宣言を追加する
- ✅ 値メンバーをスマートポインターに変換する(該当する場合)
- ✅ 実装を 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~9)を実行します
- 以下を含む詳細なレポートを作成します:
- 現在のヘッダー依存関係の分析
- 推奨する最適化とその理由
- 期待される改善
- 提案する具体的なコード変更
- レポートをユーザーに提示します
- 変更を行う前に承認を求めます
- ユーザーが承認するまで、ファイルを一切変更しないでください
compile-analysis スキルとの連携
このスキルは、コンパイル効率を測定するために compile-analysis スキルと連携します。
- compile-analysis を使用して、コンパイルコマンドを抽出します
- 最適化前のコンパイル時間を測定します
- 最適化を適用します
- 最適化後のコンパイル時間を測定します
- 結果を比較して報告します
コンパイルコマンドの抽出が必要な場合は、次を呼び出します。
Use compile-analysis skill to extract compilation command for [file.cpp]