1. C++ / Говнокод #14335

    +18

    1. 01
    2. 02
    3. 03
    4. 04
    5. 05
    6. 06
    7. 07
    8. 08
    9. 09
    10. 10
    11. 11
    12. 12
    13. 13
    14. 14
    15. 15
    16. 16
    17. 17
    18. 18
    19. 19
    20. 20
    21. 21
    22. 22
    23. 23
    24. 24
    25. 25
    26. 26
    27. 27
    28. 28
    29. 29
    30. 30
    31. 31
    32. 32
    33. 33
    34. 34
    35. 35
    36. 36
    37. 37
    38. 38
    39. 39
    40. 40
    41. 41
    42. 42
    43. 43
    44. 44
    45. 45
    46. 46
    47. 47
    48. 48
    49. 49
    50. 50
    51. 51
    52. 52
    53. 53
    54. 54
    55. 55
    56. 56
    57. 57
    58. 58
    59. 59
    60. 60
    61. 61
    62. 62
    63. 63
    64. 64
    65. 65
    66. 66
    67. 67
    68. 68
    69. 69
    70. 70
    71. 71
    72. 72
    73. 73
    74. 74
    75. 75
    76. 76
    77. 77
    78. 78
    79. 79
    80. 80
    81. 81
    82. 82
    83. 83
    84. 84
    85. 85
    86. 86
    87. 87
    88. 88
    template <typename T>
      struct canref {
        struct yes { uint8_t bytes[1]; };
        struct no  { uint8_t bytes[2]; };
        template <typename U>    static yes test(U*p);
        template <typename U>    static no  test(...);
        static const bool value = sizeof(test<T>(NULL)) == sizeof(yes);
      };
      template <typename T, int N, bool CanRef=canref<T>::value>
      class array; 
      // class for all types
      template <typename T, int N>
      class array <T,N,true>
      {
        struct Bytes
        {
          uint8_t bytes[sizeof(T)];
        };
        struct TStruct
        {
          T t;
          TStruct(T t):t(t){}
        };
    
        Bytes elements[N];
        int count;
        void copy_ctor (int index, const T& t)
        {
          new (&((*this)[index])) T(t);      
        }
        void copy (int index, const T& t)
        {
          (*this)[index] = t;
        }
    
        void dtor (int index)
        {
          (*this)[index].~T();
        }
      public:
        array () : count(0) {}
    
        ~array () 
        {
          resize(0);
        }
        T& operator [] (int index) 
        {
          assert (index>=0 && index<count);      
          return ((TStruct*)(&elements[index]))->t;
        }
        const T& operator [] (int index) const 
        {
          assert (index>=0 && index<count);      
          return ((TStruct*)(&elements[index]))->t;
        }
        template <int M>
        array (const array<T,M> &a)
        {
          assert(a.count<=N);
          count = a.count;
          for (int i=0; i<count; ++i)
            copyctor(i, a[i]);
        }
        template <int M>
        array& operator = (const array<T,M> &a)
        {
          if (this != &a)
          {
            if (count>a.count)
            {
              for (int i=0;       i<a.count; ++i) copy(i, a[i]);
              for (int i=a.count; i<count;   ++i) dtor(i);
              count = a.count;
            } else
            {
              assert(a.count<=N);
              int ocount = count;
              count = a.count;
              for (int i=0;      i<ocount; ++i) copy(i, a[i]);
              for (int i=ocount; i<count;  ++i) copy_ctor(i, a[i]);
            }
          }
        }
        int size() 
        {
          return count;
        }

    Скоро даже сратору станет очевидно откуда это.

    Запостил: LispGovno, 10 Января 2014

    Комментарии (166) RSS

    • Cast "Призыв";
      Ответить
    • Кто-то пытался навелосипедить std::array<>?
      Ответить
      • Тарас
        Ответить
        • Затянула Тараса крестоблядская трясина...
          Ответить
          • и жизнь его вечная игра
            Ответить
            • но не Кресты обычно губят...
              Ответить
              • Ты не пронимаешь всех последствия пользования крестами
                Ответить
              • ... а поганые дельфя.
                Ответить
                • ничто так не губит судьбы, как пых.
                  Ответить
                  • 1с?
                    Я тут как-то днесь код читал для 7.7, написанный человеком, не могущим в "алгоритмы". Если бы не сила воли, я бы лоб себе разбил фейспальмами.
                    Ответить
                    • Попробуй навижн посмотреть
                      после него 1с - мана небесная
                      Ответить
                • На последних уже можно писать программы.
                  Ответить
              • а одиннадцать плюс-плюс
                Ответить
      • std::array<> это другое, он сразу инициализирует элементы и по сути имеет фиксированный размер, а у меня массив, у которого полезный размер может быть меньше вместимости, а лишняя часть не инициализируется, это аналог довольно часто нужной связки {int length, T elements[MAX_LENGTH]}, да, это как вектор, только память фиксированную занимает. А ещё мой массив умеет хранить объекты, не умеющие в конструктор без параметров.
        Ответить
        • открой для себя uninitialized_fill из <memory>
          Ответить
          • Открыл, что дальше?
            Ответить
            • дальше открой uninitialized_copy
              Ответить
              • а дальше std::raw_storage_iterator
                [color]LispGovno[/color]
                Ответить
                • Кто ты и что такое std::raw_storage_iterator? Адаптер к любому другому итератору?
                  Ответить
                  • Хотя я сам его посмотрел. Можно его использовать template< class OutputIt, class T >
                    class raw_storage_iterator

                    чтобе передать в OutputIt не указатель, а например другой итератор?
                    Ответить
                    • > template< class OutputIt, class T >
                      Может быть вот так его юзают?
                      template <class T> class my_iter<T> : public std::raw_storage_iterator<my_iter<T>, T>
                      Ответить
                      • Что за извращение и что оно означает? Я в примере использования видел такое:
                        std::r_s_i<std::string*, std::string> it(&addr_of_unitialized_data);
                        *it++="хуй";
                        *it++="пизда";
                        *it++="жиргруда";

                        Ну как то так.
                        Это приведет к вызову конструктора копирования в неинициализированные области памяти.
                        Ответить
                        • А, вон там как.
                          Ответить
                          • Если подумать, то использовать для итераторов можно:
                            (компилировать не пробовал, но все логично)
                            typedef std::string ss;
                            assert(vec.size()==3);
                            std::r_s_i<std::vector<ss>::iterator, ss> it(vec.begin());
                            for(const auto& vi:vec)
                              vi.~ss();
                            *it++="хуй";
                            *it++="пизда";
                            *it++="жиргруда";
                            Ответить
              • Нафига, оно один хрен только 1 строчку занимает, я что-то теряю от того, что потратил место на ctor,dtor? А, ну да, когда надо влезть в 100 строк, то это важно.
                Ответить
                • > когда надо влезть в 100 строк
                  Ты пишешь игру в сто строк? Тогда тебе перевод каретки нужно вставлять реже.
                  Ответить
                  • > Тогда тебе перевод каретки нужно вставлять реже.
                    Самое сложное - инклуды. А остальное и в одну войдет.
                    Ответить
                  • Мне кажется, это связано с ограничением говнокода)
                    Ответить
                    • тарас пишет код так, что бы его было потом удобно заливать на говнокод....
                      Ответить
    • Бля, а как в этот массив добавлять элементы? :)

      - в конструкторе передать нельзя
      - скобки крашатся, если index >= count, а он всегда равен 0
      - конструктор копий и присваивание требуют другой такой же массив

      Вещь в себе какая-то ;(
      Ответить
      • Кастуй призыв Тараса.Он сегодня ссылку на свой код дал.
        Ответить
      • Ты не понимаешь, это Ъ-инкапсуляция же

        push_back
        он не влез в 100 строк
        а ещё там вторая реализация класса для случая, когда третий параметр false
        Ответить
    • ААААА БЛЯ НУ НАКОНЕЕЕЕЕЦ ТО!!!!
      УУУУУИИИИИХХХХААААААААА!!!!
      Моё шаблонное говно выложил не я, а другой человек.
      Ответить
    • А почему нельзя задавать верхнюю и нижнюю границу индексов массива?
      Ответить
      • Нижняя ноль.
        Верхняя сама смещается в push_back
        Ещё есть resize, который умеет только уменьшать, иначе надо чем-то заполнять добавленный хвост, а чем, ведь мой класс не требует пустого конструктора от T.
        Ответить
    • Будем надеяться, что компилятор воткнёт одинаковый паддинг в Bytes и TStruct.
      Тарас, объясни, зачем нужен TStruct?
      Ответить
      • TStruct остался с моих попыток написать один класс для нормальных типов и для ссылок.
        Сейчас он не нужен.
        Что такое паддинг? Если чё, я специально спрашивал, смещение первого поля в структурах всегда нулевое.
        Ответить
        • > TStruct
          TForm1
          Ответить
        • Паддинг в конец добавляется для выравнивания. Вроде проблем из-за паддинга возникнуть всё-же не должно.
          Но вариант с char raw_memory[sizeof(T) * N]; внутри array мне нравится гораздо больше.
          Ответить
          • А даже если и так, то он мне не помешает.
            Ну возьму я указатель на последний элемент, приведу его к TStruct (содержимое вылазит за, но мы к нему не обращаемся же!), возвращаем лишь ссылку на t, которое никуда не вылазит.

            > Но вариант с char raw_memory[sizeof(T) * N]; внутри array мне нравится гораздо больше.

            Тут всё ради того, чтобы уйти от этого.
            А если размер - миллион? Идея как раз в том, чтобы охрененно большой массив не содержал переголовы на инициализацию, и при этом не "содержал" неинициализированных полей.
            Ответить
            • > char raw_memory[sizeof(T) * N];
              > переголовы на инициализацию
              Не будет он инициализироваться. Ча содержит тривиальный конструктор. То есть пустой и он не будет вызываться.
              Ответить
              • А, понял.
                Так тоже можно, но мне показалось, что проще Bytes[N], не надо руками умножать для получения адреса, пусть компилятор умножает.
                Ответить
                • > не надо руками умножать для получения адреса, пусть компилятор умножает
                  reinterpret_cast
                  Ответить
                  • А это, случаем, не нарушает правила алиасинга указателей?
                    Вроде же только в сторону char * можно кастовать.

                    P.S. Хотя, с другой стороны, сторону с char* никто никогда не юзает, поэтому никаких проблем с алиасингом быть не должно.
                    Ответить
                    • наоборот, из char* можно кастовать
                      Ответить
                      • It is always presumed that a char* may refer to an alias of any object. It is therefore quite safe, if perhaps a bit unoptimal (for architecture with wide loads and stores) to cast any pointer of any type to a char* type.

                        http://cellperformance.beyond3d.com/articles/2006/06/understanding-strict-aliasing.html
                        Ответить
                        • То есть если не хочешь нарушить стрикт алиасинг, то сначало нужно скастить в чар*, а потом куда угодно*, а сразу в куда угодно* нельзя?
                          Ответить
                          • > а потом куда угодно*
                            А нельзя вроде бы куда угодно, даже через char.

                            Если я правильно понял, компилятор не трекает кого там в кого копировали. А просто смотрит по прстоым правилам, приведенным в стандарте:
                            - один из типов чар - может наехать
                            - типы одинаковые - может наехать
                            - типы одного размера - может наехать
                            ...
                            и т.п.
                            И исходя из этого задумывается, можно ли ему читать вот этот int *p, или сначала надо положить вон в тот int *q, который был написан пару строчек назад, а не откладывать запись в *q после записи в *p.

                            Поэтому каст через промежуточный чар будет иметь тот же самый эффект, что и каст напрямую. Ну если я не туплю.
                            Ответить
                            • тогда я memcpy буду юзать. Он по идеи внутри char* юзает
                              Ответить
                              • Да. memcpy, емнип, как раз и рекомендуют для копирования между произвольными массивами и структурами.
                                Ответить
                                • ну с массивами никогда кастить не приходилось, а вот для отдельных переменных бывало.
                                  тупо вместо:
                                  int64_t * a=...;
                                  int32_t * b=(int32_t *)a;
                                  *b=5;
                                  cout<< *a;//UB

                                  буду писать
                                  int64_t * a=...;
                                  int32_t b = 5;
                                  std::memcpy(a, &b, sizeof(b));
                                  cout<< *a;//OK

                                  Или даже так:
                                  int64_t * a=...;
                                  int32_t b = 5;
                                  std::copy((char*)&b, (char*)(&b+1), (char*)&a);
                                  cout<< *a;//OK
                                  Ответить
                                  • > тупо вместо
                                    А зачем вообще так делать?

                                    b = (int32_t)a или b = static_cast<int32_t>(a) уже не возбуждают?
                                    Ответить
                                    • Ну иногда бывает такая ситуация
                                      #include <iostream>
                                      #include <memory.h>
                                      using namespace std;
                                      
                                      int64_t a;
                                      
                                      int main() {
                                      	int64_t a = 6;//Давно очень инициализировано. 
                                      	//Хочу сохранить младший u32
                                      	int32_t b = 5;
                                      	memcpy((int32_t*)&a+1, &b, sizeof(b));
                                      	cout<< a;//OK
                                      	return 0;
                                      }
                                      Ответить
                                      • Хотя лучше так:
                                        #include <iostream>
                                        #include<memory.h>
                                        using namespace std; 
                                        int main() {
                                        	int64_ta = 6;
                                        	int32_t b = 5;
                                        	a = (int64_t)a+((int64_t)b<<32);
                                        	cout<< a;//OK
                                        	return 0;
                                        }
                                        Ответить
                                        • > Хотя лучше так:
                                          Ага. Железобетонно. Плюёт на проблемы индейцев. И, кстати говоря, всяко лучше оптимизируется. Т.к. нету лишних прокруток через память, и data flow не порушено. Может быть даже сдвигов не будет, если например int64_t был получен умножением. Или "повезет" (а вернее постараяется компилер), и куски int64_t окажутся в нужных регистрах на 32 битной архитектуре.
                                          Ответить
                                  • > int64_t * a=...;
                                    > int32_t b = 5;
                                    > std::memcpy(a, &b, sizeof(b));
                                    Кстати говно, от индейцев зависит. При больших индейцах скопирует в старшую часть, а при маленьких - в младшую. Впрочем изначальный код так же "работал".
                                    Ответить
                                • Кстати, в студии нет этого стрикт аналисинга? А то я его много раз нарушал похоже. интересуют студии 2008 и много младше
                                  Ответить
                                  • > А то я его много раз нарушал похоже
                                    Да хрен бы знал. Я тоже кучу раз нарушал. Но в gcc не баговало ;)
                                    Ответить
                                    • А предупреждения от кококомпилятора есть на эту тему?
                                      Ответить
                                      • > А предупреждения от кококомпилятора есть на эту тему?
                                        Часто есть, но он далеко не всегда может предупредить. Т.к. если бы он всегда понимал ситуацию - он бы точно знал, когда можно, а когда нет, и проблемы бы просто не существовало.

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

                                        Если приведешь примеры, где тебе понадобились разнотипные указатели на одну локацию - попробуем разобраться.
                                        Ответить
                                  • > Кстати, в студии нет этого стрикт аналисинга?
                                    > интересуют студии 2008 и много младше
                                    7.1 закрывает на это глаза. Шестая чуть ли не просит: "нарушь стрикт алиасинг, пожалуйста, я прошу тебя..."
                                    Ответить
                                    • > Шестая чуть ли не просит: "нарушь стрикт алиасинг, пожалуйста, я прошу тебя..."
                                      Каким образом? :)
                                      Ответить
                                      • > Каким образом? :)
                                        Примерно так же, как одиноко идущая ночью нетрезвая девушка, одетая в минимум одежды, неявно, всем своим видом просит любви, нежности, ласки...
                                        Ответить
                                        • Ну ты же ни разу не воспользовался её беспомощностью?
                                          Ответить
                                    • > Шестая чуть ли не просит: "нарушь стрикт алиасинг, пожалуйста, я прошу тебя..."


                                      Ох она какая проказница... Её нужно наказать.
                                      Ответить
                      • Т.е. если я правильно разобрался, можно юзать все-таки в обе стороны. И (char *)p и (int*)q, где char *q, int *p. Т.к. согласно стандарту char* может алиаситься с любым указателем, и компилятор не будет переупорядочивать записи/чтения между char * и любым другим указателем.

                        Компилятор: "я писал в этот char *, а теперь мне нужно прочитать тот int *, но они могут ссылаться на одно и то же место, поэтому мне нельзя передвигать запись в char* на потом, за чтение int *, или вообще выкидывать ее".

                        Или все-таки я туплю?
                        Ответить
                        • Меня вот смущает фраза в той статье:
                          The converse is not true. Casting a char* to a pointer of any type other than a char* and dereferencing it is usually in volation of the strict aliasing rule.

                          Не могу понять, откуда она выводится.
                          Ответить
                          • Кстати в примере к этой фразе там ситуация, о которой и говорил LispGovno: сначало нужно скастить в чар*, а потом куда угодно*.

                            Вот она как раз и падает. И char * походу там вообще не при делах, и всетаки алиасится с чем угодно... Просто в данном примере char в операциях чтения\записи не участвовал, а участвовали uint32_t и uint16_t, о алиасинге которых компилятор не подумал.

                            Поэтому предположение остается: направление каста ни на что не влияет. Главное - в какие локации памяти я пишу, и из каких читаю.
                            Ответить
                          • Ну раз это написано, то кастить char* можно, а из него во что-то уже нельзя.
                            Ответить
                            • > кастить char* можно, а из него во что-то уже нельзя.
                              Можно кастить что угодно куда угодно, и в любом порядке, если я все правильно понял. Сам каст абсолютно ни на что не влияет, т.к. компилятор не трекает их.
                              Ответить
                              • Ну он UB получает после неверного каста во время дереференса
                                Ответить
                                • Да касты вообще не при делах ;)

                                  Ты мог сами указатели (не то, на что они ссылаются, а сами указатели) через memcpy скопировать вместо каста и получить ту ж самую хуйню.

                                  Ты мог передать их другой функции, и получить ту же самую хуйню.

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

                                  Во всех случаях будет такая бага. Хотя каста, как такового, рядом вообще нет.
                                  Ответить
                          • Вообщем они это вот к чему: Типа ты можешь крупный тип разбить на байтовый тип без опаски. Переупорядочивания записи в байтовый тип с чтением из крупного типа не будет. А вот если ты внезапно запишешь в крупный тип, то вот из байтового ты уже прочитать можешь старое закешированное значение
                            Ответить
                            • > А вот если ты внезапно запишешь в крупный тип, то вот из байтового ты уже прочитать можешь старое закешированное значение
                              Не, вот на это там примера вроде как и нет.

                              В примере к этой фразе писали в int16_t*, а читали из заалиасенного с ним int32_t. char* там вообще сбоку валялся, для красоты.

                              Это пример как раз на твой случай: "а давайте наебем компилятор, и кастанем не напрямую, а через char *"
                              Ответить
                              • Все стало ясно. Дереференсить большие типы ты можешь только сквозь чар не опасаясь нарушения строгого алиасинга (ну или сквозь унион по особому, но его мы не обсуждали)
                                Ответить
                                • Посоны, так в моём коде есть УБ, или нет?
                                  Ответить
                                  • Запости весь код внизу топика после применения исправлений от Романа и Борманда. Мы ответим.

                                    Если тебя интересует нарушение строго аналисинга, то UB с его участием в приведенном фрагменте нет.
                                    Ответить
                                    • Про алиасинг спасибо, пронесло.
                                      Правда у меня не char*, а uint8_t*, но вроде бы разница не принципиальна.
                                      А УБ с паддингом?
                                      Ответить
                                      • UB аналисинга у тебя быть не может, так как ты негде не дереференсишь через разные типы, а вот про уб спадингом не уверен, но вроде нет. Чтобы ответить на твой вопрос мне придется зарыться в стандарте крестов и посмотреть влияние директив компиляторов выравнивания примененных к типу (в данном случае T), когда тот располагает t в TStruct. Но как я говорил вроде все нормально, если смотреть по тем знаниям, что у меня есть.
                                        Ответить
                                        • > выравнивая примененных к типу (в данном случае T), когда тот располагает t в TStruct
                                          Ну здесь T лежит в TStruct, а char[sizeof(T)] лежит в Bytes. И если компилятор что-то будет дописывать (а в конец структур, емнип, выравнивание не добавляется, только между полей), то допишет он это к обеим структурам.
                                          Ответить
                                          • Да, но это переголова по памяти...
                                            Ответить
                                          • а он ничего не имеет право дописывать в конец структур. это pod типы. единственное чтобы я посоветовал тарасу - убрать ненужный конструктор копирования из тструкт, так как он меняет класс к которому относится тип. что может привести к разному поведению компилятора
                                            Ответить
                                            • Да я тоже не помню, нахер он нужен
                                              Ответить
                                            • TStruct нихрена не POD. Он может быть подом, а может и не быть им, так как T не обязательно POD. Нужно все-таки ещё подумать есть ли уб падинга или нет
                                              Ответить
                                              • > Он может быть подом
                                                Не может, т.к. имеет нетривиальный конструктор.
                                                Ответить
                                                • это только увеличивает непредсказуемость, так как Bytes ПОД всегда
                                                  Ответить
                  • > reinterpret_cast
                    Может нельзя? Выравнивание между T может вставить.
                    Ответить
                    • А в массивах разве может воткнуться выравнивание? Что-то я этот момент не помню.
                      Ответить
                    • An object of array type contains a contiguously allocated non-
                      empty set of N sub-objects of type T.
                      Ответить
                      • Типа ты имеешь в виду, что сизет учитывает это выравнивание?
                        Ответить
                        • То, что он обязан расположить элементы массива впритык друг к другу. И между ними отсебятины добавлять не должен.
                          Ответить
                    • > Выравнивание между T
                      Вот тут как раз выравнивания быть не может.
                      Ответить
    • Тарас продолжает отжигать:
      class Ololo
        MyStaticArray<Kokoko, 2> kokoko;
      public:
        Ololo () :  
         {
             kokoko.emplace_back( olololo );//питух вставить в зад оляляля
             kokoko.emplace_back( olololo );
             // kokoko.emplace_back( olololo ); // ошибка в дебаге
         } 
        Ololo ( another ) :  
         {
             kokoko.reset(); // инициализировать дефолтами
         } 
      };
      Ответить
    • Наговнявкал свой вариант
      http://ideone.com/Zdbqdq
      Демо-версия, так сказать.
      Ответить
      • tiny_buffer(std::size_t n, const value_type & v)
        +
        tiny_buffer(std::size_t n)
        =
        tiny_buffer(std::size_t n, const value_type & v = value_type())

        for (std::size_t i = 0; i < size_; ++i) data()[i].~value_type();
        =
        shrink(0) // если нам пофиг на лишнее присваивание в size_, но можно и правда вынести оба тела в какой-нибудь unfill.
        Ответить
        • Вспомнил, зачем я сделал 2 констуктора. Один из них не требует наличия конструктора по умолчанию, чего так хотел Тарас. Интересно, конструктор с параметром по умолчанию будет обладать этим свойством?
          Ответить
          • > Интересно, конструктор с параметром по умолчанию будет обладать этим свойством?
            No
            Ответить
            • *facepalm.jpg* Jlucnu - 1000
              Ответить
              • Что ты сказать то хотел? У тебя лицо на пальме?
                Ответить
                • http://www.ilyke.net/uploads/2013/05/13/sub/27903-ilyke.net-large-27903-btzmjrA.jpg


                  )))
                  Ответить
                  • Так бы сразу и сказал, что хочешь запостить картинку и остановился бы на этом.
                    Ответить
          • Понятно.
            Ответить
          • Кажется, надо, чтобы оба не требовали. Потому что инае компилятор пошлёт. Даже если ты не вызывает тот, который требует.
            Ответить
            • Ну вот, приехали
              Инстанциируются и проходят полную проверку всегда только те члены шаблонов класса, которые реально используются. Если ты не вызываешь член, который требует конструктора по умолчанию, то и ошибки компиляции не будет.
              В частности, это применимо к перегруженным функциям: если одна из перегрузок вызывает ошибку компиляции, она просто исключается из рассмотрения, см. "Substitution failure is not an error"
              Ответить
              • И при всем при этом Тарас сам юзает этот SFINAE в canref :)
                Ответить
                • Я юзаю то, что из двух вариантов компилятор выбирает тот, который может компилироваться.
                  Ответить
                • в крестах настолько низкий порог вхождения, что можно тупо копипастить готовые рецепты и оно работает само
                  Ответить
                  • кстати я ж этот рецепт со стаковерфлоу и скопипастил, ну адаптировал под себя конечно, ибо он был не про ссылки, а про свиные классы вообще
                    Ответить
                    • блин, а я уж думал, ты до Modern C++ Design александреску добрался
                      Ответить
              • Странно, я вроде пытался завести вектор от класса-без-конструктора и обломался.
                Ответить
                • кэп: потому что ты явно или неявно попытался поюзать методы, использующие то, что у элемента есть конструктор по умолчанию
                  Ответить
      • > for (std::size_t i = 0; i < size_; ++i) data()[i].~value_type();
        std::unfill
        Ответить
      • показать все, что скрыто
        #include <memory>
        #include <algorithm>
        #include <cassert>
        #include <iostream>
        
        template <typename T, std::size_t N>
        class tiny_buffer {
        public:
        
            typedef T value_type;
            typedef value_type * iterator;
            typedef const value_type * const_iterator;
        
            tiny_buffer()
                : size_(0)
            {}
        
            tiny_buffer(std::size_t n)
                : size_(n)
            {
                std::uninitialized_fill(begin(), end(), (value_type()));
            }
        
            ~tiny_buffer()
            {
                for (std::size_t i = 0; i < size_; ++i) data()[i].~value_type();
            }
        
            template <typename S, std::size_t M>
            tiny_buffer(const tiny_buffer<S, M> & rhs)
                : size_(rhs.size())
            {
                check_size(size_);
                std::copy(rhs.begin(), rhs.end(), begin());
            }
        
            tiny_buffer(std::size_t n, const value_type & v)
                : size_(n)
            {
                std::uninitialized_fill(begin(), end(), v);
            }
        
            template <typename S, std::size_t M>
            tiny_buffer & operator=(const tiny_buffer<S, M> & rhs)
            {
                const std::size_t new_size = rhs.size();
        
                if (new_size < size_) {
                    shrink(new_size);
                    std::copy(rhs.begin(), rhs.end(), begin());
                } else {
                    check_size(new_size);
                    std::copy(rhs.begin(), rhs.begin() + size_, begin());
                    std::uninitialized_copy(rhs.begin() + size_, rhs.end(), end());
                    size_ = new_size;
                }
            }
        Ответить
        • показать все, что скрыто
          value_type & operator[](std::size_t i)
              { return data()[i]; }
          
              const value_type & operator[](std::size_t i) const
              { return data()[i]; }
          
              value_type & at(std::size_t i)
              {
                  check_index(i);
                  return (*this)[i];
              }
          
              const value_type & at(std::size_t i) const
              {
                  check_index(i);
                  return (*this)[i];
              }
          
              iterator begin()
              { return data(); }
          
              iterator end()
              { return data() + size_; }
          
              const_iterator begin() const
              { return data(); }
          
              const_iterator end() const
              { return data() + size_; }
          
              std::size_t size() const
              { return size_; }
          
              void push_back(const value_type & v)
              {
                  check_size(size_ + 1);
                  new(data() + size_) value_type(v);
                  ++size_;
              }
          }
          Ответить
          • показать все, что скрыто
            void shrink(std::size_t new_size)
                {
                    assert(new_size <= size_);
                    for (std::size_t i = new_size; i < size_; ++i) {
                        data()[i - 1].~value_type();
                    }
                    size_ = new_size;
                }
            
            private:
                char raw_data_[sizeof(T) * N];
                std::size_t size_;
            
                T * data()
                { return reinterpret_cast<T*>(raw_data_); }
            
                const T * data() const
                { return reinterpret_cast<const T*>(raw_data_); }
            
                void check_index(std::size_t i) const
                { assert(i < size() && "Buffer index out of bounds"); }
            
                void check_size(std::size_t s) const
                { assert(s < N && "Buffer size exceeds capacity"); }
            };
            
            // Testing
            
            static int cnt;
            
            struct tracer
            {
                tracer() { cnt++; std::cout << "ctor\n"; }
                tracer(const tracer &) { cnt++; std::cout << "copy ctor\n"; };
                ~tracer() { cnt--; std::cout << "dtor\n"; }
            };
            
            int main()
            {
                tiny_buffer<int, 5> a;
                assert(a.size() == 0);
                a.push_back(3);
                assert(a[0] == 3);
                assert(a.size() == 1);
            
                tiny_buffer<short, 3> b(3, 1);
                a = b;
            
                assert(b.size() == 3);
                assert(a.size() == b.size());
                assert(a[0] == 1 && a[1] == 1 && a[2] == 1);
            
                tiny_buffer<int, 5> c(b);
                assert(c.size() == b.size());
                assert(c[0] == 1 && c[1] == 1 && c[2] == 1);
            
                tiny_buffer<tracer, 3> ts(2);
                assert(ts.size() == 2);
                assert(cnt == ts.size());
            
                ts.shrink(1);
                assert(ts.size() == 1);
                assert(cnt == 1);
            
                ts.push_back((tracer()));
            
                return 0;
            Ответить
            • Парни, добавьте спойлеры.
              Ответить
              • Заминусуйте говорю.

                Повыше тоже
                Ответить
                • Отличная реализация спойлера, в стиле!
                  Ответить
                  • Да на ГК уже кучу всего реализовали без единой правки исходников:
                    - спойлеры
                    - личку
                    - ссылки
                    Ответить
            • assert(new_size <= size_);
                for (std::size_t i = new_size; i < size_; ++i) {
              -     data()[i - 1].~value_type();
              +     data()[i].~value_type();
                }
                size_ = new_size;
              Ответить
          • Это код Романа или ты что-то поменял в нем?
            Ответить
            • Романа. На идеон питухи. старые ссылки на код дропают, так что лучше постить отдельно ещё
              Ответить
        • > for (std::size_t i = 0; i < size_; ++i) data()[i].~value_type();

          а чё std::destroy нема?
          Ответить
          • да не обломаешься и явно написать, это редчайшая ситуация
            Ответить
            • Всё равно я считаю это фейлом стандартизаторов. Если есть операция, конструирующая range из мусора, должна быть и обратная. Сейчас этот цикл вообще единственный на всю реализацию...
              Ответить
              • ну да
                как лишп говно выше намекнул, к std::uninitialized_fill нужен std::initialized_unfill
                Ответить
          • В stl от SGI он есть, а в стандарте нету. Если бы это был production-код, я бы его написал. А так лень было, вся реализация минут 20 писалась
            Ответить
    • Вторая специализация - для хранения указателей, как в векторе? Чтобы вынести весь общий код в какой-нибудь array<void*, N> и оставить в инстанциациях одни касты?
      Ответить
      • Для хранения силек.
        вынести в array<void*, TypeSize, N> это хорошая идея, ибо меньше пустоо дублирования кода, но я до такого колдунства ещё не дорос.
        Я слишком ленив, чтобы писать std::move на двести строк.
        Ответить
        • > писать std::move на двести строк.
          Чё?
          Ответить
          • на хабре статью я видел, почему std::move занимает 200 строк
            на самом деле это больше к вопросу о том, что если язык имеет слишком низкой уровень, то для того, чтобы заставить компилятор правильно себя вести во всех случаях, то надо пиздец как изъебнуться
            Ответить
            • Хабр это очень надёжный источник информации. Вот тебе код std::move из gcc 4.7.3
              template<typename _Tp>
                  constexpr typename std::remove_reference<_Tp>::type&&
                  move(_Tp&& __t) noexcept
                  { return static_cast<typename std::remove_reference<_Tp>::type&&>(__t); }
              Вспоминаю речь Мейерса с GoingNative 2013, где он объяснял, что "мув ничего не мувает, а форвард ничего не форвардит". По сути move надо было назвать rvalue_cast.
              Ответить
              • может, я неправильно помню название
                но вопрос оптимизации под конкретные случаи бесконечен, и если им задрачиваться по полной, то в конце концов ещё и окажется, лол, что без шаблонов кода не больше
                Ответить
        • > Для хранения силек.
          Кто такие сильки? Массивы/вектора ссылок языком запрещены, так можно работать только с указателями.
          Насколько я понимаю, теперь уже вообще не нужен CanRef, т.к. общую реализацию для "силек" и значений у тебя сделать не получилось. Вроде бы легко можно обойтись гораздо более прозрачной специализацией шаблонов.
          Ответить
          • а у меня работать с сильками - можно
            да, реализации разные
            Ответить
            • > а у меня работать с сильками - можно
              Зачем нарушать семантику языка?
              Ответить
              • Это же клёво.
                Я ПОШЁЛ ПРОТИВ СИСТЕМЫ

                (первый вариант был более двусмысленный)
                Ответить
            • Ответить
    • Спасибо, вышло продуктивнее, чем обсуждение на гдрушечке.
      Ответить
    • Так, я нигде не запутался?
      http://codepaste.ru/16746/
      Ответить
      • посоны, проверьте
        Ответить
        • Проверил:
          codepaste.ru содержит материалы «для взрослых»
          В настройках вашей сети запрещён показ страниц с подобных ресурсов.
          Ответить
          • Неужели там постят эротические рассказы с запрещёнными сценами?
            Ответить
            • Меня то чего спрашиваешь? Я не вижу что ты там про кавказцев с багром запостил.
              Ответить
        • А почему размеры знаковые?
          Ответить
          • Ну если это единственное замечание, то хорошо.
            Потому что у меня в коде везде инт, и необходимость каждый раз кастить для индексации, чтобы компилятор не ебал мозг, меня угнетает.
            И да, я пиздец как много потеряю от того, что я не смогу завести больше 2млрд элементов.
            Ответить
            • Да дело даже не в размере массива. Ещё Страуструп говорил, что делать тип unsigned только ради большего размера - не айс. unsigned нужно использовать тогда, когда что-то логически не может быть отрицательным. В частности unsigned избавляет от необходимости проверять положительность индексов/размеров.

              > если это единственное замечание
              Сейчас пока времени нет нормально ревьюить, знаковость просто первой в глаза бросилась...
              Ответить
              • > unsigned нужно использовать тогда, когда что-то логически не может быть отрицательным

                да только вот гемор с кастами лишний при этом получается

                > В частности unsigned избавляет от необходимости проверять положительность индексов/размеров.

                В релизе проверок нет, в дебаге пох, к тому же компилятор сам умеет оптимизировать код в одну проверку:
                assert ( unsigned(index) < m_size );
                Ответить
                • Я твой код глянуть не могу. Перезапости. Ну а вообще запусти valgrind и lint. Узнаешь о себе много нового.
                  Ответить
          • > А почему размеры знаковые?
            Готовится к переходу на жабу.
            Ответить

    Добавить комментарий