27 votes

Comment utiliser le nouveau type std::byte là où l'ancien type unsigned char est nécessaire ?

std::byte est un nouveau type en C++17 qui se présente sous la forme de enum class byte : unsigned char . Il est donc impossible de l'utiliser sans une conversion appropriée. J'ai donc créé un alias pour le vecteur de ce type afin de représenter un tableau d'octets :

using Bytes = std::vector<std::byte>;

Cependant, il est impossible de l'utiliser dans l'ancien style : les fonctions qui l'acceptent comme paramètre échouent parce que ce type ne peut pas être facilement converti dans l'ancien style. std::vector<unsigned char> par exemple, une utilisation de zipper bibliothèque :

/resourcecache/pakfile.cpp: In member function 'utils::Bytes resourcecache::PakFile::readFile(const string&)':
/resourcecache/pakfile.cpp:48:52: error: no matching function for call to 'zipper::Unzipper::extractEntryToMemory(const string&, utils::Bytes&)'
     unzipper_->extractEntryToMemory(fileName, bytes);
                                                    ^
In file included from /resourcecache/pakfile.hpp:13:0,
                 from /resourcecache/pakfile.cpp:1:
/projects/linux/../../thirdparty/zipper/zipper/unzipper.h:31:10: note: candidate: bool zipper::Unzipper::extractEntryToMemory(const string&, std::vector<unsigned char>&)
     bool extractEntryToMemory(const std::string& name, std::vector<unsigned char>& vec);
          ^~~~~~~~~~~~~~~~~~~~
/projects/linux/../../thirdparty/zipper/zipper/unzipper.h:31:10: note:   no known conversion for argument 2 from 'utils::Bytes {aka std::vector<std::byte>}' to 'std::vector<unsigned char>&'

J'ai essayé d'effectuer des moulages naïfs, mais cela ne m'a pas aidé non plus. Donc, si cette méthode est conçue pour être utile, sera-t-elle réellement utile dans les anciens contextes ? La seule méthode que je vois est d'utiliser std::transform pour l'utilisation d'un nouveau vecteur d'octets à ces endroits :

utils::Bytes bytes;
std::vector<unsigned char> rawBytes;
unzipper_->extractEntryToMemory(fileName, rawBytes);
std::transform(rawBytes.cbegin(),
               rawBytes.cend(),
               std::back_inserter(bytes),
               [](const unsigned char c) {
                   return static_cast<std::byte>(c);
               });
return bytes;

C'est-à-dire :

  1. Moche.
  2. Cela prend beaucoup de lignes inutiles (qui peuvent être réécrites mais qui doivent être écrites avant :)).
  3. Copie la mémoire au lieu d'utiliser un morceau de mémoire déjà créé. rawBytes .

Alors, comment l'utiliser dans des lieux anciens ?

38voto

The Quantum Physicist Points 1362

Vous ne comprenez pas pourquoi std::byte a été inventé en premier lieu. La raison pour laquelle il a été inventé est de conserver un octet brut en mémoire sans supposer qu'il s'agit d'un personnage . Vous pouvez voir cela dans cppréférence .

Comme char et unsigned char, il peut être utilisé pour accéder à la mémoire brute occupée par d'autres objets (représentation d'objet), mais contrairement à ces types, il n'est pas un type de caractère et n'est pas un type arithmétique.

N'oubliez pas que le C++ est un langage fortement typé dans l'intérêt de la sécurité (les conversions implicites sont donc limitées dans de nombreux cas). Signification : Si une conversion implicite de byte a char était possible, cela irait à l'encontre du but recherché.

Donc, pour répondre à votre question : Pour l'utiliser, il faut le lancer chaque fois que l'on veut lui faire une affectation :

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

Toute autre solution est vouée à l'échec, à dessein ! Donc cette transformation est moche, d'accord, mais si vous voulez stocker des caractères, alors utilisez char . N'utilisez pas d'octets à moins que vous ne souhaitiez stocker de la mémoire brute qui ne doit pas être interprétée comme char par défaut .

La dernière partie de votre question est généralement incorrecte : il n'est pas nécessaire de faire des copies, car il n'est pas nécessaire de copier l'ensemble du vecteur. Si vous avez temporairement besoin de lire un byte en tant que char , simplement static_cast à l'endroit où vous devez l'utiliser en tant que char . Il ne coûte rien et est sans danger pour les utilisateurs.


Pour ce qui est de la question posée dans le commentaire sur le casting std::vector<char> a std::vector<std::byte> Vous ne pouvez pas faire cela. Mais vous pouvez utiliser le tableau brut en dessous. Ainsi, ce qui suit a un type (char*) :

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

Il s'agit d'un type char* qui est un pointeur sur le premier élément de votre tableau, et qui peut être déréférencé sans copie, comme suit :

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

Et la taille que vous obtenez à partir de bytes.size() . Ceci est valable, puisque std::vector est contiguë dans la mémoire. Vous ne pouvez généralement pas faire cela avec un autre conteneur std (deque, list, etc...).

Bien que cela soit valable, cela enlève une partie de la sécurité de l'équation, gardez cela à l'esprit. Si vous avez besoin de char ne pas utiliser byte .

0voto

Don Pedro Points 107

Si vous voulez quelque chose qui se comporte comme un octet de la manière à laquelle vous vous attendez probablement, mais dont le nom est nettement différent de unsigned char, utilisez uint8_t de stdint.h. Pour presque toutes les implémentations, il s'agira probablement d'un

typedef unsigned char uint8_t;

et à nouveau un char non signé sous le capot - mais qui s'en soucie ? Vous voulez juste souligner que "Ceci n'est pas un type de caractère". Il ne faut pas s'attendre à avoir deux surcharges de certaines fonctions, une pour unsigned char et une pour uint8_t. Mais si vous le faites, le compilateur vous mettra le nez dessus de toute façon...

0voto

Lorehead Points 953

Si votre ancien code prend des plages ou des itérateurs comme arguments, vous pouvez continuer à les utiliser. Dans les quelques cas où vous ne pouvez pas le faire (tels que explicit ), vous pourriez en théorie écrire une nouvelle classe d'itérateur qui engloberait un itérateur dans la classe unsigned char et convertit *it a std::byte& .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X