使用 BOLDDAY 宏解决代码分析警告(与 CMonthCalCtrl 一起使用)
Resolving code analysis warnings with the BOLDDAY macro (used with CMonthCalCtrl)
我在 CMonthCalCtrl
控件和代码现代化方面遇到了一些问题。第一个问题与 BOLDDAY
宏有关。
这个宏是用来调整day states(在日历上把具体的日期加粗),这个概念有详细的描述here。如文档所述,您需要定义一个宏:
#define BOLDDAY(ds, iDay) if(iDay > 0 && iDay < 32) \
(ds) |= (0x00000001 << (iDay-1))
这是我使用此宏的代码,以便您了解一些上下文:
void CMeetingScheduleAssistantDlg::InitDayStateArray(int iMonthCount, LPMONTHDAYSTATE pDayState, COleDateTime datStart)
{
int iMonth = 0;
COleDateTimeSpan spnDay;
CString strKey;
SPECIAL_EVENT_S *psEvent = nullptr;
if (pDayState == nullptr)
return;
memset(pDayState, 0, sizeof(MONTHDAYSTATE)*iMonthCount);
if (m_pMapSPtrEvents == nullptr && m_Reminders.Count() == 0)
{
return;
}
spnDay.SetDateTimeSpan(1, 0, 0, 0);
auto datDay = datStart;
const auto iStartMonth = datStart.GetMonth();
auto iThisMonth = iStartMonth;
auto iLastMonth = iThisMonth;
do
{
strKey = datDay.Format(_T("%Y-%m-%d"));
if (m_pMapSPtrEvents != nullptr)
{
psEvent = nullptr;
m_pMapSPtrEvents->Lookup(strKey, reinterpret_cast<void*&>(psEvent));
if (psEvent != nullptr)
{
BOLDDAY(pDayState[iMonth], datDay.GetDay());
}
}
if (m_Reminders.HasReminder(datDay))
{
BOLDDAY(pDayState[iMonth], datDay.GetDay());
}
datDay = datDay + spnDay;
iThisMonth = datDay.GetMonth();
if (iThisMonth != iLastMonth)
{
iLastMonth = iThisMonth;
iMonth++;
}
} while (iMonth < iMonthCount);
}
我在任何地方使用此 BOLDDAY
宏都会收到代码分析警告 (C26481):
warning C26481: Don't use pointer arithmetic. Use span instead (bounds.1).
我不清楚问题出在 BOLDDAY
宏还是我自己的代码上?
更新
当我将宏转换为函数时仍然收到警告:
更新 2
如果有帮助,我目前通过以下方式调用 InitDayStateArray
函数:
- 方法一:
void CMeetingScheduleAssistantDlg::SetDayStates(CMonthCalCtrl &rCalendar)
{
COleDateTime datFrom, datUntil;
const auto iMonthCount = rCalendar.GetMonthRange(datFrom, datUntil, GMR_DAYSTATE);
auto pDayState = new MONTHDAYSTATE[iMonthCount];
if (pDayState != nullptr)
{
InitDayStateArray(iMonthCount, pDayState, datFrom);
VERIFY(rCalendar.SetDayState(iMonthCount, pDayState));
delete[] pDayState;
}
}
- 方法二
void CMeetingScheduleAssistantDlg::OnGetDayStateEnd(NMHDR* pNMHDR, LRESULT* pResult)
{
NMDAYSTATE* pDayState = reinterpret_cast<NMDAYSTATE*>(pNMHDR);
MONTHDAYSTATE mdState[3]{}; // 1 = prev 2 = curr 3 = next
const COleDateTime datStart(pDayState->stStart);
if (pDayState != nullptr)
{
InitDayStateArray(pDayState->cDayState, &mdState[0], datStart);
pDayState->prgDayState = &mdState[0];
}
if (pResult != nullptr)
*pResult = 0;
}
也许如果 LPMONTHDAYSTATE
信息的容器以某种方式进行调整,它将有助于解决这个 span
问题?
Microsoft 提供的示例代码曾经作为可同时使用 C 和 C++ 编译器进行编译的代码发布。这限制了语言功能的可用性,经常生成特别是 C++ 客户端不应该逐字使用的代码。
这里的情况是 BOLDDAY
类似函数的宏,它解决了 C 中没有引用类型的问题。另一方面,C++ 有,宏可以替换为函数:
void bold_day(DWORD& day_state, int const day) noexcept {
if (day > 0 && day < 32) {
day_state |= (0x00000001 << (day - 1));
}
}
使用此函数代替 BOLDDAY
宏可使 C26481 诊断静音。
虽然这有效,但我完全无法理解编译器在宏版本中看到指针算法的位置。无论如何,在可能的情况下用实际函数(或函数模板)替换类似函数的宏总是可取的。
更新
现在事情开始变得有意义了。如上所述,虽然用函数替换类函数宏是可取的,但它不会解决问题。我的测试碰巧使用了 pDayState[0]
,它仍然为宏引发 C26481,但没有为函数引发。而是使用 pDayState[1]
,在任何一种情况下都会引发诊断。
让我们把拼图的各个部分放在一起:回想一下,当 p
是指针类型并且 N
整型。这解释了为什么编译器在看到 pDayState[iMonth]
.
时抱怨 "pointer arithmetic"
解决这个问题相当简单。根据诊断的建议,使用 std::span
(需要 C++20)。对 InitDayStateArray()
的以下更改使 C26481 诊断消失:
void CMeetingScheduleAssistantDlg::InitDayStateArray(int iMonthCount,
LPMONTHDAYSTATE pDayState,
COleDateTime datStart)
{
std::span const day_month_state(pDayState, iMonthCount);
// ...
// memset(pDayState, 0, sizeof(MONTHDAYSTATE)*iMonthCount);
std::fill(begin(day_month_state), end(day_month_state), 0);
// ...
do
{
// ...
{
bold_day(day_month_state[iMonth], datDay.GetDay());
}
}
if (m_Reminders.HasReminder(datDay))
{
bold_day(day_month_state[iMonth], datDay.GetDay());
}
// ...
} while (iMonth < day_month_state.size());
}
A std::span
“描述了一个可以引用连续对象序列的对象”。它采用描述数组的分解指针和大小参数,并将它们重新组合成一个对象,恢复数组的完整保真度。
听起来不错。但请记住,这是 C++,有一个警告:就像它邪恶的 C++17 祖先 std::string_view
一样,std::span
是一个毫不犹豫的悬挂指针工厂。您可以自由地传递它们,并在引用数据还活着的时候继续使用它们。这对于 每个 专业化都是有保证的,从 C++23 开始。
另一个问题是,解决这个诊断问题现在有几个突然冒出来的问题,这表明 std::span
不够好,应该改用 gsl::span
。解决这些问题可能需要再进行一次问答。
我在 CMonthCalCtrl
控件和代码现代化方面遇到了一些问题。第一个问题与 BOLDDAY
宏有关。
这个宏是用来调整day states(在日历上把具体的日期加粗),这个概念有详细的描述here。如文档所述,您需要定义一个宏:
#define BOLDDAY(ds, iDay) if(iDay > 0 && iDay < 32) \
(ds) |= (0x00000001 << (iDay-1))
这是我使用此宏的代码,以便您了解一些上下文:
void CMeetingScheduleAssistantDlg::InitDayStateArray(int iMonthCount, LPMONTHDAYSTATE pDayState, COleDateTime datStart)
{
int iMonth = 0;
COleDateTimeSpan spnDay;
CString strKey;
SPECIAL_EVENT_S *psEvent = nullptr;
if (pDayState == nullptr)
return;
memset(pDayState, 0, sizeof(MONTHDAYSTATE)*iMonthCount);
if (m_pMapSPtrEvents == nullptr && m_Reminders.Count() == 0)
{
return;
}
spnDay.SetDateTimeSpan(1, 0, 0, 0);
auto datDay = datStart;
const auto iStartMonth = datStart.GetMonth();
auto iThisMonth = iStartMonth;
auto iLastMonth = iThisMonth;
do
{
strKey = datDay.Format(_T("%Y-%m-%d"));
if (m_pMapSPtrEvents != nullptr)
{
psEvent = nullptr;
m_pMapSPtrEvents->Lookup(strKey, reinterpret_cast<void*&>(psEvent));
if (psEvent != nullptr)
{
BOLDDAY(pDayState[iMonth], datDay.GetDay());
}
}
if (m_Reminders.HasReminder(datDay))
{
BOLDDAY(pDayState[iMonth], datDay.GetDay());
}
datDay = datDay + spnDay;
iThisMonth = datDay.GetMonth();
if (iThisMonth != iLastMonth)
{
iLastMonth = iThisMonth;
iMonth++;
}
} while (iMonth < iMonthCount);
}
我在任何地方使用此 BOLDDAY
宏都会收到代码分析警告 (C26481):
warning C26481: Don't use pointer arithmetic. Use span instead (bounds.1).
我不清楚问题出在 BOLDDAY
宏还是我自己的代码上?
更新
当我将宏转换为函数时仍然收到警告:
更新 2
如果有帮助,我目前通过以下方式调用 InitDayStateArray
函数:
- 方法一:
void CMeetingScheduleAssistantDlg::SetDayStates(CMonthCalCtrl &rCalendar)
{
COleDateTime datFrom, datUntil;
const auto iMonthCount = rCalendar.GetMonthRange(datFrom, datUntil, GMR_DAYSTATE);
auto pDayState = new MONTHDAYSTATE[iMonthCount];
if (pDayState != nullptr)
{
InitDayStateArray(iMonthCount, pDayState, datFrom);
VERIFY(rCalendar.SetDayState(iMonthCount, pDayState));
delete[] pDayState;
}
}
- 方法二
void CMeetingScheduleAssistantDlg::OnGetDayStateEnd(NMHDR* pNMHDR, LRESULT* pResult)
{
NMDAYSTATE* pDayState = reinterpret_cast<NMDAYSTATE*>(pNMHDR);
MONTHDAYSTATE mdState[3]{}; // 1 = prev 2 = curr 3 = next
const COleDateTime datStart(pDayState->stStart);
if (pDayState != nullptr)
{
InitDayStateArray(pDayState->cDayState, &mdState[0], datStart);
pDayState->prgDayState = &mdState[0];
}
if (pResult != nullptr)
*pResult = 0;
}
也许如果 LPMONTHDAYSTATE
信息的容器以某种方式进行调整,它将有助于解决这个 span
问题?
Microsoft 提供的示例代码曾经作为可同时使用 C 和 C++ 编译器进行编译的代码发布。这限制了语言功能的可用性,经常生成特别是 C++ 客户端不应该逐字使用的代码。
这里的情况是 BOLDDAY
类似函数的宏,它解决了 C 中没有引用类型的问题。另一方面,C++ 有,宏可以替换为函数:
void bold_day(DWORD& day_state, int const day) noexcept {
if (day > 0 && day < 32) {
day_state |= (0x00000001 << (day - 1));
}
}
使用此函数代替 BOLDDAY
宏可使 C26481 诊断静音。
虽然这有效,但我完全无法理解编译器在宏版本中看到指针算法的位置。无论如何,在可能的情况下用实际函数(或函数模板)替换类似函数的宏总是可取的。
更新
现在事情开始变得有意义了。如上所述,虽然用函数替换类函数宏是可取的,但它不会解决问题。我的测试碰巧使用了 pDayState[0]
,它仍然为宏引发 C26481,但没有为函数引发。而是使用 pDayState[1]
,在任何一种情况下都会引发诊断。
让我们把拼图的各个部分放在一起:回想一下,当 p
是指针类型并且 N
整型。这解释了为什么编译器在看到 pDayState[iMonth]
.
解决这个问题相当简单。根据诊断的建议,使用 std::span
(需要 C++20)。对 InitDayStateArray()
的以下更改使 C26481 诊断消失:
void CMeetingScheduleAssistantDlg::InitDayStateArray(int iMonthCount,
LPMONTHDAYSTATE pDayState,
COleDateTime datStart)
{
std::span const day_month_state(pDayState, iMonthCount);
// ...
// memset(pDayState, 0, sizeof(MONTHDAYSTATE)*iMonthCount);
std::fill(begin(day_month_state), end(day_month_state), 0);
// ...
do
{
// ...
{
bold_day(day_month_state[iMonth], datDay.GetDay());
}
}
if (m_Reminders.HasReminder(datDay))
{
bold_day(day_month_state[iMonth], datDay.GetDay());
}
// ...
} while (iMonth < day_month_state.size());
}
A std::span
“描述了一个可以引用连续对象序列的对象”。它采用描述数组的分解指针和大小参数,并将它们重新组合成一个对象,恢复数组的完整保真度。
听起来不错。但请记住,这是 C++,有一个警告:就像它邪恶的 C++17 祖先 std::string_view
一样,std::span
是一个毫不犹豫的悬挂指针工厂。您可以自由地传递它们,并在引用数据还活着的时候继续使用它们。这对于 每个 专业化都是有保证的,从 C++23 开始。
另一个问题是,解决这个诊断问题现在有几个突然冒出来的问题,这表明 std::span
不够好,应该改用 gsl::span
。解决这些问题可能需要再进行一次问答。