Установка и получение данных из строки или потока строк - PullRequest
0 голосов
/ 20 апреля 2020

Я пытаюсь написать многократно используемый объект сообщения, который бы взял его свойства, преобразовал их в строку с разделителями (используя разделитель групп 0x1d), поместил это в буфер char, а также смог сделать реверс (от char обратно к объекту).

Причина, по которой я должен это сделать, заключается в том, что я полностью ограничен возможностями ОС по отправке только сообщений типа char и фиксированного размера, так что это моя обертка для этого.

Вот то, что у меня есть до сих пор. Легко упаковать .. теперь, как я могу написать разумный способ распаковать его. Я в порядке, если каждый ребенок этого класса должен вручную распаковать эти данные, но я просто не понимаю, как. Я попытался getline, но потом я получил строку и мне пришлось написать много функций преобразования. Должен быть более простой способ.

Обратите внимание, что я на C ++ 98.

#include <iostream>
#include <sstream>
#include <string.h>

class Msg {
    public:
        Msg(){
            delim = 0x1d;
        }
        int8_t ia;
        int16_t ib;
        int32_t ic;
        int64_t id;
        uint8_t ua;
        uint16_t ub;
        uint32_t uc;
        uint64_t ud;
        std::string str = "aaa bbb ccc dddd";
        char sz[64];
        char delim;

        // convert to a char buffer
        void ToBuffer(unsigned char* b, int s){
            std::stringstream ss;
            ss << ia << delim
               << ib << delim
               << ic << delim
               << id << delim
               << ua << delim
               << ub << delim
               << uc << delim
               << ud << delim
               << str << delim 
               << sz << delim;

            strncpy((char*)b, ss.str().c_str(), s);
            b[s-1] = '\0';
        }

        // convert from a char buffer
        void FromBuffer(unsigned char* b, int s){
            // what on earth to do here..
            // could use getline which returns a string after
            // each delimiter, then convert each string to the
            // value in a known order.. but at that point I may
            // as well have written this all in C... !
        }

        void Print(){
            std::cout 
                << " ia " << ia
                << " ib " << ib
                << " ic " << ic
                << " id " << id
                << " ua " << ua
                << " ub " << ub
                << " uc " << uc
                << " ud " << ud
                << " str " << str 
                << " sz "  << sz;
        }
};

int main()
{
    Msg msg;
    msg.ia = 0xFE;
    msg.ib = 0xFEFE;
    msg.ic = 0xFEFEFEFE;
    msg.id = 0xFEFEFEFEFEFEFEFE;
    msg.ua = 0xEE;
    msg.ub = 0xDEAD;
    msg.uc = 0xDEADBEEF;
    msg.ud = 0xDEADBEEFDEADBEEF;
    snprintf(msg.sz, 64, "this is a test");
    msg.Print();

    int s = 128;
    unsigned char b[s];
    msg.ToBuffer(b, s);

    Msg msg2;
    msg2.FromBuffer(b, s);
    //msg2.Print();


    return 0;
}

1 Ответ

1 голос
/ 20 апреля 2020

Хорошо, это работает, но довольно уродливо помещать буфер в поток строк, так что вы можете использовать std :: getline с разделителем для извлечения битов, а затем использовать другой поток строк или std :: stoi и друзей для преобразовать элементы в нужные типы:

https://repl.it/repls/GainsboroInsecureEvents

    void FromBuffer(unsigned char* b, int s){
        std::string item;
        std::stringstream ss((char *)b);
        // You don't NEED to use std::stringstream to convert
        // the item to the primitive types - you could use
        // std::stoi, std::stol, std::stoll, etc but using a
        // std::stringstream makes it so you don't need to
        // know which primitive type the variable is
        std::getline(ss,item,'\x1d'); std::stringstream(item) >> ia;
        std::getline(ss,item,'\x1d'); std::stringstream(item) >> ib;
        std::getline(ss,item,'\x1d'); std::stringstream(item) >> ic;
        std::getline(ss,item,'\x1d'); std::stringstream(item) >> id;
        std::getline(ss,item,'\x1d'); std::stringstream(item) >> ua;
        std::getline(ss,item,'\x1d'); std::stringstream(item) >> ub;
        std::getline(ss,item,'\x1d'); std::stringstream(item) >> uc;
        std::getline(ss,item,'\x1d'); std::stringstream(item) >> ud;
        // Until you get to here.  Then >> stops on a space
        // and all the sudden you can't use >> to get the data
        std::getline(ss,str,'\x1d');
        // And a C string is even worse because you need to
        // respect the length of the buffer by using strncpy
        std::getline(ss,item,'\x1d'); strncpy(sz,item.c_str(),64); sz[63] = '\0';
    }

Так что я думаю, что гораздо лучший способ - создать новый фасет ctype, который использует новый разделитель и наполнение потока строк новым фасетом, как здесь было сделано изменение разделителя для cin (c ++)

Таким образом, мы можем просто извлечь непосредственно, что НАМНОГО лучше:

https://repl.it/repls/GraveDraftyAdministrators

    void FromBuffer(unsigned char* b, int s){
        struct delimiter : std::ctype<char> {
          delimiter() : std::ctype<char>(get_table()) {}
          static mask const* get_table()
          {
            static mask rc[table_size];
            rc[0x1d] = std::ctype_base::space;
            return &rc[0];
          }
        };
        std::stringstream ss((char *)b);
        ss.imbue(std::locale(ss.getloc(), new delimiter));
        ss >> ia
           >> ib
           >> ic
           >> id
           >> ua
           >> ub
           >> uc
           >> ud
           >> str
           >> sz;
    }
...