Memento pattern

In this note, I will describe the “Snapshot” or “Memento” pattern.
This pattern refers to the “Behavioral” design patterns.

Suppose we are developing a graphics editor, and we need to add the ability to roll back actions at the user’s command. It is also very important that the system components do not have access to the internal state of the rollback “actions”. When implementing this pattern, the other system components have access only to the object snapshot without the ability to change its internal state, providing a clear, simple external interface. To solve this problem, use the “Snapshot” or “Memento” pattern.

Memento pattern example:

When you click a sprite appears, when you click on a undo button, the action is canceled – the sprite disappears. The example consists of three classes:

  1. Canvas that shows sprites, user interface.
  2. Screen controller, it handles input and controls screen logic.
  3. Canvas states that are saved with each change, and could be are reverted.

In terms of the Snapshot pattern, classes are:

  1. Canvas – originator, which stated are saved as “mementos”, to revert changes if needed. Originator must revert his state, from memento object if necessary.
  2. Screen controller – caretaker, this class controls all screen, and know how and when to revert changes.
  3. Canvas state – memento, which contains state, and some kind of index to track changes correctly.

An important feature of the pattern is that only the Originator should have access to the internal fields of the saved state in the snapshot. Embedded classes are used to implement encapsulation, and in C ++, the ability to specify friend classes is used. Personally, I implemented a simple version without encapsulation for Rise, and using Generic when implementing for Swift. In my version, Memento gives its inner state only to entities of the same class state:

Documentation

https://refactoring.guru/design-patterns/memento

Source code

https://gitlab.com/demensdeum/patterns/

Visitor pattern

In this article I will describe a design pattern called “Visitor”
This pattern refers to the group Behavioral patterns.

Think up a problem

Basically, this pattern is used to bypass the restriction of a single dispatch (“single dispatch”), in languages ​​with early binding.

Alice X by NFGPhoto (CC-2.0)
Create an abstract class/protocol Band, make a subclass of MurpleDeep, create a class Visitor with two methods – one to output any inheritor to the console, the second to output any MurpleDeep, the main thing is that the names (signatures) of the methods are the same, and the arguments differ only in class. Through the intermediate printout method with the Band argument, create an instance of Visitor and call the visit method for MurpleDeep.
Next code on Kotlin:

The output will be “This is Band class

WTF (World Taekwondo Federation)

Why this happens is described in buzzwords in many articles, including in Russian, I suggest you present how the compiler sees the code, maybe everything will become clear right away:

Silver bullet

To solve this problem, there are many solutions, then consider the solution using the Visitor pattern.
We add the accept method with the Visitor argument to the abstract class/protocol, call visitor.visit(this) inside the method, then add the override/implementation of the accept method to the MurpleDeep class, break DRY decisively and quietly, write visitor.visit(this).
The resulting code:

Documentation

https://refactoring.guru/en/design-patterns/visitor-double-dispatch

Source code

https://gitlab.com/demensdeum/patterns

Flyweight pattern

In this article I will describe the structural pattern “Flyweight”
This pattern refers to the group Structural Patterns.

Example of the pattern below:

Why is it needed? To save RAM memory. I agree that in times of widespread use of Java (which consumes cpu and memory just like that), this is not so important, but it’s worth it.
In the example above, only 40 objects are displayed, but if you raise their number to 120,000, the memory consumption will increase accordingly.
Let’s look at the memory consumption without using the flyweight pattern in the Chromium browser:

Without the use of the pattern, the memory consumption is ~ 300 megabytes.

Now add a pattern to the application and see the memory consumption:

With the use of the pattern, the memory consumption is ~ 200 megabytes, so we saved 100 megabytes of memory in the test application, in serious projects the difference can be much larger.

Wie funktioniert das?

In the example above, we draw 40 cats or, for clarity, 120 thousand. Each cat is loaded into memory as a png image, then in most renders it is converted into a bitmap for rendering (actually bmp), this is done for speed, since a compressed png is drawn for a very long time. Without using a pattern, we load 120 thousand pictures of cats into RAM and draw, but when using the lightweight pattern, we load one cat into memory and draw it 120 thousand times with different positions and transparency. All the magic lies in the fact that we implement the coordinates and transparency separately from the cat image, when rendering the render takes just one cat and uses an object with coordinates and transparency to correctly draw.

Show me the code

The following are examples for the Rise language.

Without a pattern:


The cat image is loaded for each object in the loop separately – catImage.

Using the pattern:

One cat picture is used by 120 thousand objects.

Real life example

It is used in GUI frameworks, for example, in Apple, in the “reuse” system of the cells of the UITableViewCell tables, which raise the entry threshold for beginners who do not know about this pattern.

Source code

https://gitlab.com/demensdeum/patterns/

Documents

https://refactoring.guru/en/design-patterns/flyweight
http://gameprogrammingpatterns.com/flyweight.html

С++ Application Plugins

In this post I will describe an example of adding functionality to a C ++ application using plugins. The practical part of the implementation for Linux is described; the theory can be found at the links at the end of the article.

Composition over inheritance!

To begin with, we will write a plugin – a function that we will call:

#include "iostream"

using namespace std;

extern "C" void extensionEntryPoint() {
	cout << "Extension entry point called" << endl;
};

Next, we will build the plugin as a dynamic library “extension.so”, which we will connect in the future:
clang++ -shared -fPIC extension.cpp -o extension.so

Next we write the main application that will load the file “extension.so”, look for a pointer to the function “extensionEntryPoint” there, and call it, typing errors if necessary:

#include "iostream"
#include "dlfcn.h"

using namespace std;

typedef void (*VoidFunctionPointer)();	

int main (int argc, char *argv[]) {

	cout << "C++ Plugins Example" << endl;

	auto extensionHandle = dlopen("./extension.so", RTLD_LAZY);
	if (!extensionHandle) {
		string errorString = dlerror();
		throw runtime_error(errorString);
	}

	auto functionPointer = VoidFunctionPointer();
	functionPointer = (VoidFunctionPointer) dlsym(extensionHandle, "extensionEntryPoint");
	auto dlsymError = dlerror();
 	if (dlsymError) {
		string errorString = dlerror();
		throw runtime_error(errorString);
 	}

	functionPointer();

	exit(0);
} 

The dlopen function returns a handler for working with a dynamic library; dlsym function returns a pointer to the required function by string; dlerror contains a pointer to the string with the error text, if any.

Next, build the main application, copy the file of the dynamic library in the folder with it and run. The output should be the “Extension entry point called”

Difficult moments include the lack of a single standard for working with dynamic libraries, because of this there is a need to export the function to a relatively global scope with extern C; the difference in working with different operating systems associated with this subtlety of work; the lack of a C ++ interface to implement OOP approach to working with dynamic libraries, however, there are open-source wrappers, for example m-renaud/libdlibxx

Example Source Code

https://gitlab.com/demensdeum/cpppluginsexample

Documents

http://man7.org/linux/man-pages/man3/dlopen.3.htm
https://gist.github.com/tailriver/30bf0c943325330b7b6a
https://stackoverflow.com/questions/840501/how-do-function-pointers-in-c-work

Порхай как Мишель

[Feel the power of Artificial Intelligence]
В данной заметке я расскажу как предсказывать будущее.

В статистике существует класс задач – анализ временных рядов. Имея дату и значение некой переменной, можно прогнозировать значение этой переменной в будущем.
Поначалу я хотел реализовать решение данной задачи на TensorFlow, однако нашел библиотеку Prophet от Facebook.
Prophet позволяет делать прогноз на основе данных (csv), содержащих колонки даты (ds) и значения переменной (y). О том как с ней работать, можно узнать в документации на официальном сайте в разделе Quick Start
В качестве датасета я использовал выгрузку в csv с сайта https://www.investing.com, при реализации я использовал язык R и Prophet API для него. R мне очень понравился, так как его синтаксис упрощает работу с большими массивами данных, позволяет писать проще, допускать меньше ошибок, чем при работе с обычными языками (Python), так как пришлось бы работать с лямбда выражениями, а в R уже все лямбда выражения.
Для того чтобы не подготавливать данные к обработке, я использовал пакет anytime, который умеет переводить строки в дату, без предварительной обработки. Конвертация строк валюты в number осуществляется с помощью пакета readr.

В результате я получил прогноз по которому биткоин будет стоить 8400$ к концу 2019 года, а курс доллара будет 61 руб. Стоит ли верить данным прогнозам? Лично я считаю что не стоит, т.к. нельзя использовать математические методы, не понимая их сущности.

Источники

https://facebook.github.io/prophet
https://habr.com/company/ods/blog/323730/
https://www.r-project.org/

Исходный код

https://gitlab.com/demensdeum/MachineLearning/tree/master/4prophet

Tesla coil

[Feel the power of Artificial Intelligence]

В этой заметке я опишу процесс создания генератора цитат.

TL;DR

Для обучения и генерации текста – использовать библиотеку textgenrnn, для фильтрации фраз нужно использовать проверку орфографии с помощью утилиты hunspell и ее библиотеки для C/python. После обучения в Colaboratory, можно приступать к генерации текста. Примерно 90% текста будет абсолютно не читаемым, однако оставшиеся 10% будут содержать толику смысла, а при ручной доработке фразы будут выглядеть вполне неплохо.
Проще всего запустить готовую нейросеть в Colaboratory:
https://colab.research.google.com/drive/1-wbZMmxvsm3SoclJv11villo9VbUesbc

Исходный код

https://gitlab.com/demensdeum/MachineLearning/tree/master/3quotesGenerator

Источники

https://minimaxir.com/2018/05/text-neural-networks/
http://karpathy.github.io/2015/05/21/rnn-effectiveness/
https://medium.com/deep-learning-turkey/google-colab-free-gpu-tutorial-e113627b9f5d
https://github.com/wooorm/dictionaries

Bugs count estimation

[Feel the power of Artificial Intelligence]

На Hacker News нашел очень интересную статью в которой автор предлагает использовать метод Петерсена-Линкольна, который используется биологами для подсчета популяции птичек, обезьянок и прочих животных, для *барабанная дробь* подсчета багов в приложении.

Баг в естественной среде обитания – Bigfoot Sighting by Derek Hatfield

Метод очень прост, берем двух орнитологов, они находят птичек какого-то определенного вида, их задача – определить размер популяции этих птичек. Найденные птички помечаются обоими орнитологами, далее подсчитывается количество общих, подставляется в формулу индекса Линкольна и мы получаем примерный размер популяции.
Теперь для приложений – метод также очень прост, берем двух QA и они находят баги в приложении. Допустим один тестировщик нашел 10 багов (E1), а второй 20 багов (E2), теперь берем число общих багов – 3 (S), далее по формуле получаем индекс Линкольна:

Это и есть прогноз числа багов во всем приложении, в приведенном примере ~66 багов.

Пример на Swift

Я реализовал тестовый стенд для проверки метода, посмотреть можно здесь:
https://paiza.io/projects/AY_9T3oaN9a-xICAx_H4qw?language=swift

Параметры которые можно менять:

let aliceErrorFindProbability = 20 – процент нахождения багов у QA Alice (20%)
let bobErrorFindProbability = 60 – процент нахождения багов у QA Bob (60%)
let actualBugsCount = 200 – сколько багов в приложении на самом деле

В последнем запуске я получил следующие данные:
Estimation bugs count: 213
Actual bugs count: 200

Тоесть в приложении есть 200 багов, индекс Линкольна дает прогноз – 213:
“Alice found 36 bugs”
“Bob found 89 bugs”
“Common bugs count: 15”

Estimation bugs count: 213
Actual bugs count: 200

Слабые стороны

Использовать данный метод можно для оценки количества ошибок в приложении, на всех этапах разработки, в идеале количество багов должно уменьшаться. К слабым сторонам метода я могу отнести человеческий фактор, так как количество найденных багов от двух тестировщиков должно быть разным и найдены разные баги, однако должны быть найдены и общие, иначе метод работать не будет (ноль общих багов – деление на ноль)
Также такое понятие как общие баги требует обязательное наличие эксперта для понимания их общности.

Источники

How many errors are left to find? – John D. Cook, PhD, President
The thrill of the chase – Brian Hayes

Исходный код

https://paiza.io/projects/AY_9T3oaN9a-xICAx_H4qw?language=swift
https://gitlab.com/demensdeum/statistics/tree/master/1_BugsCountEstimation/src

Black Shaped Box

[English translation may be some day]

К любому разработчику на OpenGL периодически приходит Малевич. Происходит это неожиданно и дерзко, ты просто запускаешь проект и видишь черный квадрат вместо чудесного рендера:

Сегодня я опишу по какой причине меня посетил черный квадрат, найденные проблемы из-за которых OpenGL ничего не рисует на экране, а иногда и вообще делает окно прозрачным.

Используй инструменты

Для отладки OpenGL мне помогли два инструмента: renderdoc и apitrace. Renderdoc – инструмент для отладки процесса рендеринга OpenGL, просматривать можно все – вертексы, шейдеры, текстуры, отладочные сообщения от драйвера. Apitrace – инструмент для трейсинга вызовов графического API, делает дамп вызовов и показывает аргументы. Также есть великолепная возможность сравнивать два дампа через wdiff (или без него, но не так удобно)

Проверяй с кем работаешь

У меня есть операционная система Ubuntu 16.10 со старыми зависимостями SDL2, GLM, assimp, GLEW. В последней версии Ubuntu 18.04 я получаю сборку игры Death-Mask которая ничего не показывает на экране (только черный квадрат). При использовании chroot и сборке в 16.10 я получаю рабочую сборку игры с графикой.


Похоже что-то сломалось в Ubuntu 18.04

LDD показал линковку к идентичным библиотекам SDL2, GL. Прогоняя нерабочий билд в renderdoc, я увидел мусор на входе в вертексный шейдер, но мне нужно было более солидное подтверждение. Для того чтобы разобраться в разнице между бинариками я прогнал их оба через apitrace. Сравнение дампов показало мне что сборка на свежей убунте ломает передачу матриц перспективы в OpenGL, фактически отправляя туда мусор:

Матрицы собираются в библиотеке GLM. После копирования GLM из 16.04 – я снова получил рабочий билд игры. Проблема оказалась в разнице инициализации единичной матрицы в GLM 9.9.0, в ней необходивно явно указывать аргумент mat4(1.0f) в конструкторе. Поменяв инициализацию и отписав автору библиотеки, я принялся делать тесты для FSGL. в процессе написания которых я обнаружил недоработки в FSGL, их опишу далее.

Определись ты кто по жизни

Для корректной работы с OpenGL нужно в добровольно принудительном порядке запросить контекст определенной версии. Так это выглядит для SDL2 (проставлять версию нужно строго до инициализации контекста):

    SDL_GL_SetAttribute( SDL_GL_CONTEXT_MAJOR_VERSION, 3);
    SDL_GL_SetAttribute( SDL_GL_CONTEXT_MINOR_VERSION, 2);
    SDL_GL_SetAttribute( SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE );   

Например Renderdoc не работает с контекстами ниже 3.2. Хочется отметить что после переключения контекста высока вероятность увидеть тот самый черный экран. Почему?
Потому что контекст OpenGL 3.2 обязательно требует наличие VAO буфера, без которого не работают 99% графических драйверов. Добавить его легко:

    glGenVertexArrays(1, &vao);
    glBindVertexArray(vao);

Не спи, замерзнешь

Также я встретился с интересной проблемой на Kubuntu, вместо черного квадрата у меня выводился прозрачный, а иногда все рендерилось корректно. Решение этой проблемы я нашел на Stack Overflow:
https://stackoverflow.com/questions/38411515/sdl2-opengl-window-appears-semi-transparent-sometimes

В коде тестового рендера FSGL тоже присутствовал sleep(2s); Так вот на Xubuntu и Ubuntu я получал корректный рендер и отправлял приложение спать, однако на Kubuntu я получил прозрачный экран в 80% случаев запуска из Dolphin и 30% запусков и терминала. Для решения данной проблемы я добавил рендеринг в каждом кадре, после опроса SDLEvent, как это рекомендуется делать в документации.

Код теста:
https://gitlab.com/demensdeum/FSGLtests/blob/master/renderModelTest/

Поговори с драйвером

OpenGL поддерживает канал связи между приложением и драйвером, для его активации нужно включить флаги GL_DEBUG_OUTPUT, GL_DEBUG_OUTPUT_SYNCHRONOUS, проставить оповещение glDebugMessageControl и привязать каллбек через glDebugMessageCallback.
Пример инициализации можно взять здесь:
https://github.com/rock-core/gui-vizkit3d/blob/master/src/EnableGLDebugOperation.cpp