Здравствуйте, scf, Вы писали:
scf>Здравствуйте, Wawan, Вы писали:
W>>новая перекличка тех кто С++ не осилил
scf>Ну я осилил, стандарт мог цитировать. Но зачем писать на С++, если производительность труда на Java выше в несколько раз?
А производительность программиста на скриптовых языках -- ещё выше. Про это уже 25 лет назад писалось.
Когда Java ещё и в проекте не было, а её место мог занять Tcl. Но не занял. Но вот задачки до 1000 строк кода, условно,
на нём решаются на порядок быстрей (и каждая строчка заменяет десяток строк на Java). С питоном не хуже думаю,
или с PHP, или с Perl, или с Ruby... Только вот по мере роста объёма кода, статически типизированные языки
начинают выигрывать. А скриптовые вязнут в отладке.
Если этой позиции подходить, то оптимальный вариант: комбинация скриптового языка и статически типизированного.
Где скриптовый выступает в роли "клея" для дискретных компонентов созданных на статически типизированном языке.
Здравствуйте, fk0, Вы писали:
fk0> В переводе на русский -- C++ требует обученных сотрудников, а с языком C -- может кто-то работать "по-совместительству". fk0>Да, но по мере усложнения C-подпроекта он рискует по-сложности обогнать C++. И там действительно будут незаменимые люди. fk0>Потому, что одно дело программа написанная в рамках стандартных средств языка, а другое дело, когда эти стандартные fk0>(и всем понятные) средства заменяются на самодельные аналоги (к чему приходит сложная C-программа). Потому, что ряд fk0>условно сложных концепций он в любой программе существует, не зависимо от языка. Это определяется задачей, а не языком. fk0>И хорошо, если эти сложноть концепций ограничивается в языке или в стандартной библиотеке, а не в коде который нужно fk0>поддерживать. Почему C#/Java и энтерпрайзе и лидируют. Готовая библиотека и язык имеют ответы на многие вопросы.
Ничего не мешает по мере усложнения переписать внутренности на C++, если это усложнение вообще будет и если это будет оправдано. ABI всё равно был, есть и всегда будет на C.
Здравствуйте, fk0, Вы писали:
fk0> Не соглашусь. Шаблоны -- это МЕТАпрограммирование. Это не программирование программы, а программирования компилятора, чтоб он запрограммировал программу.
Обобщённое программирование (англ. generic programming) — парадигма программирования, заключающаяся в таком описании данных и алгоритмов, которое можно применять к различным типам данных, не меняя само это описание.
Сам Бьерн Страуструп в своей книге говорит о шаблонах C++ как об обобщённом программировании. Метапрограммирование не упоминается ни разу.
При определении метапрограммирования лично я прежде всего смотрю есть ли рефлексия.
Отражение (рефлексия; холоним интроспекции, англ. reflection) — процесс, во время которого программа может отслеживать и модифицировать собственную структуру и поведение во время выполнения. Парадигма программирования, положенная в основу отражения, является одной из форм метапрограммирования и называется рефлексивным программированием.
У людей есть ещё представление о метапрограммировании как о продвинутом кодогенераторе. Но это не про шаблоны C++, там простая подстановка без возможности расширения конструкций языка. Более того, давай вспомним о макросах, вот оно где твоё "метапрограммирование".
Но если говорить по существу, то в чистом C++ нет никакого вида метапрограммирования. Программисты уже давно описали подобную ситуацию в книгах, вроде "Чистая архитектура" Мартин Роберт.
Для примера.
1. Может ли программист написать блок, ветвление и цикл на ассемблере как это делают в структурном программировании.
Да, может.
2. А может ли он написать на ассемблере подпрограмму и её подвиды, процедуру, функцию как в процедурном программировании.
Да, может.
3. Может он и класс может написать?
Естественно.
Потому что разница между языками реализующими парадигму и не реализующими не просто в возможности их использовать, а в удобстве их использования за счёт специальных конструкций языка.
В чистом C++ для рефлексии даже не нужен метаобъектный компилятор, можно же прописать всё в ручную и в программе появится точно такой же механизм как при автоматической генерации, позволяющий использовать свойства, методы, интерфейсы и многое другое.
Точно так же люди пытаются реализовать в C++ метапрограммирование за счёт парадигмы обобщённого программирования, ту самую кодогенерацию. Только большинство подобных случаев не упрощают жизнь, а усложняют. И тогда говорят, что парадигма в языке не поддерживается.
Здравствуйте, vsb, Вы писали:
vsb>Ничего не мешает по мере усложнения переписать внутренности на C++
Мешает отвратительная архитектура программы, которая такая получилась вынужденно, потому как банально не было нормальных средств сделать её хорошо на С.
Так что переписывать придётся всё и с нуля, а это дохренища работы, на которую мало кто подпишется из тех, кто решает на что потратить деньги.
... << RSDN@Home 1.3.110 alpha 5 rev. 62>>
Забанили по IP, значит пора закрыть эту страницу.
Всем пока
Здравствуйте, AleksandrN, Вы писали:
AN>Здравствуйте, Muxa, Вы писали:
M>>Ну, так и почему народ не стал использовать Java вместо плюсов?
AN>Для десктопного софта — стал.
Для декстопного применение JVM довольно маргинально, а вот для серверного — сплошь и рядом.
Правда, честно говоря, это не замена плюсов, потому что их в этой области днем с огнем не сыскать было и раньше
Здравствуйте, velkin, Вы писали:
V> Здравствуйте, fk0, Вы писали:
fk0>> Не соглашусь. Шаблоны -- это МЕТАпрограммирование. Это не программирование программы, а программирования компилятора, чтоб он запрограммировал программу.
V>Шаблоны C++ это обобщённое программирование, что позволяет писать обобщённые алгоритмы. К сожалению в C++ нет метапрограммирования из-за этого его пришлось ввести в Qt за счёт метаобъектного компилятора.
V> парадигма программирования, заключающаяся в таком описании данных и алгоритмов, которое можно применять к различным типам данных, не меняя само это описание.
V>Сам Бьерн Страуструп в своей книге говорит о шаблонах C++ как об обобщённом программировании. Метапрограммирование не упоминается ни разу.
Это информация примерно 20-летней давности, когда только Qt появлялся.
С тех пор C++ сильно уехал вперёд (в основном в C++11). Да, действительно, шаблоны не планировались
для метапрограммирования, оно само так получилось, что получилось. Но сейчас используются именно что для
метапрограммирования. Обобщённое программирование на шаблонах выходит временами так себе (в этом же треде
претензии по объёму генерируемого шаблонного кода).
Метапрограммирование началось тогда, наверное, когда поняли, что условно Class<T> может породить
совершенно разные сущности для неких разных T и U. Обобщённое же программирование предусматривает
одинаковую реализацию класса для всех T, отличающихся только собственно типом и функциями зависимыми
от типа. Краеугольным камнем является возможность специализации шаблона для конкретного типа или
константы параметра и совершенно отдельное определение или функции в таком случаев. Вторым краеугольным
камнем по-моему является SFINAE принцип, позволяющий не ожидать единственного решения заданного
наперёд программистом, а осуществлять выбор из нескольких, возможно не подходящих (что не ведёт к
остановке из-за ошибки, а продолжает поиск).
И разумеется, подразумевается программирование на, повторюсь, _декларативном_ языке шаблонов,
где компилятор самостоятельно ищет решение по заданному набору правил. А не где программист
в императивном стиле описывает последовательность действий.
V>При определении метапрограммирования лично я прежде всего смотрю есть ли рефлексия.
Рефлексия не связана с метапрограммированием. Метапрограммирование -- по-определению это программирование компилятора.
В какой-то ограниченной форме "недорефлексия" в C++ есть (по сравнению, например, с Rust).
В C++ шаблон класса может, например, исследовать данный ему тип или функцию и определить обладает
ли он определёнными свойствами. Выявить, существуют ли в неймспейсе функции или типы с такими-то свойствами,
или может породить новые типы и функции, в том числе добавить в неймспейс. Известны, например, способы
итерации по элементам структур таким образом, что позволяет их (де)сериализацию. Там правда немножко
грязный хак из-за возможности навешивания alignas свойства членам, о чём такой метод окажется не в курсе.
Впрочем последнее много где может дать негативные эффекты и идея исправлять выравнивание у переменной
(но не типа) сама по себе -- порочная. Точно такая же порочная, как packed структуры (когда указатель на
член такой структуры уже не знает об его "упакованности").
V>У людей есть ещё представление о метапрограммировании как о продвинутом кодогенераторе. Но это не про шаблоны C++, там простая подстановка
В шаблонах ни разу не подстановка. Подстановка -- это в C-препроцессоре. Подстановка в C# Generics.
Принципиальное отличие C++-шаблона, что его результат может зависеть от входных аргументов и давать совершенное
разные, не ограничивающиеся подстановкой, результаты для разных аргументов. Это должно быть очевидно глядя на
то, как шаблон может вычислить какую-либо рекурсивную функцию, например сумму или логарифм. В языках где вместо
C++-шаблона есть Generics -- так невозможно.
И да, в C-препроцессоре тоже не совсем подстановка, при определённых обстоятельтвах он тоже может выдавать
результаты зависимые от аргумента (для чего используется способ со склейкой предопределённого имени макроса и скобками) или
что-то считать рекурсивно (совсем уж рекурсивно, бесконечно, не может, но число итераций можно увеличить до достаточно
больших величин). Другое дело, что он не обладает понятием числа, поэтому результаты там -- только текст или списки.
V> без возможности расширения конструкций языка. Более того, давай вспомним о макросах, вот оно где твоё "метапрограммирование".
Без возможности. Метапрограммирование не подразумевает изменение грамматики языка. Метапрограммирование это лишь
о автоматизированном формировании программы на другом языке. Язык шаблонов -- это позволяет. Хотя и не создавался для
этого. Однако сейчас элементы метапрограммирования широко используются стандартной библиотекой и множество новых свойств
C++ без этого были бы невозможны попросту.
V>Но если говорить по существу, то в чистом C++ нет никакого вида метапрограммирования. Программисты уже давно описали подобную ситуацию в книгах, вроде "Чистая архитектура" Мартин Роберт.
Книга в значительной степени об ООП. К метапрограммированию и шаблонам оно никаким боком.
V>Потому что разница между языками реализующими парадигму и не реализующими не просто в возможности их использовать, а в удобстве их использования за счёт специальных конструкций языка.
В том же C++ многие "специальные конструкции языка" часто оказываются библиотечными функциями.
Те же смарт-поинтеры. Они вообще ни разу не часть языка. А, например, корутины без помощи компилятора никак не сделаешь.
Хотя в последнем случае каких-то конструкций особенных не нужно, просто сама кодогенерация меняется.
V>В чистом C++ для рефлексии даже не нужен метаобъектный компилятор, можно же прописать всё в ручную и в программе появится точно такой же механизм как при автоматической генерации, позволяющий использовать свойства, методы, интерфейсы и многое другое.
В ассемблере тоже можно прописать вручную. Но неудобно. Потому появляются такие штуки: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/n4856.pdf
V>Точно так же люди пытаются реализовать в C++ метапрограммирование за счёт парадигмы обобщённого программирования, ту самую кодогенерацию. Только большинство подобных случаев не упрощают жизнь, а усложняют. И тогда говорят, что парадигма в языке не поддерживается.
Шаблоны давно не обобщённое программирование и изначально им толком не было. Это совершенно другое, очевидно любому, кто понимает как они работают.
Обобщённое программирование есть в Rust, например, в C# -- и это другое. Это совершенно иной механизм уступающий шаблонам.
Без которых, повторюсь, половина функционала _современной_ стандартной библиотеки -- не реализуема. В начале 2000-х была другая ситуация,
но сегодня 2022 год, а не 2002, когда C++03 ещё не вышел.
Здравствуйте, Shmj, Вы писали:
S>Возражения?
Скорее всего С++ нас будет ещё долго радовать или "радовать" кому как, а вот С будет заменён Rust который, на мой взгляд, выглядит как более простой язык для решения задач системных уровня, но уже имеющий внутри все наработки и инструменты текущего языкостроения которых не хватает в С.
Здравствуйте, Kernan, Вы писали: K> Скорее всего С++ нас будет ещё долго радовать или "радовать" кому как, а вот С будет заменён Rust который, на мой взгляд, выглядит как более простой язык для решения задач системных уровня, но уже имеющий внутри все наработки и инструменты текущего языкостроения которых не хватает в С.
Здравствуйте, rudzuk, Вы писали:
R>Без комментария в это не просто не въехать
Писать на Rust, а не на С средствами Rust. Эта же проблема и в С++ когда С-шник начинает писать на нём как на С.
Здравствуйте, Kernan, Вы писали:
K> R>Без комментария в это не просто не въехать
K> Писать на Rust, а не на С средствами Rust. Эта же проблема и в С++ когда С-шник начинает писать на нём как на С.
А как этот код должен выглядеть по-растовски правильно?
Здравствуйте, rudzuk, Вы писали:
R>Ага, очень простой...
Непонятно что мешает на раст написать функцию (лямбду если по месту или макрос если надо развернуть не надеясь на инлайнинг) S и получить точно такой же код как в си?
И также что мешает в си засунуть все переменные в структуру (для получения "красивого" self->) и вместо макроса зафигарить такую же красоту по месту как в раст примере?
По моему тут никаких демонстраций особенностей языков нет вообще (кроме более строгих кастов раста), а только демонстрация разных стилей одинаково доступных на обеих языках.
Здравствуйте, FR, Вы писали:
FR> Непонятно что мешает на раст написать функцию (лямбду если по месту или макрос если надо развернуть не надеясь на инлайнинг) S и получить точно такой же код как в си?
Покажи уже, как правильно и без потери производительности.
FR> И также что мешает в си засунуть все переменные в структуру (для получения "красивого" self->) и вместо макроса зафигарить такую же красоту по месту как в раст примере?
Ничто не мешает И читаемость не страдает, как по мне.
FR> По моему тут никаких демонстраций особенностей языков нет вообще (кроме более строгих кастов раста), а только демонстрация разных стилей одинаково доступных на обеих языках.
Тут демонстрация того, что в раст встроен обфускатор на уровне языка. Ну правда, ведь невозможно же код читать И это примитивное выражение, а если на метод update посмотреть...
Здравствуйте, rudzuk, Вы писали:
R>А как этот код должен выглядеть по-растовски правильно?
Начнём с того, что и на С код наркоманский и без 0.7 не разобраться что он делает и вообще зачем он вот так написан. Я не спец по Раст, но мне кажется надо как-то макросы тут поюзать.
Да нашел что мешает, переполнение безнаковых целочисленных в расте. В си просто такое переполнение игнорируется и считает по модулю. Но почему трудолюбивый автор раст кода везде использует wrapping_add когда есть удобная обертка Wrapping непонятно. С ней можно сделать так:
use std::num::Wrapping;
fn main() {
// начальные переменныеlet s:[u8; 256] = [10; 256];
let (i, j, k, z) = (250u8, 250u8, 250u8, 250u8);
// заворачиваем (если операция частая можно макрос сварганить).let (i, j, k) = (Wrapping(i), Wrapping(j), Wrapping(k));
let mut z = Wrapping(z);
// сам кодlet S = |a: Wrapping<u8>| Wrapping(s[a.0 as usize]);
z = S(j + S(i + S(z + k)));
// разворачиваем результатlet z = z.0;
dbg!(z);
}
И судя по его коду по твоей ссылке можно было не обертывать при каждом использовании а сразу объявить переменные как Wrapping<u8> тогда свертки развертки не нужны. В таком случае все совсем просто будет, практически как на си:
use std::num::Wrapping;
fn main() {
// начальные переменныеlet s = [Wrapping(10u8); 256];
let (i, j, k) = (Wrapping(250u8), Wrapping(250u8), Wrapping(250u8));
let mut z = Wrapping(250u8);
// сам кодlet S = |a: Wrapping<u8>| s[a.0 as usize];
z = S(j + S(i + S(z + k)));
dbg!(z);
}
R>Покажи уже, как правильно и без потери производительности.
Судя по ассемблерному коду тут оптимизируется вусмерть.
FR>> И также что мешает в си засунуть все переменные в структуру (для получения "красивого" self->) и вместо макроса зафигарить такую же красоту по месту как в раст примере?
R>
R>Ничто не мешает И читаемость не страдает, как по мне.
Ну нет для полноты картины надо без макроса, как автор кода на расте, мы же на стили хотим посмотреть.
R>Тут демонстрация того, что в раст встроен обфускатор на уровне языка. Ну правда, ведь невозможно же код читать И это примитивное выражение, а если на метод update посмотреть...
Тут похоже демонстрация не очень хорошего знания языка автором кода, с таким знанием он и на си без макросов трудолюбиво бы все расписал.
Здравствуйте, Kernan, Вы писали:
K> Начнём с того, что и на С код наркоманский и без 0.7 не разобраться что он делает и вообще зачем он вот так написан.
Тебе не надо понимать что он делает, нужно просто распарсить выполняемые операции, что в коде на Rust сделать очень не просто (автор не зря там комментарий оставил).
Здравствуйте, FR, Вы писали:
FR> И судя по его коду по твоей ссылке можно было не обертывать при каждом использовании а сразу объявить переменные как Wrapping<u8> тогда свертки развертки не нужны. В таком случае все совсем просто будет, практически как на си: FR>
FR> use std::num::Wrapping;
FR> fn main() {
FR> // начальные переменные
FR> let s = [Wrapping(10u8); 256];
FR> let (i, j, k) = (Wrapping(250u8), Wrapping(250u8), Wrapping(250u8));
FR> let mut z = Wrapping(250u8);
FR> // сам код
FR> let S = |a: Wrapping<u8>| s[a.0 as usize];
FR> z = S(j + S(i + S(z + k)));
FR> dbg!(z);
FR> }
FR>
Клево!
FR> R>Покажи уже, как правильно и без потери производительности.
FR> Судя по ассемблерному коду тут оптимизируется вусмерть.
Не, так не интересно, он там все соптимизировал во время компиляции. А можно пример, где компилятор будет реально по индексам читать?
FR> Ну нет для полноты картины надо без макроса, как автор кода на расте, мы же на стили хотим посмотреть.
Здравствуйте, rudzuk, Вы писали:
FR>> Судя по ассемблерному коду тут оптимизируется вусмерть.
R>Не, так не интересно, он там все соптимизировал во время компиляции. А можно пример, где компилятор будет реально по индексам читать?
Пример
use std::num::Wrapping;
use std::time::{SystemTime, UNIX_EPOCH};
fn main() {
let nanos = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.subsec_nanos();
let b = nanos as u8;
//dbg!(b);
// начальные переменныеlet s = [Wrapping(10u8); 256];
let (i, j, k) = (Wrapping(b), Wrapping(b), Wrapping(b));
let mut z = Wrapping(b);
// сам кодlet S = |a: Wrapping<u8>| s[a.0 as usize];
z = S(j + S(i + S(z + k)));
//dbg!(z);
std::process::exit(z.0 as i32);
}
тут выхлоп ассемблера получается без проверок границ:
То есть компилятор смог понять что выходов за границы массива не будет и выкинул все проверки.
Вот если вместо let s = [Wrapping(10u8); 256]; сделать например let s = [Wrapping(10u8); 25]; то есть длина массива меньше 256 то добавятся проверки на границы и плюс пропадет инлайнинг замыкания.
FR>> Ну нет для полноты картины надо без макроса, как автор кода на расте, мы же на стили хотим посмотреть.
R>Без макроса подлиннее, но в рамках приличий. R>
FR> То есть компилятор смог понять что выходов за границы массива не будет и выкинул все проверки. FR> Вот если вместо let s = [Wrapping(10u8); 256]; сделать например let s = [Wrapping(10u8); 25]; то есть длина массива меньше 256 то добавятся проверки на границы и плюс пропадет инлайнинг замыкания.
А как будет выглядеть этот код (и асм), если регистровые переменные (i,j,k,z) будут не байтовыми, а в размер регистров cpu (u3232, u64)?