Мысля

Feb 13, 2011 03:53


О структурах в Си.

Недавно по долгу дружбы пришлось разбираться в программе, в которой осуществлялся обмен пакетами по сети, а реализовывалось всё это следующим страшным образом:

struct packet0 { char field0[20]; long field1; char field2[4]; long field3; }; struct packet1 { int field0[4]; char field1[32]; char field2[4]; long field3; }; union sender_t { packet1 format1; packet0 format0; char raw[MAX_PACKET] } sender; ... ... // отправка пакета: strcpy(sender.packet0.field0,"hello"); sender.packet0.field1 = 24; strcpy(sender.packet0.field2,"wow"); sender.packet0.field4 = 0xDEADBEEF; send(s,sender.raw,sizeof(packet0)); ... // приём пакета: recv(s,sender.raw,MAX_PACKET); cout << sender.format1.field0[2] << endl; // long cout << sender.format1.field1 << endl; // char*
Любой человек, хоть сколько нибудь разбирающийся в С, понимает, насколько ужасен и непригоден к использованию такой подход. Но. Количество кода для подобных операций в разы, если не на порядки, меньше, чем требуется при нормальной сериализации. Если же протокол меняется, то объём работы здесь практически ничтожен, а вот при нормальной сериализации он колоссален.

Я ни в коем случае не призываю использовать подобное на практике (хотя, хотите верьте, хотите нет, оно работает, что неудивительно для одной ОС и одного компилятора). Мне было бы вполне достаточно если бы в С++ можно было ввести некую систему типов, ясно описывающую сериализацию того или иного поля протокола, и представить её декларативно. В грядущем Ноль-Иксе такие возможности нам дадут кортежи (таплы) и это хорошо. Но. Кортеж есть неименованный набор данных разного типа. Ключевое слово - неименованный. Сделать его именованным можно, но это сильные затраты в любом случае. Я специально опускаю тот факт, что кортежи ещё и имеют заранее заданный размер - в С++ это не критично (а вот, например, в Хаскелле, где кортеж каждого размера есть отдельный независимый тип и все функции перегружаются для каждого размера отдельно, очень даже).

В биткоде LLVM доступ к полям структур в модели кода (а код там всё таки сишный в основе) осуществляется... по константным индексам! Т.е. фактически структура есть что-то типа того самого кортежа с индексированным доступом и именованностью. И если бы подобный доступ был возможен непосредственно из Си(-плюсплюс?), мы бы могли реализовывать протоколы так же легко, как в подобных ужасных способах. Как-то так:

struct packet0 { array< byte, 20 > field0; dwordBE field1; array< byte, 4 > field2; dwordBE field3; }; struct packet1 { array< dwordBE, 4 > field0; array< byte, 32 > field2; array< byte, 4 > field3; dwordLE field4; }; serializator sender; ... ... // отправка пакета: packet0 toSend; toSend.field0 = "hello"; toSend.field1 = 24; toSend.field2 = wow; toSend.field3 = 0xDEADBEEF; packet pack = sender.serialize(toSend); send(s,pack.get(),pack.size()); ... // приём пакета: packet1 toRecv; packet pack = sender.deserialize(toRecv); recv(s,pack.get(),MAX_PACKET); cout << toRecv.field0[2] << endl; // long cout << toRecv.field1 << endl; // char*
Как реализовать сериализацию, и что хранить в классе packet, я оставлю на откуп читателю. Скажу лишь, что для этого нужны аналоги кортежных type_at::type и get_at(), с которыми, зная систему типов, работать было бы легко и приятно.

С++

Previous post Next post
Up