С 19 по 21 апреля в Санкт-Петербурге прошла конференция С++ Russia 2018. Из года в год организация и проведение становится на уровень выше, что не может не радовать. Спасибо бессменному организатору С++ Russia Сергею Платонову за его вклад в развитие этого направления.

На 19 апреля были запланированы мастер-классы, на которые, к сожалению, мы не смогли попасть, а 20-21 проходила основная программа конференции, в которой мы с большим интересом поучаствовали. Сергей sermp проделал большую работу и привлёк к участию в качестве докладчиков несколько замечательных иностранных спикеров. Первый день конференции открывал Jon Kalb — организатор CppCon и автор книги C++ Today: The Beast is Back. Второй день начинался с доклада Daveed Vandevoorde, члена комитета стандартизации, одного из авторов книги C++ Templates: The Complete Guide. В эпицентре внимания оказался Andrei Alexandrescu, который после своего доклада об исключениях в один момент собрал целую толпу из желающих получить автограф и сделать совместное фото. Впервые была проведена трансляция доклада Herb Sutter по Skype, рассказывающего об операторе Spaceship для С++20.

Хотя конференция прошла уже больше 3 месяцев назад, видео (полный плейлист) по ней выложены только сейчас, поэтому пришло время освежить воспоминания и погрузиться в удивительные возможности С++.

Jon Kalb. C++ Today: The Beast is Back



This talk covers why engineers looking for performance choose C++. Jon presents an historical perspective of C++ focusing on what's going on in the C++ community right now and where the language and its user base is heading. With a renewed interest in performance for both data centers and mobile devices, and the success of open source software libraries, C++ is back and it is hot. This talk explains why C++ is most software engineers' go-to language for performance. You will receive a rough historical sketch that puts C++ in perspective and covers its popularity ups and downs.

Arno Schodl. From Iterators To Ranges



Pairs of iterators are ubiquitous throughout the C++ library. It is generally accepted that combining such a pair into a single entity usually termed Range delivers more concise and readable code. Defining the precise semantics of such Range concept proves surprisingly tricky, however. Theoretical considerations conflict with practical ones. Some design goals are mutually incompatible altogether.

Jonathan Boccara. 105 STL Algorithms in Less Than an Hour



We are all aware that we should know the STL algorithms. Including them in our designs allows us to make our code more expressive and more robust. And sometimes, in a spectacular way.

But do you know your STL algorithms?

In this talk, the author presents 105 algorithms that the STL currently has, including those added in C++11 and C++17. But more than just a listing, the point of this talk is to present the different groups of algorithms, the patterns they form in the STL, and how the algorithms relate together.

This kind of big picture is the best way to actually remember them all, and constitute a toolbox chock-full of ways to make our code more expressive and more robust.

Viktor Kirilov. Interactive C++ Compilation (REPL): The Lean Way



Ever wanted to modify some value or execute some statement while your C++ program is running just to test something out — not trivial or possible with a debugger? Scripting languages have a REPL (read-eval-print-loop). The closest thing C++ has is cling (developed by researchers at CERN) but it is built on top of LLVM and is very cumbersome to set up. RCRL (Read-Compile-Run-Loop) is a demo project showcasing an innovative approach to doing runtime C++ compilation in a platform and compiler agnostic way which can be easily embedded. In this presentation, it is shown how to use it, how it works and how it can be modified and integrated into any application and workflow.

Victor Ciura. Enough string_view to hang ourselves



Wouldn't it be nice if we had a standard C++ type to represent strings? Oh, wait… we do: std::string. Wouldn't it be nice if we could use that standard type throughout our whole application/project? Well… we can't! Unless we're writing a console app or a service. But, if we're writing an app with GUI or interacting with modern OS APIs, chances are that we'll need to deal with at least one other non-standard C++ string type. Depending on the platform and project, it may be CString from MFC or ATL, Platform::String from WinRT, QString from Qt, wxString from wxWidgets, etc. Oh, let's not forget our old friend const char*, better yet const wchar_t* for the C family of APIs…

So we ended up with two string types in our codebase. OK, that's manageable: we stick with std::string for all platform independent code and convert back-and-forth to the other XString when interacting with system APIs or GUI code. We'll make some unnecessary copies when crossing this bridge and we'll end up with some funny looking functions juggling two types of strings; but that's glue code, anyway… right?

It's a good plan… until our project grows and we accumulate lots of string utilities and algorithms. Do we restrict those algorithmic goodies to std::string? Do we fallback on the common denominator const char* and lose the type/memory safety of our C++ type? Is C++17 std::string_view the answer to all our string problems?

The author tries to explore the options, together, with a case study on a 15 year old Windows application: Advanced Installer (www.advancedinstaller.com) — an actively developed C++ project, modernized to C++17, thanks to clang-tidy and «Clang Power Tools» (www.clangpowertools.com)

Andrei Alexandrescu. Expect the expected



Writing code that is resilient upon errors has always been a pain point in all languages. Exceptions are the politically correct means to signal errors in C++, but many applications still resort to error codes for reasons related to ease of understanding, ease of handling errors locally, and efficiency of generated code.

This talk shows how a variety of theoretical and practical artifacts can be combined together to address error codes and exceptions in one wholesome, simple package. The generic type Expected can be used for both local (error-code-style) and centralized (exception-style) manners, drawing from the strengths of each.

Borislav Stanimirov. DynaMix: A New Take on Polymorphism



Software with very complex business logic, such as games, CAD systems, and enterprise systems, often needs to compose and modify objects at runtime — for example to add or override a method in an existing object. Standard C++ has rigid types which are defined at compile time and make this hard. On the other hand, languages with dynamic types like lua, Python, and JavaScript make this very easy. Therefore, to keep the code readable and maintainable, and accomplish complex business logic requirements, many projects use such languages alongside C++. Some drawbacks of this approach include the added complexity in a language binding layer, the performance loss from using an interpreted language, and the inevitable code duplication for many small utility functionalities.

DynaMix is a library which attempts to remove, or at least greatly reduce, the need for a separate scripting language by allowing the users to compose and modify polymorphic objects at runtime in C++. This talk elaborates on this problem and introduces the library and its key features to potential users or people who might benefit form the approach with an annotated example and a small demo.

Mikhail Matrosov. Versatile C++ applied



In C++, you can solve a single task in a multiple ways. The author picks an actual task from production, and investigate how it can be solved with a number of tools that C++ provides: STL containers, boost.range, C++20 ranges, coroutines. He also compares API constraints and performance of different solutions, and how they can be easily converted from one to another if the code is well-structured. During the way the author also explores applications of some useful C++17 features like constexpr if, selection statements with initializer, std::not_fn, etc. Special attention is payed to topic — standard algorithms.

Александр Гранин. Функциональный подход к Software Transactional Memory



Параллельное программирование — тема очень многогранная и глубокая. За десятки лет исследований было выработано огромное количество подходов, практик и инструментов, но вряд ли мы можем считать, что язык C++ успевал за этими тенденциями. Начиная со стандарта C++11, были введены такие концепции как std::thread, std::atomic, std::future, std::mutex, а в будущем ожидается, что будут добавлены coroutines — модель асинхронных вычислений. Что ж, это все интересные вещи для изучения, — но речь в докладе пойдет о совершенно иной идее.

Software Transactional Memory (STM) — концепция транзакционно изменяемой модели данных — существует уже давно и имеет ряд реализаций для всех языков. С помощью STM вы выражаете свою модель данных, и запускаете ее на изменение в нескольких потоках, конкурентно, при этом вам не нужно беспокоиться о синхронизации потоков, о валидном состоянии данных, о блокировках. STM сделает все за вас. Звучит это очень хорошо, однако не все STM-библиотеки одинаково полезны. Традиционные императивные STM очень сложно устроены, подвержены нетривиальным многопоточным багам, и их трудно использовать. С другой стороны, в мире функционального программирования уже давно существует концепция комбинаторной STM, транзакции в которой являются компонуемыми кирпичиками, из которых вы строите транзакции более высокого уровня. Комбинаторный подход к STM позволяет выражать конкурентную модель данных более гибко, понятно и надежно. Параллельное программирование тоже может быть приятным!

В докладе автор расскажет об особенностях комбинаторной STM, как ее использовать, и как ее можно реализовать в С++17.

Vadim Vinnik. Collection Processing. Single essence, multiple manifestations



Along the whole history of programming, sequential elementwise processing of various kinds of collections has been and still is one of the most common practical tasks. Internal representation of the collections, as well as the algorithm used to fetch subsequent elements, may vary in a very wide range: array, linked list, tree, hash table, file et al. However, behind the variety of idioms, standard library functions, ad-hoc solutions, one can reveal the essence that remains invariant for that whole class of tasks. This talk aims to show a step-by-step transition from algorithms based on explicit description of actions over individual elements towards high-level, declarative processing tools that treat a collection as an entity and adequately reveal the logic of the domain.

Дмитрий Банщиков. Разделяемые библиотеки без внешних зависимостей



Автор расскажет о своём опыте разработки антивирусного движка на C++ в виде разделяемой библиотеки. Уникальной особенностью является отсутствие каких-либо внешних зависимостей (runtime C++ или C). Вся эта связка построена вокруг использования кастомного toolchain'а на GCC для специального таргета, которым собирается libc newlib для этого же таргета, поверх которого собирается libstdc++. Соответственно, разделяемая библиотека собирается через кастомный тулчейн с кастомными libgcc_s, libc, libcstdc++ (изменения только в сборке). Все взаимодействие со средой выполнения осуществляется через ABI разделяемой библиотеки. Таким образом, внутри библиотеки сохраняется возможность использовать полноценный современный С++ без ограничений (RTTI, exceptions, iostream, etc), что уходит в libstdc++ libc (newlib)| l ibgcc-ABI. Подобный подход проверялся на тулчейнах GCC/newlib/libstdc++ для Linux, и clang/newlib/libc++ для MacOS. Доклад может быть интересен тем, кто хочет использовать С++ в разделяемых библиотеках, но не может себе этого позволить из-за внешних зависимостей.

Илья Шишков. Как научить языку C++: опыт создания курсов на Coursera



В течение последних полутора лет автор руководит созданием специализации на Coursera о современном C++. Специализация будет состоять из пяти курсов, два из которых уже запущены, а ещё один почти готов.

В докладе будет рассказано:

  • с какими проблемами можно столкнуться в работе над курсами (например, через 3 месяца работы разработчики выбросили все материалы и начали заново)
  • как формируется программа курсов и почему именно так (например, почему слово «указатель» не прозвучало в двух первых курсах ни разу)

Кроме того, за время работы над специализацией выработался набор принципов, которые применимы и в повседневной работе:

  • в процессе интеграции нового сотрудника в проект
  • во время code-review
  • при найме

Поэтому автор не просто хочет рассказать, как они делают специализацию, но и попытается переложить полученный опыт на повседневные задачи.

Иван Пономарёв. Crash репорты Android NDK



Не секрет, что разработка на C/C++ предъявляет гораздо более высокие требования к качеству кода, чем разработка на Java. Вероятность допустить фатальную ошибку значительно выше. В то же время, сбор информации о таких ошибках является нетривиальной задачей даже для опытных программистов.

В первой части доклада будут кратко рассмотрены уже имеющиеся наработки: как работает встроенный отладчик Android, какие решения уже существуют. Вторая часть посвящена рассказу о том, как это работает «под капотом»: как получить состояние процессора на момент ошибки, как раскрутить стек вызовов, как узнать номера строк в исходном коде. Будет дан обзор таких библиотек для раскрутки стека, как libcorkscrew, libunwind, libunwindstack.

Доклад будет интересен как Android-разработчикам, в чьих приложениях используется NDK, так и всем остальным для расширения кругозора.

Фёдор Короткий. Память — идеальная абстракция



int* ptr = new int;
*ptr = 42;
delete ptr;

Что на самом деле происходит, когда выполняются эти 3 строчки кода? Мы заглянем внутрь аллокатора памяти, операционной системы и современного железа чтобы дать исчерпывающий ответ на этот вопрос.

Алексей Салмин. Memory Management Tips & Tricks



В 2017 году вопрос выбора аллокатора в C++ не теряет актуальности. В стандарт добавили новый способ выбрать локальный аллокатор для контейнеров (std::pmr), глобальные tcmalloc и jemalloc продолжают развиваться, как и интерфейсы ядра, на которые они опираются. Данный доклад посвящен «нижнему этажу» этой конструкции: особенностям работы mmap и madvise в ядре Linux и влиянию этих особенностей на производительность аллокаторов.

Herb Sutter. New in C++20: The spaceship operator



The new spaceship was recently adopted as a language feature for C++20. In this talk, the designer and author of the spaceship proposal gives an overview of the feature, discusses its motivation and design, and walks through examples of how to use it. He gives particular emphasis to how the feature makes C++ code cleaner to write and read, faster by avoiding redundant work, and more robust by avoiding several important but subtle pitfalls in the more brittle code we previously had to write by hand without this feature.

Анастасия Казакова. Отладка кода на C++, без запуска и компиляции



Когда смотришь на шаблоны, рефлексию, генерацию кода на этапе компиляции, метаклассы, возникает ощущение, что C++ поставил себе задачу максимально «спрятать» итоговый код от разработчика. А нетривиальное использование препроцессора (и многочисленные ответвления) может сделать последовательность действий программы очень неочевидной. Конечно, эти подходы избавляют разработчиков от бесконечного copy-paste и повторения похожих частей кодовой базы, но требуют более продвинутой поддержки в инструментах разработки.

Можно ли отлаживать код без непрерывного его перезапуска, без отладчика, да даже без простой компиляции всей кодовой базы? Если ли возможность поиска ошибок в коде, который нельзя собрать или запустить на локальной машине? Есть! Интегрированные среды разработки (IDE) обладают обширными знаниями и пониманием пользовательского кода, и именно они могут предоставить соответствующие инструменты.

В данном докладе будет показано, как можно было бы «отлаживать» макроподстановки, вложенные typedef, понимать типы переменных (которые в современном C++ оказываются зачастую «спрятанными»), отлаживать разные ветки препроцессора или перегрузки операторов, и многое другое с помощью по-настоящему умной IDE. Какие-то из возможностей уже доступны в CLion и ReSharper C++, а какие-то являются просто интересными идеями на будущее, которые было бы интересно обсудить с аудиторией.

Евгений Лукьянец. Сборка в Docker с использованием Conan



Сборку C++ проекта можно перенести внутрь docker-контейнера, при этом, вместо установки необходимых библиотек и зависимостей в хост-систему, их можно либо устанавливать прямо в docker-образ (например, Cuda), либо устанавливать с помощью менеджера C++ библиотек Conan (например, Boost). При этом получается изолированное контролируемое (и каждый раз одинаковое) окружение для сборки, в которое можно подключить кэш Conan, поэтому разные проекты, использующие одни и те же библиотеки будут использовать одни и те же их сборки. Также отпадает зависимость сборки от дистрибутива Linux, в котором собирается проект, главное — чтобы можно было на этом дистрибутиве запустить Docker.

Денис Панин. Практическое метапрограммирование: пишем гетерогенную хэш-таблицу



В процессе доклада мы напишем маленькую библиотеку работы с std::tuple. При помощи этой библиотеки сделаем компайл-тайм гетерогенную хэш-таблицу. Далее — на ее основе напишем маленький RPC фреймворк, используя тот факт, что у нас нет type erasure.

Будет много constexpr-вычислений, шаблонов и новых возможностей C++17 (конкретно, if constexpr).

Дмитрий Соколов. Кодогенерация как рефлексия для бедных



Рефлексия часто нужна для обобщения алгоритмов сериализации. Реализация всевозможных протоколов, работа с базами данных. Для решения подобных задач нами был написан компилятор homebrew IDL для генерации C++ структур и библиотека для взаимодействия с полученным результатом. Protobuf с педалями и стоило ли оно того.

Daveed Vandevoorde. Reflective Metaprogramming in C++



Some time ago, the C++ standardization committee created a subgroup «SG-7» to explore how to add reflection capabilities to the language. More recently, that group has added «metaprogramming» to its plate and made some significant decisions regarding the shape of the eventual solution. In this talk the author looks at the past that brought us here and examine a possible path for C++'s first-class support of «reflective metaprogramming».

Dietmar Kuhl. Concept Based Testing



With concepts being added to the next revision of C++ it is expected that new concepts get defined. Each concept defines a set of operations used by generic code. One such use could be a generic test verifying that all parts of a concept are defined and checking generic interactions between a concept's operations. Ideally, such a test even works with classes only partially modelling a concept to guide the implementation of classes.

This presentation doesn't use the actual concept extensions but shows how generic tests can be created using features of C++17. For the generic tests the detection idiom and constexpr if are used to determine availability of required operations and gracefully dealing with the abseence of operations. The generic tests should be able to cover basics of classes modelling a concept. Obviously, specific behaviour for classes will still require corresponding tests.

Simon Brand. Modern C++ Parallelism from CPU to GPU



Parallel programming can be used to take advantage of multi-core and heterogeneous architectures and can significantly increase the performance of software. Modern C++ has gone a long way to making parallel programming easier and more accessible; providing both high-level and low-level abstractions. C++17 takes this further by providing high level parallel algorithms, and much more is expected in C++20. This talk gives an overview of the current parallelism utilities available, and looks to the future of how GPUs and heterogeneous systems can be supported through new standard library features and other standards like SYCL.

Андрей Карпов. Эффективный C++ (Маерс тут ни при чём :)



Язык C++ и инфраструктура вокруг него продолжает активно развиваться, что делает этот язык одним из самых эффективных инструментов в настоящее время. Хочется выделить три фактора, делающие язык C++ сейчас столь привлекательным.

  • Первое: нововведения в стандарт языка, позволяющие писать эффективный код.
  • Второе: зрелость инструментов разработки и увеличение скорости сборки проектов.
  • Третье: зрелось вспомогательного инструментария, позволяющие контролировать качество кода и другие аспекты жизненного цикла проекта.

Этот доклад — ода языку программирования C++!

Иван Пузыревский. Асинхронность в программировании



В области разработки высоконагруженных многопоточных или распределенных приложений можно все чаще услышать разговоры об асинхронном коде, в том числе спекуляции о необходимости (отсутствии необходимости) учитывать асинхронность в коде, о понятности (непонятности) асинхронного кода, о его эффективности (неэффективности). В данном докладе мы с вами попробуем более глубоко погрузиться в предметную область: разберем, что такое асинхронность; когда она возникает; как влияет на код, который мы пишем, и на язык программирования, который мы используем. Постараемся разобраться, при чем тут futures & promises, немного поговорим про корутины и акторы. Затронем JavaScript и операционные системы. Цель доклада — сделать более явными компромиссы, возникающие при том или ином подходе к разработке многопоточного или распределенного ПО.

Павел Булатов. Переход на WebAssembly: стоит ли игра свеч?



В докладе пойдет речь о современном состоянии WebAssembly применительно к реальным продуктам. Мы расскажем о нашем опыте портирования приложения, о том, какие проблемы при этом возникли и как мы их решили.

Будут затронуты темы:

  • Поддержки стандарта на разных платформах и браузерах.
  • Производительности и размера сборки в сравнение с asm.js.
  • Взаимодействия с браузером.
  • Сборки крешей с пользователя.
  • Особенностей VM.

Дмитрий Кожевников. Подводные камни CMake и где они обитают



Система сборки CMake постепенно становится стандартом де-факто для кроссплатформенного программирования на C++. Однако часто она подвергается справедливой критике, в том числе за неудобный скриптовый язык, устаревшую документацию и за то, что одни и те же задачи в ней можно выполнять разными способами, при этом понять, какой из них более правильный в конкретной ситуации, бывает довольно сложно. Автор расскажет:

  • частые популярные анти-паттерны и чем они плохи,
  • на каких уровнях абстрации работает CMake, и когда они «протекают»,
  • что такое «Modern CMake» и в чем его преимущества,
  • как локализовывать и отлаживать проблемы в CMake-скриптах (в том числе довольно экзотическими средствами).

Сергей Шамбир. Когда хорош процедурный C++



Чистая архитектура проекта, простые абстракции на каждом слое — мечта любой команды. Для воплощения этой мечты придумано много объектно-ориентированных приёмов. Увлекаясь ООП, разработчики забывают следить за чистотой кода на стыке C и C++. Именно здесь процедурный стиль поможет навести порядок, выстроить удобные и безопасные абстракции, которые легко стыкуются с объектно-ориентированным кодом проекта. Мы выясним:

  • почему вообще надо изолировать API на языке C (такой как winapi, POSIX, SQLite, OpenGL, OpenSSL)
  • почему ООП в этом деле работает плохо
  • как всё-таки написать слой абстракции поверх C-style API
  • как разобраться с колбеками, обработкой ошибок и управлением ресурсами, чтобы сделать традиционно сложный и запутанный код понятным даже для джуниора

Евгений Зуев. Semantic API for C++ programs



Сфера профессиональных интересов — семантика языков программирования, проектирование и реализация компиляторов ЯП и других языко-ориентированных инструментов. Среди наиболее значимых достижений — участие в таких проектах, как создание компилятора полного стандарта языка С++ (компания Интерстрон, Москва, 2000), реализация компилятора языка Zonnon для .NET (ETH Zurich, 2005), реализация прототипа компилятора Swift для платформы Tizen (Samsung Research Institute, Москва, 2015).

Ivan Cukic. 2020: A void_t odyssey



C++ has always had a powerful meta-programming sub-language which allowed library developers to perform magical feats like static introspection to achieve polymorhpic execution without inheritance. The problem was that the syntax was awkward and unnecessarily verbose which made learning meta-programming a daunting task.

With the recent improvements to the standard, and with the features planned for C++20, meta-programming has become much easier, and meta-programs became easier to understand and reason about.

In this talk, the author presents a few modern techniques of meta-programming, with main focus on the magical void_t meta-function.

Евгений Охотников. Акторы на C++: стоило ли оно того?



Автор доклада уже 16 лет отвечает за развитие Open-Source фреймворка SObjectizer. Это один из немногих живых и развивающихся, кросс-платформенных акторных фреймворков для C++. Разработка SObjectizer-а началась в 2002-ом году, когда C++ был в числе самых востребованных и распространенных языков программирования. За прошедшее время и C++ очень сильно изменился, и еще больше изменилось отношение к C++. В докладе речь пойдет о том, как эти изменения сказались на развитии инструмента с 16-летней историей и о том, насколько просто и удобно было делать такой инструмент для языка C++. Да и нужно ли было делать такой инструмент для C++ вообще.

Rainer Grimm. Best Practices for Concurrency in Modern C++



With the standardisation of C++11, we got in C++ a multithreading library and a memory model. The library has the basic building blocks such as atomics, threads, tasks, locks, and condition variables. The memory model provides guarantees for the thread-safe usage of this basic building blocks.

Seven years later, we have a lot of best practices to apply multithreading and the memory model in a safe way. The author's talk is precisely about these best practices to general rules for concurrency, special rules for the multithreading library, and special rules for the memory model. The focus of this best practices is far beyond C++.

Алексей Малов. Опыт применения современного C++ в разработке desktop-приложений



Доклад будет идти о средствах языка C++ и библиотек Boost и STL, а также об архитектурных подходах в построении приложений с графическим интерфейсом пользователя, которые мы применяли в разработке инструмента для создания видео-уроков.

  • Практика использования паттерна Model-View-Presenter
  • Управление жизненным циклом документа
  • Файловое хранилище на умных указателях

Сергей Васильев. Статический анализ: ищем ошибки… и уязвимости?



Новости об очередной найденной уязвимости регулярно всплывают то тут, то там. Сопутствующие потери $, как правило, колоссальны. Поэтому вместо исправления уязвимостей, следует не допускать их появления.

Один из способов борьбы с ошибками в коде — использование статического анализа. Но насколько он подходит для поиска уязвимостей? И так ли велика разница между простыми ошибками и уязвимостями с точки зрения кода?

Эти вопросы мы и обсудим в ходе доклада, а заодно поговорим о том, каким образом использовать статический анализ так, чтобы извлечь из него максимум пользы.

P.S.

От себя хочу обратить ваше внимание на мини-интригу вокруг std::string, связанную с докладами моего коллеги Андрея Карпова. Итак, по порядку:

  1. Фрагмент доклада Андрея (C++ Russia 2016) «Приватные байки от разработчиков анализатора кода» с 30:05 — ссылка.
  2. Лёгкий троллинг таких как мы от Антона Полухина (C++ Russia 2017) в докладе «Как делать не надо: C++ велосипедостроение для профессионалов» с 2:00 — ссылка.
  3. Рассказ Андрея на конференции C++ Russia 2018, что мы не динозавры и учимся новому: «Эффективный C++» с 12:21 — ссылка.

На этом всё! Приятного знакомства с докладами.

Комментарии (1)


  1. Andrey2008
    30.07.2018 21:56

    Обсуждение докладов на сайте www.linux.org.ru. Давайте и здесь в комментариях поделимся впечатлениями, обсудим, каких докладов хочется больше, что показалось интересным и так далее.