使用宏计算源文件行数?
Count source file lines using macros?
是否可以使用 C/C++ 预处理器将源文件中的行计数为宏或某种编译时可用值?例如。我可以在下面替换 MAGIC1
、MAGIC2
和 MAGIC3
,并在使用 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 的东西)。
- 显然这可以通过 运行 源文件通过一些外部处理器脚本来完成,但这不是我要问的。
有一个 __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
};
为了完整起见:如果你愿意在每一行之后添加 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 (returns 3
)
您可以通过存储 __COUNTER__
的开始和结束值使其可重复使用。
总的来说,这确实很麻烦。您也无法计算包含预处理器指令或以 //
注释结尾的行。我会改用 __LINE__
,请参阅其他答案。
根据您的评论,如果您想在 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
与基于 __LINE__
的解决方案相比,基于 __COUNTER__
的解决方案(如果该扩展可用)将工作相同。
C++ 中的 constexpr
s 应该和 enum
一样工作,但是 enum
也可以在普通 C 中工作(我上面的解决方案是普通 C 解决方案)。
我知道 OP 的要求是使用宏,但我想添加另一种不涉及使用宏的方法。
C++20 引入了 source_location
class 表示有关源代码的某些信息,例如文件名、行号和函数名。在这种情况下我们可以很容易地使用它。
#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。
一个稍微更稳健的解决方案,允许不同的计数器(只要它们不混合,并且没有将 __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__
,即
如果您使用的是 C++,则可以修改此解决方案以不污染全局命名空间 - 通过将计数器放置在 namespace macro_based_line_counts { ... }
或 namespace detail
等)
是否可以使用 C/C++ 预处理器将源文件中的行计数为宏或某种编译时可用值?例如。我可以在下面替换 MAGIC1
、MAGIC2
和 MAGIC3
,并在使用 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 的东西)。
- 显然这可以通过 运行 源文件通过一些外部处理器脚本来完成,但这不是我要问的。
有一个 __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
};
为了完整起见:如果你愿意在每一行之后添加 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 (returns 3
)
您可以通过存储 __COUNTER__
的开始和结束值使其可重复使用。
总的来说,这确实很麻烦。您也无法计算包含预处理器指令或以 //
注释结尾的行。我会改用 __LINE__
,请参阅其他答案。
根据您的评论,如果您想在 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
与基于 __LINE__
的解决方案相比,基于 __COUNTER__
的解决方案(如果该扩展可用)将工作相同。
constexpr
s 应该和 enum
一样工作,但是 enum
也可以在普通 C 中工作(我上面的解决方案是普通 C 解决方案)。
我知道 OP 的要求是使用宏,但我想添加另一种不涉及使用宏的方法。
C++20 引入了 source_location
class 表示有关源代码的某些信息,例如文件名、行号和函数名。在这种情况下我们可以很容易地使用它。
#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。
一个稍微更稳健的解决方案,允许不同的计数器(只要它们不混合,并且没有将 __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__
,即
如果您使用的是 C++,则可以修改此解决方案以不污染全局命名空间 - 通过将计数器放置在 namespace macro_based_line_counts { ... }
或 namespace detail
等)