Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Для новичков > разбор набора данных полученного по сети, из файла


Автор: semibug 12.12.2011, 23:05
Как будет лучше (правильнее, надежнее, логичнее и т.п.) разобрать набор данных, представленный куском памяти - читать из него кусочками или кастовать в указатель на структуру?
Кастовать - поменьше кода получается, читать по байтово и формировать значения нужной разрядности - более ресурсоемко (увеличение размера кода + замедление работы программы).
Сделал и так и так, не знаю на чем остановиться.
Вопрос больше академический, понимаю, что описанные недостатки второго варианта не существенны для современных машин.

Автор: newbee 12.12.2011, 23:11
Кастовать область памяти в указатель на какой-нибудь тип нельзя, если не хочешь в будущем поиметь множество сюрпризов, особенно во время портирования на другие архитектуры. Можно сделать memcpy память -> структура. Вариант с ручным разбором более гибкий в том плане, что не любые данные "ровно" ложатся в структуру, для них требуется предварительная обработка. Например массивы заранее неизвестной длины, взаимосвязи между разными структурами и т.д.

Автор: rumit7 13.12.2011, 09:18
Цитата(semibug @ 12.12.2011,  23:05)
Как будет лучше (правильнее, надежнее, логичнее и т.п.) разобрать набор данных, представленный куском памяти - читать из него кусочками или кастовать в указатель на структуру?
...

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

А вот с 
Цитата

Можно сделать memcpy память -> структура.


мне как-то не совсем понятно, как в этом случае быть с big endian? 


Автор: semibug 13.12.2011, 09:22
newbee, спасибо за ответ
Не совсем понял на счет проблем при касте в указатель на структуру, если подразумевается, как пишет 
rumit7, порядок байт и выравнивание, то как memcpy решает проблему? (присоединяюсь к вопросу 
rumit7)

Автор: newbee 13.12.2011, 09:48
Господа, если у вас намечаются проблемы с порядком байт, достаточно просто перевернуть необходимые слова внутри структуры (т.к. между словами байты не перетасуются, только в рамках слова). Если ваша система будет обмениваться информацией между собой одновременно на разных системах, конечно нужен договор о порядке байт: при отправке данных они кодируются в сетевой порядок, во время приема декодируются в хостовый.

Я писала совсем о другом: в общем случае нельзя кастовать некий буфер данных в свои переменные. http://stackoverflow.com/questions/5014268/pointer-casting-problem-with-struct-array-member. Чтобы в будущем не нажить проблем? лучше сразу приучить себя всегда использовать memcpy для копирования сырых данных в свои пользовательские.

Автор: mes 13.12.2011, 09:52
подход зависит от того что представляет из себя поток данных.. т. е. важно согласование обеих сторон, и если не известно в каком виде отправляется, непонятно как это принимать..


Автор: rumit7 13.12.2011, 10:49
Цитата(mes @ 13.12.2011,  09:52)
подход зависит от того что представляет из себя поток данных.. т. е. важно согласование обеих сторон, и если не известно в каком виде отправляется, непонятно как это принимать..

А если предположить, что формат задан по спецификации и построен по следующим принципам:
  •  первый байт является тэгом сообщения, т.е. определяет его тип;
  •  следующие 2 байта определяют общую длину данного сообщения;
  •  далее идет содержимое сообщения. Здесь могут находится вложенные сообщения, идентифицирующиеся также по тегу или просто содержимое какой-то структуры.

Тэги всех сообщений заданы. Формат их задан. Единственно что: многие сообщения являются опциональными, т.е. их присутствие не гарантируется. 

Так вот, в таком случае: как мог бы выглядеть парсер? Интересует правильный дизайн парсера с точки зрения читабельности полученного кода и эффективности реализации. 

Я ниже приведу свои мысли, а если кто-то подскажет что-то - буду очень признателен!


Имеется буфер содержащий целое количество сообщений (т.е. дочитывать еще данные нет необходимости).

Код

// код упрощен для наглядности
while( !finished )
{
    uint8_t * buffer;
    size_t     length;

    // считываем буффер
    if( (buffer = receive_buffer( &length )) != 0 )
    {
        parse_buffer(buffer, length);
        free_buffer(buffer);
    }
  }
 

Дальше буфер нужно парсить. Использовать внутри "parse_buffer" целую кучу switch, if-ов в стиле процедурного программирования не выход, особенно если спецификация на формат достаточно обширная. Здесь бы воспользоваться преимуществами с++, но как правильно это сделать?! Есть ли идеи?

Автор: semibug 14.12.2011, 12:26
Цитата(rumit7 @  13.12.2011,  10:49 Найти цитируемый пост)
Использовать внутри "parse_buffer" целую кучу switch, if-ов в стиле процедурного программирования не выход, особенно если спецификация на формат достаточно обширная.

Думаю в любом случае, получив тэг (число) надо будет выбрать обработчик, т.е. сопоставить значение с исполнителем, тут либо switch либо через табличку (switch, как правило, компилятор реализует табличкой).

Автор: rumit7 14.12.2011, 14:40
Цитата(semibug @ 14.12.2011,  12:26)
Думаю в любом случае, получив тэг (число) надо будет выбрать обработчик, т.е. сопоставить значение с исполнителем, тут либо switch либо через табличку (switch, как правило, компилятор реализует табличкой).

Да, конечно не без этого, но хотелось бы найти решение. Пока крутится в голове что-то в этом роде:

Код

void parse_buffer(uint8_t * buffer, size_t length)
{
    ss7::stream stream(buffer, length);
    
    // здесь мы знаем точно, что буффер содержит
    // данные с уровня M3UA, далее возможно SCCP, TCAP, Camel
    // хотя наличие именно последних 3 необязательно
    //
    ss7::m3ua    m3ua;
    ss7::sccp    sccp;
    ss7::tcap    tcap;
    ss7::camel    camel;
    
    stream >> m3ua >> sccp >> tcap >> camel;    // здесь каждый из уровней вызывает
                                                // оператор >> для своих подуровней
    
    // предположим нам нужны следующие данные:
    //        - из M3UA: OPC, DPC, SI;
    //        - из SCCP: UnitData
    //        - из Camel: Service Key.
    //
    if(camel)
    {
        // если уровень Camel успешно сформирован,
        // это значит что и предыдущие уровни сформированы
        //
        cout << m3ua.opc;
        cout << m3ua.dpc;
        cout << m3ua.si;
        
        cout << sccp.unit_data;
        
        cout << camel.service_key;
    }
    
}

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)