How to Use New Std::Byte Type in Places Where Old-Style Unsigned Char Is Needed

How to use new std::byte type in places where old-style unsigned char is needed?

You're missing the point why std::byte was invented in the first place. The reason it was invented is to hold a raw byte in memory without the assumption that it's a character. You can see that in cppreference.

Like char and unsigned char, it can be used to access raw memory occupied by other objects (object representation), but unlike those types, it is not a character type and is not an arithmetic type.

Remember that C++ is a strongly typed language in the interest of safety (so implicit conversions are restricted in many cases). Meaning: If an implicit conversion from byte to char was possible, it would defeat the purpose.

So, to answer your question: To use it, you have to cast it whenever you want to make an assignment to it:

std::byte x = (std::byte)10;
std::byte y = (std::byte)'a';
std::cout << (int)x << std::endl;
std::cout << (char)y << std::endl;

Anything else shall not work, by design! So that transform is ugly, agreed, but if you want to store chars, then use char. Don't use bytes unless you want to store raw memory that should not be interpreted as char by default.

And also the last part of your question is generally incorrect: You don't have to make copies, because you don't have to copy the whole vector. If you temporarily need to read a byte as a char, simply static_cast it at the place where you need to use it as a char. It costs nothing, and is type-safe.


As to your question in the comment about casting std::vector<char> to std::vector<std::byte>, you can't do that. But you can use the raw array underneath. So, the following has a type (char*):

std::vector<std::byte> bytes;
// fill it...
char* charBytes = reinterpret_cast<char*>(bytes.data());

This has type char*, which is a pointer to the first element of your array, and can be dereferenced without copying, as follows:

std::cout << charBytes[5] << std::endl; //6th element of the vector as char

And the size you get from bytes.size(). This is valid, since std::vector is contiguous in memory. You can't generally do this with any other std container (deque, list, etc...).

While this is valid, it removes part of the safety from the equation, keep that in mind. If you need char, don't use byte.

How convert std::vectorstd::byte to C-style raw data(unsigned char**)?

To provide a vector<char> to a C function accepting const char** you can access the internal buffer and return a pointer to it:

#include <iostream>
#include <vector>

void someCApi(const char** c, size_t len)
{
for (size_t i=0; i<len; ++i)
{
std::cout << static_cast<int>((*c)[i]) << std::endl;
}
}

auto main() -> int
{
std::vector<char> buff{1,3,5,7,11,13,17,19};
const char* buffPtr = buff.data();
someCApi(&buffPtr, buff.size());

return 0;
}

https://onlinegdb.com/B1BSNKyrv

Simple interface to get the pointer to array:

template <typename T>
class pptr
{
const T* buff=nullptr;
public:
ppta(std::vector<T>& v): buff(v.data()){};
const T** operator()(){return &buff;}
};

//Usage
someCApi(pptr(buff)(), buff.size());

Is there a legal way to convert a unsigned char pointer to std::byte pointer?

The strict aliasing rule never forbids any pointer conversions. It is about the type of an expression accessing an object.

std::byte may alias any other type, this is mentioned in the cppreference page you linked, as well as in the strict aliasing rule in the Standard of course (C++17 basic.lval/8.8). So it is fine to use reinterpret_cast<std::byte *> and then read or write the array of unsigned char.

If you use an expression of type uint32_t to read or write an array of unsigned char, that would violate the strict aliasing rule.

What is the purpose of std::byte?

You are perhaps misunderstanding things.

byte is very much intended for "accessing memory". You are intended to use the type when the storage is just a sequence of bytes rather than an array of characters.

Iostream types cannot be specialized with byte, since they're designed around characters as their interface. That is, they do not think of files as sequences of bytes; they think of them as sequences of characters. Now, you can certainly read directly into a byte array by using a cast or two. But that's not the way iostream natively thinks.

You have to make a distinction between the way iostream works and the way files work. Iostream is just one file IO library, after all; it is hardly the end-all, be-all of file APIs.

Most file APIs for reading binary data take void* rather than character arrays. std::fread/fwrite, and so forth.

That is, you should think of this, not as a problem with std::byte, but as a problem with iostream. Just another in a long line of them.

How to use something like `std::basic_istreamstd::byte`

Don't.

Whether you're operating in "text mode" or "binary mode", what you are still doing fundamentally is acting on characters.

std::byte is not for this purpose, and that's why it does not have these features. Indeed, it was deliberately introduced not to have them!

enum class byte : unsigned char {} ; (since C++17)

std::byte is a distinct type that implements the concept of byte as specified in the C++ language definition.

Like char and unsigned char, it can be used to access raw memory occupied by other objects (object representation), but unlike those types, it is not a character type and is not an arithmetic type. A byte is only a collection of bits, and only bitwise logic operators are defined for it.

http://en.cppreference.com/w/cpp/types/byte


Did anyone make this kind of thing work already?

No, everyone deliberately didn't, as explored above.

Use char or unsigned char, as we have done for decades!

Why is there no overload for printing `std::byte`?

From the paper on std::byte (P0298R3): (emphasis mine)

Design Decisions

std::byte is not an integer and not a character

The key motivation here is to make byte a distinct type – to improve program safety by leveraging the type system. This leads to the design that std::byte is not an integer type, nor a character type. It is a distinct
type for accessing the bits that ultimately make up object storage.

As such, it is not required to be implicitly convertible/interpreted to be either a char or any integral type whatsoever and hence cannot be printed using std::cout unless explicitly cast to the required type.

Furthermore, this question might help.

How to convert std::string to std::vectorstd::byte in C++17?

Using std::transform should work:

#include <algorithm>
#include <cstddef>
#include <iostream>
#include <vector>

int main()
{
std::string gpsValue;
gpsValue = "time[.........";
std::vector<std::byte> gpsValueArray(gpsValue.size() + 1);
std::transform(gpsValue.begin(), gpsValue.end(), gpsValueArray.begin(),
[] (char c) { return std::byte(c); });
for (std::byte b : gpsValueArray)
{
std::cout << int(b) << std::endl;
}
return 0;
}

Output:

116
105
109
101
91
46
46
46
46
46
46
46
46
46
0

How to use interfaces that expect `const char*` if I'm working with `std::byte`s

Given a contiguous buffer of std::bytes, a reinterpretation of a std::byte* to a char* is legal by design and doesn't violate the strict aliasing rule:

(wording from the most recent revision of the std::byte proposal)

Lvalues and rvalues [basic.lval]


  1. If a program attempts to access the stored value of an object through a glvalue of other than one of the following types the behavior is undefined:

    • [...]

    • (8.8) a char, unsigned char, or std::byte type.

(note: the "or std::byte" is added by the proposal)

So you can use reinterpret_cast<const char*>(pointer_to_std_byte_buffer) or static_cast<const char*>(static_cast<const void*>(pointer_to_std_byte_buffer)) or (const char*)pointer_to_std_byte_buffer, depending on what your style guide says.



Related Topics



Leave a reply



Submit