LINUX.ORG.RU

C++ — туда и обратно, или зачем нужен Boost

 , , ,


3

5

Мой предыдущий тред в Development собрал самое большое число ответов аж с сентября, то есть за последние 9 месяцев, и это лишний раз подтверждает упадок этого форума. Полагаю, кто-то должен это изменить, и поэтому мы с тобой, ЛОР, поговорим сегодня про C++.

Начиная с C++26 вместо std::function вводится пачка новых классов: std::copyable_function, std::move_only_function (доступна с C++23) и std::function_ref. Что же не так с оригинальным std::function, ты можешь спросить? А вот что:

#include <functional>
#include <print>

struct call_me {
    int x = 0;
    void operator()() {
        std::print("x was {}\n", x++);
    }
};

int main() {
    const std::function<void()> f = call_me{};
    f();
}

Несмотря на то, что переменная f объявлена константной (люблю оксюмороны!), у неё есть внутреннее состояние и оно меняется при вызовах. Компилятор это без проблем хавает.

Так вот, ковыряясь в том, зачем и кто вообще смог так насрать себе в штаны сам, я наткнулся на статью ребят, которые подводят список подобных косяков комитета C++, когда фичи живут по 10 лет и объявляются устаревшими.

Небольшой список фич, которые были придуманы, оказались не нужны/бесполезны/вредны и выкинуты:

  • Известный vector<bool>, живущий издревле в STL и про который все говорят, что его надо избегать. Частично заменяется std::bitset.
  • std::auto_ptr. Бесполезен, ломает контейнеры, выкинут на помойку в C++17.
  • Указание исключений у функции в формате throw(X, Y). Так же выкинуто в C++17.
  • std::iterator объявили устаревшим в C++17, собираются удалить в C++26.
  • std::aligned_storage и std::aligned_union добавлены в C++11, объявлены устаревшими в C++23, скоро удалят.
  • Ключевое слово register удалено в C++17, хотя всё ещё доступно в Си.
  • std::get_temporary_buffer и std::raw_storage_iterator удалены в C++20.
  • Потрясающее по эпичности фиаско с интерфейсом для сборщиков мусора. std::declare_reachable сотоварищи были добавлены в C++11. Выяснилось, что сборщики мусора для C++ писать либо никто не умеет, либо никто не хочет, поэтому в C++23 это всё удалили и сделали вид, что ничего не было.
  • Абсолютное безумие вокруг концептов, модулей и поддержки сети. Предложения одобряли, вновь отклоняли, переделывали, и по итогу теми же модулями до сих пор никто не пользуется.
  • Сопрограммы (coroutines). В том виде, в котором они есть в C++, это просто ужас. Достаточно того, что корутины требуют выделения памяти из кучи во время работы, а значит вообще не подходят для случаях, когда требуется серьёзная производительность. Например, в любом коде, требующим работы в реальном времени и не позволяющем делать системные вызовы.

Просто лютый трешак, который никто подчищать пока не собирается:

  • std::regex – лютый тормоз, рекомендуется не использовать.
  • Мертворождённый std::simd, добавленный в C++26 и уже с ходу не нужный вообще никому, потому что код с std::simd в два-три раза тормознее чем со сторонними библиотеками, просто голыми интринсиками, и даже медленнее чем просто цикл for.
  • std::async. Дескруктор ждёт завершения асинка и поэтому может залочить весь код тебе. Наконец починили в C++26, но эта штука была сломана 15 лет.
  • Отвратительно спроектированный <iostream>. Его наконец можно выкинуть и использовать std::print, но не все про это знают.
  • Абсолютно тормозные контейнеры map, set, unordered_map. Вместо первого можно использовать flat_map из C++23. Контейнеры в стандартной библиотеке Rust (BTreeMap) и другие реализации B-Tree Map их обгоняют по производительности, но тем не менее в C++ выбирают убогий дефолт.

Решения многих из этих проблем существуют в Boost и сделаны там гораздо лучше. Но в то же время возникает важный вопрос: зачем вообще нужен настолько плохо спроектированный язык, где каждое следующее поколение инженеров, работающих над ним, отменяет решения предыдущих, а код под новые стандарты часто нужно переписывать если не с нуля, то очень близко к тому? Даже процесс разработки Rust с его поехавшими клоунами в юбках на этом фоне выглядит адекватным.

В общем, всё печально, ЛОР. Такие дела.



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

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

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

а их и не надо форкать - достаточно не инклудить stl и «тяжесть» новых плюсов пройдёт

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

тебе-то главное что auto заработало в выводе типа функции

Прикольно когда хз кто объясняет что для тебя главное и как нужно поступать.

Верной дорогой идете товарищЪ.

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

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

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

Если пул в вашей терминологии это аллокатор, то пул это аллокатор. Мне тут спорить не с чем.

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

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

ad0c
()

const st...

зачем тебе это? тебе надо чтобы тело функции неизменно было? там у нутри хоть рандом(), хоть goto:, что тебе от этого?
ну кот функции не будет менятся с dt рантайма, чего добился то?

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

а это точно не баг?

ты у константного объекта вызываешь по сути метод (оператор тоже метод) который не имеет пометки const и это срабатывает. да?

это выглядит как баг компилятора.

ckotctvo
()

Потыкался я таки в примере с функцией. Ладно допустим ты нашел способ обмануть систему. Ну да, std function сама константна но не может проконтроллировать объект внутри себя. это цена свободы. подозреваю что это поведение можно починить… хотел бы я сказать но посмотрев реализацию в gcc понял откуда ноги растут и где там по дороге теряется const. В прослойке _M_invoker. Вхуж и нету const.

И что хочу сказать. Тут ниже уже приводили пример поделки от китайца которая делает печать в консоль здорового человека и весит 100кб против мегабайта iostream. Так вот, gccшная реализация - это точно такой же пример std::function курильщика. Отчасти это так потому, что есть полиморфизм и нет понимания как с ним бороться потому что делают все это не здоровые люди а курильщики. То есть сделать std::function можно, но в рамках возможностей gcc (там всякие хитрые __bla_blabla уходящие в нутро gcc) это сделать сложно.

Что касается буста - тут да, я не представляю как можно без него жить. Впрочем и его не хватает: например мне пришлось писать своё самопальное красночерное дерево и двусвязный список только потому что мне boost.intrusive не позволяет мне двигать в памяти объекты вставленные в контейнер, и до кучи страдает идиотскими ограничениями на unlink(). Но такова цена свободы. Один раз сделал и после этого дальнейшая работа в разы ускоряется. Если бы я писал на расте, я бы ничего сделать не смог с его дебильным боровом.

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

Ну понятненько, ничего не смог сделать ты, но дебильный, конечно же, бороучекер

Он не так уж и не прав. Вся эта эпопея с borrow checker имеет вполне ощутимые издержки: в частности, усложняет быструю итерацию при начальной разработке, когда ты заранее ещё сам не знаешь что именно хочешь сделать и просто делаешь всё подряд.

https://loglog.games/blog/leaving-rust-gamedev/

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

Вся эта эпопея с borrow checker имеет вполне ощутимые издержки:

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

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

Вся эта эпопея с borrow checker имеет вполне ощутимые издержки:

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

Да нет, реальная проблема – как раз геморрой с перекрёстными ссылками, которые в ECS (wikipedia.org) постоянно вылезают. Это можно обходить (о чём и написано в статье), но зачем тогда Rust, если у тебя код усеян Refcell на каждый чих?

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

я то как раз смог потому что пишу на С++.

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

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

Ну-ну, этот товарищ умеет в списки: одно-, дву-, и более связные! Уязвимости в проекте Pingora, позволяющие вклиниться (комментарий)

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

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

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

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

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

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

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

Вся эта эпопея с borrow checker имеет вполне ощутимые издержки:

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

Да нет, реальная проблема – как раз геморрой с перекрёстными ссылками,

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

In Rust, it’s always a choice of do I add an 11th parameter to this function, or add another Lazy<AtomicRefCell<T>>, or do I put this in another god object, or do I add indirection and worsen my iteration experience, or do I spend time redesigning this part of code yet again.

А плач этот вызван тем, что раст и его боров пробрасывают логику владения данными сквозь стэк вызывов, засоряя этой логикой интерфейс. В условных крестах void f(const std::string &s) можно лёгким движением руки превратить в void f(std::string s) без модификации клиентского кода. А в расте нужно тугосерить клонами в вечном цикле.

r--r--r--
()
Ответ на: комментарий от shdown

самый прикол, что растаманы МОГЛИ БЫ сделать двусвязные списки разрешив «обратную ссылку». То есть если объект А владеет на Б, то Б может иметь иммутабельную ссылку на А. можно для нее ввести новый тип «обратная ссылка». будет аналог HLIST из ядра. далее можно сделать красно-черные деревья, и прочие алгоритмы как в С++. удаляем владеющую ссылку - автоматом чистим восходящую ссылку. Проблема решена!

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

ckotctvo
()
Ответ на: комментарий от r--r--r--

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

Это лишь одна из проблем. Таких проблем там описано много.

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

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

А можно ссылки на статьи?

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

там везде плач на вынужденный рефакторинг:

Это лишь одна из проблем.

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

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

https://godbolt.org/z/qjx38q9fv

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

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

у раста модель заточена на владение, если мы начнем писать что-то многопоточное, у меня маленький опыт, то там будет выбор, самый возможно на первый взгляд не тривиальный, если графика сандбокс, то владелец канал потоков по дистанции(например 8/16 потоков они будут владельцами и причем классно по Arc работало у меня по крайней мере). Конструкторы вынесены в расте в поле дерайв(так же можно классно писать интерфейсы, и new удобный) - derive Copy,Clone,и прочее есть даже дефолт. у раста возможно самое топ отличие когда начнем сравнивать архитектуры на С++ ООП с наследованием например UI, и точно такой функционал на расте, там наверно ближе к дод(или я писал на расте в стиле дод только, так и до многопоточки добрался mpsc), но деревья конечно можно тоже создавать, просто на С++ это буквально пара строчек, читаемо. У раста удобная инфраструктура, впринципе можно и на расте оставаться и для блеска иногда на зиг заглядывать ради векторизации ис каробки и комптайм, и юнион удобной.

вообщем что-то динамическое с наследованием по мне на С++ удобно, lua там с json/xml, или еще что.

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

Наоборот же всё, как раз в плюсах дефолтное поведение максимально кривое. Из-за чего и «мув семантика» чудовищно уродливая и вся эта канитель с расписыванием - конструктор такой, оператор сякой, какие-нибудь explicit-ы по пути не забыть, вообще сишный автокастинг, дефолтная НЕинициализация переменных, [] в векторе должен был бы проверять индексы и пр. - плюсы просто переполнены такими косяками.

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

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

Наоборот же всё, как раз в плюсах дефолтное поведение максимально кривое.

Вот тебе простенький безмозглый сниппет:

#include <cstdint>
#include <iostream>

struct A
{
    uint32_t i_;
    A() = default;
};

class B
{
    struct A a;
public:
    void inface_method()
    {
        // modify internal state
        a.i_++;
        std::cout << a.i_ << std::endl;
    }
    void print_a()
    {
        std::cout << a.i_ << std::endl;
    }
};

template <typename T>
void f(const T &v)
{
    T copy = v;
    copy.inface_method();
}

int main(void)
{
    B b = B();

    f(b);

    b.print_a();
    return 0;
}

Попробуй состряпать такой же на расте и сравни объём приседаний.

А потом попробуй прикинуть, что будет, если A не из твоего кода, а из библиотеки и её автор не озадачился впендюрить #[derive(Clone, Default)].

В расте форсится мувинг,

И это корёжит весь код. "Мувинг" реально нужен раз в тыщу лет. Копии - всегда везде и постоянно. Фрос экономии байтов мувингом только создаёт гемморой на ровном месте.

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

её автор не озадачился впендюрить #[derive(Clone, Default)].

О, да. Категорический запрет на orphaned instances меня безумно вымораживает. И если автор просто забыл, то это ещё фигня – можно форкнуть, добавить строчку и заслать PR. Гораздо хуже, когда у тебя трейт из одного пакета, а тип – из другого, оба от разных авторов и никак не зависят друг от друга.

yorshka
() автор топика
Ответ на: комментарий от r--r--r--
struct A(pub i32);

struct B(A);

impl Clone for B {
    fn clone(&self) -> Self {
        Self( A(self.0.0) )
    }
}
trait Inface { fn inface(&mut self); }
impl Inface for B {
    fn inface(&mut self){
        self.0.0 += 1;
        println!("{}", self.0.0);
    }
}
impl Default for B {
    fn default() -> Self {
        Self(A(42))
    }
}
impl B {
    pub fn print_a(&self){
        println!("{}", self.0.0);
    }
}

fn f<T: Clone+Inface>(v:&T){
    let mut copy = v.clone();
    copy.inface();
}
fn main() {
    let b = B::default();
    f(&b);
    b.print_a();
}

Прямо вот употел приседая (нет). В чём проблема-то, собственно? UB, пардон, я повторять не буду. И почему растовик вдруг забудет Copy, (а так же Clone c Debug), необходимость рассмотрения которых вылезет сразу же после первой попытки тестировать? Плюсовик же вот не забыл поставить struct вместо class и A() = default? И это у плюсовика вероятность забыть что-то подобное намного выше - язык дико раздутая и измученная стандартизацией помойка. А раст это как раз язычок специально спроектированный чтобы компилятор не давал забывать и не прощал. А вот попробуй повторить в плюсах что-то такое:

    // метод  в любом типе
    pub fn move_out(self)->Self {
        self

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

И это корёжит весь код.

Это только если мозг покорёжен дурными сишными привычками. Можно так рассматривать: мув - это такая минимальная фундаментальная операция, сохраняющая его идентичность и максимальный контроль над всевозможными контрактами/инвариантами объекта данного типа. Копирование - это дополнительная фича, уже в какой-то мере ослабляющая/размывающая этот контроль, соответсвенно должна добавляться с осознанием программистом. Что консистентно с идеей языка, который позиционируется как системный, т.е. надёжный. А значит по дефолту всё должно быть максимально ограниченное - инициализированое, иммутабельное, приватное, не копируемое и т.д.

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

Прямо вот употел приседая (нет). В чём проблема-то, собственно?

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

struct A
{
    uint32_t i_;
    // ещё много много членов структуры
    // ...
};

А теперь вот вот это же

impl Clone for B {
    fn clone(&self) -> Self {
        Self( A(self.0.0) )
    }
}

Для структурки в несколько десятков членов и тремя уровнями вложенности. Может, тогда дойдёт.

И почему растовик вдруг забудет Copy

Ты гарантируешь, что каждая структурка во всех трейтах имеет Copy? 75%? 50% ?

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

Секта скопцов, второе пришествие.

позиционируется как системный

Да всем насрать, на нём копроративную вебню лабают сейчас в первую очередь.

r--r--r--
()
Ответ на: комментарий от yorshka

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

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

я когда первое сообщение писал, думал вступить не так как там, сейчас по моим прикидкам есть такая ситуация какую я вижу, если мы говорим о минимализме скачивания зависимостей, то на расте с нуля это будет зависимость от С - тоесть от системы. Java 25 - например уже может использовать схожий механизм jextract, C++ это имеет по умолчанию поидее, получается у нас выбор Java/Rust/C/C++, типо удобство FFI, например можно в расте воспользоваться bindgen, а можно и без биндген обойтись, как и serde, всё зависит от желания уйти от зависимости поиидее.

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

Тогда можно пойти от обратного,

#include <iostream>
#include <vector>
#include <algorithm>
#include <ranges>
int main() {
    std::vector<bool> temp{true, false, true, false, false, true, true};

    // Инвертируем каждый элемент на месте
    std::transform(temp.begin(), temp.end(), temp.begin(), [](bool b) {
        return !b;
    });
    for(auto b: temp) std::cout << b << std::endl;
    std::cout<<std::endl;

    bool a=true;
    // std::views::enumerate возвращает кортеж (индекс, элемент)
    for (auto [idx, elem] : std::views::enumerate(temp)) {
        if (idx % 3 == 0 && elem == a) {
            elem = !elem;
        }
    }


    for(auto b: temp) std::cout << b << std::endl;
    return 0;
}

делаем класс, и договариваемся что он такой-то выделяя под такие классы отдельную шаблонную логику, получается у нас 2 типа абстракций, сама абстракция обьект, и абстрактные примитивные обертки наверно

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

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

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

если пытаться приблизиться к Расту,

#include <iostream>
#include <vector>
#include <ranges>

std::vector<bool> find_and_modify(const std::vector<bool>& input) {
    bool a = true; // Ваше условие "равно а"

    return input 
        | std::views::enumerate // 1. Превращаем в поток пар [индекс, элемент]
        | std::views::transform([a](auto pair) {
              // Распаковываем пару внутри лямбды
              auto [idx, elem] = pair; 
              
              // 2. Если условие выполняется — инвертируем, иначе — возвращаем как есть
              if (idx % 3 == 0 && elem == a) {
                  return !elem;
              }
              return static_cast<bool>(elem); 
          })
        | std::ranges::to<std::vector<bool>>(); // 3. Выделяем память и собираем результат
}

int main() {
    std::vector<bool> temp{true, false, true, false, false, true, true};

    auto result = find_and_modify(temp);

    for (bool b : result) std::cout << b << " "; 
    // Выведет измененный вектор
    return 0;
}

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

anonymous
()

Насчет модулей я пользуюсь, но на винде и маке я не смотрел, теоретически если перейти на кланг-20+ то будет работать, скрипт билдера не скину, но сам модуль вот пожалуйста.

// base2D.cppm
module; // Начало глобального фрагмента
//clang++-20 -std=c++23 -stdlib=libc++ --precompile -o std.pcm /usr/lib/llvm-20/share/libc++/v1/std.cppm
//clang++-20 -std=c++23 -stdlib=libc++ -fmodule-file=std=std.pcm main.cpp -o app -lSDL2
//clang++-20 -std=c++23 -c base2D.cppm --precompile -o base2D.pcm
//clang++-20 -std=c++23 -c base2D.pcm -o base2D.o
//clang++-20 -std=c++23 -stdlib=libc++ -fmodule-file=std=std.pcm -fprebuilt-module-path=. main.cpp base2D.o -lSDL2 -o app
#include <SDL2/SDL.h> // Все инклуды ДОЛЖНЫ быть здесь
#include <SDL2/SDL_image.h>
#include <SDL2/SDL_mixer.h>
#include <map>
#include <string>
#include <memory>
#include <functional>
#include <vector>
#include <map>
export module base2D;

export struct Options{
    std::string name="Test";

    int posx=SDL_WINDOWPOS_CENTERED,
    posy=SDL_WINDOWPOS_CENTERED,
    width=1290,
    height=720,
    index=-1;

    uint32_t window_flags=SDL_WINDOW_ALLOW_HIGHDPI|SDL_WINDOW_RESIZABLE,
    render_flags=SDL_RENDERER_ACCELERATED|SDL_RENDERER_PRESENTVSYNC,
    sdl_init=SDL_INIT_VIDEO|SDL_INIT_AUDIO,
    sdl_image_init=IMG_INIT_PNG;
};
// Теперь экспортируем нужные функции или структуры
export using SDL_Event = ::SDL_Event; // Экспортируем тип из глобального пространства
export using SDL_Window = ::SDL_Window;
// Экспортируем функции (теперь они часть модуля)
export using ::SDL_SetRenderDrawColor;
export using ::SDL_CreateTexture;
export using ::SDL_CreateRGBSurface;
export using ::SDL_CreateTextureFromSurface;
export using ::SDL_RenderClear;
export using ::SDL_RenderPresent;
export using ::SDL_PollEvent;
export using ::SDL_FillRect;
export using ::SDL_RenderDrawRect;
export using ::SDL_RenderFillRect;
export using ::SDL_MapRGB;
export using ::SDL_RenderCopy;
export using ::SDL_HasIntersection;
export using ::SDL_GetMouseState;
export using ::SDL_GetWindowSize;
export using ::SDL_SetWindowSize;
export using ::SDL_SetWindowPosition;
export using ::SDL_Quit;
export using ::SDL_CreateRenderer;
// Экспортируем типы, которые эти функции используют
export using ::SDL_Renderer;
export using ::SDL_Window;
export using ::SDL_Texture;
export using ::SDL_Surface;
export using ::SDL_Rect;
export using ::SDL_Color;
export using ::SDL_Point;
export using ::Uint32;
export using ::IMG_Load;


// Универсальный хелпер для SDL ресурсов
export template<typename T, auto Deleter>
using SDL_UniquePtr = std::unique_ptr<T, std::integral_constant<decltype(Deleter), Deleter>>;


// Теперь создание новых типов превращается в элегантный список:
export {
    using WindowPtr  = SDL_UniquePtr<SDL_Window,   SDL_DestroyWindow>;
    using RenderPtr  = SDL_UniquePtr<SDL_Renderer, SDL_DestroyRenderer>;
    using TexturePtr = SDL_UniquePtr<SDL_Texture,  SDL_DestroyTexture>;
    using SurfacePtr = SDL_UniquePtr<SDL_Surface,  SDL_FreeSurface>;
    using SoundPtr = SDL_UniquePtr<Mix_Chunk, Mix_FreeChunk>;
}

export class EventManager {
public:
    using Handler = std::function<void(const SDL_Event&)>;

    // Подписка на событие конкретного типа
    void subscribe(Uint32 eventType, Handler handler) {
        handlers[eventType].push_back(handler);
    }

    // Обработка всех накопившихся событий SDL
    void update() {
        SDL_Event event;
        while (SDL_PollEvent(&event)) {
            if (handlers.count(event.type)) {
                for (auto& handler : handlers[event.type]) {
                    handler(event);
                }
            }
        }
    }

private:
    std::map<Uint32, std::vector<Handler>> handlers;
};
//...

так же очень удобно теперь стало пользоваться самим import std; можно сначала собрать прототип проверку как обычно на инклудах, и потом всё это прокинуть в модуль.

anonymous
()

Абсолютно тормозные контейнеры map, set, unordered_map. Вместо первого можно использовать flat_map из C++23. Контейнеры в стандартной библиотеке Rust (BTreeMap) и другие реализации B-Tree Map их обгоняют по производительности, но тем не менее в C++ выбирают убогий дефолт.

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

С вокселем тоже придётс смиотреть 1 в 1, но у вокселя преимущество, каналы доступны, и если идти по канал-владелец на дистанции, может быть разная реализация на С++ по сравнению с Раст.

С++ как по мне не хватает утилиты под проект, по-типо команд: new, build, run, add <чтото>, config add <чтото>, чтобы это было под многие возможности клангд, .clangd, .clang-tidy хотчбы, чтобы я в редакторе не делал повторяющиеся, возможные действия, я себе такое написал кстати - счастлив немножко )

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

Что ты пытаешься доказать, анон? Что с vector<bool> можно писать код? Ну да, можно. Это не отменяет того факта, что vector<bool> не является в строгом смысле вектором, потому что в функцию, работающую с любым другим vector<T> его сунуть нельзя.

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

так можно создать

class MyBool{ bool value; };

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

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

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

anonymous
()
Ответ на: комментарий от anonymous
// 1. Общие состояния для ЛЮБОГО элемента интерфейса
#[derive(Debug, Clone, Default)]
pub struct WidgetState {
    pub is_visible: bool,
    pub is_hovered: bool,  // Мышка наведена (ваш highlight)
    pub is_focused: bool,  // Элемент активен (для ввода текста)
    pub is_pressed: bool,  // Зажата кнопка мыши
    pub is_disabled: bool, // Серый/неактивный элемент
    pub is_dragdrop: bool,
    pub is_move: bool,
}
#[derive(Debug, Clone, Default)]
pub struct Tooltip {
    pub text: String,
}
#[derive(Debug, Clone)]
pub struct WidgetStyle {
    pub sprite: String,
}
#[derive(Debug, Clone)]
pub struct ItemSlotStyle {
    pub normal_sprite: String,
    pub hover_sprite: String,
}
#[derive(Debug, Clone)]
pub struct TextFieldStyle {
    pub color: u32,
}
#[derive(Debug, Clone)]
pub struct ButtonStyle {
    pub normal_sprite: String,
    pub hover_sprite: String,
    pub pressed_sprite: String,
    pub disabled_sprite: String,
}
#[derive(Debug, Clone)]
pub struct TabStyle {
    pub normal_sprite: String,
    pub hover_sprite: String,
    pub pressed_sprite: String,
    pub disabled_sprite: String,
}
#[derive(Debug, Clone)]
pub struct SliderStyle {
    pub bg: String,
    pub knob: String,
    pub normal_knob: String,
    pub hover_knob: String,
    pub pressed_knob: String,
    pub disabled_knob: String,
}
#[derive(Debug, Clone)]
pub struct RadioButtonStyle {
    pub bg: String,
    pub activator: String,
}
#[derive(Debug, Clone)]
pub struct CheckBoxStyle {
    pub bg: String,
    pub activator: String,
}
#[derive(Debug, Clone)]
pub struct ActivateStyle {
    pub active_normal_sprite: String,
    pub disactive_normal_sprite: String,
    pub hover_sprite: String,
    pub pressed_sprite: String,
    pub disabled_sprite: String,
}
#[derive(Debug, Clone)]
pub struct DropDownStyle {
    pub bg_sprite: String,
}
#[derive(Debug, Clone)]
pub struct LoadingBarStyle {
    pub bg_sprite: String,
    pub fill_sprite: String,
}
//
// 2. Только уникальные данные конкретного виджета
#[derive(Debug, Clone)]
pub enum WidgetKind {
    ItemSlot {
        item_count: u32,
        item_id: u32,
        style: ItemSlotStyle,
    },
    TextField {
        text: String,
        wrap: bool,
        style: TextFieldStyle,
    },
    Button {
        text: String,
        style: ButtonStyle,
    },
    Tab {
        text: String,
        is_active: bool, // Активна ли вкладка прямо сейчас
        style: TabStyle,
    },
    Slider {
        value: f32,
        min: f32,
        max: f32,
        style: SliderStyle,
    },
    RadioButton {
        is_selected: bool,
        style: RadioButtonStyle,
    },
    CheckBox {
        is_selected: bool,
        style: CheckBoxStyle,
    },
    Activate {
        is_activate: bool,
        style: ActivateStyle,
    },
    DropDown {
        is_open: bool,
        text: String,
        select_index: u32,
        container: Vec<String>,
        style: DropDownStyle,
    },
    LoadingBar {
        progress: f32,
        text: String,
        style: LoadingBarStyle,
    },
    Container {
        children: Vec<Widget>,
        scroll_y: f32,
    },
}

// 1. Все возможные действия, которые могут произойти в UI
#[derive(Debug, Clone)]
pub enum UiEvent {
    ButtonClicked {
        widget_id: u64,
    },
    DropDownChanged {
        widget_id: u64,
        new_index: u32,
    },
    SliderMoved {
        widget_id: u64,
        new_value: f32,
    },
    ActivateToggled {
        widget_id: u64,
        is_activate: bool,
    },
    DragDrop {
        from_widget_id: u64,
        to_widget_id: u64,
    },
    MoveWidget {
        widget_id: u64,
        new_x: f32,
        new_y: f32,
    },
    InputText {
        widget_id: u64,
        text: String,
    },
}

а это удобнее типо да? когда можно отнаследоваться от базового элемента в С++ и накидать темплейтов.

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

так можно создать

class MyBool{

bool value;

};

Можно. Но это не vector<bool> уже.

раст не может работать с многими последовательностями по той же причине чисто технически

Ты не понял. Всё моё замечание по этой части тут в том, что vector<bool> – не пришей кобыле член в C++, и что конкретно эта специализация шаблона больше мешает чем делает что-то полезное.

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

вот и я о том же, что про мой пример с vector можно в расте точно так же уперется рогом в ptr unsafe, str, String где работа с ними целый пазл, а при ptr вообще опасно )

anonymous
()
  • Markdown
Пустая строка (два раза Enter) начинает новый абзац. Знак '>' в начале абзаца выделяет абзац курсивом цитирования.
Внимание: прочитайте описание разметки Markdown.
Используйте Ctrl-Enter для размещения комментария