The Issue
sizeof returns size_t type, so when passed as argument to functions that takes in narrower types (e.g. unsigned char), implicit conversion occurs. In many cases these are 3rd party library functions, so their prototypes are beyond my control. Compilers are now typically smart enough to detect whether such conversions would really cause truncation and warn you about it, but some static code analyzers will still flag such cases out, leading to lots of false positives. Explicitly casting the result of sizeof typically resolves the analysis warnings but it would hide the compiler warnings, not to mention that it makes things clunky.
My Solution
template<class T1, class T2>
struct sizeofxx {
static constexpr T2 value{ sizeof(T1) };
};
template <class T>
constexpr unsigned int sizeof32 = sizeofxx<T, unsigned int>::value;
template <class T>
constexpr unsigned short sizeof16 = sizeofxx<T, unsigned short>::value;
template <class T>
constexpr unsigned char sizeof8 = sizeofxx<T, unsigned char>::value;
Usage:
unsigned int foo = sizeof32<float>;
const char bar[255];
unsigned char foo3 = sizeof8<decltype(bar)>;
It relies on aggregate initialization to guard against narrowing conversion at compile time. So if I had used bar[256], the build fails.
Limitation
But as you can see, using it on variables is rather clunky (due to the need for decltype). Is there a simpler way to do this? I know one way is wrap it in a macro, but this would prevent IDEs like Visual Studio from helping you resolve the value when you mouseover it. Another way is to create a constexpr function:
template <class T1>
constexpr unsigned char sizeof8f(T1&) {
return sizeof(T1);
}
But this also does not allow for IDE code-time resolution, and would expand the number of symbols involved since they need to be of different names from the earlier implementation that operates on types.
Any other suggestions on resolving the root issue (static code analysis warnings) are welcomed. And no, suppressing them is not feasible.