Просмотр сообщений
|
Страниц: [1] 2 3 ... 6
|
2
|
Qt / Общие вопросы / Re: Опредение того, является ли каталог подкаталогом другого
|
: Ноябрь 17, 2021, 21:05
|
Да, при условии что оба каталога существуют. Только использовать canonicalPath(), т.к. она резолвит симлинки.
Вообще, если обобщить на случай, когда каталог может не существовать (а это сплошь и рядом, например, юзер задает выходной каталог консольной тулзы), то это далеко нетривиальная задача (если делать на Qt), как может показаться на первый взгляд.
Эти QDir, QQFileInfo и т.п. "относительно ленивы". В том смысле, что если директория/файл не существует, то ф-ии типа absolutePath() не будут работать (возвращают пустые строки).
Собственно, ядро проблемы - для произвольного пути (возможно, пока несуществующего) получить canonicalPath(). А дальше так, как вы указали. Поэтому, если есть каталог "/a/b/c//d/..///./e", то нужно самому сделать то, что делает canonicalPath(). Только это будет чисто строковая обработка. Альтернативно (много проще), временно создать этот каталог и заюзать QFileInfo::canonicalPath, но это как-то топорно. Как-то так мыслю.
|
|
|
3
|
Qt / Общие вопросы / Опредение того, является ли каталог подкаталогом другого
|
: Ноябрь 17, 2021, 06:47
|
Друзья, заданы два каталога. Это могут быть абсолютные пути, относительные, с симлинками, .. что угодно. Пути локальные. Нужно установить, не является ли один подкаталогом другого (любого уровня).
Qt 5.15. Собственно, в QDir, QQFileInfo я не вижу нужной функции. Неужели мне cdUp()-ить от одного и сравнивать со вторым и наоборот (ну, или там в canonicalPath() подстроки искать)? std::file_system - как там?
Спасибо.
|
|
|
4
|
Компиляторы и платформы / Mac OS X / Запрет автогенерации LaunchScreen.storyboard (Qt 5.15/iOS)
|
: Сентябрь 02, 2021, 06:06
|
Мне не нужен генерируемый qmake'ом по дефолту LaunchScreen.storyboard, ибо используется свой. Потроха этого процесса находятся в mkspecs/features/uikit/default_post.prf. Сходу не разобрался как отключить (не правя сорцы).
Вообще, мне нужна локализованная версия дефолтного LaunchScreen.storyboard (там только имя приложения на белом фоне).
|
|
|
5
|
Qt / Установка, сборка, отладка, тестирование / Qt 5.15.2 for Android: build.gradle: неверное указание buildToolsVersion
|
: Июль 23, 2021, 09:44
|
В файле ~/Qt/5.15.2/android/src/android/templates/build.gradle имеем следующее: buildToolsVersion '28.0.3' С какого здесь хардкорная версия? Напомню, это исходный шаблон, и он копируется при сборке Qt-Android приложения. Должно же быть buildToolsVersion androidBuildToolsVersion где androidBuildToolsVersion уже изменяются через локальный gradle.properties проекта. Если нет некоего тонкого момента, которого я сходу не улавливаю, возможно, связанного с совместиностью версий, то это просто небрежный баг, свидетельствующий о снижении качества разработки Qt/
|
|
|
6
|
Qt / Qt Quick / Получить applicationDirPath из QML-файла
|
: Июнь 18, 2021, 19:19
|
Акромя установки контекстного свойства из C++ engine.rootContext()->setContextProperty("applicationDirPath", QGuiApplication::applicationDirPath()); это как-то возможно? Вот вижу, что Qt.application обделен этим. P.S. Нужно сохранять размер/позицию и т.п. QML окна с помощью Settings в ini-файл, лежащий рядом с экзешником.
|
|
|
8
|
Qt / Общие вопросы / QCoreApplication::exec(): определение нахождения вне этой функции
|
: Май 02, 2021, 08:44
|
Как внутри foo() мы можем определить, что foo() вызвана не из QCoreApplication::exec(), а из точек 1 или 2? static void foo() { ... }
int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); foo(); // 1 const int result = QCoreApplication::exec(); foo(); // 2 return result;
} Вообще есть QEventLoop::isRunning(). Альтернативно - как добраться до экземпляра QEventLoop главного потока? Сходу из документации не могу выудить. Есть еще QCoreApplication::eventDispatcher(), но он создается вне QCoreApplication::exec().
|
|
|
9
|
Программирование / С/C++ / Re: Итераторы
|
: Апрель 25, 2021, 09:59
|
Да, любовь - странная штука: #include <QSet> #include <iostream>
using namespace std;
typedef int CData; using CDataRef = reference_wrapper<CData>;
QSet<CData*>* mSelection = new QSet<CData*>;
QSet<CDataRef>*& SelectionAsRefs( void ) { static_assert(sizeof(CData*) == sizeof(CDataRef)); return *(QSet<CDataRef> **) &mSelection; }
static QSet<CData*>* makeUB(QSet<CData*>** src, QSet<CDataRef>** alias) { static QSet<CData*> data = { new CData }; // set with one element
*src = &data; *alias = nullptr;
return *src; // what will be there: &data or nullptr? }
int main(int, char *[]) { mSelection = makeUB(&mSelection, &SelectionAsRefs()); std::cout << (!mSelection ? "OK" : "UB") << std::endl; }
Этот код дает UB при -O2. Я вынужден был перейти от QSet<CData*> к QSet<CData*>*. Непосредственно тип QSet<CData*> UB не дает. Полагаю, что давал бы, если бы в нем был "простой" конструктор.
|
|
|
10
|
Программирование / С/C++ / Re: Итераторы
|
: Апрель 24, 2021, 21:57
|
Те же яйца, только в профиль: int i; void* p; // declaration p = &i; // p begins its lifetime with dynamic type int*
memcpy: Notes std::memcpy may be used to **implicitly create** objects in the destination buffer. ... Where strict aliasing prohibits examining the same memory as values of two different types, std::memcpy may be used to convert the values. И далее почитать по ссылке https://en.cppreference.com/w/cpp/language/object#Object_creation что такое объекты и как они создаются.
|
|
|
11
|
Программирование / С/C++ / Re: чтение и запись битовых структур данных
|
: Апрель 24, 2021, 15:58
|
В плане использования на уровне бизнес-логики - просто наложить placement new: // Packed data struct A { int a; // 2 bits int b; // 15 };
// State less business-logic class class UnpackedA { public: int a() const { return *reinterpret_cast<const int*>(buf()) & 0x3; } int b() const { return *reinterpret_cast<const int*>(buf() + sizeof(int)) & 0x7FFF; } private: // Returns buffer of packed data const char* buf() const { return reinterpret_cast<const char*>(this); } };
int foo() { A a; // e.g. got from network auto ua = new (&a) UnpackedA; }
Это я привел вариант для чтения (распаковки) данных. Для записи (упаковки) - абсолютно тоже самое, только в другую сторону. Да, и здесь класс бизнес логики предполагает время жизни буфера большим своего. Короче, этот класс - интерпретатор, создаваемый по месту. Ну как бы не совсем бизнес-логика, а скорее хелпер.
|
|
|
13
|
Программирование / С/C++ / Re: Итераторы
|
: Апрель 24, 2021, 14:28
|
Да, и в С++20 это делается уже так (std::bit_cast): QSet<std::reference_wrapper<CData>>& mSelectionAsRefs() { static_assert(sizeof(CData*) == sizeof(std::reference_wrapper<CData>)); return std::bit_cast<QSet<std::reference_wrapper<CData>>&>(mSelection); }
Т.е. дали заднюю со своим strict type aliasing. Еще вариант (ИМХО, в общем случае малоприемлемый) - выключить strict type aliasing опцией компилятора. Ядро Линукса, например, компилится с -fno-strict-aliasing, а там, как можно догадаться, еще те любители реинтерпретации.
|
|
|
14
|
Программирование / С/C++ / Re: Итераторы
|
: Апрель 24, 2021, 12:02
|
С вашего позволения вернусь к обозначенной проблеме UB reinterpret_cast'a. Изначальный вариант: QSet<std::reference_wrapper<CData>>& mSelectionAsRefs() { static_assert(sizeof(CData*) == sizeof(std::reference_wrapper<CData>)); return reinterpret_cast<QSet<std::reference_wrapper<CData>>&>(mSelection); }
полагаю, можно сделать полностью безопасным вот так: QSet<std::reference_wrapper<CData>>& mSelectionAsRefs() { static_assert(sizeof(CData*) == sizeof(std::reference_wrapper<CData>));
QSet<CData*>* src = &mSelection; QSet<std::reference_wrapper<CData>>* alias; std::memcpy(&alias, &src, sizeof(alias)); // starts lifetime of alias as QSet<CData*>* return *alias; }
Фишка этого приема в том, что memcpy() дает понять компилятору, что alias стартует как тип src, т.е. QSet<CData*>*. Или эквивалент (м.б. более понятный): QSet<std::reference_wrapper<CData>>& mSelectionAsRefs() { static_assert(sizeof(CData*) == sizeof(std::reference_wrapper<CData>));
QSet<CData*>* src = &mSelection; void* alias;
std::memcpy(&alias, &src, sizeof(alias)); // starts lifetime of alias as QSet<CData*>* return *static_cast<QSet<std::reference_wrapper<CData>>*>(alias); }
Здесь alias прямо декларируется как void* - тип, полностью совместимый с любым указателем (альтернативно, можно char* alias[sizeof(src)]), и далее используется static_cast. Естественно, после оптимизации новый вариант относительно старого не должен иметь никаких дополнителных расходов. Остается только одна потенциальная проблема - возможная специализация контейнера для указателей. В принципе, ее легко решить, если сделать свою специализацию контейнера для std::reference_wrapper<CData>, которая будет иметь битовую идентичность с контейнером CData*. Но это лениво (много букв).
|
|
|
15
|
Программирование / С/C++ / Re: Итераторы
|
: Апрель 23, 2021, 11:26
|
Рекомендация выше юзать ссылку (вместо указателя). Обычно я охотно это делаю, но в данном случае это "создает впечатление владения", а selection ну вот никак не владеет. Некоторый минус - QSet жрет многовато памяти при обильном selection.
В этом правда есть. Изложу лишь следущее: если бы selection был владеющий отдельной копией объекта, т.е. QSet<CData> (не CData*) - это как-то принципиально сломало бы ваш код? Предположу, что нет. Указатель вы используете для уменьшения потребления памяти, ну и бонусом хэш-функция уже есть. Т.е. использование в selection указателей или самих данных определяется их размером, что, согласитесь, семантически ничтожный аргумент. Поэтому ссылка в данном случае - это эффективное владение. Я допускаю, что выбор указателя может быть как-то связан с хэшированием, например CData неуникальны. Или для них нетривиально придумать хорушую хэш функцию. Ваш код был бы проще, если бы было единообразие, которое позволило бы единую обработку шаблонной функцией без is_pointer: struct CBigClass { ... QList<CData*> mData; QSet<CData *> mSelection; ... }
или
struct CBigClass { ... QList<CData> mData; QSet<reference_wrapper<CData>> mSelection; ... };
Порядок таков, что если CData - это класс-сущность (identity class), то используются указатели, если класс данных - то сами классы. Не думаю здесь есть какие-то "варианты зависящие от задачи" От специфики задачи зависит, порой, все Если у вас часто случаются пулеметные вставки, может рассмотреть вариант сперва сформировать вставку в виде вектора, а потом вставить ее в контейнер как единое целой, т.е. никаких multiply вставок? Или популярная байка "std::vector лучше" (чем QList). Ну как байка? Например, в QList не засунешь std::unique_ptr, а std::unique_ptr в контейнере - это сплошь и рядом. Да, вы можете засунуть std::shared_ptr/QSharedPointer - но это другая (более широкая) семантика, да и код будет менее эффективным. Например, QList нет еmplace, а еmplace, в свою очередь, может оказаться решающим доводом в пользу типа данных контейнера, соот-но QList дает меньшую свободу.
|
|
|
|
|