我的要求是在程序的整个运行时间内,计算每个for语句在程序中循环的总次数,例如:
int for_counter_1 = 0;
int for_counter_2 = 0;
void f() {
for (int i = 0; i < 10; i++, for_counter_2++) {
// ...
}
}
int main() {
for (int i = 0; i < 10; i++, for_counter_1++) {
f();
}
printf("%d\n", for_counter_1); // should output: 10
printf("%d\n", for_counter_2); // should output: 100
}
字符串
由于在我的程序中有大量的for循环(一些加密算法),我将继续扩展它,我考虑使用宏来实现具有自己的循环计数器的for语句,如下所示:
#define CONCAT_INNER(a, b) a ## b
#define CONCAT(a, b) CONCAT_INNER(a, b)
#define FOR_COUNTER() CONCAT(for_counter_, __LINE__)
#define for_with_counter(...) \
static int FOR_COUNTER() = 0; \
for (__VA_ARGS__, ++FOR_COUNTER())
void f() {
for_with_counter(int i = 0; i < 10; ++i) {
// ...
}
}
int main() {
for_with_counter(int i = 0; i < 10; ++i) {
f();
}
}
型
它可以为每个for循环生成一个静态计数器for_counter_N
,其中N代表循环所在的行号,我可以在调试环境中看到这个循环发生的总次数,但是由于它们是局部静态变量,我不能在程序结束时从main函数中全部打印出来。
有什么方法可以实现我的目的吗?它不一定是宏,因为我使用的是C++,如果我可以使用模板等来绕过它就好了。或者如果有一个调试工具可以做到这一点,也可以工作。
编辑:
很抱歉我没有指定,这并不是说我没有考虑过使用map或unordered_map,但是由于在我的程序中for循环的性能要求相当苛刻(因为我正在做一些加密算法优化的工作),使用这些数据结构会对性能产生一些影响。
4条答案
按热度按时间tcbh2hod1#
只要有可能,我就尽量远离宏。在这种情况下,我会创建一个行为类似于
int
的类型,但会跟踪它的递增频率。对于这个演示(https://onlinegdb.com/zTJqjvNvE),报告是在析构函数中(所以当计数器超出范围时)。我的思路是,你不需要知道什么样的循环,只是需要知道某个东西递增的频率(这种方法也适用于while循环)。
字符串
mnowg1ta2#
使用全局
map
计数器,__FILE__
和__LINE__
的组合作为键(例如std::pair<std::string_view, int>
键)。8zzbczxx3#
模板功能似乎更好:
字符串
与使用
型
未明确提供名称的版本:
型
并将Map更改为
std::string
作为键。Demo
eqfvzcg84#
我想我会像其他人建议的那样使用全局Map,* 但是 * 通过创建一个 Package 类来初始化对ctor中counter对象的引用,从而避免了大量的开销,所以从那时起,它就像您的代码一样,只是做了几个正常的增量。
如果您真的愿意,您甚至可以通过创建一个局部变量、在每次迭代中更新它,然后将其添加到dtor中的全局变量中,来避免在每次迭代中使用引用的潜在开销(但对我来说,这似乎是一项相当繁重的工作,不太可能完成很多工作)。
对于从0开始每次循环并总是递增1的典型情况,可以通过在每次循环迭代中递增局部变量,然后将其添加到dtor中的全局计数器中,来进一步减少开销。
一方面,这可能会在进入循环时增加开销;另一方面,它可能会减少每次循环迭代的开销。
下面是一个快速的概念级代码验证:
字符串
为了使开销最小,我编写了这个函数,让它的后增量运算符只返回
int
形式的前一个值,而它通常应该返回*this
的一个副本(作为临时的LoopCounterImpl
对象)。但是在循环中,通常只执行foo++
,以避免增加变量的副作用,而不关心返回值,你能用临时循环计数器做的就是把它的当前值作为一个int
来检索,所以我只返回了一个int
来减少开销。目前,它使用一个
LoopCounter
宏来自动检索__LINE__
的正确值。这反过来又迫使你使用像auto foo = LoopCounter(initialValue)
这样的东西,这在我看来并不可怕,但我想有些人可能会不喜欢。如果你不介意像LoopCounter x{0, __LINE__}
这样的东西,你可以省去它。显然,您还可以添加
operator+=
、-=
、--
等。我想,从现有的内容来看,大部分内容都很明显。如果要将其放入标题中,则需要使用
LoopCounter
宏来收集__FILE__
沿着__LINE__
,并将两者都用作Map中的键。