web-dev-qa-db-ja.com

構造体のシリアル化

メンバーの値がwinsock 2を使用してネットワーク経由で別のシステムに送信する構造体があるとします。C++言語を使用しています。送信する前に構造体をシリアル化する必要があることを念頭に置いて、それをchar *に変換するにはどうすればいいですか?同様の質問の提案としてブーストシリアル化を見つけましたが、シリアル化と逆シリアル化の両方の小さなコードスニペットで誰でも説明できますか?

この質問は非常に基本的に思われるかもしれませんが、関連する投稿への他の回答はあまり役に立ちませんでした。

12
Vigo

次の例は、structchar配列にシリアル化し、逆シリアル化する最も簡単な方法を示しています。

#include <iostream>
#include <cstring>

#define BUFSIZE 512
#define PACKETSIZE sizeof(MSG)

using namespace std;

typedef struct MSG
{
    int type;
    int priority;
    int sender;
    char message[BUFSIZE];
}MSG;

void serialize(MSG* msgPacket, char *data);
void deserialize(char *data, MSG* msgPacket);
void printMsg(MSG* msgPacket);

int main()
{
    MSG* newMsg = new MSG;
    newMsg->type = 1;
    newMsg->priority = 9;
    newMsg->sender = 2;
    strcpy(newMsg->message, "hello from server\0");
    printMsg(newMsg);

    char data[PACKETSIZE];

    serialize(newMsg, data);

    MSG* temp = new MSG;
    deserialize(data, temp);
    printMsg(temp);

    return 0;
}

void serialize(MSG* msgPacket, char *data)
{
    int *q = (int*)data;    
    *q = msgPacket->type;       q++;    
    *q = msgPacket->priority;   q++;    
    *q = msgPacket->sender;     q++;

    char *p = (char*)q;
    int i = 0;
    while (i < BUFSIZE)
    {
        *p = msgPacket->message[i];
        p++;
        i++;
    }
}

void deserialize(char *data, MSG* msgPacket)
{
    int *q = (int*)data;    
    msgPacket->type = *q;       q++;    
    msgPacket->priority = *q;   q++;    
    msgPacket->sender = *q;     q++;

    char *p = (char*)q;
    int i = 0;
    while (i < BUFSIZE)
    {
        msgPacket->message[i] = *p;
        p++;
        i++;
    }
}

void printMsg(MSG* msgPacket)
{
    cout << msgPacket->type << endl;
    cout << msgPacket->priority << endl;
    cout << msgPacket->sender << endl;
    cout << msgPacket->message << endl;
}
16
Amith Chinthaka

あなたはただすることができます

struct MyStruct {

    int data;
    char* someNullTerminatedName; // Assuming not larger than 1023 chars

    std::ostream& serialize(std::ostream& os) const {
        char null = '\0';
        os.write((char*)&data, sizeof(data));
        os.write(someNullTerminatedName, strlen(someNullTerminatedName));
        os.write(&null, 1);
        return os;
    }
    std::istream& deserialize(std::istream& is) {
        char buffer[1024];
        int i = 0;
        is.read((char*)&data, sizeof(data));
        do { buffer[i] = is.get(); ++i; } while(buffer[i] != '\0');
        if (someNullTerminatedName != NULL) free(someNullTerminatedName);
        someNullTerminatedName = (char*)malloc(i);
        for (i = 0; buffer[i] != '\0'; ++i) {
            someNullTerminatedName[i] = buffer[i];
        }
        return is;
    }
};

エンディアンとintsのサイズの違いなどに注意するのはあなた次第です。

例:

MyStruct foo, bar;
std::stringstream stream;
foo.serialize(stream);
// ... Now stream.str().c_str() contains a char* buffer representation of foo.
// For example it might contain [ 1f 3a 4d 10 h e l l o w o r l d \0 ]
bar.deserialize(stream);
// ... Now bar is a copy, via a serial stream of data, of foo.

C++ iostreamを介してインターフェイスを公開するソケットライブラリがある場合、stringstreamも必要ありません。

5
rwols

ホスト間でデータを送信するためのプラットフォーム/言語に依存しないライブラリである Protocol Buffers from Googleもご覧ください。

ただし、パラダイムは最初にプロトコルを記述し、次にデータ構造をそれに適合させる方向にシフトしています。ただし、これの利点は、ソフトウェアアーキテクチャを単純なデータ型にうまく適合させることです。

4
Nick

わかりません。ブーストWebサイトから example を取得します。理解できないことは理解できません。
コメントを追加し、ネットワーク経由で転送する方法を変更しました。ネットワークコード自体はここにはありません。これについては、 boost :: asio を見てください。

int main() {
    // create and open a character archive for output
    // we simply use std::strinstream here
    std::stringstream ofs;

    // create class instance
    const gps_position g(35, 59, 24.567f);

    // save data to archive
    {
        boost::archive::text_oarchive oa(ofs);
        // write class instance to archive
        oa << g;
        // archive and stream closed when destructors are called
    }

    // now we have const char* ofs.str().c_str()
    // transfer those bytes via network
    // read them on the other machine

    gps_position newg;
    {
        // create and open an archive for input
        std::stringstream ifs(the_string_we_read_from_the_network);
        boost::archive::text_iarchive ia(ifs);
        // read class state from archive
        ia >> newg;
        // archive and stream closed when destructors are called
    }
    return 0;
}
1
mkaes

構造体がPODの場合、memcpyを使用できます。

::memcpy(data, &your_struct, sizeof(YourStruct)));

レセプションでその逆:

::memcpy(&your_struct, data, sizeof(YourStruct)));

ここで、datachar*。十分に大きいことを確認して割り当て、最後に削除する必要があることを忘れないでください。

0
Arthur