web-dev-qa-db-ja.com

#includeディレクティブのパス式に対する反対

C/C++の#includeディレクティブの使用について、他のプログラマーと話し合う準備をしています。自動車の標準に改造しなければならないコードベースには、#include <path/out/of/the/blue.h>という形式のインクルードが含まれています。正確に言うと、プロジェクトはコンパイラのインクルードパスの大規模なセット(-Iinclude/meなど)を処理しますが、パス式はこれらの場所の外にも到達するため、コンパイラが内部的にすべてのインクルードパスの組み合わせを生成する場合にのみblue.hを見つけることができます。ステートメント自体のパス:include/me/ + path/out/of/the/blue.h。この慣習には多くの不満があります。

  • AFAIK <>は、プラットフォームからのシステムヘッダー用に予約されており、プロジェクトコードでの使用はお勧めしません。 CおよびC++標準では、最初のパスでファイルが見つからない場合にファイルに""が指定されている場合と同様に、コンパイラーが再度検索する必要があるため、コンパイルのみが機能します。
  • それはレビューの悪夢を生み出します:インクルードファイルは、インクルードするCまたはC++ファイルが置かれているディレクトリをルートとするパスにも、インクルードの場所にもありません。コンパイラーの検索を繰り返して、最終的にどこかにそれを見つける必要があります。 、しかし、現時点では、あなたは本当に自分自身を信頼していません-コンパイラは別の方法で検索した可能性があります。
  • プロジェクトツリーには複数のファイル名がいくつかあります。blue.hは複数の場所にあり、1つのblue.hは、ディレクトリツリーの下に、より具体的な真のblue.hを含めるためのディスパッチャファイルとして機能する場合があります。どのblue.hが選択されるかは、#define PLATFORMマクロなどによって区別されます。
  • それは、モノリシックで再編成に抵抗するプロジェクト構造を作成し、CおよびC++インターフェイス(言語に関する限り、ディレクトリのないスペースに存在する)をファイルシステムと結合します。
  • それは新しいプロジェクトに波及します。それ自体が他のパス依存ヘッダーを含むヘッダーを使用するとすぐに、新しいプロジェクトのビルドスクリプトはこの使用法に適応する必要があります。

私たちは mbed-os を使用していますが、そのソースツリーは(IMHO)の悪いコード構造化の選択と同じ問題を抱えているようです。

TL; DRとして、プロジェクト構造をソースコードに組み込むことは不適切であると私は確信していると言えます。とにかく、ビルドシステムとリンカーに多くの構造と依存関係を提供する必要があります-ソースファイルごとにセカンダリカップリングを導入すると、少なくともビルドシステムを変更しようとすると、大混乱を招きます(私は今、強制されています)。

これについて世論はどうですか?インクルードをどのようにフラットまたはツリー状に管理しますか?

PS:MISRAはリモートでこの問題について話しているだけですが、「ヘッダーファイル名以外は使用しないでください」と読むこともできます。

PPS:パスの使用に完全に反対しているわけではありません(まあ、私は自分のコードにいますが、継承されたコードでこれを使用することもできます)これが外部から見えない限り。 しかし、プロジェクトの現在のバージョンは、この使用法に正確に適応するように強制します。

PPPS: 物理構造 に関する不注意がどこにつながるかを説明します。ここでは、前に述べたmbed-osのコンパイルに必要なインクルードパスの一部を示します。

mbed-os/features/nanostack/mbed-mesh-api/
mbed-os/features/nanostack/sal-stack-nanostack-eventloop/
mbed-os/features/nanostack/mbed-mesh-api/mbed-mesh-api/
mbed-os/features/nanostack/mbed-mesh-api/source/include/
mbed-os/features/nanostack/nanostack-interface/
mbed-os/features/nanostack/sal-stack-nanostack-eventloop/nanostack-event-loop/
mbed-os/features/lwipstack/
mbed-os/features/lwipstack/lwip-sys/
mbed-os/features/lwipstack/lwip/src/include/
mbed-os/features/lwipstack/lwip/src/include/lwip/

これらのパスの一部は、より深いリーチ(つまり、「sub/subsub/inc_this.hpp」)の単なる開始点であり、一部は、単純に古い「インクルードが見つかります」ディレクトリです。

これは、単純な「インクルードパスをインクルードファイルの場所に設定する」というルールを超えて、さらに別の反対論につながります。時間の経過や異なるコーディングカルチャーでより複雑なものを通信することは明らかに不可能です。

5
Vroomfondel

ここでの重要な問題は、明確で一貫したプロジェクト構造と良い名前だと思います。 12の異なるblue.hファイルを持つ非構造化モノリスは、#includeのルールに関係なく、ディレクトリツリーの異なる深さの異なるディレクトリにあり、混乱のように感じられます。

あなたのプロジェクト

あなたの例と同じくらい一般的ですが、あなたの特定のケースに推奨を与えるのは難しいです。大規模なレガシープロジェクトの場合、大規模な再構築は非常に高額になる可能性があり、通常の質問を自問する必要があります。所定のリソース量で、どの手順で最も痛みを軽減できると予想されますか?

少なくとも簡単に実現できるソリューションは、-Iフラグから#includeディレクティブに変更することです。たとえばから:

-Iinclude/me
#include <path/out/of/the/blue.h>

-Iinclude
#include <me/path/out/of/the/blue.h>

一方では、パスがさらに長くなります。一方で、より具体的になります。結局のところ、コードにはヘッダーパスが表示されますが、-Iフラグは表示されません。

#include規則

彼らがインスピレーションとして役立つことを願っています。 :)

  • #include ""は、ローカルのプライベートヘッダーファイルを含むcppファイルなどのプライベート使用のために予約されています。これはファイル名のみであることになっていますが、サブディレクトリのヘッダーを参照することは問題外ではありません。
  • #include <>現在のソフトウェアコンポーネント外からの外部依存関係、またはコンポーネント自体のパブリックヘッダー用です。技術レベルでは、ソフトウェアコンポーネントは通常ライブラリです。 Externalは、サードパーティの依存関係またはマルチコンポーネントプロジェクトの別のコンポーネントにすることができます。

ヘッダーパスには意味情報が含まれており、次のような構造になっているはずです。

#include <project/component/possibly/subdirs/header.hpp>

たとえば、コンポーネントPawおよびtailを含むcatプロジェクトがあるとします。次にtailには以下が含まれます:

#include <boost/filesystem/directory.hpp>
#include <cat/Paw/claw.hpp>

どちらの場合も、パスはincludeディレクティブの重要な部分です。ヘッダーが属するプロジェクトとコンポーネントが正確にわかります。しかし、私はあなたに部分的に同意します。 おそらく/ subdirsパスの一部はできるだけ短くする必要があり、理想的にはまったく使用しないでください。サブディレクトリの数の増加は、コンポーネントが大きくなりすぎていることを示しています。

ただし、project/componentの部分は不可欠です。それなしでは

#include <directory.hpp>
#include <claw.hpp>

そして、非常に多くの情報を失うため、#include行はヘッダーを見つけるのにほとんど役に立たなくなります。少なくとも「ヘッダーへのジャンプ」がなければIDE goodness。

ディレクトリ階層と名前空間階層の混乱を避けるために、それらを同期させます。 cat/Paw/claw.hppのコードは、説得力のある理由がない限り、cat::Paw名前空間に実装する必要があります。

私が完全に禁止しているのは、<>および ""表記の両方のヘッダーパスで親ディレクトリにアクセスすることです。必要な場合:

#include "../../path/to/header.h"

プロジェクト構造にバグがあります。行って修正してください。

7
besc