10

To measure position/offsets/size of streams, the standard specify std::streampos, std::streamoff and std::streamsize, but they are implementation defined.

How to convert these types to long long int in a secure and portable way ? (for example to measure a file size and inject it in a function that take a long long int as argument)

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
Vincent
  • 57,703
  • 61
  • 205
  • 388

3 Answers3

7

Well, as far as C++98/03 is concerned, there is no long long int. So I'll assume you're asking about C++11.

The streamsize and streamoff are required to be typedefs of an integral type (streampos is not an integer, so you won't be passing that to anything that takes a long long). Since integral types are basic types, they can only be defined by either C++ or as a compiler-specific definition.

Thus, the only question is this: are these typedefs larger than long long? All integral types are convertible to a larger or equal-sized type (signed/unsigned notwithstanding, but all of the types here are signed, so no problem). But if it is larger... what are you going to do about it?

Assuming you can't change the signature of the function you are "injecting" it into (because if you could, there's no reason not to just take streamsize as the parameter type and thus avoid the problem), you don't have any options. You have a data value that is larger than what the function takes. There's no way of getting around it here.

You can perform a static_cast into a long long to shut the compiler up, but this won't help if the actual size can't fit in a long long.

Ultimately, this is an intractable problem. You have a function that takes a parameter which is potentially too small for what you're passing. The most you can do is detect when it might be a problem via a static_assert. Something like this:

static_assert(sizeof(std::streamsize) <= sizeof(long long), "Oops.");

To be honest, I wouldn't worry about it. Odds are good that long long will be the largest integral size that your compiler natively supports.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • Apart from the case of exabytes file size, can a problem occur during the conversion to long long int ? – Vincent Oct 08 '12 at 00:52
  • @Vincent: No. Integer types can be converted to a larger (or equal-sized) integer type without problem. – Nicol Bolas Oct 08 '12 at 02:15
  • `streampos` implicitly converts to `streamoff`, so you might pass it to something expecting a `long long`. The difference between `streampos` and `streamoff` is support for stateful character encodings. – Potatoswatter Nov 06 '13 at 04:25
2

Just pass the value to whatever function needs a long long. std::streamoff and std::streamsize are both signed integral types, and std::streampos is implicitly convertible to std::streamoff.

edit: I suppose an assert that streamsize/streamoff is not bigger than long long won't hurt, in case someone comes up with __int128 file sizes.

Cubbi
  • 46,567
  • 13
  • 103
  • 169
-1

Short response for MINGW-64: std::streampos has a conversion operator to 64-bit-signed-integer type std::streamoff as

std::streampos pos = ...;
std::streamoff ofs = (std::streamoff) pos;

So, e.g. to find the length of a file you do not need to do more than opening an std::ifstream and evaluate ...

static unsigned long long getStreamSize(std::ifstream& is)
{
    std::streampos savePos = is.tellg();
    is.seekg(0, std::ios::end);
    std::streampos endPos = is.tellg();
    is.seekg(savePos);
    return (std::streampos)endPos;
}

... or think STL was another island ...

Sam Ginrich
  • 661
  • 6
  • 7
  • Where in the C++ standard does it specify that `tellg()` returns an integer type that represents a count of bytes from the start of a file? And `is.seekg(0, std::ios::end)` is undefined behavior for a binary stream: "The restrictions on reading and writing a sequence controlled by an object of class `basic_filebuf` are the same as for reading and writing with the Standard C library FILEs." See https://port70.net/~nsz/c/c11/n1570.html#note268 – Andrew Henle Feb 08 '21 at 14:45
  • No idea, where you go with your question. https://www.cplusplus.com/reference/istream/istream/tellg/ Specifications are specifications and use case are use cases and this time disjoint. – Sam Ginrich Feb 09 '21 at 22:47
  • cplusplus.com is not only no reference, it's actually not very accurate. Just because cplusplus.com says something doesn't make it true. [**What's wrong with cplusplus.com?**](https://stackoverflow.com/questions/6520052/whats-wrong-with-cplusplus-com) – Andrew Henle Feb 10 '21 at 08:41
  • :) We understand, that "the standard" design does not foresee someone asking for a file size as an integer and insofar is useless. – Sam Ginrich Feb 11 '21 at 09:56
  • If that was meant to be a standard way to get a file size, riddle me this, Batman: Why did C++17 add `std::filesystem::file_size`? Umm, because it wasn't there before... And I guess you're not aware that once you ask for a file size, it's not longer relevant and using it is a [TOCTOU bug](https://en.wikipedia.org/wiki/Time-of-check_to_time-of-use) waiting to happen? I code to higher standards then TOCTOU bugs and explicit undefined behavior per the language specification – Andrew Henle Feb 11 '21 at 13:13
  • And you've also fallen into the trap that just because something related to a file is an integer value that it must be the file size. – Andrew Henle Feb 11 '21 at 13:24
  • Some Upper Cloud Cuckoo Home has no file size. – Sam Ginrich Mar 20 '21 at 15:07
  • finally there is a horizon of hope: we see in C++ 2020, there is a contains-relation for std::set<> ;) – Sam Ginrich Jun 02 '21 at 06:00