使用宏计算源文件行数?

ein*_*ica 14 c++ c-preprocessor

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

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
Run Code Online (Sandbox Code Playgroud)

笔记:

  • 对预处理器功能的编译器特定扩展是可以接受的,但不可取。
  • 如果这只能在某些 C++ 的帮助下实现,而不是 C 构造,那也是可以接受的,但不可取(即我想要一些适用于 C 的东西)。
  • 显然,这可以通过一些外部处理器脚本运行源文件来完成,但这不是我要问的。

Fir*_*cer 15

有一个__LINE__预处理器宏,它为您提供一个整数,用于显示该行。您可以在某行上取其值,然后在以后的某行上取值,然后进行比较。

static const int BEFORE = __LINE__;
foo();
bar();
baz();
quux();
static const int AFTER = __LINE__;
static const int COUNT = AFTER - BEFORE - 1; // 4
Run Code Online (Sandbox Code Playgroud)

如果您想计算某些内容而不是源代码行的出现次数,则__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
Run Code Online (Sandbox Code Playgroud)

我采用了初始值,__COUNTER__因为它可能以前在源文件或某些包含的头文件中使用过。

在 C 而不是 C++ 中,对常量变量有限制,因此enum可以使用an代替。

enum MyEnum
{
    FOO = COUNT // C: error: enumerator value for ‘FOO’ is not an integer constant
};
Run Code Online (Sandbox Code Playgroud)

将常量替换为enum:

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

  • `__COUNTER__` 在 C 或 C++ 中不是标准的。如果您知道它适用于特定的编译器,请指定它们。 (2认同)

Nut*_*ker 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;
}
Run Code Online (Sandbox Code Playgroud)

和现场例子在这里。


Max*_*hof 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;
}
Run Code Online (Sandbox Code Playgroud)

https://godbolt.org/z/i8fDLx(返回3)

您可以通过存储 的开始和结束值使其可重用__COUNTER__。

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


ein*_*ica 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 };
Run Code Online (Sandbox Code Playgroud)

这隐藏了实现细节(尽管它将它们隐藏在宏中......)。这是@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;
}
Run Code Online (Sandbox Code Playgroud)

此外,这是有效的 C - 如果您的预处理器支持__COUNTER__,即。

适用于 GodBolt。

如果您使用的是 C++,您可以修改此解决方案,甚至不污染全局命名空间 - 通过将计数器放置在namespace macro_based_line_counts { ... }, 或namespace detail等内)


PSk*_*cik 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[]
Run Code Online (Sandbox Code Playgroud)

如果您需要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
Run Code Online (Sandbox Code Playgroud)

甲__COUNTER__基溶液(如果该扩展是可用的),而不是一个__LINE__基于一个将工作是相同的。

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