Задать вопрос
  • Когда narrowing conversion Не является implementation defined?

    Lite_stream
    @Lite_stream Автор вопроса
    res2001, поправил на uint64_t для большей общности

    Если бы длины типов были разные, то wide type обрезался до длины narrow type и то же самое побитовое копирование.

    Правильно ли я понял, что если значение wide type будет находиться в допустимом диапазоне для narrow type, то результат будет так сказать "полностью defined", как будто типы изначально были одинаковые ?
  • Как правильно выделять сырую память для размещения в ней объектов?

    Lite_stream
    @Lite_stream Автор вопроса
    Армянское Радио, я показал просто идею в коде, в реальном примере не получится использовать никакие обертки
    ну и вообще вектор внутри использует allocator.allocate(), который как раз у себя делает что-то вроде T * buffer = reintrepret_cast(new uint8_t[sizeof(T) * capacity])
  • Как правильно выделять сырую память для размещения в ней объектов?

    Lite_stream
    @Lite_stream Автор вопроса
    Армянское Радио, просто суть вопроса была в том, можно ли как-то хитро создать T buffer[] без вызова конструктора, а то при uint8_t buffer[] приходится использовать reinterpret_cast, что слегка напрягает
    при этом вариант выделить отдельно T * buffer = reintrepret_cast(new uint8_t[sizeof(T) * capacity]) тоже особо не привлекает, так как это доп. аллокация
  • Как правильно выделять сырую память для размещения в ней объектов?

    Lite_stream
    @Lite_stream Автор вопроса
    Армянское Радио, здесь и используется placement new в методе add, но вместо массива типа T выбран uint8_t (из-за стрикт алиасинга нужно было брать std::byte или signed/unsigned char) чтобы при создании Container'а не вызывался дефолтный конструктор T
  • Как правильно выделять сырую память для размещения в ней объектов?

    Lite_stream
    @Lite_stream Автор вопроса
    Евгений Шатунов,

    Интерес в том, что даже unsigned char не подходит для подмены. Стандартом разрешены только char и std::byte.


    Мб раньше так было, но я вот смотрю в 11.3 присутствует unsigned char
  • Как правильно выделять сырую память для размещения в ней объектов?

    Lite_stream
    @Lite_stream Автор вопроса
    Евгений Шатунов, а, нет, тут ответ есть


    Ok, let's get truly pedantic. After reading this, this and this, I'm pretty confident that I understand the intention behind both Standards.

    So, doing reinterpret_cast from std::uint8_t* to char* and then dereferencing the resulting pointer is safe and portable and is explicitly permitted by [basic.lval].

    However, doing reinterpret_cast from char* to std::uint8_t* and then dereferencing the resulting pointer is a violation of strict aliasing rule and is undefined behavior if std::uint8_t is implemented as extended unsigned integer type.


    Ну значит буду использовать вместо (u)int8_t - signed/unsigned char или std::byte

    Как я понимаю std::byte предпочтительнее, так как он нужен как раз для случаев с raw memory, чтобы запретить арифметические операции + явно показать в коде, что это именно как сырые байты используется, а не символы или числа
  • Как правильно выделять сырую память для размещения в ней объектов?

    Lite_stream
    @Lite_stream Автор вопроса
    Евгений Шатунов, хм, я думал что для signed/unsigned int8_t, signed/unsigned char, и std::byte ( и вроде бы ещё void *) можно reinterpret в обе стороны делать, так как все они байты
  • Как правильно выделять сырую память для размещения в ней объектов?

    Lite_stream
    @Lite_stream Автор вопроса

    Нет, если вы не вызвали конструктор и обратились к объекту, вы схлопочете UB, формально говоря.
    Да и какой смысл в том, чтобы навыделять памяти, а конструкторы не вызывать? Это нарушает принцип RAII как минимум.

    Обращение к не инициализированной памяти и смысл этих действий, думаю, стоит оставить за рамками вопроса. Можно было бы точно так же обратиться к T * (если бы оно было выделено отдельно в дин. памяти) к ещё неинициализированному объекту и получтиь ub

    Кстати, именно статической памяти я в вашем коде не увидел.

    Наверное я не совсем корректно выразился, в общем под статической памятью я имел в ввиду, что uint8_t buffer не выделяется отдельно (отдельно от всего объекта Container) в динамической памяти
  • Существует ли такой анализатор кода?

    Lite_stream
    @Lite_stream Автор вопроса
    Viktor T2, спасибо, кажется, это то, что я искал
  • Член класса/структуры типа uint8_t * или int8_t *, оптимизация?

    Lite_stream
    @Lite_stream Автор вопроса
    res2001, в MSVC это слово в сигнатуре метода просто делает тип restrict у this
    61bb48c4dd1ab523613715.png

    Почему не влияет ? Как раз теперь, в someSelfAliasableMember точно не может быть this (по крайней мере так будет думать компилятор)
  • Член класса/структуры типа uint8_t * или int8_t *, оптимизация?

    Lite_stream
    @Lite_stream Автор вопроса
    res2001, хм, тогда либо википедия дезинформирует
    Ключевое слово __restrict для метода структуры или класса C++ обозначает, что указатель this имеет тип «T * __restrict const». Пример:
    ,
    либо я не понимаю, тогда зачем this делать константным
  • Член класса/структуры типа uint8_t * или int8_t *, оптимизация?

    Lite_stream
    @Lite_stream Автор вопроса
    res2001, на самом деле кое-где это и не такая уж микро оптимизация, например, в методе для разрешения коллизий, который сначала находят пары-кандидаты для разрешении коллизии, а затем разрешает их, а пар (пара - состоит из каких-то двух типов T *, например, T - это какой-нибудь Shape) этих может быть очень много, это довольно прилично скажет на скорости работы алгоритма

    я просто не понимаю, почему __restrict по умолчанию не применяется к сигнатуре метода, делая this - константным указателем, потому как редко в коде встретишь что-то вроде this = something
  • Член класса/структуры типа uint8_t * или int8_t *, оптимизация?

    Lite_stream
    @Lite_stream Автор вопроса
    res2001, а для случая, когда заалиасится может this и его член - uint8_t * (как в шапке моего вопроса), я так понимаю, лучшим решением будет просто пометить метод как __restrict ? __restrict для методов в C++

    На всякий случай для ясности:
    class Example
    {
    private:
        uint8_t * someSelfAliasableMember;
        int a;
        int b;
        int c;
    
    public:
        
        Example() : someSelfAliasableMember(new uint8_t[100]) {}
    
        void doSomething() __restrict
        {
            for (int32_t i = 0; i < 10; ++i)
            {
                someSelfAliasableMember[i] = i;
            }
        }
    };


    То есть, насколко я понимаю, в случае передаваемых в функцию аргументов указателей (если они одного типа) они могут ссылаться на один участок памяти, и тогда, если они таки не ссылаются на один участок, их можно пометить, как __restrict
    А в случае с членом класса, из-за того, что signed/unsigned char * может ссылаться на что угодно(не наруушая strict aliasing), включая this, как __restrict уже нужно пометить именно сам метод (Первый ответ к вопросу)
  • Член класса/структуры типа uint8_t * или int8_t *, оптимизация?

    Lite_stream
    @Lite_stream Автор вопроса
    res2001, хм, а если у меня, например, в объекте первое поле uint8_t * buffer, и я в методе сделаю что-то вроде &(this->buffer[0]), тогда и &(this->buffer[0]) и uint8_t * buffer на одну и ту жу область ссылаться будут, не приведёт ли это к UB? Или достаточно просто не пользоваться псевдонимом для buffer в виде &(this->buffer[0]) ? ну то есть, если начал писать buffer[i] = value, то в этом же месте не нужно писать что-то вроде this->buffer[i] = value (где i может быть = 0)

    или я этим просто обещаю компилятору, что именно сам this не буду менять, а не то что под ним лежит, то есть:
    this->buffer[0] = value - так можно
    а вот this = something нельзя

    просто тогда не понятно, почему все методы класса по умолчанию не __restrict, ведь мы обычно никогда ничего не записываем в сам this ( this = something )
  • Член класса/структуры типа uint8_t * или int8_t *, оптимизация?

    Lite_stream
    @Lite_stream Автор вопроса
    Тут проблема не в том, что член класса char*, а в том, что запись идет в char*, и из-за strict aliasing правил - это может быть запись куда угодно, в том числе в &this. Кешировать пришлось бы любой член структуры, любого типа.


    То есть если в классе/структуре есть член типа unsigned/signed char *, то из-за strict aliasing пришлось бы кэшировать любую переменную (например, перед обработкой её в цикле) ?

    Эту же проблему можно воспроизвести в меньшем масштабе, если у вас в цикле есть запись в int* и чтение какой-то другой не меняющейся int переменной. Особенно, когда переменная в куче и указатель пришел в параметре функции.

    Ну и второе правило, если метод принимает указатели на один и тот же тип и они точно не overlapp'ятся в памяти, то нужно добавлять __restrict (в зависимости от компилятора(лучше макросом)), если конечно метод очень часто вызывается

    static void InterpolateSnapshot_Hermite_WithExtrapolation( float t,
                                                               float step_size,
                                                               float extrapolation,
                                                               const __restrict CubeState * a,
                                                               const __restrict CubeState * b,
                                                               __restrict view::ObjectUpdate * output )

    Вот, например, нашёл в реальном проекте, только непонятно зачем __restrict у view::ObjectUpdate * output, он же тут только 1, значит ни с кем алиасится не может
  • Член класса/структуры типа uint8_t * или int8_t *, оптимизация?

    Lite_stream
    @Lite_stream Автор вопроса
    Если нет необходимости в подобной локальной переменной, то не нужно "кэшировать".
    На уровне ассемблера все обращения к памяти происходят через регистры, так что в любом случае адрес из указателя будет записан в регистр и этот регистр будет индексироваться.


    В версии без кэширования в локальную переменную появляется дополнительная инструкция перед каждым присваиванием
  • Член класса/структуры типа uint8_t * или int8_t *, оптимизация?

    Lite_stream
    @Lite_stream Автор вопроса
    Частично это можно решить кешированием, можно попробовать поменять типы кое где.


    А добавление в сигнатуру метода __restrict (void method () __restrict {}) не является эквивалентным кэшированию в локлаьную переменную ? за исключением того, что ничего кэшировать не придётся, так как компилятор уже будет знать, что переменная ни с чем в рамках данного метода не overlapp'ится" в памяти ?
  • Statements внутри списка инициализации конструктора?

    Lite_stream
    @Lite_stream Автор вопроса
    Евгений Шатунов,
    там, к сожалению, более сложная логика, чем просто вызовы методов, поэтому использовать цепочки вызовов не получится
  • Statements внутри списка инициализации конструктора?

    Lite_stream
    @Lite_stream Автор вопроса
    res2001,

    извиняюсь, забыл уточнить: createObj() - приватный статический метод *


    floppa322, Если это статический метод Example/ExtremelyHeavyObjectBuilder/ExtremelyHeavyObject, то можете так делать.
    В случае статического метода ExtremelyHeavyObjectBuilder/ExtremelyHeavyObject требуется указать класс, т.е. будет что-то вроде ExtremelyHeavyObjectBuilder::createObj().

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


    Так же createObj может быть и свободной функцией.

    естественно может, просто предпочитаю в ооп-стиле писать )