Inline function which is equal to "sizeof expression", but returns a signed value
Asked Answered
c++
B

2

1

Is it possible to create an inline function wrapper around "sizeof expression", which returns a signed number?

How would a wrapper function look like? It should work with all kind of expressions (C arrays, etc.), so it can be a one-for-one replacement of sizeof, but it returns a signed number.

So, basically, I'd like to have a ssizeof, which returns a signed number, something like this:

constexpr std::ptrdiff_t ssizeof(X) {
    return static_cast<std::ptrdiff_t>(sizeof(X));
}

So for example:

long a;
int b = 8;

// no signed/unsigned comparison warning here, because
// ssizeof(a) returns a signed number
if (ssizeof(a)<b) {
}

The solution is maybe not that simple, because of the automatic array->pointer decay rules (and maybe there could be other problems?).

If one-for-one replacement is not possible (because ssizeof's parameter will always be evaluated), is it possible to do this, if evaluation is allowed?

Binate answered 12/12, 2017 at 16:12 Comment(28)
If you want a function for that, then it wont work for types such as ssizeof(int). How about using a macro for this? I know macro is bad in general, but in this case only #define SIZEOF(x) ... seems to work. Apart from that the expression you pass to it, needs to be unevaluated as well but ssizeof(f()) doesn't ensure f() is unevaluated.Toupee
Well, it can not be a function because sizeof is supposed to be unevaluating expression.Hanover
@VTT: oops, I didn't think of that. I've edited my question, to release the requirements a little bit.Binate
@Nawaz: sizeof(int) is not a "sizeof expression", but "sizeof(type)". But yes, I'd like to avoid macros for this, if possible, hence the question.Binate
I'm curious, where do you need a signed expression where an unsigned one wouldn't do?Facies
@MarkRansom: I use signed values for almost everything, and I dislike casting the result of sizeof all the time.Binate
You better stop using signed values rather than trying to write your own sizeof()Colicroot
Although you really shouldn't do this, you can just use a macro to "automate" your casting: #define MY_SIZEOF(x) std::ptrdiff_t(sizeof(x))Honey
"Is it possible to create an inline function wrapper around "sizeof expression"" short answer is - no you cannot, because sizeof() is not a function.Colicroot
But when is casting necessary? Implicit conversion works most of the time.Facies
@MarkRansom: I got warnings at signed/unsigned comparisonBinate
How about making a shorter casting function? like constexpr const ::std::ptrdiff_t s(::std::size_t const size) noexcept{ return(static_cast<::std::ptrdiff_t>(size)); } auto x = s(sizeof(4 + 4.0));Hanover
Now I understand, I see those warnings all the time too. But most of the time it's comparisons with container.size().Facies
@VTT: thanks, basically that's what I'm doing now, but I dislike it.Binate
@Colicroot nope, unsigned is PITA and we would be much better off with signed size_t (of course it wasn't really possible in the bad old days of 16-bit hardware).Maenad
Unfortunately you cannot have a sizeof expression without a macro. If you are going to use a macro, you just can #define ssizeof (ssize_t)sizeof and call it a day.Maenad
@Slava: I've been using signed numbers for almost everything for 15 years, and they are OK. The only problem is sizeof (the other would be the standard library, which uses size_t for size(), but I have my own containers, which returns a signed size()). This short CppCon presentation tells my standpoint pretty well: youtube.com/watch?v=wvtFGa6XJDUBinate
@n.m. bad old days? You think everybody programs for windows on Xeon these days?Colicroot
@Binate I understand it is ok, but what is the benefit vs unsigned type?Colicroot
If evaluation is allowed you can just have a reference parameter to your ssizeof function, but it's purely theoretical because you don't want to use an evaluating sizeof replacement. Or at the very least if you use it, you don't want to show it to anyone. I know I wouldn't.Maenad
@Slava: unsigned types have a disadvantage. Subtraction can do unexpected things. My favorite example is for (unsigned i=0; i<s-1; i++), if s is zero, then bad things happen. In the 32-bit world, there is no disadvantage of using signed numbers in my opinion, that's why I use them, I have no problems at all. The last thing which bothers me is sizeof (not a huge problem, I can live with it, I rarely use this operator)Binate
@n.m.: yes, I agree with you. So it seems only the macro is the one-for-one replacement. Not a big deal, I'll keep casting sizeof then :)Binate
@Binate then just write it as for (unsigned i=0; i+1<s; i++) problem solved. " I have no problems at all" you should make your mind, you have no problems at all or sizeof is not a huge problem.Colicroot
@Colicroot Most do. The language could have made lives of the majority easy and lives of the minority possible, rather than the other way around.Maenad
@n.m. I do not see how signed would make lives easy, I do not like that and doubt it would make my life easier, if you personally prefer this way does not mean everybody agree with you. Adding that most will not program on PC pretty soon so you may become that minority.Colicroot
@Colicroot I used to use unsigned types religiously, now I see it as mosyly wasted effort. YMMV. "PC" is a red herring. Most people program for some kind of 64 bit or larger hardware, and their part isn't going to get smaller any time soon; when and if it will, we'll think what to do next.Maenad
@Slava: What if that expression is more complex? With signeds I could forget this problem. With unsigneds and subtraction, one always have to be careful. And unsigneds don't give you anything (one bit of plus range? Doesn't matter...). sizeof is not a problem, but a minor inconvenience. But I don't really want to argue about this further. It seems that my problem cannot be solved with functions, so I'll keep casting the result of sizeof.Binate
@Binate problem is your approach is agiants the language and so it has readability issue. I hope that only you will have to read your code.Colicroot
S
2

There are many ways of doing it, of course, but I have settled on the following:

using ssize_t = std::make_signed_t<size_t>;

template <size_t usize> static inline constexpr ssize_t ssizeof_impl() {
  static_assert(usize <= std::numeric_limits<ssize_t>::max());
  return static_cast<ssize_t>(usize);
}

#define ssizeof(type_or_expr) (ssizeof_impl<sizeof(type_or_expr)>())

It uses a preprocessor macro to do the absolute minimum of work needed, and the rest is done by a constexpr function.

You then use ssizeof exactly the same way you'd use sizeof, and it does what's expected - with no undefined behavior.

Sphenic answered 12/12, 2017 at 16:12 Comment(0)
D
1

To get you started, you can use templates:

#include <iostream>

template <typename T>
constexpr std::ptrdiff_t ssizeof(T&& expression = {})
{
    return static_cast<std::ptrdiff_t>(sizeof(T));
}

int main()
{
    {
        int a[10];

        std::cout << "ssizeof<int>() = " << ssizeof<int>() << std::endl;
        std::cout << "ssizeof<int[3]>() = " << ssizeof<int[3]>() << std::endl;
        std::cout << "ssizeof<int[3]>() = " << ssizeof(a) << " / " << (sizeof a) << std::endl;
    }

    {
        int a;
        int b = 4;

        std::cout << "ssizeof<int[3]>() = " << ssizeof(a) << " / " << sizeof(a) << std::endl;
    }

    std::cout << "Done...";

    std::getchar();

    return EXIT_SUCCESS;
}
Dosimeter answered 12/12, 2017 at 16:20 Comment(7)
This is "sizeof(type)", not "sizeof expression".Binate
The compiler will pick the right template parameters for the template function depending on your expression so shouldn't this work?Rodriquez
@geza, can you provide an example where the code I've written doesn't do what you want?Dosimeter
@keith: I'd like to have something like this: int a; int b = 4; if (ssizeof(a)<b) ...;Binate
@geza, I've updated to show how you could get that, my answer was just a pointer ;-)Dosimeter
@keith: thanks, basically that's my solution too. I'm just afraid this solution could miscalculate in some circumstances. But maybe it works perfectly...Binate
@geza, the reliability boils down to T being deduced correctly; it would probably require a new question on template argument deduction.Dosimeter

© 2022 - 2024 — McMap. All rights reserved.