Comments 224
Ну можно например внести гигиенические макросы аля Rust, хоть они тоже не идеальные, но намного мощнее и при этом безопаснее.
А расскажите поподробнее, как вы видите подобные макросы в C++?
т.е. без конфликтующего синтаксиса, breaking changes и чтобы был прок
Мне казалось, что функции с восклицательным знаком запрещены в плюсах. Соответственно их никто не может писать, а если на них сделать макросы, то нигде совместимость не сломается
Вот, например, я хочу написать один шаблон с большим количеством кода, но обрабатывающий либо 1 элемент без списка либо множество элементов из списка и функция обхода элементов списка сама по себе сложная. Так вот макросом или внутренними директивами компилятора я могу не плодить адовую копипасту, а вот на C++ даже с шаблонами и последним стандартом до сих пор не могу, точнее могу могу но костыли, вроде, sfinae не выглядят решением ибо это костыли, ужасные костыли, а я хочу писать, примерно так:
<typename T>
... some_func(..., T x, ...)
...
if (exist(x.getList))
{
... код который даже не скомпилируется без метода getList у типа T
}
...
...
Всё упирается в элементарное if этапа компиляции которое могло бы выключить кусок функции если у нас в классе есть метод для получения одного элемента и нет метода для получения коллекции.
P.S. в идеале хотелось бы даже шаблон не создавать, но это уже совсем мечты.
template <class T>
concept Listable = requires(T x) {
x.getList();
};
<typename T>
... some_func(..., T x, ...)
...
if constexpr(Listable<T>)
{
... код который даже не скомпилируется без метода getList у типа T
}
...
...
// the__if_exists_statement.cpp
// compile with: /EHsc
#include <iostream>
template<typename T>
class X : public T {
public:
void Dump() {
std::cout << "In X<T>::Dump()" << std::endl;
__if_exists(T::Dump) {
T::Dump();
}
__if_not_exists(T::Dump) {
std::cout << "T::Dump does not exist" << std::endl;
}
}
};
class A {
public:
void Dump() {
std::cout << "In A::Dump()" << std::endl;
}
};
class B {};
bool g_bFlag = true;
class C {
public:
void f(int);
void f(double);
};
int main() {
X<A> x1;
X<B> x2;
x1.Dump();
x2.Dump();
__if_exists(::g_bFlag) {
std::cout << "g_bFlag = " << g_bFlag << std::endl;
}
__if_exists(C::f) {
std::cout << "C::f exists" << std::endl;
}
return 0;
}
Вот именно такое я хочу в стандарте, но даже в 20 этого не будет. Такую замену макросов хочется ^_^
А вот чего не хватает концептам, так это ключевого слова `implements` (как в Java/C#), чтобы контроль можно было осуществлять при определении сущности, а не при её использовании.
Вещи, связанные с удобным для программиста генерированием повторяющегося исходного кода.
Можете привести пример кода, который невозможно сгенерировать с помощью шаблонов и constexpr?
А вот constexpr — пожалуйста:
github.com/hanickadot/compile-time-regular-expressions
А как их можно макросами реализовать?Плюсовыми — никак. А вот с макросами вроде Rust-овских или Nermele-овых вполне себе.
github.com/hanickadot/compile-time-regular-expressionsТак затем в рантайме все равно приходится работать со string-view и формировать из результатов разбора нужные значения.
Вот на продвинутых макросах (или на D-шных CTFE+mixin), гипотетически, можно получить что-то вроде:
auto [year, month, day] = $super-puper-macro("^(?<year:short>[0-9]{4})/(?<month:byte>[0-9]{1,2}+)/(?<day:byte>[0-9]{1,2}+)$").match(s)
где year будет иметь тип int, а month и day — тип byte.
if( error ) return error;
и прочие модификации control-flow?Конкатенация строк в названиях переменных?
Можете привести пример кода, который невозможно сгенерировать с помощью шаблонов и constexpr?
#define LIST(F)F(int,a)F(bool,b)F(char,c)F(string,d)
struct t_foo{
#define F(TYPE,NAME)TYPE NAME;
LIST(F)
#undef F
void use_func1(){
#define F(TYPE,NAME)::func1(#TYPE,this->NAME);
LIST(F)
#undef F
}
void use_func2(){
#define F(TYPE,NAME)::func2(#TYPE,this->NAME,#NAME);
LIST(F)
#undef F
}
};
#undef LIST
habr.com/post/243581
Стандартная либа в таких проектах обычно не используется, а вот все плюшки языка доступны
В комитете присутствующие embedded разработчики были вне себя от радости. Теперь можно легко заставлять компилятор вычислять что-то только на этапе компиляции, без попадания этого в рантайм.
youtu.be/PDSvjwJ2M80
Из-за того что обработка исключений генерирует code-bloat, и появился
www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0709r0.pdf
очередной метод обработки ошибок. С текущей реализацией STL под bare metal очень и очень слабо юзабелен.
Я сам мудохался с тем чтобы под вполне себе комфортные 8мб на плюсах писать. Да, в итоге все же остановился на коде с поддержкой исключений, но какие же они жирные…
Dan Saks потрясающее видео на эту тему сделал:
www.youtube.com/watch?v=D7Sd8A6_fYU
Можно сколько угодно распинаться о преимуществах С++, усилия будут тщетны, пока разработчик на С не будет слышать то что лично ЕМУ нужно.
Классы? Деструкторы им, как правило, не нужны, потому что память чаще всего статически выделяется. Две с половиной виртуальных функции можно и руками вызвать через указатели или switch. Писать аргумент, передаваемый в местном аналоге ECX, они привыкли справа от названия функции, а не слева, так что синтаксический сахар тоже мимо.
Ну то есть какие-то преимущества у плюсов есть, но ни одной киллер-фичи конкретно для эмбеда нет. При том что плюсы намного, НАМНОГО сложнее как язык.
Но вот с какого перепугу вы решили отказаться от шаблонов и классов? Про их оверхэд можете рассказать?
«шаблоны раздувают код» (при этом заменяют шаблоны макросами, которые так же инстанцируются по месту и раздувают код точно не меньше).Но это вы почему-то игнорируете.
Первые 10 лет её вообще никто не понималОтучаемся говорить за всех. И про понимание, и про юзание велосипедов с виртуальными функциями.
В 90-е с шаблонами главная проблема была в том, что не во всех компиляторах они поддерживались. Я, например, начал использовать шаблоны где-то с 1995-го. Но года до 1998-го или даже до 2000-го приходилось оглядываться на компиляторы, в которых шаблонов не было вообще.
code::dive conference 2015 — Bartosz Szurgot — C++ vs C the embedded perspective
там показан пример, как раз на основе шаблона, который уменьшает размер бинарника ( часть кода становится просто не нужна), и увеличивать скорость работы по этой же причине ( проверки if удаляются за ненадобностью)
- RAII (да, даже в embedded надо порой чистить ресурсы)
- constexpr и conseval
- алгоритмы ( и их можно взять из STL, они на исключения и RTTI не завязаны, кроме пары исключений)
- constexpr и consteval
- ООП (да, внезапно c ООП код получается более читаемый)
- Ссылки
- Type safety
- Частичное невелирование проблем а аслиасингом — большая производительность и меньший по размеру код
- ??? (наверняка что-то забыл)
RAII — отличная концепция… для языка с исключениями, но без finally. Так-то ресурсы можно и в процедурном стиле почистить.
Когда в эмбед завезут constexpr и особенно consteval — ещё неизвестно, а макросы вот они тут.
Алгоритмы — это книжка Кормена сотоварищи.
В ООП стиле можно писать и на С (про сахар см. выше замечание про расположение аргумента this).
Type safety сводится к тому, что у вас есть три с половиной типа: int, int*, (int*)() и иногда struct*. Структуры дадены системными библиотеками и железом, и прикрутиться к ним с этими вашими классами не так-то просто.
Отсутствие ссылок компенсируется тем, что nullptr указывает на вполне себе доступную область памяти.
Ну в общем всё перечисленное это клёво, модно, молодёжно, но киллер-фичей не является (кроме м.б. consteval и таки алгоритмов).
RAII — отличная концепция… для языка с исключениями, но без finally. Так-то ресурсы можно и в процедурном стиле почистить.
…
Алгоритмы — это книжка Кормена сотоварищи
зачем делать 100 раз то, что можно сделать (как правило, сделали за тебя) единожды?
Когда в эмбед завезут constexpr и особенно consteval — ещё неизвестно, а макросы вот они тут.
берем обычный gcc, ставим таргетом контроллер, получаем с++17 уже сегодня.
Структуры дадены системными библиотеками и железом, и прикрутиться к ним с этими вашими классами не так-то просто.
их можно обернуть и забыть о том, что это сырые структуры
Отсутствие ссылок компенсируется тем, что nullptr указывает на вполне себе доступную область памяти.
по стандарту nullptr (в отличие от NULL) может указывать только на заведомо недоступную область памяти
зачем делать 100 раз то, что можно сделать (как правило, сделали за тебя) единожды?
Далеко не все алгоритмы работают со статическими массивами языка С. А всякие сбалансированные деревья стиральной машине не нужны.
берем обычный gcc, ставим таргетом контроллер, получаем с++17 уже сегодня.
Грустное ха-ха три раза. ДАЖЕ если gcc знает целевой машинный код (что не факт), это ещё ничего не значит. Потому что набор железа, конфигурация памяти, соглашение о вызовах, нестандартная линковка, системные либы на несовместимом с GCC диалекте — всё это превращает затею в скачку по граблям. Как правило, у вас есть компилятор от производителя, который принимает «что-то похожее на Си», а всё остальное от лешего.
их можно обернуть и забыть о том, что это сырые структуры
Дети, не слушайте дядю, он говорит глупости.
В классе, обёрнутом вокруг системной структуры, вы:
1. не можете использовать наследование
2. не можете использовать полиморфизм, ни виртуальный, ни просто перегрузку
3. инкапсуляцию использовать можете, но это вам ничего не даст
4. не можете использовать деструкторы
5. и final у вас тоже нет, не завезли.
Поясняю.
Допустим, вы воткнули в класс-обёртку виртуальную функцию.
Это значит, что перед структурой появился указатель vptr на таблицу виртуальных функций.
Системные либы про него ничего не знают, и тупо копируют память без вашего vptr.
В итоге вам рано или поздно вернут объект, у которого мусор вместо vptr (и об этом никто не знает). Пояснять, чем закончится вызов метода?
Что хуже, вы можете даже не понять, что у вас появился vptr. Это может быть перегруженная внешняя функция, например. В двух вариантах, с конверсией типа аргумента и без. Возможно шаблонная, да. Просто заинклюдили header-only библиотечку и ага.
С дополнительными полями аналогично, только растут они в памяти сверху, а не снизу. Но точно также могут потеряться внутри системы, или вы можете затереть память, выделенную системой.
Теперь допустим вы огородили поля данных модификатором private, нагородили геттеры-сеттеры и добавили в них логику. И до кучи влепили деструктор.
Беда в том, что системные либы не знают про ваши сеттеры и деструкторы. Т.е. они в ваших тестах вызываются, а в процессе прохождения через либу — нет. Счастливой отладки!
по стандарту nullptr (в отличие от NULL) может указывать только на заведомо недоступную область памяти
Ага, ага, по стандарту.
Допустим, у вас всего 256 байт (не мега- и не кило, просто байт) ОЗУ. Как думаете, можно ли читать адрес 0х00?
Кажется другой дядя не умеет в ООП. Прятать стрёмные системные вызовы за красивыми интерфейсом весьма просто.
Было:
int fd = open("light_device");
light_settings s{0xf1, 100};
ioctl(fd, SET_LIGHT, &s);
close(fd);
Стало:
Light lamp("light_device");
lamp.set(Green, 10_kHz);
Виртуальными функциями и правда не воспользовались… Если у вас в руках молоток, это не значит что всё вокруг становится гвоздями.
Вот допустим, что в 99% случаев мне нужно зажечь лампочку в одном месте (допустим, сейчас), а погасить в непредсказуемом другом. По асинхронному таймеру, или если юзер нажмёт кнопку, или (здесь большой список или).
Зачитайте, пожалуйста, все преимущества RAII и ООП в обозначенном мной случае.
Расскажите про строчку close(fd).Вы сперва расскажите как в вашем гипотетическом сценарии будете передавать fd из места, где вызвали open() в место, где вы хотите вызвать close().
Поскольку вам привели самодостаточный пример. Вы его пытаетесь во что-то преобразовать, но не показываете во что именно.
Дальше она равна 0 если файл закрыт или не 0 если открыт. Проблем с гонками сигнала нет, т.к. можно безопасно закрывать закрытый или даже некорректный номер файла.
fd будет глобальной статической переменной.А теперь представьте, что у вас lamp будет глобальной переменной.
При этом, представьте, у вас может быть шаблонный класс Light, который настраивается политикой поведения: RAII или не RAII. Например, у вас может быть:
static Light<NO_RAII> red_lamp;
...
void turn_on() {
red_lamp.open("/device/red_lamp");
red_lamp.on();
Light<RAII> green_lamp{"/device/green_lamp", Light<RAII>::on};
...
}
void shutdown() {
if(red_lamp.is_on()) {
red_lamp.off();
red_lamp.close();
}
...
}
И для red_lamp у вас не будет ни конструктора, ни деструктора. А для green_lamp будет и то, и другое. При этом накладные расходы будут такие же, как случае, если бы вы все это реализовывали ручками на чистом C.
А если вам нужна статическая переменная (вы именно так предлагаете решать проблему ниже в коментариях) — делайте статическую lamp, никто вам не мешает.
Далеко не все алгоритмы работают со статическими массивами языка С. А всякие сбалансированные деревья стиральной машине не нужны.
«со статическими массивами языка С» работают std::begin/std::end, возвращающие достаточно для передачи в любой алгоритм. Проблемы могут быть разве что с std::sort, выделяющим память
Как правило, у вас есть компилятор от производителя, который принимает «что-то похожее на Си», а всё остальное от лешего.
тогда и вопрос не стоит. Мы же всё-таки говорим о тех случаях, когда можно использовать с++ вместо си.
Поясняю.
Допустим, вы воткнули в класс-обёртку виртуальную функцию.
Это значит, что перед структурой появился указатель vptr на таблицу виртуальных функций.
Системные либы про него ничего не знают, и тупо копируют память без вашего vptr.
я вас здесь остановлю и приведу минимальный пример, демонстрирующий где вы неправы: при касте указателя на наследник к указателю на базовый класс адрес может меняться, и он будет указывать на корректный инстанс базового класса. А если «системная либа» вдруг лезет в память вокруг переданного ей объекта, в ней UB
Да и оборачивать надо с умом. Например, можно не наследованием, а композицией. По-хорошему, небезопасные сишные кишки в плюсовом коде лучше полностью обернуть, от греха подальше. Благо обычно это можно сделать без оверхеда.
«со статическими массивами языка С» работают std::begin/std::end
Да?! Ну вот у меня есть указатель wtf*, поясните, как будет работать std::end.
при касте указателя на наследник к указателю на базовый класс адрес может меняться, и он будет указывать на корректный инстанс базового класса
простите, вы не понимаете, как работает С++. Указатель на инстанс будет корректный, но только, ещё раз подчеркну, только с точки зрения конкретного компилятора С++.
А если «системная либа» вдруг лезет в память вокруг переданного ей объекта, в ней UB
Нет, с системной либой всё ок, это у вас в плюсовой программе будет даже не UB, а просто баг.
Да и оборачивать надо с умом. Например, можно не наследованием, а композицией.
Не например, а единственный рабочий вариант. Только оверхед получается немаленький. Проще node.js воткнуть например.
Да?! Ну вот у меня есть указатель wtf*, поясните, как будет работать std::end.
статический массив языка си, о котором шла речь, это не wtf*. Впрочем, указатель является корректным random access итератором. begin =wtf, end = wtf + size. Вы же знаете размер статического массива?
простите, вы не понимаете, как работает С++. Указатель на инстанс будет корректный, но только, ещё раз подчеркну, только с точки зрения конкретного компилятора С++.
Нет, с системной либой всё ок, это у вас в плюсовой программе будет даже не UB, а просто баг.
А можете привести пример? Пока что я не в состоянии интерпретировать ваши утверждения из предположения что вы правы
Не например, а единственный рабочий вариант. Только оверхед получается немаленький. Проще node.js воткнуть например.
компиляторы с++ ныне более чем способны справиться с инлайнингом пары функций
для языка с исключениями, но без finally.Внезапно, RAII помогает и для обычного случая return, и для обычного break.
А про finally. Его что, уже в стандарт C завезли?
Type safety сводится к тому, чтоу вас void* не будет автоматически каститься к чему угодно.
Вот вам типичный код на embedded:
set_light(LIGHT_GREEN, 10);
Читается как-то не очень, а ещё оба параметра на вход имеют тип int. Ошибиться крайне легко.
А вот как это можно написать в C++
set_light(Green, 10_kHz);
Получается намного читаемее, да и компилятор не даст вам перепутать порядок аргументов:
enum Color: unsigned short { Red = 0x1, Green = 0xF1, };
enum Frequency: unsigned {};
constexpr Frequency operator"" _kHz(unsigned val);
По остальному уже отписались… Отдельно замечу, что писать алгоритмы в C стиле — это плохо для оптимизатора (см бенчмарки qsort) и как правило нечитаемо.
Поддерживаю antoshkka хотя бы в том, что С++ компилятор может дать большую типобезопасность без увеличения размера выходного кода (даже с отключенными исключениями и RAII).
Может быть, вышеприведенные примеры и не убедят закоренелого сишника, но по крайней мере помогут человеку, который уже знаком с обоими языками, и думает на чем писать проект под embedded — на C или на урезанном C++.
Ну а что плюсы могут дать эмбеду, если stl не брать, шаблоны не использовать, исключения отключить?
из stl всё еще остается хороший пласт header-only библиотек/классов, навскидку алгоритмы, утилитарные классы аля tuple/pair/variant/optional/bitset, более широкая и консистентная мат. библиотека. И не забывайте про фичи самого языка — всякие range-based for, constexpr, лямбды, перегрузки, инициализация по умолчанию. Опять же, шаблоны могут многое из того чего не умеют макросы
Классы? Деструкторы им, как правило, не нужны, потому что память чаще всего статически выделяется
и вдруг возникают сценарии типа «вот здесь на входе подними бит, а на выходе опусти», в которых RAII превосходно себя показывает
Ну то есть какие-то преимущества у плюсов есть, но ни одной киллер-фичи конкретно для эмбеда нет
Например, типобезопасность — нет нужды постоянно кастить указатели из/в void*/char*. Это дает компилятору/анализатору больше информации о данных и позволяет лучше диагностировать ошибки и оптимизировать.
При том что плюсы намного, НАМНОГО сложнее как язык.
но писать на них код во столько же раз проще, на подмножестве языка едва превосходящим по объему си.
ембед сишники вообще народ сложный.
А ещё очень многие не осилили, притом не только C++, но и C до конца, у них «компилятор код ломает» поэтому они дебаг вместо релиза собирают, а читать про volatile им также лень как и C++ изучать.
А проблему вы верно указали — поведение std::varaint<int, int&> при присваиваниях будет определяться внутренним содержимым. Это показалось странноватым комитету, и решили не поощрять подобное и не переусложнять класс variant. Те кому всё же нужно подобное поведение всегда могут воспользоваться reference_wrapper
И соответственно, раз компилятор о ней всё знает, то он может и отслеживать тип переменной, даже если она передаётся по ссылкам на базовый класс. Ну а раз так, то почему бы не разрешить ему и dynamic_cast делать:
struct base {
constexpr virtual ~base() = default;
};
struct forty_two: public base {
constexpr int say_42() const { return 42; }
};
constexpr int try_say(const base& b) {
forty_two* p = dynamic_cast<forty_two*>(&base);
if (p) {
return p->say_42();
}
return 0;
}
constexpr void do_something() {
constexpr forty_two ft{};
static_assert(try_say(ft) == 42);
}
void do_something() {
/*constexpr*/ forty_two ft{};
// Следующая строчка не соберётся. Компилятор будет жаловаться,
// что переменная `ft` не известна на этапе компиляции и он не может
// выполнить `try_say` как constexpr функцию.
static_assert(try_say(ft) == 42);
}
А про не ловить? Вот если в вашем примере заменить dynamic_cast с указателей, на ссылки и приводить не к тому типу, да еще и try/catch-ем не обложить, что будет?
Или dynamic_cast на ссылках нужно обязательно заварачивать в try/catch блок, при чем в месте вызова?
По сути все идет к тому что в каждом компиляторе C++ будет встроенный интерпретатор C++. Интересный путь развития…
Это ведь достаточно редко нужно, а так clang, вроде, достаточно модульный, чтобы его библиотеки использовать в своём коде.
У такого подхода есть большой плюс. Функции, которые генерят строки для mixin-а, — это обычные функции и их можно обычным образом отлаживать и покрывать тестами. Поэтому написать генератор кода для какого-то своего DSL не сложно.
Но есть и проблема, как говорят D-шники: если в mixin отдали сгенерированный в compile-time текстовый фрагмент, например, такого вида:
class Generated {
void f() {...}
void g() {...}
}
а потом в программе написали:auto obj = new Generated;
obj.g();
то войти отладчиком внутрь Generated.d() пока нельзя. Но это особенность текущей реализации D-шных компиляторов, они не генерируют отладочную информацию для таких случаев.
В C++ часть того, что можно делать на текстовых mixin-ах из D, может стать доступной через метаклассы. Но, наверное, не все.
вот это вроде, долго чет искал) Таймкод с пруфом про «надо ребейзить» не подскажу. Если я ошибся, то может и не это видео вовсе. Извините в случае чего.
До их рассмотрения дело не дошло.Жаль. Видимо, придется ждать C++23 или даже C++26.
Зато успели немного пообсуждать монадические интерфейсы для std::optional.Дурное дело не хитрое.
Шутка :)
// Следующие две строчки могут пагубно повлиять на рассудок:
assert(c_auto[4] == 11); // не константно
assert(c_auto[5] == 11); // константно
из примера про is_constant_evaluated? Почему так?
Может ли к с++20 constexpr приехать в математические функции стандартной библиотеки? Просто глупо получается, что после всех притянутых в стандарт constexpr-фич попытка буквально «что-нибудь посчитать на этапе компиляции» спотыкается об какой-нибудь std::sin
Основная проблема — это имплементация. У GCC всё хорошо в этом месте, а вот Clang не может воспользоваться библиотекой от GCC для математических вычислений на этапе компиляции — лицензия не позволяет. А написать подобную библиотеку самим — это огромные трудозатраты (приблизительно сотня или тысяча человеколет).
Так что когда примут в стандарт, некоторые компиляторы часть функций не смогут сделать constexpr в течение длительного времени.
const int v_runtime0 = __builtin_is_constant_evaluated() ? in : 11;
const int v_runtime1 = __builtin_is_constant_evaluated() ? 11 : in;
Так вот, если для v_runtime0 функция is_constant_evaluated вернёт true, то получится что v_runtime0 надо подсчитать константно с помощью рантайм переменной. Это что-то невозможное, поэтому тут возвращается false.
Для v_runtime1 если функция is_constant_evaluated вернёт true, то никаких противоречий нет и можно подсчитать константно.
Как справедливо заметили в чатике t.me/ProCxx, решать надо как в загадках про лжеца: «могло быть в первом ответ y? нет, потому что было бы противоречие».
К C++20 многие (и я в их числе) хотят увидеть в стандарте контейнеры, которыми можно пользоваться на этапе компиляции.
А можно юзкейсов немного? А то я пока даже в принципе не понимаю зачем нужны constexpr. constexpr контейнеры же для меня вообще rocket-science какой-то.
void foo() {
static const std::string date_time_fmt = "DATE: YYYY MM DD TIME: BLA-BLA";
}
Другими словами, вся инициализация будет происходить на этапе компиляции, и при первом заходе в функцию не будет залочиваться мьютекс (или ещё как-то потоко-безопасно инициализироваться статическая переменная), не будет динамически выделяться память…
Весь код с статическими/глобальными строковыми переменными станет работать немного быстрее.
Другой практический пример — это constexpr std::unordered_map. Можно будет делать подобие switch по строкам.
Любителям метапрограммирования с constexpr std::vector откроются новые возможности. Так например подобное нечитаемое безобразие просто не надо будет писать, так как можно будет обойтись std::vector и алгоритмами из стандартной библиотеки. Метапрограмный код получится читаемый, а не тот что мы имеем в C++14/17.
Ну и наконец — без constexpr контейнеров не сделать рефлексию, а её очень хотят успеть к C++23.
Любителям метапрограммирования с constexpr std::vector откроются новые возможности. Так например подобное нечитаемое безобразие просто не надо будет писать, так как можно будет обойтись std::vector и алгоритмами из стандартной библиотеки. Метапрограмный код получится читаемый, а не тот что мы имеем в C++14/17.
Не является ли данный пример типичным местом применения для gsl::span?
Кстати, как дела у std::array_view, всё заглохло?
constexpr строки и вектора это оч хорошо (вью же не сложишь), но на моей практике многое делается уже c одним gsl::span (хотя я знаю у себя место где constexpr вектор бы упростил код)
Upd: а, там массив возвращается; ну да, вектор полезен был бы
А теперь допустим мы ходим искать в этом массиве не по индексу, а по содержимому.
Сделаем constexpr хэшмапу и будем искать за (примерно) константное время. Впрочем, этот велосипед был скорее на поиграться с возможностями нового с++; в мире розовых поней я лучше подожду constexpr std::unordered_map.
Нужен новый Майерс, раз уж старый Майерс завязал с объяснениями хитростей С++!
Но будет ли помещаться С++20 в голову разработчику?В мою и C++98 не помещался. Но, как показала практика, если ты не разрабатываешь компилятор C++ или стандартную библиотеку для C++, то знать _весь_ C++ и не нужно.
Но будет ли помещаться С++20 в голову разработчику?
Большинство стандартных библиотек современных языков программирования не помещаются в голову разработчику: Java, C#, C++, Python…
Я немного не следил за событиями: в других языках тоже жалуются?
Слава богу, решили выпилить этот восклицательный знак, фуф.
www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0707r3.pdf
по рефлексии
www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0194r6.html
изменение с 8 по 9 ревизии посмотрите
old: $class M
new: constexpr void M(meta::type target, const meta::type source)
Ммда, ну, поживем-увидим.
отрисовать картинку со статистикой и отдать её пользователю
Ну пользователю-то картинку и пожать придется? я почитал
www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0267r7.pdf
там же рендеринг только.
Не будут же туда еще видео кодеки пихать)
В такой постановке, согласен, может быть полезно для минималистичного взаимодействия. Все равно меня немного напрягает «внешняя зависимость» в виде PNG кодеков в стандартной библиотеке, и как это все будет специфицировано (с учетом того что периодически всплывают уязвимости в libpng той же).
То есть Cairo в стандарте не смущает (а это и есть пропозал), а libpng смущает?)
Смущает. Все серьезные приложения (вроде браузеров) уже переехали на Skia, а тут стандартизацию Cairo ещё только обсуждают.
Networking, std::thread, Filesystem
Это всё следствия бетонирования POSIX. А графический стек везде разный и меняется постоянно, чтобы соответвовать новым ускорителям и железкам. Ваша библиотека морально устареет раньше, чем её успеют стандартизировать.
Поправьте меня если я не прав.
Или, с другой стороны, дальнейшее развитие 2D рисования — это выкинуть текущее АПИ в помойку и сделать новое с нуля; что и происходит (но только в 3D) — Vulkan, Metal
www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0276r0.html
и
www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1283r0.html
Пусть у нас в хедер файле
[[shared]] void library_foo();
Когда мы его подключаем из реализации, считается, что мы делаем экспорт.
Когда включаем это объявление, и реализации нет — считаем что импортируем из библиотеки.
Теперь вопрос, а если у нас этот же хедер используется для статической библиотеки? тут уже без макроса не обойтись, верно? Иначе использующий API код не сможет определить, что ему там вставлять, обычную слабую ссылку или импорт (в MSVC там будут отличаться имена символов ж).
Если комитет этого не понимает, то анонсированного выше в комментариях С++35 просто не будет.
Существующая практика такова, что почти всегда для реализации нужных функций нужны сторонние библиотеки.
Посмотрите на Boost.Text, может вам понравится:
www.youtube.com/watch?v=944GjKxwMBo
p.s. я даже не буду комментировать «провал» языка который живет 30 лет и загибаться не планирует.
Что у нас было 20 лет назад? Был STL, которым никто не пользовался, даже самыми базовыми вещами. Все юзали те же строки и массивы или в стиле С, или CString/CArray, или свои велосипеды. При этом CString уже поддерживал (тогдашний) юникод. Однако С++ являлся топовым универсальным языком, на котором писали всё — от системных утилит до фронтенда.
Прошло 20 лет, строки С++ всё ещё не поддерживают юникод.
Ну ок, фронтенд потерян безвозвратно. Весь бизнес-приклад давно переехал на java/.net. Как и 90% кода операционных систем. Бэкенд, всякие веб-сервисы, отжали пых и руби. Новые системные тулзы пишут на расте или го, не на плюсах. Наука со своим матаном переехала на питон. Геймдев ещё держится, но с этими вашими смартфонами и там уже не всё гладко.
Может С++ загибаться и не планирует, но по факту он уже сегодня воспринимается ближе к фортрану или коболу, чем к промышленному языку.
Простите, не хотел никого обидеть.
… при этом работаю в фирме, где «бизнес приклад» написан на C++, и даже веб сервисы написаны на C++.
За всю науку говорить не могу, но в CERN бозон хиггса искали используя плюсы (и Boost), в ИММ РАН физические задачи решают на C/C++/Fortran… да и в МГУ тоже физики используют C++.
Что-то в ваших показаниях не сходится.
Был STL, которым никто не пользовался, даже самыми базовыми вещами.Вы вот как-то с ходу начали оперировать ошибочными постулатами. Я, например, один из тех, кто начал использовать STL еще до принятия C++98. Тогда по рукам ходила реализация STL от Rogue Wave, кажется, которую можно было скотчем и спичками прицепить к Watcom C++, Borland C++. А в 1998-ом в Visual Studio 6.0 STL уже был из коробки, хотя и не стандартный еще.
Так что вы уже со своим тезисом «никто не пользовался» не правы. Ну и дальше, если копнуть, везде такая же история.
Сейчас мы ниши перечисляем. Вот мол у автора статьи ОСь и браузер написаны на плюсах. А у меня ось на джаве, а браузер потихоньку на раст переписывают.
Если бы лямбды, auto и нормальный for появились на 10 лет раньше, этого бы не было.
А у меня ось на джавеЭто какая?
Если бы лямбды, auto и нормальный for появились на 10 лет раньше, этого бы не было.Да ладно, устойчивая тенденция миграции с C++ на Java началась где-то в 2000-ом или 2001-ом. К 2004-ому она уже была вполне себе четкой. И Java тогдашняя по выразительности с C++98 не могла соревноваться.
Зато она была безопасной, со сборкой мусора, большим JDK. И даже умудрялась не так уж и сильно тормозить на тогдашней технике.
За прошедшие 15 лет C++ уже вытеснили из тех ниш, где его выгодно было заменить на Java, C#, Scala и пр. Но там, где он остался и применяется сейчас только Rust и, отчасти, Go может что-то предложить. Да и тому же Rust-у еще экосистему свою вырастить нужно. А то что ни растоман, то и жалоба, что Qt у них нет.
Это какая?
Ведроид жи ну. Да-да, я знаю, что там внутре у ней неонка, в смысле линукс, но он тоже не на плюсах написан.
Возврат гугла к плюсам обусловлен, в первую очередь, тем, что у самой java проблем выше крыши, причём как технических, так и юридических и организационных. Ну и да, здесь надо похвалить комитет, плюсы сейчас развиваются действительно поживее, чем java.
Это библиотеки, утилиты, планировщикиИ вот это все на C или C++.
рюшечки, окошечки и прочие пакетные менеджерыА это уже относится к ОС, если под ОС понимать что-то вроде Windows. А вот в том же Linux-е есть сама ОС и есть Desktop Environment. Вот окошечки-рюшечки — это про Desktop Environment, а не про ОС.
Слышал что уже длительное время идёт обратная миграция.Да тут сложно оценки сделать. С одной стороны — да, есть прецеденты, когда с Java переходят на C++ (или используют в Java код на плюсах через JNI). С другой стороны, на Java и C# делают тот же самый HFT, где плюсам самое место (и где они хорошо себя чувствуют).
Несколько лет назад здесь, на Хабре было интервью с Романом Елизаровым, он там рассказывал про разработки трейдинговой системы на Java. Там же есть ссылка на OpenHFT.
Про разработку на C# в обсуждении на RSDN-е кто-то рассказывал (отсюда и далее вниз по ветке).
P.S.: А передёргивать слова некрасиво, я такого не говорил.
1) Java иногда используются для студенческих курсовых. Жаль, ведь получается что этот язык — лишь удел недоучек.
2) Андроид использует Java. Не спроста vanxant утверждает что данная ОС — удел разработчиков недоучек.
Тут приличное общество, давайте не будем опускаться в дискуссиях до подобных приёмов.
Комитет на то и нужен, чтобы изучить ошибки других, сжать тестикулы в кулак, принять решение и добровольно-принудительно перейти всем сообществом. А не как у нас в плюсах принято, что в языке несколько видов строк, несколько видов «умных» указателей, а к четырём одновременно существующим видам обработки ошибок предлагается добавить ещё столько же. Берёшь либу и не знаешь, что от неё ожидать.
Есть, в конце концов, буст, где можно обкатать потенциальных кандидатов «в живой природе» до их стандартизации.
Пока для меня, основная боль как разработчика, это не то что в С++ нет какой-то там поддержки utf-8, а то что сторонние кроссплатформенные C библиотеки просто используют fopen/stat и т.п. которые разумеется на Win работают, но без поддержки юникода.
— ffmpeg/vidstab
— opencv — весь файловый api
— glog
— dlib
(первое что за минуту в голову пришло, тысячи их)
Да, патчи-то наложить недолго, но е-ёмое, не понимаю я разработчиков библиотек, под винду эти функции существуют два десятка лет, вы делаете «кросплатформенность» и забиваете на поддержку юникода в FS? что с вами не так?
MS, сделайте уже поддержку setlocale(«utf-8»), чтобы fopen работал как везде, это не лечится)
With insider build 17035 and the April 2018 update (nominal build 17134) for Windows 10, a «Beta: Use Unicode UTF-8 for worldwide language support» checkbox appeared for setting the locale code page to UTF-8.[a] This allows for calling «narrow» functions, including fopen and SetWindowTextA, with UTF-8 strings. Microsoft claims this option might break some functions (a possible example is _mbsrev) as they were written to assume multibyte encodings used no more than 2 bytes per character, thus until now code pages with more bytes such as GB 18030 (cp54936) and UTF-8 could not be set as the locale.
Но походу сделано как я понимаю криво, настройка — глобальная общесистемная, выбирается юзером, никакой не setlocale(«utf-8»).
Наука со своим матаном переехала на питонтолько частично. Вычислительные ядра все на плюсах (если поновее) или вообще на fortran. Ну и много чего на CUDA/OpenCL.
Ну ок, фронтенд потерян безвозвратно.
А Qt'шники то и не в курсе. Во многих фронтендных задачах нужно лишь выводить текст, а для этого можно и std::string как контейнер использовать.
Ну вот сейчас 2018 год, С++17 полностью (по крайней мере на словах) 3 популярных компилятора поддерживают. У нас все проекты собираются с флагами 17 стандарта.
Благодаря регулярным и предсказуемым релизам, разработчики компиляторов могут как раз быстрее реализовывать фичи. Т.е. в 2019 заморозят набор фич, и к 20 году большая часть в том или ином виде будет реализована со спец флагами в компиляторах. Для концептов, контрактов и модулей, кстати, флажки или экспериментальные ветки уже есть.
С++17 полностью (по крайней мере на словах) 3 популярных компилятора поддерживают.
Как там поживает <memory_resource>? А < execution >?
Компилятор — «да! да здравствует новый стандарт!», его реализация STL — «подождите, пожалуйста помедленнее, я запсваю» =)
Если не брать штуки по constexpr — то большая часть мажорных нововведений как раз по части компилятора, я думаю с ними проблем не возникнет.
в любом случае, согласитесь, что 5% отдельных фич, с которыми проблемы, это не та вещь про которую можно ныть «ой беда, 10 лет теперь стандарт реализовывать будут».
Во современных компиляторах есть __assume(condition) или __builtin_unreachable, при помощи которых можно сообщать оптимизатору любую доп. информацию. Вместо того, чтобы вводить специальную функцию только для выравнивания, лучше было стандартизовать функцию assume и дать рекомендации по тому, как с её помощью можно описывать выравнивание.
C++20 и Modules, Networking, Coroutines, Ranges, Graphics. Итоги встречи в Сан-Диего