標準ドキュメント ジェネレーター バージョン 0.2:エンティティ リンク、インデックス生成など

2 か月前に、標準バージョン 0.1 をリリースしました。次のバージョンは、最初のバージョンほど長くはかからないと約束しましたが、それには 1 か月かかりました。

しかし、このリリースでは、standardese を実際に使用可能なドキュメント ジェネレーターにするために最後に欠けていた機能が追加されました。インデックスの生成、ドキュメントの他の部分の参照、より多くの出力形式、およびオーバーホールされたコメント システムなどのその他の素晴らしい機能です。内部の変更も多数あります。およびバグ修正。

foonathan/standardese は、Doxygen の代替となることを目的とした C++ ドキュメンテーション ツールです。まだ WIP ですが、多くのバグが含まれている可能性がありますが、ドキュメンテーションに使用できるように十分にサポートしています。

解析状況の更新

前回の投稿で、libclang とその制限について不満を述べました。機能が不足しているため、Boost.Wave を使用して、ソース コードを独自に解析する必要がありました。

私の構文解析コードが完全ではないことは明らかです。前回の更新以降、私のコードでは処理できなかった C++ 機能の多かれ少なかれ異常な組み合わせについて、多くのバグを修正しました。私のコードが正しく処理できない特殊なケースがいくつかあります。しかし、これらは特殊な構文規則の非常に奇妙な組み合わせです。私のコードを故意に壊そうとせずにそれらを書く人がいるとは思えません。

コードの解析についてはあまり作業しません。残りのバグは「オンデマンド」で修正されます。コードが正しく処理されない場合は修正します。しかし、誰もバグを見つけられない場合は修正しません。それらを修正してください。

私の解析コードには別の欠点があります。Boost.Wave は巨大なヘッダーのみのライブラリであり、ライブラリのコンパイル時間が大幅に増加します。したがって、これは恒久的な解決策ではありません。

解析状況への反応として、多くの人が私が開発者に連絡してそれについて尋ねるべきだと提案しました.私はメーリングリストを書きました-ええ-メールでそれを行いました.一般的な反応は、libclangは私がバグに対して行っているのと同じポリシーに従うというものでした:誰かが文句を言って、彼らは何かをするかもしれません.しかし、自分でパッチを提出するだけの方が確実に高速です.

長期的な目標として、私はまさにそれをしなければなりません.しかし、今のところ、私はBoost.Waveパーサーを使用します.結局のところ、それは機能します.標準化が私が計画した機能のほとんどを備えた後、私は元に戻ります. libclang について何かを行いますが、わかりません。

コメントのフォーマット

以前のバージョンでは、Markdown を使用してドキュメント コメントをフォーマットできました。しかし、出力形式が Markdown で、コメントがコピーされただけだったため、Markdown しか使用できませんでした。

これが変更され、コメント テキストが適切に解析されるようになりましたが、実際には、Markdown を使用できます:CommonMark.解析は、cmark ライブラリによって行われます。

前回の投稿で、libclang について怒鳴りました。今度は、cmark を賞賛したいと思います。これは素晴らしいライブラリであり、シンプルで、適切に設計された、一貫性のある C API であり、必要なものをすべて公開します。 CMake は、私の用途にぴったりです。強くお勧めします。

cmark の C 階層が解析され、単純なクラス階層を作成するために使用されます。この AST は、必要に応じてわずかに変更されており、標準のセクションとコマンドもサポートしています。

CommonMark 段落の先頭にセクションを指定し、コマンド 段落の各行にコマンドを指定できるようになりました 、コマンドで始まる段落。これは次のようになります:

/// The implicit brief section.
///
/// \effects The effects paragraph.
/// Still effects.
///
/// \returns The returns paragraph.
/// \effects <- this is a literal string here.
///
/// \param bar A parameter documentation.
/// Still the entire paragraph.
///
/// \unique_name foo
/// \exclude
void foo(int bar);

最後の段落はコマンドで始まるため、リテラル文字列とは異なり、各行が適切に解析されます。セクションとコマンドの詳細については readme を、コマンドの詳細についてはこの投稿の残りの部分をお読みください。

コメント マッチング

libclang 関数 124 を使用する前に エンティティのコメントを取得します。libclang の多くのものと同様に、これにはいくつかの制限があり、マクロなどの一部のエンティティに対してコメントを返しませんでした。

現在、このシステムは完全に変更されています。ソース コード全体がドキュメント コメント用にスキャンされるようになりました。サポートされるのは 136 です。 、 147150164 および行末コメント 174 - 保存されたコンテンツ。コメント マーカーとそれに続く 1 つの空白が自動的に削除されます。C スタイルのコメントでは、不要な 186 も無視されます。 ある場合は、次の行に:

/** This is comment text.
 * This again, without the star.
 * This has two leading spaces, because one is stripped.
 */

行末コメントもサポートされており、次の C++ スタイル ドキュメント コメントとマージされます:

enum foo
{
 a, //< Comment for a.
 b, //< Comment for b.
 /// Still comment for b.
 c, //< Comment for c.
};

コメントは、コメントのすぐ下または同じ行にあるエンティティに一致します。さらに、このシステムでは リモート コメント が許可されていました。 および インライン コメント .

リモート コメント

各エンティティには 198 が関連付けられています 、それについてはリンク段落で詳しく説明します。対応するエンティティなしでコメントを書き、 207 を使用してそれを自分で指定することもできます コマンド:

void foo();

/// \entity foo
///
/// This is the comment for the function foo.

これはリモート コメントであり、ドキュメントをエンティティとは別の場所に置くことができます。

ファイルの特別なショートカット コマンドは 212 です command.225と同じです また、ファイルのドキュメントを作成できます。

インライン コメント

一致するコメントで文書化できないエンティティがいくつかあり、リモート コメントを使用する必要があります。これらは (テンプレート) パラメータと基本クラスです。

私がサポートしたインライン コメントを文書化する 239 を使用して、対応する親エンティティのコメントでそれらを文書化できます。 、 243 または 257 コマンド。

次の段落は、そのインライン エンティティのドキュメントです。

/// Documentation for function.
///
/// \param foo Documentation for parameter foo.
///
/// \param bar Documentation for parameter bar.
void func(int foo, int bar);

これは次と同じです:

/// Documentation for function.
void func(int foo, int bar);

/// \entity func(int,int).foo
///
/// Documentation for parameter foo.

/// \entity func(int,int).bar
///
/// Documentation for parameter bar.

現在、インライン コメントは特別にレンダリングされていないことに注意してください。それらは他のエンティティとして扱われ、概要を含む独自の見出しを取得します。

エンティティ リンク

機能させるために多くの内部リファクタリングを要した重要な機能の 1 つは、エンティティのリンクです。別のエンティティにリンクする機能。通常の CommonMark リンクを使用することにしましたが、URL はありません:

/// See [here as well](<> "foo").
void bar();

/// This is foo.
void foo();

これは、空の URL (265) を持つ単なる CommonMark リンクです。 ) と一意の名前であるタイトル この場合、リンク テキストは、リンク先のエンティティの一意の名前とは異なります。しかし、ほとんどの場合、そうではないため、次の省略形の構文を使用できます:

/// See [foo]().

構文標準に関係なく、リンクされたエンティティの URL へのリンクが埋められます。

固有の名前

リンクとリモート コメントの両方に、一意の名前が必要です 一意の名前は、基本的にエンティティの完全な名前ですが、例に示すようにいくつかの例外があります:

struct foo {}; // unique name is `foo`

void func(); // unique name is `func()`

void func(int a, const char* b); // unique name is `func(int, const char*)`
 // unique name of parameter a is `func(int, const char*).a`
 // unique name of parameter b is `func(int, const char*).b`

namespace ns // unique name is `ns`
{
 class bar {}; // unique name is `ns::bar`

 template <typename T> // unique name of parameter is `ns::templ<T>.T`
 struct templ // unique name is `ns::templ<T>`
 : T // unique name is `ns::templ<T>::T`
 {
 void func() const; // unique name is `ns::templ<T>::foo() const`
 }; 
}

関数の場合は署名も含める必要があり、テンプレートの場合はテンプレート パラメーターの名前も含める必要があります。(テンプレート) パラメーター自体は 273 の後にあります。 一意の名前のすべての空白は処理前に消去されるため、どのようにフォーマットしても問題ありません。さらに、空の括弧 280 を入れる必要はありません。 署名のない関数の場合。

関数がオーバーロードされていない場合、署名を付ける必要はまったくありません。これは一意の短い名前です。 .

それでも、これは長すぎて冗長になる可能性があるため、291 を使用して一意の名前を任意の文字列に変更できます。 コマンド:

/// The unique name is now `function`.
///
/// \unique_name function
void fancy_function(const char* ptr, int a, ...);

これはリモート コメントでも機能します:

/// \unique_name function
void fancy_function(const char* ptr, int a, ...);

/// \entity function
///
/// Comment for the former `fancy_function`.

たとえば、ファイルの名前は出力形式に依存するため、エンティティに対応する URL を計算するのは非常に困難でした。そのため、リンクはすべてがファイルに書き出される前にのみ解決されます。ファイル単位なので、ファイルのすべてのエンティティが 1 つの出力ファイルに記録されます。ドキュメントを生成するときに、出力ファイル名が設定されます。 - すべてのエンティティに対して。

レンダリングによってリンクが解決されると、出力ファイル名と最終的な拡張子の両方がわかっているため、URL を生成できます。

ファイルだけでなく、特定のエンティティにリンクするには、出力にアンカーが含まれている必要があります。> 実際には 311 にマップされます HTML アンカー コードをレンダリングします。エンティティのアンカーは一意の名前であるため、完全に機能します。

Jekyll.Jekyll を使用して、この Web サイトに自分のプロジェクトの標準ドキュメントを埋め込みます。> 一方、Jekyll は HTML 拡張子を使用するようにすべてのファイルを変更します!この問題を解決するために、339 も追加しました オプション。これは、レンダリングがリンクに使用する拡張子をオーバーライドします。

必要なのは、ドキュメントの他のエンティティへのリンクだけではありません。たとえば、他のプロジェクトのエンティティやそのドキュメントへのリンクも必要です。

/// See [std::vector::push_back()]().
void foo();

これでうまくいきます!cppreference.com にリンクします。

これは、348 による外部ドキュメントのサポートによるものです。 オプション。次の値が暗黙的に設定されます:

std::=http://en.cppreference.com/mwiki/index.php?title=Special%3ASearch&search=$$

名前空間 359 内のすべてのエンティティ リンク これは、そのエンティティの検索にリンクします; 365 エンティティの指定された一意の名前に置き換えられます。ただし、他の名前空間とドキュメントに設定できます。

インデックスの生成

エンティティ リンクに必要なインフラストラクチャのおかげで、インデックス ファイルの生成も非常に簡単でした。ファイルとエンティティ インデックスの 2 つのインデックスが生成されます。

ファイル インデックスは 378 という名前のファイルにあります 文書化されているすべてのファイルのリストが含まれています。エンティティ インデックスは、387 という名前のファイルにあります。 名前空間スコープ内のすべてのエンティティのリストが含まれています。エンティティ インデックスは名前空間によってグループ化され、390 も含まれます。 ドキュメントのセクション。

その他の出力形式

cmark のおかげで、さらに多くの出力フォーマットを追加することも簡単になりました.standardese は現在、CommonMark でのレンダリング、HTML、および Latex と Man の実験的サポートをサポートしています.また、XML フォーマットでの AST のダンプもサポートしています.

これは 401 によって実装されています 関数、HTML を含まない Latex および Man 形式は、私のアンカー ハックによりうまく機能しませんが、これは別のバージョンで取り組まれる予定です。

その他の変更

他にもいくつかの機能を追加しました。

たとえば、ライブラリは最初からマルチスレッド実行用に設計されており、ツールはスレッド プールを使用してより多くのコアに生成を分散します。ワーカー スレッドのデフォルト数はコア数であり、412 または 420 オプション。

435 も追加しました command.除外されたエンティティはドキュメントにまったく表示されません:

/// \exclude
struct foo {};

/// A type.
using type = foo;

生成します:

using type = implementation-defined;

あらすじとして。

ビルドシステムもオーバーホールされ、外部依存関係の処理が変更されました。しかし、それについては別のブログ投稿で既に説明しました。

今何が?

このアップデートは、成熟した標準語に多くの重要な機能を追加し、単なる基本的なプロトタイプ以上のものにします.多くの素晴らしい人々のおかげで、さまざまなプラットフォームでのサポートも強化されています.パーサーも改善されているので、標準語を使い始めることをお勧めします.また、最終的に標準語自体のドキュメントを作成するためにも使用します。

もちろん、作業はまだ終わっていません。次のバージョンでは、エンティティ グループとモジュールに取り組むだけでなく、最終的に標準化を真に最高の C++ ドキュメント ジェネレーターにするいくつかのより高度な機能に取り組みます。

ぜひチェックして共有してください!