当前位置: 首页 > news >正文

C++ try{}catch{} 语句块中潜藏问题排查指南

报错内容

home/zry/src/modules/web/OpWebMD.cpp: In member function ‘virtual grpc::Status WeatherObservationServiceImpl::SaveEmergency(grpc::ServerContext*, const protoBufferWeb::SaveEmergencyRequest*, protoBufferWeb::CommonResponse*)’:
/home/zry/src/modules/web/OpWebMD.cpp:11680:47: error: ‘const class std::exception’ has no member named ‘evaporation’
11680 |             TRY_CONVERT_DOUBLE(sData.EVAPB, e.evaporation().v(), "EVAPB");|                                               ^~~~~~~~~~~
/home/zry/src/modules/web/../../include/tool/../zryHeard.hpp:111:24: note: in definition of macro ‘ZRY_LOG_ERROR’111 |                 msg, ##__VA_ARGS__)|                        ^~~~~~~~~~~
/home/zry/src/modules/web/OpWebMD.cpp:11680:13: note: in expansion of macro ‘TRY_CONVERT_DOUBLE’
11680 |             TRY_CONVERT_DOUBLE(sData.EVAPB, e.evaporation().v(), "EVAPB");|             ^~~~~~~~~~~~~~~~~~
/home/zry/src/modules/web/OpWebMD.cpp:11681:54: error: ‘const class std::exception’ has no member named ‘totalevap’
11681 |             TRY_CONVERT_DOUBLE(sData.EVAPB_ddaccu, e.totalevap().v(), "EVAPB_ddaccu");|                                                      ^~~~~~~~~
/home/zry/src/modules/web/../../include/tool/../zryHeard.hpp:111:24: note: in definition of macro ‘ZRY_LOG_ERROR’111 |                 msg, ##__VA_ARGS__)|                        ^~~~~~~~~~~
/home/zry/src/modules/web/OpWebMD.cpp:11681:13: note: in expansion of macro ‘TRY_CONVERT_DOUBLE’
11681 |             TRY_CONVERT_DOUBLE(sData.EVAPB_ddaccu, e.totalevap().v(), "EVAPB_ddaccu");

问题出现代码

// 安全转换宏定义
#define TRY_CONVERT_DOUBLE(target, source, field_name) \try { \if (!source.empty()) { \ZRY_LOG_DEBUG("Converting double for {}: source='{}'", field_name, source); \target = std::stod(source); \ZRY_LOG_DEBUG("Converted double for {}: success={}", field_name, target); \} else { \ZRY_LOG_DEBUG("Skipping double conversion for {}: source is empty", field_name); \} \} catch (const std::exception& e) { \ZRY_LOG_ERROR("{} double conversion failed! Source='{}' Error: {}", field_name, source, e.what()); \}#define TRY_CONVERT_INT(target, source, field_name) \try { \if (!source.empty()) { \ZRY_LOG_DEBUG("Converting int for {}: source='{}'", field_name, source); \target = std::stoi(source); \ZRY_LOG_DEBUG("Converted int for {}: success={}", field_name, target); \} else { \ZRY_LOG_DEBUG("Skipping int conversion for {}: source is empty", field_name); \} \} catch (const std::exception& e) { \ZRY_LOG_ERROR("{} int conversion failed! Source='{}' Error: {}", field_name, source, e.what()); \}try
{std::unique_ptr<CWebModuleMysqlTool> mysqlOnceUserTool; // 数据库操作工具类指针mysqlOnceUserTool = std::make_unique<CWebModuleMysqlTool>(strMySqlIp, strMySqlUser, strMySqlPassword, strMySqlDatabase);sta_obdata_hourly sData;const auto& data = request->data();// 1. 基础时间字段sData.sta_time = request->time();  // YYYYMMDDHH格式ZRY_LOG_DEBUG("Set sta_time: {}", sData.sta_time);// 6. 蒸发数据if (data.has_evaporation()) {ZRY_LOG_DEBUG("Processing evaporation data");const auto& e = data.evaporation();TRY_CONVERT_DOUBLE(sData.EVAPB, e.evaporation().v(), "EVAPB");TRY_CONVERT_DOUBLE(sData.EVAPB_ddaccu, e.totalevap().v(), "EVAPB_ddaccu");}// 二、更新传入时次天数的天气现象try {ZRY_LOG_DEBUG("Updating weather phenomena for the day");// 提取日期部分 (格式: YYYY-MM-DD)std::string dateOnly = sData.sta_time.substr(0, 10);// 构造当天的起始时间和次日零时时间点std::string startTime = dateOnly + " 00:00:00";std::string nextDayTime = dateOnly + " 23:59:59"; // 当日截止时间(含毫秒)// 修改SQL:更新指定日期的所有数据行std::string strSql = "UPDATE sta_obdata_hourly ""SET WEATA_wwWW = '" + sData.WEATA_wwWW + "', ""    WEATA = '" + sData.WEATA + "' ""WHERE sta_time BETWEEN '" + startTime + "' AND '" + nextDayTime + "';";ZRY_LOG_DEBUG("Executing weather phenomena update SQL: {}", strSql);mysqlOnceUserTool->Myexecute(strSql);}catch (const std::runtime_error &e) {ZRY_LOG_ERROR("Runtime error occurred during weather phenomena update: {}", e.what());}catch (const std::exception &e) {ZRY_LOG_ERROR("Exception occurred during weather phenomena update: {}", e.what());}catch (...) {ZRY_LOG_ERROR("Unknown exception occurred during weather phenomena update");}// 三、最后更新日照数据到辐射快照表try {ZRY_LOG_DEBUG("Updating sun data to radiation snapshot table");// 提取日期部分 (格式: YYYY-MM-DD)std::string dateOnly = sData.sta_time.substr(0, 10);if (data.has_sunlist()) {const auto& sl = data.sunlist();for (const auto& sh : sl.hoursunlist()) {std::string strTimec = dateOnly + " " + sh.hour() + ":00:00";if (!sh.data().v().empty()) {// 修正SQL:移除末尾多余的逗号std::string strSql = "UPDATE sta_obysdr_hourly ""SET SUNDA_p0accu = '" + sh.data().v() + "' " // 注意这里移除了逗号"WHERE timec = '" + strTimec + "';";ZRY_LOG_DEBUG("Executing sun data update SQL for {}: {}", strTimec, strSql);mysqlOnceUserTool->Myexecute(strSql);} else {ZRY_LOG_WARN("Skipping sun data update for {}: data is empty", strTimec);}}}}catch (const std::runtime_error &e) {ZRY_LOG_ERROR("Runtime error occurred during sun data update: {}", e.what());}catch (const std::exception &e) {ZRY_LOG_ERROR("Exception occurred during sun data update: {}", e.what());}catch (...) {ZRY_LOG_ERROR("Unknown exception occurred during sun data update");}
}
catch (const std::exception& e) {ZRY_LOG_ERROR("Overall processing error: {}", e.what());
}
catch (...) {ZRY_LOG_ERROR("Unknown overall processing error");
}

错误分析

根据错误信息,编译器抱怨在e.evaporation()e.totalevap()中,e的类型是const class std::exception,而std::exception并没有evaporationtotalevap成员。这显然是不正确的,因为e应该是蒸发数据类型的对象。

观察错误发生的上下文:

在代码中,蒸发数据部分如下:

if (data.has_evaporation()) {const auto& e = data.evaporation();// ...
}

错误信息显示编译器将e视为std::exception,这通常发生在宏展开时出现了问题。注意,在宏中我们使用了e作为参数,而宏内部可能使用了e作为变量名,从而与异常对象e冲突。

让我们检查宏定义:

#define TRY_CONVERT_DOUBLE(target, source, field_name) \try { \if (!source.empty()) { \ZRY_LOG_DEBUG("Converting double for {}: source='{}'", field_name, source); \target = std::stod(source); \ZRY_LOG_DEBUG("Converted double for {}: success={}", field_name, target); \} else { \ZRY_LOG_DEBUG("Skipping double conversion for {}: source is empty", field_name); \} \} catch (const std::exception& e) { \ZRY_LOG_ERROR("{} double conversion failed! Source='{}' Error: {}", field_name, source, e.what()); \}

注意,在宏的catch块中,我们捕获了异常并命名为e。而在蒸发数据部分,我们使用了e作为蒸发数据的常量引用。当宏展开时,在catch块中使用的e就会与外部作用域中的e冲突。

例如,对于TRY_CONVERT_DOUBLE(sData.EVAPB, e.evaporation().v(), "EVAPB");,宏展开后:

try {if (!e.evaporation().v().empty()) {ZRY_LOG_DEBUG(...);sData.EVAPB = std::stod(e.evaporation().v());...}
} catch (const std::exception& e) {ZRY_LOG_ERROR(..., e.what());   // 这里的e是异常对象,而不是外部的e
}

但是,在蒸发数据部分,我们有一个变量e(代表蒸发数据),而在宏的catch块中又有一个变量e(代表异常),这在同一个作用域内会导致冲突吗?实际上,在catch块中,e的作用域仅限于该块内,不会影响外部作用域。但是,在宏参数中我们使用了e.evaporation().v(),而宏内部在catch块中又使用了e(异常对象),那么在catch块中,我们无法再访问外部的e(因为被异常对象e覆盖了)。

然而,在catch块中我们并没有使用外部的e,所以问题应该不大。但是,错误信息中却出现了const class std::exception’ has no member named ‘evaporation’,这提示在宏内部,当我们在try块中使用e(例如e.evaporation().v())时,编译器认为estd::exception类型,这是不可能的。

另一种可能是,在宏展开时,由于宏参数中使用了e,而宏内部catch块中也使用了e,导致预处理器产生了混淆。但是,预处理器并不理解C++作用域,这种可能性不大。

实际上,错误信息是在编译阶段报出的,提示在蒸发数据的e.evaporation()中,econst std::exception类型。这很奇怪,因为e应该是data.evaporation()返回的类型,该类型是协议缓冲区中的一个消息类型。

重新考虑错误信息:

/home/zry/src/modules/web/OpWebMD.cpp:11680:47: error: ‘const class std::exception’ has no member named ‘evaporation’
11680 |             TRY_CONVERT_DOUBLE(sData.EVAPB, e.evaporation().v(), "EVAPB");

这行代码中,e应该是一个协议缓冲区的消息,但编译器却认为它是std::exception。这可能是由于宏展开后,在某些情况下,e被错误地认为是其他类型。

仔细观察错误位置:11680行。在宏展开后,实际代码被展开成多行,编译器报错指出在11680行(也就是宏调用的那一行)的e.evaporation()有错。但这时,e应该是蒸发数据消息,为什么会被认为是std::exception呢?

一个可能的解释是,在宏展开过程中,由于宏内部使用了e作为异常变量,而展开后的代码中,在catch块之前,e仍然被当作蒸发数据消息,但是在catch块中,e被重新定义。然而,这不应该影响try块中的e

我猜测实际的问题可能是在宏的调用参数中,e.evaporation().v()中的e被编译器错误地解析了。但这种情况很少见。

另一种可能性:在包含这个宏调用的作用域内,之前已经定义了一个名为e的变量,其类型是std::exception?这不太可能,因为我们在蒸发数据部分刚刚定义了const auto& e = ...

我们来看一下在蒸发数据部分之前的代码,但根据错误信息,问题出现在11680行,这一行就在定义e之后。

结论: 这个错误表明在宏展开过程中出现了符号冲突。问题在于宏内部的 e 被误解释为 std::exception 对象,而不是我们期望的协议缓冲区对象。

修改方案

我们可以尝试修改宏,将异常对象重命名为其他名字,以避免任何潜在的冲突:

修改宏:

// 修改安全转换宏,使用不同的异常变量名
#define TRY_CONVERT_DOUBLE(target, source, field_name) \try { \if (!source.empty()) { \ZRY_LOG_DEBUG("Converting double for {}: source='{}'", field_name, source); \target = std::stod(source); \ZRY_LOG_DEBUG("Converted double for {}: success={}", field_name, target); \} else { \ZRY_LOG_DEBUG("Skipping double conversion for {}: source is empty", field_name); \} \} catch (const std::exception& ex) { \  // 将 e 改为 ex 避免冲突ZRY_LOG_ERROR("{} double conversion failed! Source='{}' Error: {}", field_name, source, ex.what()); \}#define TRY_CONVERT_INT(target, source, field_name) \try { \if (!source.empty()) { \ZRY_LOG_DEBUG("Converting int for {}: source='{}'", field_name, source); \target = std::stoi(source); \ZRY_LOG_DEBUG("Converted int for {}: success={}", field_name, target); \} else { \ZRY_LOG_DEBUG("Skipping int conversion for {}: source is empty", field_name); \} \} catch (const std::exception& ex) { \  // 将 e 改为 ex 避免冲突ZRY_LOG_ERROR("{} int conversion failed! Source='{}' Error: {}", field_name, source, ex.what()); \}

同时,在蒸发数据处理部分,你需要使用协议缓冲区对象而不是 std::exception。在完整代码中应该这样处理:

// 6. 蒸发数据
if (data.has_evaporation()) {ZRY_LOG_DEBUG("Processing evaporation data");// 从协议缓冲区获取蒸发数据对象const auto& evaporationData = data.evaporation();  // 使用更有描述性的变量名// 处理蒸发量TRY_CONVERT_DOUBLE(sData.EVAPB, evaporationData.evaporation().v(), "EVAPB");// 处理总蒸发量TRY_CONVERT_DOUBLE(sData.EVAPB_ddaccu, evaporationData.totalevap().v(), "EVAPB_ddaccu");
}

主要修改点:

  1. 将宏中的异常对象变量名从 e 改为 ex,避免与局部变量 e 冲突
  2. 在蒸发数据处理部分,使用更具描述性的变量名 evaporationData 代替 e
  3. 确保通过 evaporationData 访问协议缓冲区的字段

总结

  1. 当使用 try catch 语块时应当避免 try 中的 变量名错误捕获异常对象变量名 一致。
  2. 频繁的 try catch 语块应该改为 函数调用,并在层级结构中合理的捕获错误或者 抛回上级错误。

相关文章:

  • 第十二节:第六部分:集合框架:LinkedHashSet集合底层原理、TreeSet集合
  • Android 中的 DataBinding 详解
  • 利用 Scrapy 构建高效网页爬虫:框架解析与实战流程
  • 谷歌地图手机版(Google maps)v11.152.0100安卓版 - 前端工具导航
  • 嵌入式笔试题+面试题
  • SKUA-GOCAD入门教程-第八节 线的创建与编辑2
  • 谷歌地图2022高清卫星地图手机版v10.38.2 安卓版 - 前端工具导航
  • 数据挖掘顶刊《IEEE Transactions on Knowledge and Data Engineering》2025年5月研究热点都有些什么?
  • 服装产品属性描述数据集(19197条),AI智能体知识库收集~
  • Hadoop 3.x 伪分布式 8088端口无法访问问题处理
  • Stone 3D新版本发布,添加玩家控制和生物模拟等组件,增强路径编辑功能,优化材质编辑
  • Could not get unknown property ‘mUser‘ for Credentials [username: null]
  • uniapp 开发企业微信小程序,如何区别生产环境和测试环境?来处理不同的服务请求
  • AWS VPC 网络详解:理解云上专属内网的关键要素
  • 机器学习:集成学习概念、分类、随机森林
  • 机器学习在多介质环境中多污染物空间预测的应用研究
  • 结合 AI 生成 mermaid、plantuml 等图表
  • EscapeX:去中心化游戏,开启极限娱乐新体验
  • 关于Tabs组件下TabPane使用v-if导致顺序错误以及页面渲染异常的解决方法
  • 机器学习——聚类算法
  • 苏州网站搜索优化/seo搜索引擎优化兴盛优选
  • 百度收录网站但首页快照不更新/时事新闻最新
  • wordpress 强密码 弱/英文关键词seo
  • 邢台做移动网站哪儿好/广州各区风险区域最新动态
  • 房地产类型的网站建设/网站优化公司哪家效果好
  • 企业网站seo报价/广州婚恋网站排名