我正在尝试编写一个包装函数,该函数每 N 次执行一次给定函数(类似于 Google 日志记录的 LOG_EVERY_N)。
到目前为止我所做的是:
#include <cstddef>
#include <functional>
#include <utility>
template<size_t N, typename Callable, typename... Args>
void call_every_n(Callable&& c, Args... args)
{
static size_t __counter = 0;
if(__counter == N - 1)
{
std::invoke(std::forward<Callable>(c), std::forward<Args>(args)...);
__counter = 0;
} else ++__counter;
}
#define CALL_EVERY_N(N, FUNC, ...) call_every_n<N, decltype(&FUNC), decltype(__VA_ARGS__)>(FUNC, __VA_ARGS__)
Run Code Online (Sandbox Code Playgroud)
问题在于内部计数器使用静态变量,但我不知道如何以不同的方式实现这一点。事实上,这段代码会按预期工作,直到我使用相同的 N 调用相同的函数。
这是一个演示该问题的示例:
#include <iostream>
void test(int a, int num)
{
std::cout << num << " = " << a << "\n";
}
int main() {
for(int i = 1; i < 20; ++i)
{
CALL_EVERY_N(3, test, i, 1);
CALL_EVERY_N(3, test, i, 2);
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
该代码输出:
1 = 2
2 = 3
1 = 5
2 = 6
1 = 8
2 = 9
1 = 11
2 = 12
1 = 14
2 = 15
1 = 17
2 = 18
Run Code Online (Sandbox Code Playgroud)
即两个不同的函数使用(然后修改)完全相同的静态变量。
我还想强制 Callable 必须是一个返回 void 的函数,我该怎么做呢?
for*_*818 10
每个 lambda 表达式都有不同的类型,因此您可以将其用作标签:
#include <cstddef>
#include <functional>
#include <iostream>
template<size_t N, typename Callable,typename Dummy, typename... Args>
void call_every_n(Dummy,Callable&& c,Args&&... args)
{
static size_t counter = 0;
if(counter == N - 1)
{
std::invoke(std::forward<Callable>(c), std::forward<Args>(args)...);
counter = 0;
} else ++counter;
}
#define CALL_EVERY_N(N, FUNC, ...) call_every_n<N>([]{},FUNC, __VA_ARGS__)
// in each expansion, this is of different type ^^
void test(int a, int num)
{
std::cout << num << " = " << a << "\n";
}
int main() {
for(int i = 1; i < 20; ++i)
{
CALL_EVERY_N(3, test, i, 1);
CALL_EVERY_N(3, test, i, 2);
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
输出:
1 = 3
2 = 3
1 = 6
2 = 6
1 = 9
2 = 9
1 = 12
2 = 12
1 = 15
2 = 15
1 = 18
2 = 18
Run Code Online (Sandbox Code Playgroud)
仅自 C++20 起,decltype
在需要传递虚拟参数以推断其类型之前,lambda 表达式才能出现在未计算的上下文 ( ) 中。您不需要使用decltype
其他参数,模板参数可以从函数参数中推导出来。完美转发通过std::forward
需要通用引用(即Args&&
)。
请注意,以 开头的名称__
是为实现而保留的。不要使用它们。
另一种方法是根据调用 CALL_EVERY 宏的行创建一个计数器变量,并将其传递给 call_every,如下所示:
#include <iostream>
//------------------------------------------------------------------------------------------------
// template for calling a function, using lambda expresion
template<typename fn_t>
int call_every(int times, int n, fn_t fn)
{
if (n % times == 0)
{
fn();
}
return n+1;
}
//------------------------------------------------------------------------------------------------
// helpers to create a variable for each CALL_EVERY based on line number
#define CAT1(a, b) a##b
#define CAT(a, b) CAT1(a, b)
#define VARIABLE_NAME(prefix) CAT(prefix,__LINE__)
// the full macro
#define CALL_EVERY(N, fn) \
static int VARIABLE_NAME(call_every_counter_) {0}; \
VARIABLE_NAME(call_every_counter_) = call_every(N, VARIABLE_NAME(call_every_counter_), (fn));
//------------------------------------------------------------------------------------------------
void test(int a, int num)
{
std::cout << num << " = " << a << "\n";
}
int main()
{
for (int i = 0; i < 20; ++i)
{
CALL_EVERY(3, [&i] { test(i, 3); });
CALL_EVERY(5, [&i] { test(i, 5); });
}
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
831 次 |
最近记录: |