マクロを使用してソースファイルの行を数えますか?


15

C / C ++プリプロセッサを使用して、ソースファイル内の行をマクロまたは何らかのコンパイル時に使用可能な値にカウントすることは可能ですか?例えば私は置き換えることができMAGIC1MAGIC2かつMAGIC3使用している場合、以下では、何とか値4を取得しますかMAGIC3

MAGIC1 // can be placed wherever you like before the relevant 
       // lines - either right before them, or in global scope etc.
foo(); MAGIC2
bar(); MAGIC2
baz(); MAGIC2
quux(); MAGIC2
// ... possibly a bunch of code here; not guaranteed to be in same scope ...
MAGIC3

ノート:

  • プリプロセッサーの機能に対するコンパイラー固有の拡張は許容可能ですが、望ましくありません。
  • これが、CではなくC ++の一部の助けを借りてのみ可能である場合は、これも許容されますが望ましくありません(つまり、Cで機能するものを希望します)。
  • 明らかにこれは、外部プロセッサスクリプトを介してソースファイルを実行することで実行できますが、それは私が求めていることではありません。

6
現在の行番号を表すと呼ばれるマクロ__LINE__があります
ForceBru

2
__COUNTER__および/またはを検索していBOOST_PP_COUNTERますか?
KamilCuk

11
解決する必要がある実際の問題は何ですか?なぜこれが必要なのですか?
一部のプログラマー、

1
、このヘルプは?
user1810087

1
@PSkocik:たとえばint arr[MAGIC4]、コードの以前にカウントされたセクションの行数を言って取得するために、コンパイル時の定数として使用できるものが欲しいです。
アインポクルム

回答:


15

ある__LINE__ラインが上に現れているため、あなたに整数を与えるプリプロセッサマクロが。いくつかの行でその値を取得し、その後にいくつかの行で取得して比較することができます。

static const int BEFORE = __LINE__;
foo();
bar();
baz();
quux();
static const int AFTER = __LINE__;
static const int COUNT = AFTER - BEFORE - 1; // 4

ソース行で__COUNTER__はなく何かの出現をカウントしたい場合は、GCCやMSVCなどの一部のコンパイラーでサポートされている非標準のオプションである可能性があります。

#define MAGIC2_2(c)
#define MAGIC2(c) MAGIC2_2(c)
static const int BEFORE = __COUNTER__;
void foo(); MAGIC2(__COUNTER__);
void bar(
    int multiple,
    float lines); MAGIC2(__COUNTER__);
void baz(); MAGIC2(__COUNTER__);
void quux(); MAGIC2(__COUNTER__);
static const int AFTER = __COUNTER__;
static const int COUNT = AFTER - BEFORE - 1; // 4

__COUNTER__以前にソースファイルで使用されていたか、含まれているヘッダーがあるため、の初期値を使用しました。

C ++ではなくCでは、定数変数に制限enumがあるため、代わりにを使用できます。

enum MyEnum
{
    FOO = COUNT // C: error: enumerator value for ‘FOO’ is not an integer constant
};

constをenum次のように置き換えます:

enum {BEFORE = __LINE__};
foo();
bar();
baz();
quux();
enum { COUNT = __LINE__ - BEFORE - 1};
enum MyEnum
{
    FOO = COUNT // OK
};

これは、プリプロセッサだけで取得できる最も近いものだと思います。プリプロセッサはワンパスなので、後で計算された値をバックパッチすることはできませんが、グローバル変数参照は機能し、同じように最適化する必要があります。これらは整数定数式では機能しませんが、カウントに必要ないようにコードを構造化できる場合があります。
PSkocik

2
__COUNTER__CまたはC ++では標準ではありません。特定のコンパイラで動作することがわかっている場合は、それらを指定します。
Peter

@einpoklumいいえ、マクロBEFOREAFTERはありません
Alan Birtles

このソリューションの非カウンターバージョンには問題があります。変更前と変更後は、ソース行と同じスコープでのみ使用できます。これが問題であることを反映するために、私の「eg」スニペットを編集しました。
アインポクルム

1
@ user694733真の質問にタグが付けられました[C ++]。Cの列挙型定数では機能します。
ファイアランサー

9

OPの要求がマクロを使用することであることはわかっていますが、マクロの使用を伴わない別の方法を追加したいと思います。

C ++ 20では、source_locationファイル名、行番号、関数名など、ソースコードに関する特定の情報を表すクラスが導入されています。この場合、これはかなり簡単に使用できます。

#include <iostream>
#include <source_location>

static constexpr auto line_number_start = std::source_location::current().line();
void foo();
void bar();
static constexpr auto line_number_end = std::source_location::current().line();

int main() {
    std::cout << line_number_end - line_number_start - 1 << std::endl; // 2

    return 0;
}

そして、ここで実例


マクロなしの方がマクロありよりも優れています。ただし、このアプローチでは、カウントした行と同じスコープでしか行カウントを使用できません。またsource_location-C ++ 20で実験的ですか?
アインポクルム

マクロなしのソリューションはマクロありよりもはるかに優れていることに同意します。source_location現在、正式にC ++ 20の一部です。こちらをチェックしてください。私はgodbolt.orgでgccコンパイラのバージョンを見つけることができませんでした。もう少しあなたの声明を説明していただけませんか- 私は数えた行と同じスコープでしか行数を使用できませんか?
NutCracker

私があなたの提案を関数内に置いたとしましょう(つまり、カウントされた行は宣言ではなく呼び出しです)。それは機能しますが、私はその範囲内にのみありline_number_startline_number_end他の場所にはありません。別の場所でそれが必要な場合は、実行時に渡す必要があります-これは目的に反します。
アインポクルム

ここで標準が提供する例を見てください。それがデフォルトの引数である場合、それはまだコンパイル時の一部ですよね?
NutCracker

はい。ただし、line_number_endコンパイル時にスコープ外では表示されません。私が間違っていたら訂正してください。
アインポクルム

7

完全を期すためにMAGIC2、すべての行の後に追加したい場合は、次を使用できます__COUNTER__

#define MAGIC2 static_assert(__COUNTER__ + 1, "");

/* some */     MAGIC2
void source(); MAGIC2
void lines();  MAGIC2

constexpr int numberOfLines = __COUNTER__;

int main()
{
    return numberOfLines;
}

https://godbolt.org/z/i8fDLx(リターン3

の開始値と終了値を保存して、再利用可能にすることができます __COUNTER__

全体的にこれは本当に面倒です。また、プリプロセッサディレクティブを含む行や//コメントで終わる行をカウントすることもできません。__LINE__代わりに使用します。他の回答を参照してください。


1
なぜ使うのstatic_assert
idclev 463035818

1
これにより、私がドロップしたソースファイルに「9」が表示されました__COUNTER__。他のヘッダーなどが使用している可能性があるため、最初はまだゼロであるとは想定できません。
ファイアランサー

__COUNTER__2回の値を使用して差を取る必要があります
idclev 463035818

1
__COUNTER__それ自体の@ formerlyknownas_463035818 は許可されず、何かに展開する必要があるか、カウントされません(これに関する100%のルールを思い出せません)。
ファイアランサー

7

いくぶんより堅牢なソリューションで、異なるカウンターを可能にします(それらが混在せず__COUNTER__、他のタスクに使用されない限り)。

#define CONCATENATE(s1, s2) s1##s2
#define EXPAND_THEN_CONCATENATE(s1, s2) CONCATENATE(s1, s2)

#define COUNT_THIS_LINE static_assert(__COUNTER__ + 1, "");
#define START_COUNTING_LINES(count_name) \
  enum { EXPAND_THEN_CONCATENATE(count_name, _start) = __COUNTER__ };
#define FINISH_COUNTING_LINES(count_name) \
  enum { count_name = __COUNTER__ - EXPAND_THEN_CONCATENATE(count_name, _start) - 1 };

これにより、実装の詳細が非表示になります(ただし、マクロ内では非表示になります...)。@MaxLanghofの回答を一般化したものです。ご了承ください__COUNTER__カウントを開始すると、ゼロ以外の値になる場合がことに。

使用方法は次のとおりです。

START_COUNTING_LINES(ze_count)

int hello(int x) {
    x++;
    /* some */     COUNT_THIS_LINE
    void source(); COUNT_THIS_LINE
    void lines();  COUNT_THIS_LINE
    return x;
}

FINISH_COUNTING_LINES(ze_count)

int main()
{
    return ze_count;
}

また、これは有効なCです-プリプロセッサーがサポートしている場合 __COUNTER__、それはそうです。

GodBoltで動作しますます。

内のカウンターを置くことによって-あなたがCを使用している場合++、あなたもグローバルな名前空間を汚染しないように、このソリューションを変更することができnamespace macro_based_line_counts { ... }、またはnamespace detailなど)


5

コメントに基づいて、CまたはC ++で(コンパイル時の)配列サイズを指定したい場合は、

int array[]; //incomplete type
enum{ LINE0 = __LINE__ }; //enum constants are integer constant expressions
/*lines to be counted; may use array (though not sizeof(array)) */
/*...*/
int array[ __LINE__ - LINE0 ]; //complete the definition of int array[]

間にsizeof(array)行が必要な場合は、それを静的変数参照で置き換えることができ(整数定数式である必要がない限り)、最適化コンパイラーはそれを同じように扱う必要があります(静的変数を配置する必要をなくします)メモリ内)

int array[]; static int count;
enum{ LINE0 = __LINE__ }; //enum constants are integer constant expressions
//... possibly use count here
enum { LINEDIFF = __LINE__ - LINE0 }; 
int array[ LINEDIFF ]; /*complete the definition of int array[]*/ 
static int count = LINEDIFF; //fill the count in later

__COUNTER__対照的に、ベースの溶液(すなわち拡張が利用可能な場合)__LINE__ベースの一方は同じように機能します。

constexprs C ++はと同様に機能しますがenumenumプレーンCでも機能します(上記の私のソリューションはプレーンCソリューションです)。


これは、行カウントの使用がカウントされた行と同じスコープ内にある場合にのみ機能します。IIANM。注:問題になる可能性があることを強調するために、質問を少し編集しました。
アインポクルム

1
@einpoklum __COUNTER__ベースのソリューションにも問題があり__COUNTER__ます。少なくとも、の使用を完了する前に、マジックマクロがの唯一のユーザーであることを望みます__COUNTER__。基本的にすべての問題__COUNTER__/__LINE__は、プリプロセッサー機能である単純な事実に起因し、プリプロセッサーは1つのパスで機​​能するため、後で__COUNTER__/ に基づいて整数定数式をバックパッチすることはできません__LINE__。(少なくともCで)唯一の方法は、最初に必要性を回避することです。たとえば、サイズなしの前方配列宣言(不完全に型指定された配列宣言)を使用します。
PSkocik

1
レコードの場合、\ 影響はありません__LINE__-改行がある場合は__LINE__増加します。例1例2
Max Langhof

@MaxLanghofありがとう。気づかなかった。修繕。
PSkocik
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.