我正在尝试编写一个通用的函数调度机制,在调用实际函数之前我会做一些额外的工作(例如,执行函数的时间)。以下代码有效,但类型void f(....)
的函数除外,因为我们声明了ret。
#define execute(fn, ...) exec_helper(#fn, fn, ##__VA_ARGS__)
#define execute0(fn) exec_helper(#fn, fn)
template <typename TASK, typename... ARGS>
auto exec_helper(const char *fn_name, TASK&& task, ARGS&&... args)
{
//std::function<std::result_of_t<TASK(ARGS...)> ()> func
// = std::bind(std::forward<TASK>(task),
// std::forward<ARGS>(args)...);
#ifdef TIME_FUNC
auto start = std::chrono::steady_clock::now();
#endif
auto ret = task(std::forward<ARGS>(args)...);
#ifdef TIME_FUNC
auto end = std::chrono::steady_clock::now();
auto diff = end - start;
auto time = std::chrono::duration<double, std::milli>(diff).count();
std::cout << "\n" << fn_name << "\t = "
<< std::setprecision(3) << time << " ms\n";
#endif
return ret;
}
有没有办法让它适用于这些类型的功能?也许使用一些模板技巧?
答案 0 :(得分:5)
C ++的一个有趣特性是,你可以这样写:
return f();
即使f
的返回类型为void
。这样,您实际上可以避免使用ret
变量。
记住这一点,以及析构函数是如何工作的,你可以将任务前工作和后续工作包装在一个类中 - 并分别在构造函数和析构函数中调用它们,如下所示:
struct watch
{
extern watch(char const *fn_name)
: _fn_name(fn_name),
_start(std::chrono::steady_clock::now()) {}
~watch()
{
auto end = std::chrono::steady_clock::now();
auto diff = end - _start;
auto time = std::chrono::duration<double, std::milli>(diff).count();
std::cout << "\n" << _fn_name << "\t = "
<< std::setprecision(3) << time << " ms\n";
}
char const *_fn_name;
decltype(std::chrono::steady_clock::now()) _start;
};
并将其用作:
template <typename TASK, typename... ARGS>
auto exec_helper(const char *fn_name, TASK&& task, ARGS&&... args)
{
#ifdef TIME_FUNC
watch start_watching { fn_name };
#endif
return task(std::forward<ARGS>(args)...);
}
希望有所帮助。
您甚至可以将包装类概括为:
struct invariant_executor //choose a more general name now!
{
using F = std::function<void()>;
invariant_executor(F before, F after): _after(std::move(after))
{
before();
}
~invariant_executor()
{
_after();
}
F _after;
};
并将其用作:
template <typename TASK, typename... ARGS>
auto exec_helper(const char *fn_name, TASK&& task, ARGS&&... args)
{
#ifdef TIME_FUNC
decltype(std::chrono::steady_clock::now()) start;
invariant_executor local
{
[&] { start = std::chrono::steady_clock::now(); },
[&] {
auto end = std::chrono::steady_clock::now();
auto diff = end - start;
auto time = std::chrono::duration<double, std::milli>(diff).count();
std::cout << "\n" << fn_name << "\t = "
<< std::setprecision(3) << time << " ms\n";
}
};
#endif
return task(std::forward<ARGS>(args)...);
}
现在,您可以灵活地在构造函数和invariant_executor
的析构函数中执行任何代码。
答案 1 :(得分:1)
您可以使用packaged_task
:
20
这已经处理了std::packaged_task<Sig> pack_task(std::forward<Task>(task));
auto fut = pack_task.get_future();
// pre-time stuff
// run task
pack_task(std::forward<Args>(args)...);
// post-time stuff
// get the result - works with either void or a real type
return fut.get();
特殊情况,但现在您必须提出void
,并处理构建Sig
的开销。
答案 2 :(得分:0)
您可以使用std::packaged_task
并等待std::future
的结果可用。当然,这会产生开销。
template<typename Func, typename ... Args>
auto func(Func &&f, Args &&... args) -> decltype(f(std::forward<Args>(args)...)){
using return_type = decltype(f(std::forward<Args>(args)...));
std::packaged_task<return_type(Args&&...)> task(f);
auto future = task.get_future();
// do something
task(std::forward<Args>(args)...);
// do something else
return future.get();
}
然后你的函数可以有任何返回类型,甚至是void
。
答案 3 :(得分:0)
您可以让exec_helper()
分派给两个辅助函数exec_helper2()
中的一个,具体取决于返回类型是否为void。这些函数相互重载,但区别在于它们的第一个参数isvoid
的类型。
#include <chrono>
#include <iomanip>
#include <iostream>
#include <utility>
#include <type_traits>
#define TIME_FUNC
#define execute(fn, ...) exec_helper(#fn, fn, ##__VA_ARGS__)
#define execute0(fn) exec_helper(#fn, fn)
template <typename TASK, typename... ARGS>
auto exec_helper2(std::false_type isbool,
const char *fn_name, TASK&& task, ARGS&&... args)
{
//std::function<std::result_of_t<TASK(ARGS...)> ()> func
// = std::bind(std::forward<TASK>(task),
// std::forward<ARGS>(args)...);
#ifdef TIME_FUNC
auto start = std::chrono::steady_clock::now();
#endif
auto ret = task(std::forward<ARGS>(args)...);
#ifdef TIME_FUNC
auto end = std::chrono::steady_clock::now();
auto diff = end - start;
auto time = std::chrono::duration<double, std::milli>(diff).count();
std::cout << "\n" << fn_name << "\t = "
<< std::setprecision(3) << time << " ms\n";
#endif
return ret;
}
template <typename TASK, typename... ARGS>
auto exec_helper2(std::true_type isbool,
const char *fn_name, TASK&& task, ARGS&&... args)
{
#ifdef TIME_FUNC
auto start = std::chrono::steady_clock::now();
#endif
task(std::forward<ARGS>(args)...);
#ifdef TIME_FUNC
auto end = std::chrono::steady_clock::now();
auto diff = end - start;
auto time = std::chrono::duration<double, std::milli>(diff).count();
std::cout << "\n" << fn_name << "\t = "
<< std::setprecision(3) << time << " ms\n";
#endif
}
template <typename TASK, typename... ARGS>
auto exec_helper(const char *fn_name, TASK&& task, ARGS&&... args)
{
return exec_helper2(std::is_same<std::result_of_t<TASK(ARGS...)>, void>{},
fn_name, std::forward<TASK>(task),
std::forward<ARGS>(args)...);
}
void void_f() {}
int int_f() { return 42; }
int main()
{
execute0(void_f);
execute0(int_f);
}
答案 4 :(得分:0)
以下几行可能对您有用:
#include <iostream>
template <typename Func, typename T> class ResultStash {
public:
explicit ResultStash(Func f) : m_t(f()) {}
T get() { return std::move(m_t); }
private:
T m_t;
};
template <typename Func> class ResultStash<Func, void> {
public:
explicit ResultStash(Func f) { f(); }
void get() { return; }
};
template <typename Func>
auto wrap_function_execution(Func f) -> decltype(f()) {
std::cout << "Doing something before executing the function" << std::endl;
// Call f() and remember the result
ResultStash<decltype(f), decltype(f())> result(f);
std::cout << "Doing something after executing the function" << std::endl;
// Return the result of f()
return result.get();
}
void someVoidFunction(int * a) {
if (a) {
*a = 5;
std::cout << "Setting a = " << *a << std::endl;
}
}
int someNonVoidFunction(int a) {
int b = 10;
std::cout << "Multiplying " << a << " by " << b << std::endl;
return a * b;
}
int main(int argc, char * argv[]) {
int a;
wrap_function_execution([&a] { someVoidFunction(&a); });
auto b = wrap_function_execution([&a] { return someNonVoidFunction(a); });
std::cout << "b = " << b << std::endl;
return 0;
}
这引入了一个模板类,它在包装器中保存底层函数的结果,并且专门用于void函数。在类的非void版本中使用std :: move可以在发布&#39;时最大限度地减少开销。你的包装器中隐藏的结果。
与Nawaz的方法相比,这具有以下(轻微)优势:
它有以下缺点:
示例代码生成:
Doing something before executing the function
Setting a = 5
Doing something after executing the function
Doing something before executing the function
Multiplying 5 by 10
Doing something after executing the function
b = 50