Upload
sergey-platonov
View
625
Download
1
Embed Size (px)
Citation preview
Детали разработки кроссплатформенного фреймворка.
SPB TV, руководитель отдела перспективных разработок
Никита Глушков
Чем мы занимаемся
Условия разработки
• Историческое наследие • Кросс-платформенность• Продолжительные проекты
Технический долг
• Для продолжительных проектов поддержка кода много дороже его разработки.
• Стоимость поддержки может расти со временем.
• Плохой код – не синоним быстрого результата.
Технический дефолт*
• Разработчики время тратят, но новых фич больше не появляется.
• Всё время уходит на:– исправление проекта– попытки в нём просто разобраться– регулярное переписывание значительной части
функционала
* Попрощайтесь с отличным бизнесом
Архитектура
• Совокупность способов организации системы, которые позволяют после реализации очередного мейлстоуна, релиза или обновления сохранить относительно дешёвую возможность развивать продукты далее*
* После короткого рефакторинга
Структура нашего проекта
Представление
Переносимые графические примитивы
SceneGraph Animators
Шаблоны SceneGraph + DataBinding
💗
Модель данных
Источник данных
Методов
Событий
Машина состояний
Кстати, о С++
Указатели
• Аналог intrusive_ptr• Аналог weak_ptr• std::shared_ptr – антипаттерн.
class Foo : public Referenced {...}auto obj = make_shared<Foo>();shared_ptr<Foo> obj2 = this;
class IMyInterface {public:WEAK_REFERABLE( IMyInterface );
Указатели
Label
• Field 1• Field 2• Field 3
Shape
Group
Animator
weak_ptr
void*
Descriptors
• Структуры хранящие строковый буфер + длину.
• Позволяют работать с подстроками без выделения памяти.
TPtrC8 ptr("Test TPtrC");ptr.Length() == 10;ptr.Mid(1, 3) == "est";
std::string str( "string" );TPtrC8 ptr2( str.c_str(), 3 );ptr2 == "str";
Descriptors
• Комбинируют в одном int длину и флаги.• Полностью заменяют все записи вида char*,
const wchar_t* и т.п.
const char* const TDesC8&
• Можно распарсить xml или json без аллокации памяти под строки.
Strings
• Утино-типизированы с ATL/WTL::CString• Прозрачно работают с дескрипторами• Используют счётчик ссылок• std::string - антипаттерн
void Test( const TDesC8& desc );
CStringA str = "Test";str.Append( "2" );Test( str );
Strings
• Выполняется правило:– CStringA отображает строки необходимые для
работы системы– CStringW отображает строки, которые может
увидеть пользователь• Универсальные строковые преобразования:
size_t CastFromString( const TDesC8& str, SomeType& t );CStringA CastToString( SomeType );
RTTI
• Позволяет идентифицировать классы в рантайме, содержит их базовое описание.
class variant_type{ const char* name; size_t size; size_t aligned_size;
func_constructor constructor; func_destructor destructor; func_copy_constructor copy_constructor;
RTTI
• template<class T>static variant_type const* type_id(){ static variant_type static_type; return static_type.m_true_type;}
• Проблема – при работе с несколькими исполняемыми модулями может получаться несколько идентифицирующих указателей для одного типа.
Variant
• class variant: контейнер, в котором можно безопасно хранить объект (почти) любого типа
variant v1 = 1;variant v2 = "Test";variant v3 = myMegaObject;variant v4 = Calculate( "1 + 2 * ( 3 + GetInt('Settings.someValue') )", some_ctx );int i = variant_cast<CString>( v4 ); // Oops!
Variant
• Хранит указатель на структуру, содержащую счётчик, указатель на идентификатор RTTI и место под хранимый объект.
• При сохранении объекта используется только одна аллокация.
struct variant_info{variant_type const* m_type;int m_ref;};variant_info* m_pInfo;
Variant
• Один из ключевых элементов системы, поэтому ради оптимизации за милым интерфейсом прячутся страшные вещи:template<class T>void Store(T const& val){ variant_type const* type = variant_type::type_id<T>(); size_t size = type->align_size + sizeof(variant_info); m_pInfo = (variant_info*)SmallAlloc( size, SA_VARIANT ); m_pInfo->m_ref = 1; m_pInfo->m_type = type; new (GetValue()) T( val );}
Varianttemplate<class T>T& variant_cast(variant& var){ LCCHECKM( var.GetType() == variant_type::type_id<T>(), "Variant cannot be casted from: " << (var.IsEmpty() ? "'empty'" : var.GetType()->name ) << ", to: " << variant_type::type_id<T>()->name ); return *static_cast<T*>(var.GetValue());}
class IVariantSerializator{ virtual CStringA Save( const variant&, IReferencedToId& )=0; virtual variant Load( const TDesC8& str, IIdToReferenced& )=0;};
Variant
• Не хватает возможности автоматического приведения типов внутри контейнера.
TexturePtr texture;if ( it->m_Value.GetType() == rtti::type_id<Texture2DPtr>() ) ...else if ( it->m_Value.GetType() == rtti::type_id<TexturePtr>() ) ...else if ( it->m_Value.GetType() == rtti::type_id<IUniformSourceTexture2DPtr>() ) ... else ...
Serialization
• Маршалинг:– Из и в xml, json, binary– Конвертация в нативный формат– Оптимизация сцен
• Рефлексия:– Поиск объектов и их мемберов в иерархии– Модификация сцен аниматорами или
скриптами
Serialization
• Форма бралась из boost:
class AnimaControl : public Referenced{public:...template<class TArchive>void Serialize(TArchive& ar){ar & SERIALIZE_ITEM_EXT( m_CurrentTime, "Time", SER_ATTRIBUTE );ar & SERIALIZE_ITEM_EXT( m_PlayRate, "PlayRate", SER_ATTRIBUTE );ar & SERIALIZE_ITEM_EXT( m_Enabled, "Enabled", SER_ATTRIBUTE );ar & SERIALIZE_ITEM_EXT( m_Weight, "Weight", SER_ATTRIBUTE );...
Serialization
<Modify Element="MainView:Animator" Variable="AnimaControl:PlayRate" Value="-1"/>
<AnimaControl TimeEnd="0.5" PlayRate="0" ClampTime="1"/>
1.
2.
3. <Material> <Platform Name="OpenGL"> <UniformValues> <Texture Name="sampler"/> <Real Name="alpha" Value="MainView:Animator:AnimaControl:Time"/> </UniformValues> ...
Функторыclass FunctorImpl : public Referenced{public: virtual void operator()() = 0;};
class Functor : public shared_ptr<FunctorImpl>{ typedef shared_ptr<FunctorImpl> baseClass;public: template<class T, class TPtr> Functor( T ptr, TPtr func ) ...
Система запросов
• Запросы сетевые, к системе, внутренним процессам, пользователям.
RequestHandle RequestSomeInfo( CStringA someParam1, Function<void, Foo> on_success, Function<void, ErrorReason> on_fail );
class ITVService : public lc::ILcPlugin{public:virtual lc::RequestHandle TryToConnect( lc::Functor onSuccess, lc::Function<void, lc::ErrorReason> ) = 0;
Декларативные элементы
{ action1; scope_exit { cleanup1 }; scope_fail { rollback1 }; // nope action2; scope_exit { cleanup2 }; scope_fail { rollback2 }; // nope next2;}www.github.com/panaseleus/stack_unwinding
Процесс разработки
• Разработчики вольны выбирать наиболее удобную ОС для работы.
• UI в основном разрабатывается под Windows
• Модификация интерфейса и основных переходов происходит преимущественно без перезагрузки системы
Оформление кода
Комментарии в коде
• Хочешь понять код? Читай его.• Тянет написать комментарий? Потрать
время на рефакторинг.• Комментарии = дублирование кода = $$$.• Полезного комментария: // Google “3DDDA”• Осмысленные логи – совсем другое дело!• А так же – многочисленные юнит-тесты,
примеры и обзорные статьи
Вопросы?