web-dev-qa-db-fra.com

Un moyen plus simple de créer un flux de mémoire C ++ à partir de (char *, size_t), sans copier les données?

Je n'ai rien trouvé de prêt à l'emploi, alors j'ai trouvé:

class membuf : public basic_streambuf<char>
{
public:
  membuf(char* p, size_t n) {
    setg(p, p, p + n);
    setp(p, p + n);
  }
}

Usage:

char *mybuffer;
size_t length;
// ... allocate "mybuffer", put data into it, set "length"

membuf mb(mybuffer, length);
istream reader(&mb);
// use "reader"

Je connais stringstream, mais il ne semble pas pouvoir fonctionner avec des données binaires de longueur donnée.

Suis-je en train d'inventer ma propre roue ici?

MODIFIER

  • Il ne doit pas copier les données d'entrée, il suffit de créer quelque chose qui itérera sur les données.
  • Il doit être portable - au moins il devrait fonctionner à la fois sous gcc et MSVC.
30
Marcin Seredynski

Je suppose que vos données d'entrée sont binaires (pas de texte) et que vous souhaitez en extraire des morceaux de données binaires. Le tout sans faire de copie de vos données d'entrée.

Vous pouvez combiner boost::iostreams::basic_array_source et boost::iostreams::stream_buffer (de Boost.Iostreams ) avec boost::archive::binary_iarchive (de Boost.Serialization ) pour pouvoir utiliser des opérateurs >> d'extraction pratiques pour lire des morceaux de données binaires.

#include <stdint.h>
#include <iostream>
#include <boost/iostreams/device/array.hpp>
#include <boost/iostreams/stream.hpp>
#include <boost/archive/binary_iarchive.hpp>

int main()
{
    uint16_t data[] = {1234, 5678};
    char* dataPtr = (char*)&data;

    typedef boost::iostreams::basic_array_source<char> Device;
    boost::iostreams::stream_buffer<Device> buffer(dataPtr, sizeof(data));
    boost::archive::binary_iarchive archive(buffer, boost::archive::no_header);

    uint16_t Word1, Word2;
    archive >> Word1 >> Word2;
    std::cout << Word1 << "," << Word2 << std::endl;
    return 0;
}

Avec GCC 4.4.1 sur AMD64, il génère:

1234,5678

Boost.Serialization est très puissant et sait sérialiser tous les types de base, les chaînes et même les conteneurs STL. Vous pouvez facilement rendre vos types sérialisables. Consultez la documentation. Caché quelque part dans les sources Boost.Serialization est un exemple d'archive binaire portable qui sait comment effectuer le swap approprié pour l'endianité de votre machine. Cela pourrait également vous être utile.

Si vous n'avez pas besoin de la fantaisie de Boost.Serialization et que vous êtes heureux de lire les données binaires à la manière de fread (), vous pouvez utiliser basic_array_source d'une manière plus simple:

#include <stdint.h>
#include <iostream>
#include <boost/iostreams/device/array.hpp>
#include <boost/iostreams/stream.hpp>

int main()
{
    uint16_t data[] = {1234, 5678};
    char* dataPtr = (char*)&data;

    typedef boost::iostreams::basic_array_source<char> Device;
    boost::iostreams::stream<Device> stream(dataPtr, sizeof(data));

    uint16_t Word1, Word2;
    stream.read((char*)&Word1, sizeof(Word1));
    stream.read((char*)&Word2, sizeof(Word2));
    std::cout << Word1 << "," << Word2 << std::endl;

    return 0;
}

J'obtiens la même sortie avec ce programme.

28
Emile Cormier

Je ne suis pas sûr de ce dont vous avez besoin, mais est-ce que cela fait ce que vous voulez?

char *mybuffer;
size_t length;
// allocate, fill, set length, as before

std::string data(mybuffer, length);
std::istringstream mb(data);
//use mb
5
crmoore

Le tampon de flux standard a cette fonctionnalité.
Créez un flux. Obtient son tampon puis le contourne.

#include <sstream>
#include <iostream>
#include <algorithm>
#include <iterator>

int main()
{
    // Your imaginary buffer
    char    buffer[]    = "A large buffer we don't want to copy but use in a stream";

    // An ordinary stream.
    std::stringstream   str;

    // Get the streams buffer object. Reset the actual buffer being used.
    str.rdbuf()->pubsetbuf(buffer,sizeof(buffer));

    std::copy(std::istreambuf_iterator<char>(str),
              std::istreambuf_iterator<char>(),
              std::ostream_iterator<char>(std::cout)
             );
}
4
Martin York

L'interrogateur voulait quelque chose qui ne copie pas les données, et sa solution fonctionne très bien. Ma contribution est de le nettoyer un peu, vous pouvez donc simplement créer un seul objet qui est un flux d'entrée pour les données en mémoire. J'ai testé cela et cela fonctionne.

class MemoryInputStream: public std::istream
    {
    public:
    MemoryInputStream(const uint8_t* aData,size_t aLength):
        std::istream(&m_buffer),
        m_buffer(aData,aLength)
        {
        rdbuf(&m_buffer); // reset the buffer after it has been properly constructed
        }

    private:
    class MemoryBuffer: public std::basic_streambuf<char>
        {
        public:
        MemoryBuffer(const uint8_t* aData,size_t aLength)
            {
            setg((char*)aData,(char*)aData,(char*)aData + aLength);
            }
        };

    MemoryBuffer m_buffer;
    };
2
Graham Asher