Using gsl::narrow fails

203 Views Asked by At

I know there are similar questions and I don't know the best wording for this one.

I find it a little ironic that the reason for the code analysis warning in the first place was that it told me to use gsl::narrow into two instances:

Instance 1:

auto* pCell1 = gsl::narrow<CGridCellBase*>(lParam1);
auto* pCell2 = gsl::narrow<CGridCellBase*>(lParam2);

Compilation error:

    6>D:\My Libraries\GSL-main\include\gsl\util(105,1): error C2440: 'static_cast': cannot convert from 'U' to 'T'
6>        with
6>        [
6>            U=LPARAM
6>        ]
6>        and
6>        [
6>            T=CGridCellBase *
6>        ]
6>D:\My Libraries\GSL-main\include\gsl\util(105,12): message : Conversion from integral type to pointer type requires reinterpret_cast, C-style cast or function-style cast

Instance 2:

auto* pItem = gsl::narrow<NM_GRIDVIEW*>(pNotifyStruct);

Compilation error:

6>D:\My Libraries\GSL-main\include\gsl\narrow(58,1): error C2440: 'static_cast': cannot convert from 'const T' to 'U'
6>        with
6>        [
6>            T=NM_GRIDVIEW *
6>        ]
6>        and
6>        [
6>            U=NMHDR *
6>        ]
6>D:\My Libraries\GSL-main\include\gsl\narrow(58,9): message : Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast

Those messages are telling me to do the reverse:

  • Conversion from integral type to pointer type requires reinterpret_cast, C-style cast or function-style cast
  • Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast

Going around in circles! Given the situation then, am I to understand that the correct way forward is:

  1. Use reinterpret_cast and...
  2. Add appropriate prama warning to suppress the warning.

Correct?

1

There are 1 best solutions below

3
On BEST ANSWER

You can't (and shouldn't try to) use anything other than a reinterpret_cast to convert between a pointer and a non-pointer, or between pointers to different (unrelated) types. The gsl::narrow function is just a 'fancy' version of static_cast: Understanding gsl::narrow implementation.

Further, when writing programs that use the WinAPI or MFC, it is virtually impossible to completely avoid casting between pointer and non-pointer types; notably, many of the message handling routines take a pointer to some data or other as their lParam argument (the LPARAM type is defined as either __int64 or int, depending on the target platform).

So, your suggestion is, IMHO, the best option:

  1. Use reinterpret_cast and...
  2. Add appropriate pragma warning to suppress the warning.

However, you will most likely need to add that #pragma... directive in many places in your code. So, what you can do is to create a 'helper' (or wrapper) cast of your own, which you can then use throughout your code.

For example, you can add the following to your "stdafx.h" (or "pch.h") file (or to any header that is included wherever the cast is needed):

template<typename T, typename U> static T inline pointer_cast(U src) noexcept
{
    static_assert(sizeof(T) >= sizeof(U), "Invalid pointer cast"); // Check sizes!
    __pragma(warning(suppress:26490)) // Note: no semicolon after this expression!
    return reinterpret_cast<T>(src);
}

You can then use that pointer_cast and avoid having to add the pragma each time. Here's a typical example, using a potential message handler for the WM_NOTIFY message in a custom dialog box class:

BOOL MyDialog::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT *pResult)
{
    NMHDR* pHdr = pointer_cast<NMHDR*>(lParam);
    switch (pHdr->code) {
        //... remaining code ...

Note: on the use of the __pragma() directive (rather than #pragma), see here.