【问题标题】:Count source file lines using macros?使用宏计算源文件行数?
【发布时间】:2020-01-08 10:47:39
【问题描述】:

是否可以使用 C/C++ 预处理器将源文件中的行数计入宏或某种编译时可用值?例如。我可以在下面替换MAGIC1MAGIC2MAGIC3,并在使用MAGIC3时以某种方式获得值4吗?

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 的东西)。
  • 显然这可以通过一些外部处理器脚本运行源文件来完成,但这不是我要问的。

【问题讨论】:

  • a macro called __LINE__代表当前行号
  • 正在搜索__COUNTER__ 和/或BOOST_PP_COUNTER
  • 您需要解决的实际问题是什么?为什么需要这个?
  • this 有帮助吗?
  • @PSkocik:我想要一些可以用作编译时常量的东西,例如用于说int arr[MAGIC4] 并获取我的代码的某些先前计算的部分中的行数。

标签: c++ c-preprocessor


【解决方案1】:

有一个__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
};

【讨论】:

  • 我认为这是您仅使用预处理器所能获得的最接近的。预处理器是一次性的,因此您不能对稍后计算的值进行回补,但全局变量引用将起作用并且应该对其进行优化。它们只是不适用于整数常量表达式,但可以构造代码以便计数不需要这些。
  • __COUNTER__ 在 C 或 C++ 中不是标准的。如果您知道它适用于特定的编译器,请指定它们。
  • @einpoklum no,BEFOREAFTER 不是宏
  • 此解决方案的非计数器版本存在问题:之前和之后只能在与源代码行相同的范围内使用。编辑了我的“例如” sn-p 反映这是一个问题。
  • @user694733 真正的问题被标记为 [C++]。对于 C 枚举常量起作用。
【解决方案2】:

我知道 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;
}

还有活生生的例子here

【讨论】:

  • 没有宏甚至比有宏更好。但是 - 使用这种方法,我只能在与我计算的行数相同的范围内使用行数。另外 - source_location 在 C++20 中进行实验?
  • 我同意没有宏的解决方案比使用宏要好得多。 source_location 现在正式成为 C++20 的一部分。检查here。我只是在godbolt.org 上找不到已经在非实验意义上支持它的 gcc 编译器版本。您能否再解释一下您的陈述 - 我只能在与我计算的行数相同的范围内使用行数
  • 假设我将您的建议放在一个函数中(即计算的行数是调用,而不是声明)。它有效 - 但我只有 line_number_startline_number_end 在该范围内,没有其他地方。如果我想在其他地方使用它,我需要在运行时传递它——这违背了目的。
  • 看看标准提供的例子here。如果它是默认参数,那么它仍然是编译时的一部分,对吧?
  • 是的,但这不会使line_number_end 在其范围之外的编译时可见。如果我错了,请纠正我。
【解决方案3】:

为了完整性:如果您愿意在每行后添加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__ 的开始值和结束值来使其可重用。

总的来说,这确实很麻烦。您也无法计算包含预处理器指令或以// cmets 结尾的行。我会改用__LINE__,请参阅其他答案。

【讨论】:

  • 你为什么用static_assert
  • 这在我放入的源文件中给出了“9”,你不能假设 __COUNTER__ 最初仍然为零,因为其他标题等可能会使用它。
  • 你必须使用两次__COUNTER__的值并取差
  • @foreknownas_463035818 __COUNTER__ 本身是不允许的,它需要扩展为某些东西,否则它不会计数(我不记得 100% 的规则)。
【解决方案4】:

一种更强大的解决方案,允许使用不同的计数器(只要它们不混合,并且在其他任务中没有使用 __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__,那就是。

Works on 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__ 的解决方案的工作方式相同。

    C++ 中的constexprs 应该和enum 一样工作,但enum 也可以在普通 C 中工作(我上面的解决方案是普通 C 解决方案)。

    【讨论】:

    • 只有当我对行数的使用与计数的行在同一范围内时,这才有效。伊安姆。注意我稍微编辑了我的问题以强调这可能是一个问题。
    • @einpoklum 基于__COUNTER__ 的解决方案也存在问题:您最好希望您的魔术宏是__COUNTER__ 的唯一用户,至少在您使用完__COUNTER__ 之前是这样。问题基本上都归结为__COUNTER__/__LINE__ 是预处理器功能并且预处理器一次性工作的简单事实,因此您不能稍后基于__COUNTER__/__LINE__ 对整数常量表达式进行回补。唯一的方法(至少在 C 中)是首先避免需要,例如,通过使用没有大小的前向数组声明(不完全类型化的数组声明)。
    • 郑重声明,\ 不会影响__LINE__ - 如果有换行符,__LINE__ 会增加。 Example 1, example 2.
    • @MaxLanghof 谢谢。没有意识到这一点。固定。
    猜你喜欢
    • 2022-01-16
    • 1970-01-01
    • 2017-09-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多