Russian Qt Forum
Апрель 30, 2024, 07:04 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало Форум WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  
  Просмотр сообщений
Страниц: 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());

18  Программирование / С/C++ / Re: Итераторы : Апрель 21, 2021, 11:29
И как вы предполагали - какой Rand будет вызван первым - левый или правый?
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.
26  Программирование / С/C++ / Re: Итераторы : Апрель 20, 2021, 11:08
Не вопрос. Да и это делеко не последний писк моды - это c C++11.
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;
Компоненты: 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))
};
Страниц: 1 [2] 3 4 ... 6

Страница сгенерирована за 0.054 секунд. Запросов: 23.