- PVSM.RU - https://www.pvsm.ru -
Несколько недель назад состоялась встреча международного комитета по стандартизации C++. На ней люди (в основном) не разменивались на мелочи и совершили несколько больших шагов на пути к С++20.
Главные новости:
Что всё это значит, как это упростит написание кода и что было ещё — читайте под катом.
Замечательная вещь под названием Concepts внесена в черновик будущего стандарта С++20. Это большая радость для разработчиков обобщенных библиотек, использующих идиому SFINAE.
v += data;
v -= data;
v *= data;
v /= data;
Задача — написать функции `*_optimal`, которые используют версию `*_fast`, если это возможно:
#include <iostream>
template <class Container, class Data>
void compute_vector_fast(Container& v, const Data& data) {
std::cout << "fastn";
// ...
}
template <class Container, class Data>
void compute_vector_slow(Container& v, const Data& data) {
std::cout << "slown";
// ...
}
template <class Container, class Data>
void compute_vector_optimal(Container& v, const Data& data) {
// ??? call `compute_vector_slow(v, data)` or `compute_vector_fast(v, data)` ???
}
Без концептов эта задача, например, решается через `std::enable_if_t` и множество нечитаемого шаблонного кода.
С концептами всё намного проще:
#include <iostream>
template <class T, class Data>
concept bool VectorOperations = requires(T& v, const Data& data) {
{ v += data } -> T&;
{ v -= data } -> T&;
{ v *= data } -> T&;
{ v /= data } -> T&;
};
template <class Container, class Data>
requires VectorOperations<Container, Data>
void compute_vector_optimal(Container& v, const Data& data) {
std::cout << "fastn";
}
template <class Container, class Data>
void compute_vector_optimal(Container& v, const Data& data) {
std::cout << "slown";
}
Концепты позволяют:
С концептами уже можно поэкспериментировать в GCC, если использовать флаг -fconcepts, например, тут [1]. Вот последний доступный proposal на Concepts [2].
Ranges увидят свет в виде технической спецификации. Это значит, что поэкспериментировать с ними можно будет еще до C++20.
С Ranges можно писать `sort(container)` вместо `sort(container.begin(), container.end())`, нужно только заиспользовать нужный namespace.
А еще расширяются возможности стандартных алгоритмов. Например, можно искать быстрее, если мы точно знаем, что элемент содержится в контейнере:
#include <vector>
#include <experimantal/ranges/algorithm>
namespace ranges = std::experimental::ranges;
int main () {
// Функция get_some_values_and_delimiter() фозвращает вектор,
// в котором гарантированно есть число 42
std::vector<int> v2 = get_some_values_and_delimiter();
// Необходимо найти число 42 и отсортировать все элементы, идущие после него:
auto it = ranges::find(v.begin(), ranges::unreachable{}, 42);
ranges::sort(++it, v.end());
}
Нечто подобное Александреску делал для получения супербыстрого поиска [3].
Любителям SFINAE и обобщённых библиотек Ranges тоже принесут счастье, так как они определяют огромное количество концептов: Sortable, Movable, Copyable, DefaultConstructible, Same…
Можно поэкспериментировать, скачав библиотеку отсюда [4]. Вот последний доступный черновик Ranges [5].
Все, что необходимо для работы с сокетами (в том числе для асинхронной работы), будет выпущено в эксперимент еще до C++20. В основе Networking TS лежит доработанный и улучшенный ASIO.
Вот пара приятных различий:
Можно поэкспериментировать, скачав библиотеку отсюда [8]. Вот последний доступный черновик Networking [9].
Сопрограммы — это «возможность сохранить текущий стек, переключиться на другой стек и поработать там, а потом вернуться». В основном они используются для создания генераторов и асинхронной работы.
Самый смак получается, если смешать Coroutines TS и Networking TS. Тогда вместо асинхронного нечитабельного кода на +100 строк [10] можно получить то же самое, но на 40 строк:
#include <ctime>
#include <iostream>
#include <string>
#include <experimental/net>
using net = std::experimental::net;
using net::ip::tcp;
std::string make_daytime_string() {
using namespace std; // For time_t, time and ctime;
time_t now = time(0);
return ctime(&now);
}
void start_accept(net::io_context& io_service) {
tcp::acceptor acceptor{io_service, tcp::endpoint(tcp::v4(), 13)};
while (1) {
tcp::socket socket(acceptor.get_io_service());
auto error = co_await acceptor.async_accept(socket, net::co_future);
if (error) break;
std::string message = make_daytime_string();
auto& [error, bytes] = co_await async_write(
socket, net::buffer(message), net::co_future
);
if (error) break;
}
}
int main() {
net::io_context io_service;
io_service.post([&io_service](){
try {
start_accept(io_service);
} catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
}
});
io_service.run();
}
Но вот плохие новости: такую интеграцию Coroutines TS и Networking TS в стандарт еще не привнесли. Пока что придется реализовывать ее самим.
С сопрограммами уже можно поэкспериментировать в CLANG-6.0, если использовать флаги -stdlib=libc++ -fcoroutines-ts, например, тут [11]. Вот последний доступный черновик Coroutines [12].
Подготовлен черновик TS. Дело сдвинулось и есть шансы увидеть модули уже в течение года!
Однако, как правило, вы используете одни и те же заголовочные фалы в каждом файле cpp. За счет того, что компиляция различных файлов cpp никак не связана друг с другом, при каждой компиляции компилятор разбирает одни и те же заголовочные файлы снова и снова. Именно этот разбор и тормозит (заголовочный файл iostream весит более мегабайта, подключите 20 подобных заголовочных файлов — и компилятору придётся просмотреть и разобрать около 30 мегабайт кода при компиляции одного файла cpp).
И тут на сцену выходят модули! Модуль — это набор файлов, собранных воедино и сохранённых на диск в понятном для компилятора бинарном виде. Таким образом, при подключении модуля компилятор просто считает его с диска в свои внутренние структуры данных (минуя этапы открытия нескольких фалов, парсинга, препроцессинга и некоторых другие вспомогательные этапы).
Дополнительный прирост скорости при компиляции будет получен за счёт того, что в модуле вы явно указываете его публичный интерфейс. То есть компилятор сможет сделать ряд оптимизаций ещё при создании модуля. Это сильно уменьшит затраты оперативной памяти, ускорит поиск подходящих перегруженных функций, структур и т. д. за счёт того, что их попросту будет меньше.
И наконец, финальная стадия сборки проекта — линковка. В данный момент линковщик может тратить много времени на выкидывание одинаковых блоков скомпилированного кода (вы написали функцию inline/force_inline, 100 раз её использовали, а компилятор решил её не встраивать — линкер выкинет 99 скомпилированных тел вашей функции и оставит одно). С модулями такого происходить не должно, поскольку файл модуля не будет «вкомпиливаться» внутрь собранного файла cpp.
Модули в черновике не экспортируют макросы, поэтому будет сложновато использовать их для системных файлов с множеством макросов (`<windows.h>`, я на тебя намекаю!) и поддерживать код, использующий модуль и его старый заголовочный файл (если у вас std::string описан в модуле и в заголовочном файле , то при подключении модуля и заголовочного файла будет multiple definitions, поскольку макрос для include guards не экспортируется из модуля). Это как раз такие модули, за которые вы проголосовали в прошлом посте [13] (ваши голоса мы донесли до комитета).
Вот последний доступный черновик Modules [14].
В C++20 можно будет инициализировать bitfields в описании класса:
struct S {
unsigned x1:8 = 42;
unsigned x2:8 { 42 };
};
Можно будет понимать платформы endianness стандартными методами:
if constexpr (std::endian::native == std::endian::big) {
// big endian
} else if constexpr (std::endian::native == std::endian::little) {
// little endian
} else {
// mixed endian
}
Можно будет инициализировать поля структур, прям как в чистом C:
struct foo { int a; int b; int c; };
foo b{.a = 1, .b = 2};
У лямбд можно будет явно указывать шаблонные параметры:
auto bar = []<class... Args>(Args&&... args) {
return foo(std::forward<Args>(args)...);
};
На встречу в Торонто мы ездили с несколькими предложениями:
Кроме того, ряду библиотек уже нужны constexpr-функции: [1] [23], [2] [24].
Вдобавок нас попросили представить комитету два предложения, непосредственно над написанием которых мы не работали:
Обсуждали предложение по форматированию текста [27], и многим понравились предлагаемые возможности (вероятно, потому, что людям нравится Python):
fmt::format("The answer is {}", 42);
Обсуждали ring_span [28], который по функциональности напоминает boost::circular_buffer, но не владеет элементами (является view над контейнером).
На подходе битовые операции [29]. Когда их примут, правильным ответом на вопрос «Как подсчитать количество выставленых битов в переменной X?» на собеседованиях станет «std::popcount(X)».
РГ21 планирует в ближайшее время написать предложения на std::stacktrace (в качестве прототипа послужит Boost.Stacktrace [30]), доработать предложение на std::shared_library [31] и на экспорт символов из динамических библиотек [32].
Если у вас есть идеи для C++20, если вы нашли проблемы в C++17/14/11 либо просто хотите подстегнуть разработку той или иной фичи C++ — заходите на сайт рабочей группы stdcpp.ru [33]. Добро пожаловать!
Есть желание помочь с написанием предложений и внести своё имя в историю? Мы подготовили мини-инструкцию по написанию предложений [34].
Автор: antoshkka
Источник [35]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/programmirovanie/264609
Ссылки в тексте:
[1] тут: https://wandbox.org/permlink/6pGDtvfgJVA7wZ3w
[2] последний доступный proposal на Concepts: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/n4674.pdf
[3] супербыстрого поиска: http://www.drdobbs.com/generic-efficient-generic-sorting-and-se/184403841
[4] отсюда: https://github.com/ericniebler/range-v3
[5] последний доступный черновик Ranges: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/n4685.pdf
[6] функциональный объект с unique_ptr: https://github.com/apolukhin/Boost-Cookbook/blob/second_edition/Chapter06/02_tasks_processor_timers/tasks_processor_timers.hpp#L17
[7] для callback: https://github.com/apolukhin/Boost-Cookbook/blob/second_edition/Chapter06/02_tasks_processor_timers/tasks_processor_timers.hpp#L52-L59
[8] отсюда: https://github.com/chriskohlhoff/networking-ts-impl
[9] последний доступный черновик Networking: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/n4656.pdf
[10] асинхронного нечитабельного кода на +100 строк: http://www.boost.org/doc/libs/1_65_0/doc/html/boost_asio/tutorial/tutdaytime3/src.html
[11] тут: https://wandbox.org/permlink/xdT68y89s6YL4B1P
[12] последний доступный черновик Coroutines: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/n4663.pdf
[13] прошлом посте: https://habrahabr.ru/company/yandex/blog/323972/
[14] последний доступный черновик Modules: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/n4689.pdf
[15] РГ21: https://habrahabr.ru/company/yandex/blog/301514/
[16] P0652R0: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0652r0.html
[17] P0539R1: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0539r1.html
[18] P0639R0: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0639r0.html
[19] P0415R0: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0415r0.html
[20] предложением на constexpr для стандартных алгоритмов: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0202r1.html
[21] рефлексии: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0194r4.html
[22] метаклассов: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0707r0.pdf
[23] [1]: https://github.com/boostorg/type_index/blob/develop/include/boost/type_index/detail/compile_time_type_info.hpp#L105-L140
[24] [2]: https://github.com/apolukhin/magic_get/blob/develop/include/boost/pfr/detail/size_array.hpp#L16
[25] P0457R0: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0457r0.html
[26] P0458R0: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0458r0.html
[27] предложение по форматированию текста: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0645r0.html
[28] ring_span: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0059r4.pdf
[29] битовые операции: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0553r1.html
[30] Boost.Stacktrace: http://boost.org/libs/stacktrace/
[31] предложение на std::shared_library: https://stdcpp.ru/proposals/b061944a-dc76-431d-ac57-5832978d63aa
[32] экспорт символов из динамических библиотек: https://stdcpp.ru/proposals/feb5244f-f6a9-4cc0-ae30-f6b549d2d6c9
[33] stdcpp.ru: https://stdcpp.ru/
[34] по написанию предложений: https://stdcpp.ru/podgotovka-predlozheniya-v-standart-c-instruktsiya
[35] Источник: https://habrahabr.ru/post/336264/
Нажмите здесь для печати.