secon'2014 - Павел Щеваев - Метаданные и автогенерация...
DESCRIPTION
Надежный обмен данными в гетерогенной среде, между разными платформами и технологиями является одним из ключевых моментов разработки сложных систем. Во время обмена данные преобразуются в некоторый промежуточный формат совместимый между платформами. Преобразование в подобный формат и из него — крайне рутинная и подверженная ошибкам работа. Метаописание данных неким декларативным языком с последующей автогенерацией типизированных структур облегчает жизнь разработчику. Снимает с него необходимость задумываться о промежуточном формате, о правильном порядке полей, а типизированность гарантирует выявление ошибок еще на этапе компиляции кода. В докладе будет рассмотрено несколько подобных решений, их плюсы и минусы. Также будет рассмотрен с практической стороны наш собственный формат метаданных, используемый нами на протяжении более 5 лет. Целевая аудитория: Ограничений нет, но в большей степени разработчики, имеющие уже определенный опыт разработки разнородных систем или приступающие к подобной задаче.TRANSCRIPT
![Page 1: SECON'2014 - Павел Щеваев - Метаданные и автогенерация кода](https://reader035.vdocuments.net/reader035/viewer/2022062707/557fd6fcd8b42aab088b51ec/html5/thumbnails/1.jpg)
Метаданные и автогенерация кода.
SECON 2014.Щеваев Павел, @pachash
![Page 2: SECON'2014 - Павел Щеваев - Метаданные и автогенерация кода](https://reader035.vdocuments.net/reader035/viewer/2022062707/557fd6fcd8b42aab088b51ec/html5/thumbnails/2.jpg)
Предыстория
![Page 4: SECON'2014 - Павел Щеваев - Метаданные и автогенерация кода](https://reader035.vdocuments.net/reader035/viewer/2022062707/557fd6fcd8b42aab088b51ec/html5/thumbnails/4.jpg)
zveriki.com
■ Амбициозная и успешная MMO 3D игра с целым зоопарком технологий:○ Сервер: Linux, C++, MySQL, ассеты и конфиги в
файловой системе○ Клиент: Shockwave Director (Lingo, JavaScript)○ Портал: PHP
![Page 5: SECON'2014 - Павел Щеваев - Метаданные и автогенерация кода](https://reader035.vdocuments.net/reader035/viewer/2022062707/557fd6fcd8b42aab088b51ec/html5/thumbnails/5.jpg)
Клиент (Lingo, JavaScript)
Сервер (C++)
Портал (PHP)БД (MySQL)
Ассеты, конфиги
Информационные потоки
![Page 6: SECON'2014 - Павел Щеваев - Метаданные и автогенерация кода](https://reader035.vdocuments.net/reader035/viewer/2022062707/557fd6fcd8b42aab088b51ec/html5/thumbnails/6.jpg)
Пример сущностиstruct PetConfig { Breed breed; u16 max_health; vector<u32> anim_ids;};
struct Pet { const PetConfig* config; u32 id; u16 health; string name; float x; float y;};
![Page 7: SECON'2014 - Павел Щеваев - Метаданные и автогенерация кода](https://reader035.vdocuments.net/reader035/viewer/2022062707/557fd6fcd8b42aab088b51ec/html5/thumbnails/7.jpg)
Загрузка конфигов
<pet> <breed>bulldog</breed> <max_health>100</max_health> <anim_ids>2,4,6,7,8</anim_ids></pet>
Данные ассоциативны: key => value
![Page 8: SECON'2014 - Павел Щеваев - Метаданные и автогенерация кода](https://reader035.vdocuments.net/reader035/viewer/2022062707/557fd6fcd8b42aab088b51ec/html5/thumbnails/8.jpg)
Загрузка конфиговint PetСonfig::read(const XmlFile& data) { string breed_tmp; if(int err = data.readString("type", breed_tmp)){ return err; if(!Breed::lookupByStr(breed_tmp.c_str(), breed)) return ERR_BAD_ENUM; }
if(int err = data.readU32("max_health", max_health)) return err;
string ids_tmp; if(int err = data.readString("anim_ids", ids_tmp)) return err; if(!explode(",”, ids_tmp, anim_ids)) return ERR_BAD_ARRAY;}
![Page 9: SECON'2014 - Павел Щеваев - Метаданные и автогенерация кода](https://reader035.vdocuments.net/reader035/viewer/2022062707/557fd6fcd8b42aab088b51ec/html5/thumbnails/9.jpg)
Сохранение и загрузка в БД
id conf_id name x y health
10 128 K-9 128 126 5
20 165 Polkan 764 356 9
... ...
Данные ассоциативны: key => value
![Page 10: SECON'2014 - Павел Щеваев - Метаданные и автогенерация кода](https://reader035.vdocuments.net/reader035/viewer/2022062707/557fd6fcd8b42aab088b51ec/html5/thumbnails/10.jpg)
Сохранение и загрузка в БД
int Pet::read(const DbReader& data) { if(int err = data.readU32("id", id)) return err; if(int err = data.readString("name", name)) return err; if(int err = data.readFloat("x", x)) return err; ...}
int Pet::write(DbWriter& data) const { if(int err = data.writeU32("id", id)) return err; if(int err = data.writeString("name", name)) return err; if(int err = data.writeFloat("x", x)) return err; ...}
![Page 11: SECON'2014 - Павел Щеваев - Метаданные и автогенерация кода](https://reader035.vdocuments.net/reader035/viewer/2022062707/557fd6fcd8b42aab088b51ec/html5/thumbnails/11.jpg)
Передача по сети■ Максимально компакное представление ■ Набор байтов■ Данные представлены последовательно
id conf_id
health x y
name (size)
name
== 1 byte
![Page 12: SECON'2014 - Павел Щеваев - Метаданные и автогенерация кода](https://reader035.vdocuments.net/reader035/viewer/2022062707/557fd6fcd8b42aab088b51ec/html5/thumbnails/12.jpg)
Передача по сетиint Pet::read(const ByteBuffer& data) { if(int err = data.readU32(id)) return err;
if(int err = data.readU32(conf_id)) return err;
if(int err = data.readU16(health)) return err; if(int err = data.readFloat(x)) return err; ...}
int Pet::write(ByteBuffer& data) const { if(int err = data.writeU32(id)) return err;
if(int err = data.writeU32(conf_id)) return err;
if(int err = data.writeU16(health)) return err; ...}
![Page 13: SECON'2014 - Павел Щеваев - Метаданные и автогенерация кода](https://reader035.vdocuments.net/reader035/viewer/2022062707/557fd6fcd8b42aab088b51ec/html5/thumbnails/13.jpg)
RPC (клиент)
И
ByteBuffer pckt;pckt.writeU16(PACKET_MOVE_PET);pckt.writeU32(pet.id);pckt.writeFloat(pet.x);pckt.writeFloat(pet.y);
net_send_packet(pckt, function(err) { … } );
![Page 14: SECON'2014 - Павел Щеваев - Метаданные и автогенерация кода](https://reader035.vdocuments.net/reader035/viewer/2022062707/557fd6fcd8b42aab088b51ec/html5/thumbnails/14.jpg)
RPC (сервер)
int handle_rpc(const ByteBuffer& data) {
u16 packet_type; if(int err = data.readU16(packet_type)) return err;
switch(packet_type) { case PACKET_MOVE_PET: return handle_move_pet(data); ... }}
![Page 15: SECON'2014 - Павел Щеваев - Метаданные и автогенерация кода](https://reader035.vdocuments.net/reader035/viewer/2022062707/557fd6fcd8b42aab088b51ec/html5/thumbnails/15.jpg)
Чот не прикольно
![Page 16: SECON'2014 - Павел Щеваев - Метаданные и автогенерация кода](https://reader035.vdocuments.net/reader035/viewer/2022062707/557fd6fcd8b42aab088b51ec/html5/thumbnails/16.jpg)
Проблемы
■ Отсутствие проверки структуры данных на этапе компиляции для ассоциативных данных
■ Строгий порядок следования инструкций в сетевом протоколе
■ Громоздкая поддержка обработки ошибок■ Дублирование кода для различных
платформ■ Добавление новых RPC вызывов неудобно■ etc, etc, etc
![Page 17: SECON'2014 - Павел Щеваев - Метаданные и автогенерация кода](https://reader035.vdocuments.net/reader035/viewer/2022062707/557fd6fcd8b42aab088b51ec/html5/thumbnails/17.jpg)
Решение, изменившее мою жизнь
![Page 18: SECON'2014 - Павел Щеваев - Метаданные и автогенерация кода](https://reader035.vdocuments.net/reader035/viewer/2022062707/557fd6fcd8b42aab088b51ec/html5/thumbnails/18.jpg)
![Page 19: SECON'2014 - Павел Щеваев - Метаданные и автогенерация кода](https://reader035.vdocuments.net/reader035/viewer/2022062707/557fd6fcd8b42aab088b51ec/html5/thumbnails/19.jpg)
Пути решения
■ Найти готовое решение○ Из всего глянулся Thrift (http://thrift.apache.org)○ … но это была середина 2007 года: “никакая”
документация, запутанный код, ориентация только на RPC и проч. и проч.
■ Придумать “нечто” свое
![Page 20: SECON'2014 - Павел Щеваев - Метаданные и автогенерация кода](https://reader035.vdocuments.net/reader035/viewer/2022062707/557fd6fcd8b42aab088b51ec/html5/thumbnails/20.jpg)
Придумать “нечто” свое
![Page 21: SECON'2014 - Павел Щеваев - Метаданные и автогенерация кода](https://reader035.vdocuments.net/reader035/viewer/2022062707/557fd6fcd8b42aab088b51ec/html5/thumbnails/21.jpg)
metagen
![Page 22: SECON'2014 - Павел Щеваев - Метаданные и автогенерация кода](https://reader035.vdocuments.net/reader035/viewer/2022062707/557fd6fcd8b42aab088b51ec/html5/thumbnails/22.jpg)
metagen
■ Простой декларативный мета формат■ Поддержка ассоциативных и
последовательных источников данных■ Новые форматы данных просто добавить■ Парсинг и кодогенерация написаны на
PHP: относительно просто расширить■ RPC, сохранение/загрузка в/из БД,
файловой системы■ C++, AS3, PHP, JavaScript, Go (активно
пилится)
![Page 23: SECON'2014 - Павел Щеваев - Метаданные и автогенерация кода](https://reader035.vdocuments.net/reader035/viewer/2022062707/557fd6fcd8b42aab088b51ec/html5/thumbnails/23.jpg)
■ Максимальное исключение “человеческого фактора”
■ Возможность быстро пусть и “грязно” расширить для решения частной проблемы
metagen
![Page 24: SECON'2014 - Павел Щеваев - Метаданные и автогенерация кода](https://reader035.vdocuments.net/reader035/viewer/2022062707/557fd6fcd8b42aab088b51ec/html5/thumbnails/24.jpg)
metagen: как это работает
■ Парсинг декларативного описания■ Формирование промежуточной структуры■ Кодонегерация под разные “таргеты” из
промежуточной структуры
struct DataPet id : uint32 conf_id : uint32 x : float y : floatend
<?phpclass mtgMetaStruct {}
class mtgMetaField {}
...
PHP
C++
AS3 Go
![Page 25: SECON'2014 - Павел Щеваев - Метаданные и автогенерация кода](https://reader035.vdocuments.net/reader035/viewer/2022062707/557fd6fcd8b42aab088b51ec/html5/thumbnails/25.jpg)
metagen: простые типы и структуры
struct ConfPoint x : float y : floatend
struct ConfShapeBase points : ConfPoint[]end
struct ConfShape extends ConfShapeBase id : uint32 name : stringend
![Page 26: SECON'2014 - Павел Щеваев - Метаданные и автогенерация кода](https://reader035.vdocuments.net/reader035/viewer/2022062707/557fd6fcd8b42aab088b51ec/html5/thumbnails/26.jpg)
metagen: поддержка enum
enum EnumShape UNDEFINED = 0 CIRCLE = 1 SQUARE = 2end
struct DataShape type : EnumShape ...end
![Page 27: SECON'2014 - Павел Щеваев - Метаданные и автогенерация кода](https://reader035.vdocuments.net/reader035/viewer/2022062707/557fd6fcd8b42aab088b51ec/html5/thumbnails/27.jpg)
metagen: токены
struct DataPet @POD @table:pet @id:id @owner:player_id id : uint32 player_id : uint32 name : string @default:"K-9" @strmax:128 breed : EnumBreed @default:"BULLDOG" health : uint32 @default:10 ...end
■ Структурные токены■ Токены на отдельные поля
![Page 28: SECON'2014 - Павел Щеваев - Метаданные и автогенерация кода](https://reader035.vdocuments.net/reader035/viewer/2022062707/557fd6fcd8b42aab088b51ec/html5/thumbnails/28.jpg)
metagen : RPC
RPC 101 MATH_CALC( op : OpType x : float y : float) error : uint32 answers : float[]end
![Page 29: SECON'2014 - Павел Щеваев - Метаданные и автогенерация кода](https://reader035.vdocuments.net/reader035/viewer/2022062707/557fd6fcd8b42aab088b51ec/html5/thumbnails/29.jpg)
metagen: приятные мелочи
#комментарии поддерживаются
#...как и поддерживаются некоторые #другие препроцессорные директивы, например
#include shared.meta
![Page 30: SECON'2014 - Павел Щеваев - Метаданные и автогенерация кода](https://reader035.vdocuments.net/reader035/viewer/2022062707/557fd6fcd8b42aab088b51ec/html5/thumbnails/30.jpg)
metagen: опциональные поляstruct DataPet @POD @table:pet @id:id @owner:player_id id : uint32 player_id : uint32 health : uint32 ...end
struct DataPet @POD @table:pet @id:id @owner:player_id id : uint32 player_id : uint32 health : uint32 ... #added in version 1.01 age : uint32 @optional @default:0 end
![Page 31: SECON'2014 - Павел Щеваев - Метаданные и автогенерация кода](https://reader035.vdocuments.net/reader035/viewer/2022062707/557fd6fcd8b42aab088b51ec/html5/thumbnails/31.jpg)
metagen: результат генерации
struct ConfPoint x : float y : floatend
![Page 32: SECON'2014 - Павел Щеваев - Метаданные и автогенерация кода](https://reader035.vdocuments.net/reader035/viewer/2022062707/557fd6fcd8b42aab088b51ec/html5/thumbnails/32.jpg)
metagen: результат генерации
struct ConfPoint : public MetaBaseStruct { GAME_RTTI_EX(ConfPoint, MetaBaseStruct); enum { CLASS_ID = 230648249, FIELDS_COUNT = 2, };
ConfPoint(Allocator* = NULL); virtual ~ConfPoint();
bool operator==(const ConfPoint&) const;
i32 x; i32 y;
virtual size_t getFieldsCount() const { return FIELDS_COUNT; }
protected: virtual DataError _read(DataReader&); virtual DataError _write(DataWriter&) const;};
![Page 33: SECON'2014 - Павел Щеваев - Метаданные и автогенерация кода](https://reader035.vdocuments.net/reader035/viewer/2022062707/557fd6fcd8b42aab088b51ec/html5/thumbnails/33.jpg)
metagen: результат генерации
DataError ConfPoint::_read(DataReader& reader) { CHECK_READ(MetaBaseStruct::_read(reader), "Parent 'MetaBaseStruct' read error");
CHECK_READ(reader.readI32("x", x), "x"); CHECK_READ(reader.readI32("y", y), "y");
return DATA_OK;}
DataError ConfPoint::_write(DataWriter& writer) const { CHECK_WRITE(MetaBaseStruct::_write(writer), "Parent 'MetaBaseStruct' write error");
CHECK_WRITE(writer.writeI32("x", x), "x"); CHECK_WRITE(writer.writeI32("y", y), "y");
return DATA_OK;}
![Page 34: SECON'2014 - Павел Щеваев - Метаданные и автогенерация кода](https://reader035.vdocuments.net/reader035/viewer/2022062707/557fd6fcd8b42aab088b51ec/html5/thumbnails/34.jpg)
metagen: использование#include "autogen/ConfPoint.h"
ConfPoint pt;
{ JSONAssocReader r; r.init("{x: 10, y: 20}"); pt.read(r);}
{ JSONAssocWriter w; pt.write(w); game::string content; w.dump(content); print(content.c_str());}
![Page 35: SECON'2014 - Павел Щеваев - Метаданные и автогенерация кода](https://reader035.vdocuments.net/reader035/viewer/2022062707/557fd6fcd8b42aab088b51ec/html5/thumbnails/35.jpg)
metagen: расширяемость
class DataReader {public: virtual ~DataReader(){}
virtual DataError readI32(const char*, i32& dest) = 0; virtual DataError readU32(const char*, u32& dest) = 0; virtual DataError readFloat(const char*, float& dest) = 0; virtual DataError readString(const char*, string& dest) = 0; virtual DataError beginArray(const char*, size_t* arr_size) = 0; virtual DataError endArray() = 0;};
![Page 36: SECON'2014 - Павел Щеваев - Метаданные и автогенерация кода](https://reader035.vdocuments.net/reader035/viewer/2022062707/557fd6fcd8b42aab088b51ec/html5/thumbnails/36.jpg)
metagen: расширяемость
class DataWriter {public: virtual ~DataWriter(){}
virtual DataError writeI32(const char*, const i32 source) = 0; virtual DataError writeU32(const char*, u32 source) = 0; virtual DataError writeFloat(float source) = 0; virtual DataError writeString(const char*, const char* source, size_t len) = 0; virtual DataError beginArray(const char*, size_t size) = 0; virtual DataError endArray() = 0;
virtual void dump(string& content) = 0;};
![Page 37: SECON'2014 - Павел Щеваев - Метаданные и автогенерация кода](https://reader035.vdocuments.net/reader035/viewer/2022062707/557fd6fcd8b42aab088b51ec/html5/thumbnails/37.jpg)
metagen: расширяемость
function mtg_go_buf_read(mtgMetaInfo $info, $name, $super_type, $type, $buf, array $tokens = array()) { $str = '';
if($super_type == "scalar") { if($type == 'float') $str .= mtg_go_read($tokens, "{$buf}.ReadFloat(&$name, \"$name\")"); else if (strpos($type, "uint") === false) $str .= mtg_go_read($tokens, "{$buf}.ReadI32(&$name, \"$name\")"); else $str .= mtg_go_read($tokens, "{$buf}.ReadU32(&$name, \"$name\")"); $str .= "\n"; } else if($super_type == "string") { $str .= mtg_go_read($tokens, "{$buf}.ReadString($name, \"$name\");"); } else if($super_type == "struct") { $str .= mtg_go_read($tokens, "({$name}.read($buf), \"$name\");"); } else if($super_type == "array") { ...
![Page 38: SECON'2014 - Павел Щеваев - Метаданные и автогенерация кода](https://reader035.vdocuments.net/reader035/viewer/2022062707/557fd6fcd8b42aab088b51ec/html5/thumbnails/38.jpg)
metagen : использованиеenum EnumBreed UNKNOWN = 0 BULLDOG = 1 SHEPHERD = 2end
struct PetConfig breed : EnumBreed max_health : uint16 anim_ids : uint32[]end
![Page 39: SECON'2014 - Павел Щеваев - Метаданные и автогенерация кода](https://reader035.vdocuments.net/reader035/viewer/2022062707/557fd6fcd8b42aab088b51ec/html5/thumbnails/39.jpg)
metagen : использованиеstruct PetData conf_id : uint32 id : uint32 health : uint16 name : string x : float y : floatend
![Page 40: SECON'2014 - Павел Щеваев - Метаданные и автогенерация кода](https://reader035.vdocuments.net/reader035/viewer/2022062707/557fd6fcd8b42aab088b51ec/html5/thumbnails/40.jpg)
metagen : использованиеstruct Pet { PetData data; const PetConfig* config;};
![Page 41: SECON'2014 - Павел Щеваев - Метаданные и автогенерация кода](https://reader035.vdocuments.net/reader035/viewer/2022062707/557fd6fcd8b42aab088b51ec/html5/thumbnails/41.jpg)
metagen: RPCRPC 10 MOVE_PET( pet_id : uint32 x : float y : float) error : uint32end
![Page 42: SECON'2014 - Павел Щеваев - Метаданные и автогенерация кода](https://reader035.vdocuments.net/reader035/viewer/2022062707/557fd6fcd8b42aab088b51ec/html5/thumbnails/42.jpg)
metagen: RPC (клиент)static void on_move(RPCError err, RPCArgs args, void* user) { if(err.occured()) { assert(0); return; } RPC_EXTRACT_ARGS(MOVE_PET, args, req, resp);
printf("Result: %u", req->error);}
RPC_NEW(MOVE_PET, req);req->pet_id = 199;req->x = 18;req->y = 97;
rpc_call(req, &on_move);
![Page 43: SECON'2014 - Павел Щеваев - Метаданные и автогенерация кода](https://reader035.vdocuments.net/reader035/viewer/2022062707/557fd6fcd8b42aab088b51ec/html5/thumbnails/43.jpg)
metagen: RPC (сервер)<?php
class RPC { ... function RPC_MOVE_PET($in, $out) { $pet = find_pet($in->pet_id); if(!$pet) { $out->error = 1; return; } $out->error = 0; $pet->setPos($in->x, $in->y); } ...}
![Page 44: SECON'2014 - Павел Щеваев - Метаданные и автогенерация кода](https://reader035.vdocuments.net/reader035/viewer/2022062707/557fd6fcd8b42aab088b51ec/html5/thumbnails/44.jpg)
metagen: минусы
■ Требуется настоящий лексер, а не regex парсинг декларативного описания
■ Местами “стихийная” реализация в кодогенерации
■ Пока нет поддержки по-настоящему опциональных полей (data.x.set(10); if(data.x.exists()) { … } )
■ Не является универсальным решением, решает исключительно наши потребности
■ Сlosed source ;)
![Page 45: SECON'2014 - Павел Щеваев - Метаданные и автогенерация кода](https://reader035.vdocuments.net/reader035/viewer/2022062707/557fd6fcd8b42aab088b51ec/html5/thumbnails/45.jpg)
Альтернативы
■ Thrift (thrift.apache.org)■ Protocol Buffers (code.google.
com/p/protobuf)■ Avro (avro.apache.org)■ Roll your own! :)
![Page 46: SECON'2014 - Павел Щеваев - Метаданные и автогенерация кода](https://reader035.vdocuments.net/reader035/viewer/2022062707/557fd6fcd8b42aab088b51ec/html5/thumbnails/46.jpg)
Вопросы?