Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating custom sizeof() that returns narrower types

Tags:

c++

templates

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.

like image 807
wmjdgla Avatar asked Jan 28 '26 10:01

wmjdgla


1 Answers

For your specific problem, there need not be any runtime checks even on debug builds as some has suggested, since the value is itself a constexpr. You can write a simple utility to cast a value to the smallest type that is able to hold it.

template<size_t N>
inline constexpr auto minuint = []{
    if constexpr(N >= 1ull << 32)
        return N;
    else if constexpr(N >= 1ull << 16)
        return uint32_t(N);
    else if constexpr(N >= 1ull << 8)
        return uint16_t(N);
    else
        return uint8_t(N);
}();

On the other hand, no function or template can ever accept both expressions and types. The only possible way to imitate sizeof behaviour is to use a macro.

#define Sizeof(x) minuint<sizeof(x)>

With this, you never get false warnings on narrowing conversions: if there is a warning, you are doing something wrong.

like image 153
Passer By Avatar answered Jan 30 '26 00:01

Passer By



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!