Просмотр сообщений
|
Страниц: 1 [2] 3 4 ... 6
|
16
|
Программирование / С/C++ / Re: Итераторы
|
: Апрель 21, 2021, 16:33
|
Кстати, при прочих равных (когда нет выделения общих вычислений на аргументах) на практике соглашение о вызове по идее должно коррелировать с порядком вычисления аргументов - тот, который пушим первым первым и вычисляем, но не вычисляем первым аргумент из середины, т.к. его нельзя сразу запушить. Но, например, при register calling convension то, что будет передаваться в регистрах можно вычислять в любом порядке. Так что вариаций много.
|
|
|
17
|
Программирование / С/C++ / Re: Итераторы
|
: Апрель 21, 2021, 11:41
|
В этом контексте (порядка вычисления аргументов) частая ошибка, допускающая лик: void foo(std::shared_ptr<MyClass> c, int x);
foo(std::shared_ptr<MyClass>(new MyClass), getXValue());
|
|
|
19
|
Программирование / С/C++ / Re: Итераторы
|
: Апрель 21, 2021, 11:02
|
Да, и порядок выполнения операций здесь не при чем. Порядок выполнения не нарушет видимый (ожидаемый) результат однопоточного исполнения. Это же императивный язык. UB возникает именно из-за оптимизатора и формального правила стандарта, которое он использует для оптимизации.
|
|
|
20
|
Программирование / С/C++ / Re: Итераторы
|
: Апрель 21, 2021, 10:53
|
UB, потому что следом *f = 0.0f; зануляет туже самую область памяти. Так и происходит, но компилятор, при возврате в return *i; попросту не перезагружает значение из памяти, потому как считает, что f не может указывать на ту же ячеку, что и i, соответственно, она не может быть изменена через f. Для return *i; компилятор просто хардкодит 1 в eax/rax (регистр, через который идет возврат значения функции).
|
|
|
21
|
Программирование / С/C++ / Re: Итераторы
|
: Апрель 20, 2021, 23:00
|
От-т! Опять я упустил внешний контейнер (QSet).
Авварон, по алиасингу вынужден с вами также согласиться. Друзья, спасибо!
|
|
|
22
|
Программирование / С/C++ / Re: Итераторы
|
: Апрель 20, 2021, 22:35
|
Там внутри могут быть всякие traits которые для разных типов разные. Но в любом случае, даже если битовое представление одинаковое, это UB. Могут, но это лишено практического смысла. Для контейнера важен только размер элемента (точнее, скажем "семантический размер", как в случае с bool), а сам элемнт, будь хоть POD, хоть класс с VMT - в плане хранения - все равно. Если обратное, то это уже не контейнер, а контейнер + еще что-то (т.е. bad design). В чем проблема-то? Единственный валидный юзкейз reinterpret_cast между разными типами это каст к массиву байт (или void*) и обратно. Компилятор знает что вон тот char buffer[] потенциально может алиаситься с вот этим указателем и сгенерит менее производительный код. placement new как раз подпадает под это - буфер чаров может алиаситься (и делает это) с указателем на произвольный T. А вот любые другие типы (например int* в double*) алиаситься не могут Я к тому, что поинтер-алиасинг это не только про reinterpret_cast. Вот этот код дает UB (печатает -1 вместо ожидаемого 0) в обоих случаях (MinGW 8.3, O2): #include <iostream>
int foo(float* f, int* i) { *i = -1; *f = 0.0f;
return *i; }
int main(int, char *[]) { int i; //i = foo(reinterpret_cast<float*>(&i), &i); // 1 i = foo(new(&i) float, &i); // 2 std::cout << i << std::endl; }
А теперь вернусь в контекст нашей задачи - слегка модифицирую код на манер std::reference_wrapper, для упрощения я использовал просто структуру: #include <iostream>
#if 0 // 0 - OK, 1 - UB typedef float F; #else struct F { int i; F(float v = 0.0f) { reinterpret_cast<float&>(i) = v; } }; #endif
int foo(F* f, int* i) { *i = -1; *f = 0.0f;
return *i; }
int main(int, char *[]) { int i; //i = foo(reinterpret_cast<F*>(&i), &i); // 1 i = foo(new(&i) F(), &i); // 2 std::cout << i << std::endl; }
При #if 0 UB больше нет в обоих случаях. #if 1 - это как в предыдущем случае с UB. Почему больше нет UB? - сходу не найду - но еще вроде со всремен C++11 правил алиасинга если один из типов агрегатный и включает другой членом - то это не UB. Итого: Типы reference_wrapper<T> и Т* должны быть безопасны в плане reinerpret_cast, т.к. первый инкапсулирует второй как член, что следует из того, что sizeof(reference_wrapper<T>) == sizeof(Т*)т.е. reference_wrapper<T> никак не может быть реализован по-другому, кроме как инкапсулировать указатель и больше ничего.
|
|
|
23
|
Программирование / С/C++ / Re: Итераторы
|
: Апрель 20, 2021, 15:53
|
А что если у QSet для указателей и нет разные специализации?
Ах, таки вы меня подсекли! Про возможную специализацию внешнего типа я упустил. В данном случае специализации QSet нет, работать будет. Но в общем случае - да, это нужно учитывать. Авварон, vector<bool> bools; auto &chars = reinterpret_cast<vector<char>&>(bools);
Как метко подметил Old, причина этого UB в специализации vector<bool>. Она хранит биты, а не байты. По поводу strict alias rule и оптимизации (привет restrict poiners): А как вы соотнесете это с placement new(), например?
|
|
|
24
|
Программирование / С/C++ / Re: Итераторы
|
: Апрель 20, 2021, 14:24
|
Это не UB. В худшем (маловероятном) случае, это compile-time error. Потому что так перед грязным хаком (reinterpret_cast) идет static_assert(sizeof(CData*) == sizeof(std::reference_wrapper<CData>)); что делает хак чистым. Прпосто вдумайтесь в смысл условия ассерта. Если уж подходить совсем параноидально, то в теории UB возможен, но такая реализация std::reference_wrapper лишена практического смысла. Например, std::reference_wrapper хранит не исходный указатель, а смещенный на 5, например, или в форме инверсных битов.
|
|
|
25
|
Qt / Qt Quick / Re: Какую структуру программы вы делаете при использовании QML?
|
: Апрель 20, 2021, 11:48
|
Presenter - связующее звено между вьюхой и моделью, формирует данные для вьюхи, получаемые из модели. Реализует всю UI логику вьюхи и навигацию. Его можно реализовывать и в QML и на плюсах, по вкусу. Presenter должен быть на плюсах, ибо он один для разных видов. Например, QML и консольный интерфейс. При создании интерфейса на QtQuick делал упор на то чтобы каждый элемент и каждое окно отображались и работали отдельно от программы, т.е. можно запустить весь интерфейс или отдельный элемент через qmlscene и проверить его работу. ... Очень стояще! Делаю примерно также, только всегда подаю на вход вида модель или класс бизнес-логики. Сразу видно от чего зависит вид. Dialog { required property Week week // класс бизнес логики ... }
Создаю, соответственно, динамически var component = Qt.createComponent("WeekInfoDialog.qml") var object = component.createObject(appWindow, {week: currentWeek})
Но я только начал использовать QML.
|
|
|
27
|
Программирование / С/C++ / Re: const до/после *
|
: Апрель 20, 2021, 07:35
|
Как насчет следующей формализации при объявлении переменных сложных типов: 1. Тип просто определяет размер области памяти (он, конечно, определяет много чего еще, но нам здесь это неважно). Более никаких атрибутов тип не имеет, т.е. const - это не про тип. Когда объявляется переменная типа, то мы получаем конкретный кусок памяти, характеризуемый размером и адресом. И вот тут уместно определить атрибут доступа к этому конкретному куску - если только чтение, то const. Т.е. const - это атрибут конкретного адреса памяти, а не типа. 2. const пишется всегда слева и воздействует на то, что справа. 'const T' - запрещено, т.к. из п.1 мы определели, что "const - это не про тип". 3. Будем считать косвенный доступ '*' (т.е. доступ через указатель) как отдельную сущность с атрибутом доступа 'запись и чтение' или 'только чтение', т.е. '*' и 'const *' (как приняли в п.2, const пишется всегда слева). Итак, следуя этой формализации, константный указатель на константные данные типа int (или прямо - типа int константных данных константный указатель) пишется так: Компоненты: int - тип, const * - косвенность доступа (в данном случае только для чтения), const p - адрес памяти (в данном случае только для чтения). Вообщем 'const int*' и 'int const *' это как, например, 'спиртовой раствор' и 'спирта раствор'. 1-е для обывателей, 2-е - для технарей/профи , потому что здесь главное (спирт) выноситсяна первое место, например, удобно строить алфавитный указатель.
|
|
|
28
|
Программирование / С/C++ / Re: Итераторы
|
: Апрель 19, 2021, 22:51
|
struct CBigClass { ... QList<CData> mData; QSet<CData *> mSelection; ... }; Я бы сделал так: struct CBigClass { ... QList<CData> mData; QSet<std::reference_wrapper<CData>> mSelection; ... };
Теперь тип элемента в контейнерах, можно сказать, одинаковый. Если QSet<CData *> mSelection; нужно оставить, то сделайте zero-cost адаптер, чтобы оперировать ссылками, а не указателями: struct CBigClass { ... QSet<std::reference_wrapper<CData>>& mSelectionAsRefs() { static_assert(sizeof(CData*) == sizeof(std::reference_wrapper<CData>)); return reinterpret_cast<QSet<std::reference_wrapper<CData>>&>(mSelection); } };
Итак, std::reference_wrapper внутренне реализован как указатель, но имеет семантику ссылки. Собственно, она часто и используется с контейнерами. Теперь адаптер: он не имеет накладных расходов (ссовсем), поскольку размер std::reference_wrapper == размеру указателя, и мы жестко реинтерпретируем. Но равенство размеров не гарантируется (не уверен), поэтому мы это проверяем в compile-time static_assert-ом. И еще. В вашем QSet<CData *> mSelection; могут быть нулевые указатели? Предполагаю - нет. Если указатель не может быть нулевым - используйте ссылку (во всех таких случаях). Читаемость и строгость кода же повышаются. Хотя, в плане читаемости - спорно (std::reference_wrapper<> - много букв ).
|
|
|
29
|
Программирование / С/C++ / Re: Compile-time определение реверсивности итератора (reverse_iterator)
|
: Апрель 19, 2021, 15:34
|
Это несколько не то. Привожу свое решение. Это ready-to-use консольная прога. Собственно, то что нужно сделать находится в тэгах //<<<<<<<< ... //>>>>>>>>. Осмелюсь заявить, что это очень элегантное/эффективное решение ввиду того, что код инвариантен направлению сдвига. Единственный участок, использующий направление, это IsReverseIterator<IteratorT>, и притом это compule-time. Если кто приведет еще лучше - буду очень признателен. main.cpp #include <QtCore/QCoreApplication> #include <QtCore/QDate> #include <QtCore/QDebug> #include <QtCore/QList> #include <QtCore/QSharedPointer>
class Week { public: explicit Week(QDate date) : date_(date) { qDebug() << "Week: ctor()"; } ~Week() { qDebug() << "Week: dtor()"; }
QDate date() const { return date_; }
private: QDate date_; };
//<<<<<<<<
template <typename, typename = void> inline constexpr bool IsReverseIterator = false;
template <typename T> inline constexpr bool IsReverseIterator<T, std::void_t<decltype(std::declval<T>().base())>> = true;
/// Shifts the range of Week*s to left or right on \a offset positions (weeks). The shift direction /// is determined by the iterators kind - forward ones indicate shifting to left (with insertion on /// the right side future weeks) and reverse ones - to right (with insertion on the left side past /// weeks). \a offset may be any value (even greater then the range size). Shifted out Weeks /// are destroyed, shifted in ones are created with the proper dates. template <typename IteratorT> static void shift(IteratorT begin, IteratorT end, typename std::iterator_traits<IteratorT>::difference_type offset) { if (Q_UNLIKELY(!offset)) return;
// std::copy() uses '!=', not '<' for iterator comparison, so values above end are invalid; we // decompose offset as offset + offsetExceeding, where the offset < end. auto size = std::distance(begin, end); auto offsetExceeding = offset <= size ? 0 : offset - size; offset -= offsetExceeding;
// Date step and first shifting into element's date static const int DayStep = 7 * (IsReverseIterator<IteratorT> ? -1 : 1); QDate date = (*std::prev(end))->date().addDays(DayStep * (offsetExceeding + 1));
// Shift left/right, shifted out elements are freed qDeleteAll(begin, begin + offset); auto pos = std::copy(begin + offset, end, begin);
// Create new elements placing them from left/right side for (; pos != end; date = date.addDays(DayStep)) *pos++ = new Week(date); }
//>>>>>>>>
// Just syntactic sugar for whole container. Shift direction is determined by offset's sign. template <typename ContainerT> static void shift(ContainerT& container, int offset) { if (offset > 0) shift(container.begin(), container.end(), offset); else if (offset < 0) shift(container.rbegin(), container.rend(), -offset); }
static void print(const QList<Week*>& weeks) { for (auto week : weeks) qDebug() << week->date(); }
int main(int argc, char *argv[]) { QCoreApplication a(argc, argv);
QList<Week*> weeks = { new Week(QDate::currentDate().addDays(-7)), new Week(QDate::currentDate()), new Week(QDate::currentDate().addDays(7)) }; qDebug() << "\nOriginal:"; print(weeks);
qDebug() << "\nShift left on 1:"; shift(weeks, 1); print(weeks);
qDebug() << "\nShift right on 1, so we get the original range:"; shift(weeks, -1); print(weeks);
qDebug() << "\nShift left on 4:"; shift(weeks, 4); print(weeks);
qDebug() << "\nShift right on 4, so we get the original range:"; shift(weeks, -4); print(weeks);
return a.exec(); }
ContainerShifting.pro QT -= gui
CONFIG += c++17 console CONFIG -= app_bundle
# You can make your code fail to compile if it uses deprecated APIs. # In order to do so, uncomment the following line. #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += \ main.cpp
# Default rules for deployment. qnx: target.path = /tmp/$${TARGET}/bin else: unix:!android: target.path = /opt/$${TARGET}/bin !isEmpty(target.path): INSTALLS += target
|
|
|
30
|
Программирование / С/C++ / Re: Compile-time определение реверсивности итератора (reverse_iterator)
|
: Апрель 16, 2021, 13:35
|
Можно сделать как угодно в рамках заданного прототипа (указывал выше). Элемент такой (все лишнее выкинул). Он создается с даты. Дату первого вставляемого элемента мы определяем из даты первого/последнего элемента диапазона (в зависимости от направления сдвига) + или - (в зависимости от направления сдвига) смещение * 7. Элемент: class Week { public: explicit Week(QDate date) : date_(date) {} QDate date() const { return date_; }
private: QDate date_; };
Диапазон может быть задан, например, так (шаред-поинтер выкинул для упрощения): QList<Week*> weeks = { new Week(QDate::currentDate().addDays(-7)), new Week(QDate::currentDate()), new Week(QDate::currentDate().addDays(7)) };
|
|
|
|
|