【问题标题】:c++ add statements to an expressionc++ 向表达式添加语句
【发布时间】:2020-09-17 11:55:47
【问题描述】:

在某些情况下,我必须添加语句,例如将日志或跟踪添加到表达式中,例如(我想准确记录 prep_stmt.executeQuery() 的时间,但我仍然需要它的返回值):

zdb::PreparedStatement prep_stmt = conn.prepareStatement(sql.data());
prep_stmt.setLLong(1, key);
zdb::ResultSet result = prep_stmt.executeQuery();

// do some stuff with the result

if (result.next()) {
    SCOPE_LOG(HANDLE_RESULTS);
    auto [data, size] = result.getBlob("v");
    // value is a protobuff message
    value->ParseFromArray(image, size);
}

假设我想记录prep_stmt.executeQuery() 中的时间。所以我添加了一些这样的宏:

// only for debug
#define EXPR_LOG_BEGIN(Name) \
    [&]() { \
        SCOPE_LOG(Name); \
        return
#define EXPR_LOG_END }();

这里的SCOPE_LOG 是一个使用raii 进行日志记录的宏(愚蠢的SCOPE_EXIT 宏和spdlog):

#define SCOPE_LOG(Expr) \
    auto start =  chrono::system_clock::now(); \
    SCOPE_EXIT {        \
        auto end = std::chrono::system_clock::now(); \
        std::chrono::duration<double, std::milli> elapsed_milliseconds = end - start; \
        SPDLOG_LOGGER_INFO(::Logger::getLogger(),  \
            "[[title={},elapsed_ms={}]]",                  \
            #Expr, elapsed_milliseconds.count()); \
    }

并将上面的代码改为:

zdb::PreparedStatement prep_stmt = conn.prepareStatement(sql.data());
prep_stmt.setLLong(1, key);
zdb::ResultSet result =
        EXPR_LOG_BEGIN(DB_Execute)
        prep_stmt.executeQuery();
        EXPR_LOG_END

所以代码展开为:

zdb::PreparedStatement prep_stmt = conn.prepareStatement(sql.data());
prep_stmt.setLLong(1, key);
zdb::ResultSet result = [&]() {
    SCOPE_LOG(Name);
    return prep_stmt.executeQuery();
}();

这些东西安全吗?使用立即 lambda 为表达式添加语句会是任何隐藏的错误吗?有没有更好的方法来做到这一点?我发现了一个类似的问题:How to immediately invoke a C++ lambda? 但它没有明确说明其用例

【问题讨论】:

  • lambda 有什么用?为什么不简单地 zdb::ResultSet result = prep_stmt.executeQuery(); SCOPE_LOG(Name); 或者 zdb::ResultSet result = prep_stmt.executeQuery(); { SCOPE_LOG(Name); } ?您要解决的实际问题是什么?
  • @idclev463035818 我想记录运行prep_stmt.executeQuery() 的时间。当前范围内还有其他一些声明。 SCOPE_LOG 定义一个将在其析构函数中打印日志的对象
  • lambda(temporary) 在通过 () 执行后立即被销毁。所以,关于它的范围(它不会比任何捕获的变量更长寿)它应该是安全的。

标签: c++


【解决方案1】:

最好写一个这样的模板化包装器:

void log(const std::string& str)
{
  std::cout << str << std::endl;
}

template <class Functor>
typename std::result_of<Functor()>::type my_wrapper(Functor f)
{
  // do whatever you need inside
  auto start = std::chrono::system_clock::now();
  auto result = f();
  auto end = std::chrono::system_clock::now();
  std::chrono::duration<double, std::milli> elapsed_milliseconds = end - start;
  log("Elapsed time: " + std::to_string(elapsed_milliseconds.count()));
  return result;
}

int main()
{
  std::cout << my_wrapper([]()
    {
      std::this_thread::sleep_for(std::chrono::seconds(1));
      return 3.4;
    }) << std::endl;
  return 0;
}

它比宏更舒服一点,因为你不需要担心括号等。

从c++14开始可以简化签名:

template <class Functor>
auto my_wrapper(Functor f)

在 c++20 中可以添加约束

template <class Functor>
auto my_wrapper(Functor f) requires std::invocable<Functor>

在您传递一些需要参数的函数时获得更好的编译器诊断。

【讨论】:

    猜你喜欢
    • 2015-04-08
    • 2014-11-06
    • 2018-01-25
    • 2021-11-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-28
    • 2015-09-21
    相关资源
    最近更新 更多