LINUX.ORG.RU

То что сложно реализовать на других языках

 


0

3

Разбираюсь с библиотекой qsp и решил с вами поделится кусочком кода, а точнее реализация crc32. Автор кода, решил создать свой собственный crc32, на базе CRC-32/ISO-HDLC, взяв от туда таблицу, но изменил стартовое значение, и еще по мелочи:

int qspCRC(void *data, int len)
{
    unsigned char *ptr;
    int crc = 0;
    ptr = (unsigned char *)data;
    while (len--)
        crc = (qspCRCTable[(crc & 0xFF) ^ *ptr++] ^ crc >> 8) ^ 0xD202EF8D;
    return crc;
}

В чем прелесть: Использует int, но фактически работает как unsigned int. Вся логика на переполнение - и это как то работает, хотя вроде это UB. Автор кода крут (без шуток), я не спорю, но переписать это на любой другой язык, который будет контролировать переполнение - будет крайне сложно. Почему автор не использовал любой из стандартных алгоритмов crc, как он использовал библиотеку для regexp - загадка.

★★★★
Ответ на: комментарий от MOPKOBKA

нет, сравнение получилось gcc vs llvm, по ассемблеру достоверно можно определить только равенство если ассемблер одинаковый, если нет то пальцем в небо, т.е. вот это твое - «gcc царь компиляторов, генерирует красивше» - просто сектантская мантра

zurg
()
Ответ на: комментарий от zurg

нет, сравнение получилось gcc vs llvm

Не стоит цитировать мои комментарии мне же ...

по ассемблеру достоверно можно определить только равенство

А по запуску что ты определишь? На одном процессоре будет хорошо работать, на другом плохо. На одном ABI одно лучше, на другом другое. Ассемблер полезно почитать, но нужны хотя бы минимальные знания что бы из чтения извлечь пользу. Хотя у этого метода есть свои ограничения.

т.е. вот это твое - «gcc царь компиляторов, генерирует красивше» - просто сектантская мантра

К обсуждению отношения не имеет, но конечно gcc намного лучше код генерирует, это видно на других примерах. Ты наверное думаешь что я код только читаю, и никогда не запускаю, видимо стоило мне написать комментарий так: «в дополнение с ассемблером сравнивать надежнее, иногда хватает только его».

MOPKOBKA ★★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 1)
Ответ на: комментарий от zurg

Такое правильно так сравнивать:

плохое сравнение. нет совершенно гарантии, что раст в своих фолдах-шмолдах и прочей высоко уровневой функциональной куэте не запутается и в друг не выдаст совершенно непотребный код. или такие гарантии есть?

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

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

alysnix ★★★
()
Ответ на: комментарий от zurg
typedef unsigned int* u8p;

#define for_each_u8(adr,len) \
for (\
  u8p _curr = (u8p) adr, _end = _curr + len;\
  _curr < _end; \
  ++_curr \
)\

unsigned int qspCRC(void *data, int len) {
  unsigned int crc = 0;
  for_each_u8(data, len) {
    crc = (qspCRCTable[(crc & 0xFF) ^ *_curr] ^ (crc >> 8)) 
          ^ 0xD202EF8D;
  }
  return crc;
}

сравни с таким сишным кодом расчета crc. его даже шланг вроде генерит более эффективно чем рустовый аналог. а может у меня ошибка но глазками не вижу ее.

https://godbolt.org/z/GxsW4az9Y

alysnix ★★★
()
Ответ на: комментарий от MOPKOBKA

В чем безопасность?

В том что создатель структуры точно знает как считать crc для своей структуры и явно описывает это. Пользователю всё равно как реализован трейт Crc, для него API безопасный и не меняется, нет никакого const void * (читай как «будь что будет»).

numas13
()
Ответ на: комментарий от alysnix

Слушай, alysnix, а что язык rust стал для тебя такой занозой? Красивый язык, продуманный, стройный и изящный. С библиотеками только недодумали, но когда задумывался язык, мир был немного другим.

C++ тоже когда-то был таким на заре своего появления. С++ тогда действительно на голову выше был своих конкурентов, той же Ады. Но время шло, и тот задел, на основе которого выстроили C++, оказался недостаточным для вызовов времени. Оказалось, что C++ не поспевает за современными тенденциями в программировании. Язык стали спешно менять, но фундамент так просто не сменишь. Вот, и выглядит современный C++ местами косо и однобоко. Один std::variant чего стоит, хотя типы-суммы как бы естественная вещь в 2025 года - must have.

Однако стоит отдать должное создателям C++ и всем сопричастным к языку. Инструмент получился одним из лучших, даже по современным меркам. А вот как язык C++ уже давно и безнадежно отстает. В общем, отношение к C++ преимущественно положительное у людей, даже у значительной части поклонников языка rust.

Так чего же тебя так плющит от одного только слова rust?

anonymous
()
Ответ на: комментарий от MOPKOBKA

как С

Как в GNU C. :)

Такой проверки для Rust не знаю и беглый поиск ничего не нашёл. Это можно частично выразить трейтами.

unsafe trait Packed {}

#[repr(C, packed)]
struct Foo;

unsafe impl Packed for Foo {}

fn need_packed<T: Packed>(value: &T) {
    // value is packed
}

need_packed(&Foo);
numas13
()
Ответ на: комментарий от anonymous

Один std::variant чего стоит, хотя типы-суммы как бы естественная вещь в 2025 года - must have.

Вы не поверите, но тип - «switch declaration» был еще в алголе 60!

И он есть надмножество ваших «типов-сумм». А «типы-суммы» это переименованная «вариантная запись» аж от паскаля еще(подмножество свитч-деклараций алгола). Этим «новым понятиям» - 100 лет в обед, короче.

И их не втянули в с++ просто потому, что главная фишка с++ - сделать развесистую типизацию, полиморфизьм и переопределение операторов и минимум встроенных типов, чтобы все остальное реализовать на них.

То есть множество встроенных типов должно быть минимальным(!!!), но достаточным для эффективной реализации всех прочих типов. Короче - если вам не нравится std::variant - напишите свой. Стандартный вас ни к чему не обязывает. Это сделано намеренно.

Тот кто вопиет, что в с++ нет типа, который есть в языке XXX - и это плохо! - не понимает самой идеи с++. Вот если в с++ нет тех фич, что позволят реализовать этот тип эффективно библиотечным способом - вот это реально плохо.

Раст не нравится ни агрессивным поведением своих адептов(напоминающих деструктивную секту), ни фичами, которые его бы выделяли среди множества других языков. Единственно что там есть заметного - borrow checker, но технически он лично мне не нужен и не особенно интересен. Отслеживание графа «владений» ресурсами - это погружение в какие-то ненужные детали реализации. Иногда они может и важны, но не являются ключевыми.

alysnix ★★★
()

Не понял причем здесь переполнение.

Переполнение это когда ты умножаешь два положительных числа, а в результате получится -1 или 0 потому что регистра не хватит, чисто математически это недопустимо и ни в одном высокоуровневом языке не допустят такого и сишный компилятор тебя настоятельно предупредит что надо бы регистровое число расширить что бы не обосраться.

Здесь же сплошные битовые операции над 32 битами, никакие математические правила тут не действуют и соответственно никаких переполнений нет. Единственная битовая операция различающая число со знаком и без это математический сдвиг с зацеплением знака, и если вот здесь: i ^ crc >> 8 так и задумано что он может случится, то надо это писать как то явно что ли:

int qspCRC(void *ptr, int len)
{
    unsigned char *data = (unsigned char *)ptr;
    union {
        unsigned int u;
          signed int i;
    } tmp, crc = { .u = 0};

    for (int i = 0; i < len; i++) {
        tmp.i = qspCRCTable[(crc.u & 0xFF) ^ data[i]],
        crc.i = tmp.i ^ crc.i >> 8, // арифметический сдвиг из-за знаковых
        crc.u ^= 0xD202EF8Du;
    }
    return crc.i;
}

К тому же буквально только что узнал что арифметический сдвиг sar eax, 8 оказывается выполняется первым (только потом xor), а не как обычные битовые операции последовательно, поэтому с такой мешаниной надо быть аккуратней.

uin ★★★
()
Последнее исправление: uin (всего исправлений: 1)
Ответ на: комментарий от alysnix

Ну, вот смотри! Есть множество типов, где каждый элемент такого множества - это какой-то тип данных в неком определенном языке программирования.

Что такое умножение двух типов? По сути - это декартово произведение элементов из обоих типов. Что это будет в С++? Это будет структура. Берем два типа, умножаем, получаем снова тип. Операция замкнута.

Теперь в математике есть такое понятие как взаимно непересекающиеся множества. То есть, берем два типа. Складываем. Считаем, что типы не пересекаются. Снова получаем тип. Операция замкнута. В Rust это будет enum. Интерпретация такая: либо элемент из первого типа, либо элемент из второго. Ну, в C++ это будет тот самый std::variant.

Итак, у нас есть множество элементов. Элементами являются типы данных. Мы определили две операции: умножение и сложение. Обе операции замкнуты в том смысле, что снова возвращают типы, то есть элементы исходного множества.

На языке математики это называется алгеброй. То есть, мы имеем дело с «алгебраическими типами данных». В хаскеле не стесняются математики - поэтому там прямо так это и называется.

Это - настолько фундаментальная вещь! И ты понимаешь, что без сложения выглядит это как-то недостойно, непонятно и странно. Почему не может быть сложения, если почти везде есть произведение, даже в C++?

Проблема тут иная. Большинство программистов не касалось толком ни алгебры, ни математики, и они просто не понимают, насколько это все фундаментальные вещи. Да, вот это - действительно проблема. Проблема в отсутствии знаний и искаженном восприятии.

Как пел Высоцкий: «жираф большой - ему видней». Если тип-сумм нет в мейнстримных языках, значит, что это не нужно якобы. Правильно понимаю твой посыл?

Я полагаю, что авторы мейнстримных языков прекрасно разбирались в этой теме. А почему не добавили сразу - это отдельный разговор.

anonymous
()
Ответ на: комментарий от anonymous

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

вы там заблудились в своих математиках. то что вы тут называете типом-суммой в с++ и даже си называется union. а std::variant - это «размеченное» обьединение - то есть тип-сумма и тег актуального типа.

у вас тип-сумма это что? данные актуального типа с тегом или без? о чем там ваша алгебра говорит?

и вообще разговор был не о «типах-суммах» которым сто лет в обед, а о языке раст.

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

alysnix ★★★
()
Ответ на: комментарий от numas13

В чем безопасность?

В том что создатель структуры точно знает как считать crc для своей структуры и явно описывает это

Через X Macro создатель структуры тоже может описать явно как будет считываться crc. Только X Macro удобнее.

позволяет ли Rust, как С, проверить packed?

Как в GNU C. :)

Ну так и твой пример прибит к rustc и не запускается на gccrs.

Такой проверки для Rust не знаю и беглый поиск ничего не нашёл. Это можно частично выразить трейтами.

Там же может быть запаковка по 1, 2, 4 байтам, это как описывать? А если упаковка сменится, у тебя же не изменится твой трейт. Немного бессмысленная проверка получается, что до этого надо было гарантировать руками упакованность, что после %)

MOPKOBKA ★★★★★
()
Ответ на: комментарий от anonymous

Один std::variant чего стоит, хотя типы-суммы как бы естественная вещь в 2025 года - must have.

Ничего лучше сишного union еще не изобрели. Часто тебе вообще этот enum ненужен, потому что он определяется «внешним enum», который может быть не связан с твоей структурой.

А если тебе нужна именно пара enum+union, то и тут у С преимущества, потому что можно оперировать этими элементами по отдельности, например достать значение из enum и найти другую пару enum+union с тем же enum, то есть найти по похожему по тегу. Анонимные структуры и union сокращают вложенность.

MOPKOBKA ★★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 1)
Ответ на: комментарий от MOPKOBKA

Ну так и твой пример прибит к rustc и не запускается на gccrs.

Для справки, gccrs ещё в очень зачаточном состоянии. Давно не проверял как он поживает, но несколько месяцев назад были разговоры про «скоро сможем собрать старенький libcore». Сравнение даже хуже чем сравнивать GCC с TCC, короче опять притянуто за уши.

Там же может быть запаковка по 1, 2, 4 байтам, это как описывать? А если упаковка сменится, у тебя же не изменится твой трейт. Немного бессмысленная проверка получается, что до этого надо было гарантировать руками упакованность, что после %)

Я бы такое на Rust вообще не делал, а сделал бы реализацию через derive(Crc) и все проблемы с repr(packed) просто улетучатся. Это же ты попросил показать как , вот я и показал. А так решается так же, трейт на каждый случай и автоматическая реализация через процедурные макросы.

Давай я просто напишу код для процедурных макросов, а ты такое же на Си и закроем уже этот тупняк? :)

ПыСы. Вообще, тебе ты подучить Rust перестал бы генерировать 90% тупняка.

numas13
()
Ответ на: комментарий от alysnix

Какое еще вычитание типов, уважаемый? Сумма непересекающихся множеств - это будет то же, что и объединение для них же. Просто соглашение такое. Если заранее известно, что множества не пересекаются, то их объединение обозначают как сумму. Еще и предел могут взять до кучи. Упомянутые тобой тег как раз гарантирует, что пересечения нет.

Ладно, вернемся на землю. По сути своей, иерархия наследования может имитировать сложение типов. Где-то это работает хорошо, где-то - не очень. В scala, например, возможны оба варианта. Сумму типов можно выразить и в терминах ООП, и в терминах ФП.

А теперь вернемся к нашим баранам. Нет, C++ - конечно, не бараны, но так просто говорят.

Проблема C++ здесь в том, что для union очень тяжело определить базовые конструкторы, деструктор и операторы присваивания. Геморрой на ровном месте. Для примера можно взять ту портянку, что Страуструп расписал в свой фундаментальной книге «Язык C++». Книга эта, конечно, скучная и занудная невыносимо, но полезной информации там много. Удручает то, как же сложно-то работать с union без std::variant. Тогда как в rust это делается одной левой.

Итак, почему ты так не любишь rust? Я так и не могу разгадать. В чем причина?

Ну-да, растоманы откусывают кусок пирога C++, и откусывают сильно. Только в этом дело? Ты просто видишь в них конкурентов? Стараешься сделать так, чтобы меньше людей изучало rust?

anonymous
()
Ответ на: комментарий от alysnix

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

Ты просто трактуешь термин «алгебра» в его значении из 10 класса средней школы, потому что ничего более не осилил. А вообще алгебра – это любое множество значений и набор операций над ними.

С другой стороны, произведение такие есть. В Rust это банальный кортеж (A, B), потому что его множество его значений будет произведением множеств значений A и B.

anonymous
()
Ответ на: комментарий от anonymous

Тогда как в rust это делается одной левой.

То, что вы описываете как достоинство rust - по имени tagged union существовало еще в алголе 60 и 68, и оттуда разлетелось повсеместно. И вообще непонятно причем тут раст. Почему фича из алгола 60… есть аргумент за раст против Си++? В си есть чистый тип сумма (без тега) - union, и некий «стандартный библиотечный» std::variant, можете написать свой, их вообще полно.

https://en.wikipedia.org/wiki/Tagged_union

Это наоборот аргумент против раста. Они долго не думали, и
срисовали классическую вариантную запись(tagged union) с других языков.

В чем причина?

вот за то, что его адепты втюхивают конструкции из 60 годов, как новации из раста.

по сути - раст есть эклектический набор из проверенного старого и ненужного нового.

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)
Ответ на: комментарий от numas13

Ну смотри, я показал что Rust не дает возможность проверить аттрибуты для безопасности, а С дает, когда как ты говорил, что вариант на Rust якобы безопаснее. Еще два решения на Rust длиннее чем аналогичные на С, и после этого ты еще рассказываешь что я генерирую тупняк, это мягко говоря не так, просто один расто-фанат отказывается признавать правду.

Думаю тебе стоит отказаться от своей растомании, и начать видеть реальность, она перед твоими глазами кстати, смотри выше. А во вторых подучить С, потому что аналог «процедурных макросов» можно писать и на С, в ядре они используются, и рандомизируют порядок полей например.

Знаешь почему ты еще не написал свои чудесные макросы? Потому что это долго, неудобно, легко налажать. А я свои примеры уже написал, и это у меня заняло минуту или две.

MOPKOBKA ★★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 1)
Ответ на: комментарий от anonymous

Какое еще вычитание типов, уважаемый?

ну раз у вас есть сумма типов, то определите и операцию разности. Для пущей полноты своей мегаалгебры, чего стесняться?

если a + b = c, то с - b = a

alysnix ★★★
()
Ответ на: комментарий от MOPKOBKA
trait Packed
    where Self:Sized {
   fn as_bytes(&self)-> &[u8] {
        unsafe {
            let p = &raw const *self as  *const u8;
            slice::from_raw_parts(p, size_of::<Self>())
        }
    }
}
#[repr(C, packed)]
struct Foo {
    m1: u8,
    m2:u32
}
impl Packed for Foo {}

fn main(){
    let foo = Foo{m1:1, m2:2};
    let foobys = [1,2,0,0,0];
    dbg!( qspCRC(&foo.as_bytes()));
    dbg!( qspCRC(foobys.as_slice()));
}
[src/main.rs:65:5] qspCRC(&foo.as_bytes()) = 1370756825
[src/main.rs:66:5] qspCRC(foobys.as_slice()) = 1370756825

трейт Packed ничего не знает про устройство пакованной структуры, и он же является защитным трейтом

zurg
()
Ответ на: комментарий от zurg

он же является защитным трейтом

Это ты к теме о проверке на packed? Ну вот при чистке кода или его переписывании кто то случайно уберет #[repr(C, packed)], и Rust даже не предупредит, а в C будет ошибка, в моем примере выше на godbolt есть демонстрация.

Не заметил защитных свойств, даже структуру можно перепутать если обе Packed.

MOPKOBKA ★★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 1)
Ответ на: комментарий от Silerus

явно принимает значения больше INT_MAX - они и в таблице больше INT_MAX, 0xD202EF8D - это больше INT_MAX

Для битовой операции, тем более xor какая разница? Для нее это просто битовое поле/маска, когда будешь складывать/умножать тогда компилятору надо просто явно указывать (unsigned)0xD202EF8D или 0xD202EF8DU.

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

uin ★★★
()
Последнее исправление: uin (всего исправлений: 1)
Ответ на: комментарий от MOPKOBKA

не менее случайно же в сишке уберётся attribute((packed)), который руками ставится, чем это отличается?

Не заметил защитных свойств,

да кто бы сомневался, посмотри лучше на bytemuck какой-нибудь хоть знать будешь что такое правильный тайппунинг, а не как в сишке. Ну реально совсем уж какие-то клоунские набросы, а если бы компиляторщики не захордкодили проверку свойства типа? а в расте ты можешь свои свойства(трейты) делать и навешивать почти на любые типы, т.е. систематичное общее решение, а не как повезёт

zurg
()
Ответ на: комментарий от zurg

не менее случайно же в сишке уберётся attribute((packed)), который руками ставится, чем это отличается?

Компилятор выдаст ошибку. А, стой, я тебе именно это и написал в предыдущем комментарии. Видимо Rust мозги растворяет посерьезнее любых наркотиков, давай примером покажу, если три слова это уже серьезная когнитивная нагрузка: https://godbolt.org/z/qY4d84sso

Ну реально совсем уж какие-то клоунские набросы

Я спросил где защитные свойства, вместо этого подобный ответ... Неужели потому что Rust не только НЕ предоставляет дополнительных гарантий по сравнению с С, но и наоборот, имеет их МЕНЬШЕ?

MOPKOBKA ★★★★★
()
Ответ на: комментарий от alysnix

все эти memset, getmem, memcpy и прочие, используют void* и не могут использовать char*(или u8*), потому что пришлось бы или везде втыкать преобразования типов в u8*, или делать u8* совместимым со всеми прочими указателями, и сломать строгую типизацию тут. то есть если T* будет по дефолту приводиться к u8*

это в сишке не могут, в которой типы мало того что предельно убогие, но даже то что есть напрочь сломано, поэтому и нужен void*, и char - особенный тип с алиасингом который на самом деле байт но какого-то хрена ещё и символ и числа и это вообще не различимо. В нормальном же языке есть параметрические типы и прочие виды полиморфизма плюс автовывод. Посмотри что стандартный аллокатор возвращает https://doc.rust-lang.org/std/alloc/trait.GlobalAlloc.html, и на этом все контейнеры построены, и u8 при этом обычный тип хоть и используется как «сырой» байт, почему бы собственно и да, так как у чиселок есть битовые операции, которые и для байтов полезны.

И, да, препроцессорная твоя опердень сегфолтится, гы, символично.

zurg
()
Ответ на: комментарий от zurg

В нормальном же языке есть параметрические типы и прочие виды полиморфизма плюс автовывод

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

а параметрические свои типы в мусорку снеси. в данном случае они не нужны. как и половина вашего раста.

Посмотри что стандартный аллокатор возвращает

спасибо за антипаттерн. когда вы там в расте уже поймете, что u8 это не байт, а арифметический тип строго говоря. а байт это даже не 8 бит, потому что 8 бит - это битсет. байт это просто байт. - нечто обладающее 256 состояниями.

короче все типы у раста в куче. в сишке тоже своя кучка имеется, но там хоть void* есть.

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)
Ответ на: комментарий от MOPKOBKA

это полностью эквивалентно убиранию impl Packed for Foo {}, в растовой версии, какая нахрен разница? Реально вообще будет derive макрос который одновременно добавит или удалит трейт и соответсвующий repr, клиент макроса не сможет случайно что-то не то удалить. Вот только в отличие от сишечки ты можешь свои защитные трейты делать, а не только то что тебе компиляторщики подарят.

Сишечка… гарантии… тем более в сравнении с растом, ну вот сам же подставляешься и позоришься

zurg
()
Ответ на: комментарий от zurg

И, да, препроцессорная твоя опердень сегфолтится, гы, символично.

в голове у тебя сегфолты. работает все. вот по мотивам твоего косорукого примера

https://godbolt.org/z/4xs3jax4e

там сверху ваш раст(никому не нужный), снизу си в двух вариантах - считая в лоб и с макрой. самый визуально чистый код - с макрой естессно.

alysnix ★★★
()
Ответ на: комментарий от zurg

Посмотри что стандартный аллокатор возвращает https://doc.rust-lang.org/std/alloc/trait.GlobalAlloc.html,

unsafe fn alloc(&self, layout: Layout) -> *mut u8;

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

я просто не очень понимаю, как после такой декларации писать «защищенный код». тут вообще-то написано - не пользуйте ее, она вам все испортит :) а safe аллокаторы у вас имеются?

alysnix ★★★
()
Ответ на: комментарий от zurg

это полностью эквивалентно убиранию impl Packed for Foo {}, в растовой версии, какая нахрен разница?

Ты потерял мысль, я говорил не про убирание Packed, а про убирание #repr[C, pack].

Вот только в отличие от сишечки ты можешь свои защитные трейты делать

Трейтов в сишке нету, но к теме это не относится. Изначально я говорил о том что в Rust очень разрастается код на таком простом действии как преобразование в байты.

Реально вообще будет derive

Это уже другая тема, с помощью derive можно сделать хорошо, только это длиннее и вообще уже от части разработка своего языка.

MOPKOBKA ★★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 2)

Человек по недосмотру скорее всего unsigned не поставил, а вы это фичей считаете.

Результат функции от этого может и изменится, а фактическая правильность кода нет. Потому что от хэша требуется только быть разным при разных входных данных. Речь о конкретных значениях идёт только в контексте совместимости с другими программами.

Так или иначе реализовать это в любом другом языке нет никаких проблем.

unDEFER ★★★★★
()
Ответ на: комментарий от MOPKOBKA

Ну смотри, я показал что Rust не дает возможность проверить аттрибуты для безопасности, а С дает,

Ещё раз повторяю, проверить атрибут можно в процедурном макросе и реализовать трейт. Если атрибута не будет то сгенерировать ошибку.

#[derive(Packed)]
#[repr(C, packed)] // checked in derive proc macro
struct Foo {
    // ...
}

// generated if repr(packed)
unsafe impl Packed for Foo {}

// generated if not repr(packed)
compile_error!("missing repr(packed) for struct Foo")

Еще два решения на Rust длиннее чем аналогичные на С, и после этого ты еще рассказываешь что я генерирую тупняк, это мягко говоря не так, просто один расто-фанат отказывается признавать правду.

Давай пиши код и выкладывай, а потом расскажешь про кол-во кода.

#[derive(Default)]
pub struct CrcState32 {
    crc: u32,
}

impl Hasher for CrcState32 {
    fn write(&mut self, bytes: &[u8]) {
        for i in bytes {
            let index = (self.crc as u8 ^ i) as usize;
            self.crc = ((QSP_CRC_TABLE[index] ^ self.crc) as i32 >> 8) as u32 ^ 0xD202EF8D;
        }
    }

    fn finish(&self) -> u64 {
        self.crc as u64
    }
}

pub fn crc32<T: Hash>(value: &T) -> u32 {
    let mut state = CrcState32::default();
    value.hash(&mut state);
    state.finish() as u32
}

#[derive(Copy, Clone, Hash)]
#[repr(C, packed)]
struct Foo<T> {
    x: T,
    y: u32,
}

#[derive(Hash)]
#[repr(C)]
struct Bar<T> {
    f: T,
    x: u8,
    y: u32,
}

rust-playground

Думаю тебе стоит отказаться от своей растомании, и начать видеть реальность, она перед твоими глазами кстати, смотри выше. А во вторых подучить С, потому что аналог «процедурных макросов» можно писать и на С, в ядре они используются, и рандомизируют порядок полей например.

Я и предлагаю тебе написать, учесть все свои претензии к коду на Rust и потом сравнить. Я даже готов тебе подыграть и сделать реализацию на процедурных макросах, а не задействовать Hash/Hasher из std, т.к. твой тупняк уже достал.

Знаешь почему ты еще не написал свои чудесные макросы? Потому что это долго, неудобно, легко налажать.

facepalm.jpg

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

numas13
()
Ответ на: комментарий от numas13

Ещё раз повторяю, проверить атрибут можно в процедурном макросе и реализовать трейт.

И опять кода выйдет в разы больше + ты даже не знаешь как это сделать.

Давай пиши код и выкладывай, а потом расскажешь про кол-во кода.

Все примеры есть выше.

rust-playground

Ты же понимаешь что там опять нету проверки pack? Rust это не С, он не гарантирует точный порядок полей без repr(C, pack). И опять кода больше, и невозможно положить f32 в структуру. Переделывай, что бы работало.

Я и предлагаю тебе написать, учесть все свои претензии к коду на Rust и потом сравнить.

Дело в том что С не требует написания процедурных макросов, я написал два примера выше с подсчетом crc32 структуры, и оба намного меньше чем предложенные тобой макросы в Rust.

Первый пример это был прямой вызов crc32 по указателю на структуру, второй это X Macro, если ты уже забыл.

Знаешь почему ты еще не написал свои чудесные макросы? Потому что это долго, неудобно, легко налажать.

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

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

MOPKOBKA ★★★★★
()
Ответ на: комментарий от numas13

Выше по треду полно кода, от меня и от alysnix, выбирай какой больше нравится.

Хотя я конечно знаю что никакой код тебе не нужен, ты его уже видел, просто не хочешь признавать какое раст убожество.

MOPKOBKA ★★★★★
()
Ответ на: комментарий от numas13

Сектанту совсем плохо стало... Ручной способ записи каждого поля показывал именно ТЫ, я его никогда не показывал.

Я кстати не вижу пояснений по моему предыдущему комментарию. Что у тебя за странный пример, не учитывающий порядок, и не проверяющий pack? Когда ты покажешь настоящий пример, без ручного вписывания каждого поля?

Смотри как умею кстати, твой пример не собирается на point: https://godbolt.org/z/xeYvcv65s

MOPKOBKA ★★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 3)
Ответ на: комментарий от MOPKOBKA

Ахахахахаха.

То что сложно реализовать на других языках (комментарий)

#define X_POINT \
  X(int, x) \
  X(int, y) \
  X(int, z) \
  X(int, w)

struct point {
#define X(T, N) T N;
  X_POINT
#undef X
};

void point_crc32(const struct point *p, crc32_state_t *state)
{
#define X(_, N) crc32(state, &p->N, sizeof(p->N));
  X_POINT
#undef X
}

void point_crc64(const struct point *p, crc64_state_t *state)
{
#define X(_, N) crc64(state, &p->N, sizeof(p->N));
  X_POINT
#undef X
}

Отвечу твоей же цитатой:

Ахах, вот оно будущее с Rust, вместо crc(&my_struct) нужно описать crc_my_struct где собрать все поля, и не забыть подструктуры, и не забыть при добавлении нового поля изменить все crc32_my_struct, crc64_my_struct, xxx_my_struct.

Это же так круто дублировать код, и выполнять самую тупую работу машинистки переписывая все поля. Отягощается все отсутствием X Macro из сишки.

А теперь серьёзно. Цитирую, что я написал в первом сообщении на которые ты ответил.

Можно ещё обмазаться процедурными макросами для автоматического derive(Crc), но это уже сам копай если интересно.

Всё остальное это твой тупняк…

numas13
()
Ответ на: комментарий от numas13

А что ты смеешься? Тут кода меньше, чем будет в Rust. И не забывай, что ты даже не смог показать повторение этого примера на Rust, только лоускильный пример с незафиксированным порядком полей, и без поддержки базовых типов.

Надоело слушать твое повторение про процедурные макросы, как говорил Линус - заткнись и покажи код.

MOPKOBKA ★★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 5)
Ответ на: комментарий от alysnix

ну раз у вас есть сумма типов, то определите и операцию разности. Для пущей полноты своей мегаалгебры, чего стесняться?

Сумма была из теории множества. Тип данных - это множество значений. Алгебра над типами строится на основе другого множества, где элементами являются уже сами типы. Получается, множество подмножеств.

Ладно, вернемся на землю снова. Ты же понимаешь, что ты ругаешь то, в чем не разбираешься? Кстати, уже не первый раз. Прошлый раз было с семантикой перемещения.

Ты понимаешь, что при таком подходе желающих изучать раст может появиться больше? Хотя C++ тоже надо знать в 2025 году безусловно. Причем, необязательно выбирать или-или. Можно использовать оба языка.

И достоинства раста не в том, что они там что-то новое придумали. Я даже не знаю, что они там нового придумали. Достоинство в том, как они там все вместе собрали органично и красиво. В rust есть «система».

В C++ система тоже была на ранних этапах. Но сейчас там больше спешная достройка и пристройка вещей, которая не совсем совместима с ядром языка. Как тот же пример - семантика перемещения. Не случайно, что она вызывает сложности, поскольку немного инородна для C++. Тогда как семантика перемещения в rust (она отличается в деталях) - плоть от плоти сам язык rust.

Понимаешь, не поспевает язык C++ за некоторыми современными идеями. Хотя компиляторы C++ хороши, почти безупречны, - тут спору нет

anonymous
()
Ответ на: комментарий от anonymous

Ладно, вернемся на землю снова.

погоди. есть вопросы

  1. в твоей мегаалгебре типов умножение и сложение коммутативны?

видно что нет, поскольку

произведение

struct {int, float} =/= struct {float, int} - тут неравенство очевидно. эти типы равны быть не могут с точки зрения строгой типизации

и сумма

summ {int, float} =/= summ {float, int} - тут как минимум будут разные теги у значений одного типа. это тоже разные типы

также это все не ассоциативно, и не дистрибутивно.

то есть у тебя некая «алгебра» вида кольцо, у которого не выполняется ни один приличный закон. и ты называешь это теорией множеств? у теории множеств операций как минимум 5, и хотя бы сумма коммутативна.

короче кончай эту болтовню про «алгебры», в данном случае это притянутое за уши нечто, исключительно ради наукообразия.

ходит тут с надутым пузом и «типом-суммой». стыд один.

alysnix ★★★
()
Ответ на: комментарий от anonymous

В rust есть «система».

ну так покажи ее. своими словами. не видно никакой особенной системы кроме какой-то эклектики с открытыми воротами для дальнейших расширений.

вот прям видится как туда добавят в конце концов исключения. это потребует строгого raii, то есть конструкторов-деструкторов, это потянет за собой нормальные классы, а не структуры. Классы потянут виртуальность деструкторов, для красивой жизни придется делать наследование в классах, еще какие-нить миксины, и пошло поехало. То есть или пойдет закос в с++, или все застрянет на текущем этапе изнурительной реализации трейтов для посторонних структур.

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

они кстати так и не поняли, что операции навроде ^ тупо не нужны. это деление множеств, и выражение вида a/b должно быть делением для арифметических типов, и хоr для типов навроде - множество бит.

тут они содрали с си, а в си это историческое наследие и слабая типизация в данном месте (нет разделения типов на нумерические - арифметика и битовые - работа с битами и сдвиги).

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

alysnix ★★★
()
Ответ на: комментарий от MOPKOBKA
use qsp::{Crc, Packed, crc32, crc64};

#[derive(Copy, Clone, Packed)]
#[repr(C, packed)]
struct Foo {
    x: u8,
    y: u32,
    z: u8,
}

#[derive(Crc)]
#[repr(C)]
struct Bar<T> {
    f: T,
    x: u8,
    y: u32,
}

#[derive(Crc)]
struct Baz<'a> {
    x: &'a u8,
    y: &'a u32,
    z: &'a u8,
}

fn main() {
    let f = Foo { x: 1, y: 2, z: 3 };

    dbg!(crc32(&Foo { x: 1, y: 2, z: 3 }));
    dbg!(crc32(&Baz { x: &1, y: &2, z: &3 }));
    dbg!(crc32(&[1_u8, 2, 0, 0, 0, 3]));
    dbg!(crc32(&Bar { f, x: 1, y: 2 }));
    dbg!(crc32(&[1_u8, 2, 0, 0, 0, 3, 1, 2, 0, 0, 0]));

    dbg!(crc64(&Foo { x: 1, y: 2, z: 3 }));
    dbg!(crc64(&Baz { x: &1, y: &2, z: &3 }));
    dbg!(crc64(&[1_u8, 2, 0, 0, 0, 3]));
    dbg!(crc64(&Bar { f, x: 1, y: 2 }));
    dbg!(crc64(&[1_u8, 2, 0, 0, 0, 3, 1, 2, 0, 0, 0]));
}
❯ cargo run -q
[src/main.rs:30:5] crc32(&Foo { x: 1, y: 2, z: 3 }) = 3531519846
[src/main.rs:31:5] crc32(&Baz { x: &1, y: &2, z: &3 }) = 3531519846
[src/main.rs:32:5] crc32(&[1_u8, 2, 0, 0, 0, 3]) = 3531519846
[src/main.rs:33:5] crc32(&Bar { f, x: 1, y: 2 }) = 3525862077
[src/main.rs:34:5] crc32(&[1_u8, 2, 0, 0, 0, 3, 1, 2, 0, 0, 0]) = 3525862077
[src/main.rs:36:5] crc64(&Foo { x: 1, y: 2, z: 3 }) = 3531519846
[src/main.rs:37:5] crc64(&Baz { x: &1, y: &2, z: &3 }) = 3531519846
[src/main.rs:38:5] crc64(&[1_u8, 2, 0, 0, 0, 3]) = 3531519846
[src/main.rs:39:5] crc64(&Bar { f, x: 1, y: 2 }) = 3525862077
[src/main.rs:40:5] crc64(&[1_u8, 2, 0, 0, 0, 3, 1, 2, 0, 0, 0]) = 3525862077

https://github.com/numas13/qsp-crc

Надоело слушать твое повторение про процедурные макросы, как говорил Линус - заткнись и покажи код.

Теперь твоя очередь. Только в этот раз давай без тупых отмазок.

numas13
()
Ответ на: комментарий от numas13

Пошли сотни строк на Rust, и все равно repr проверяется некорректно, enum не поддерживается, тем временем на С делает свою работу X Macro вполне успешно и без дополнительной писанины в сотни строк.

MOPKOBKA ★★★★★
()