Кресты / Говнокод #14335 Ссылка на оригинал

0

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  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 LispGovno, (Updated )

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

    • std::array<> это другое, он сразу инициализирует элементы и по сути имеет фиксированный размер, а у меня массив, у которого полезный размер может быть меньше вместимости, а лишняя часть не инициализируется, это аналог довольно часто нужной связки {int length, T elements[MAX_LENGTH]}, да, это как вектор, только память фиксированную занимает. А ещё мой массив умеет хранить объекты, не умеющие в конструктор без параметров.
      Ответить
              • Кто ты и что такое 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 остался с моих попыток написать один класс для нормальных типов и для ссылок.
      Сейчас он не нужен.
      Что такое паддинг? Если чё, я специально спрашивал, смещение первого поля в структурах всегда нулевое.
      Ответить
      • Паддинг в конец добавляется для выравнивания. Вроде проблем из-за паддинга возникнуть всё-же не должно.
        Но вариант с 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.

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

                          Поэтому каст через промежуточный чар будет иметь тот же самый эффект, что и каст напрямую. Ну если я не туплю.
                          Ответить
                            • Да. 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. Нужно все-таки ещё подумать есть ли уб падинга или нет
                                            Ответить
                • > reinterpret_cast
                  Может нельзя? Выравнивание между T может вставить.
                  Ответить
                  • А в массивах разве может воткнуться выравнивание? Что-то я этот момент не помню.
                    Ответить
                  • An object of array type contains a contiguously allocated non-
                    empty set of N sub-objects of type 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(); // инициализировать дефолтами
       } 
    };
    Ответить
    • 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
          Ответить
        • Кажется, надо, чтобы оба не требовали. Потому что инае компилятор пошлёт. Даже если ты не вызывает тот, который требует.
          Ответить
          • Ну вот, приехали
            Инстанциируются и проходят полную проверку всегда только те члены шаблонов класса, которые реально используются. Если ты не вызываешь член, который требует конструктора по умолчанию, то и ошибки компиляции не будет.
            В частности, это применимо к перегруженным функциям: если одна из перегрузок вызывает ошибку компиляции, она просто исключается из рассмотрения, см. "Substitution failure is not an error"
            Ответить
            • И при всем при этом Тарас сам юзает этот SFINAE в canref 🙂
              Ответить
              • Я юзаю то, что из двух вариантов компилятор выбирает тот, который может компилироваться.
                Ответить
              • в крестах настолько низкий порог вхождения, что можно тупо копипастить готовые рецепты и оно работает само
                Ответить
                • кстати я ж этот рецепт со стаковерфлоу и скопипастил, ну адаптировал под себя конечно, ибо он был не про ссылки, а про свиные классы вообще
                  Ответить
            • Странно, я вроде пытался завести вектор от класса-без-конструктора и обломался.
              Ответить
              • кэп: потому что ты явно или неявно попытался поюзать методы, использующие то, что у элемента есть конструктор по умолчанию
                Ответить
    • #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;
          Ответить
          • Романа. На идеон питухи. старые ссылки на код дропают, так что лучше постить отдельно ещё
            Ответить
      • > for (std::size_t i = 0; i < size_; ++i) data()[i].~value_type();

        а чё std::destroy нема?
        Ответить
          • Всё равно я считаю это фейлом стандартизаторов. Если есть операция, конструирующая range из мусора, должна быть и обратная. Сейчас этот цикл вообще единственный на всю реализацию...
            Ответить
        • В stl от SGI он есть, а в стандарте нету. Если бы это был production-код, я бы его написал. А так лень было, вся реализация минут 20 писалась
          Ответить
  • Вторая специализация - для хранения указателей, как в векторе? Чтобы вынести весь общий код в какой-нибудь array<void*, N> и оставить в инстанциациях одни касты?
    Ответить
    • Для хранения силек.
      вынести в array<void*, TypeSize, N> это хорошая идея, ибо меньше пустоо дублирования кода, но я до такого колдунства ещё не дорос.
      Я слишком ленив, чтобы писать 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, т.к. общую реализацию для "силек" и значений у тебя сделать не получилось. Вроде бы легко можно обойтись гораздо более прозрачной специализацией шаблонов.
        Ответить
  • Спасибо, вышло продуктивнее, чем обсуждение на гдрушечке.
    Ответить
      • Проверил:
        codepaste.ru содержит материалы «для взрослых»
        В настройках вашей сети запрещён показ страниц с подобных ресурсов.
        Ответить
        • Неужели там постят эротические рассказы с запрещёнными сценами?
          Ответить
          • Меня то чего спрашиваешь? Я не вижу что ты там про кавказцев с багром запостил.
            Ответить
        • Ну если это единственное замечание, то хорошо.
          Потому что у меня в коде везде инт, и необходимость каждый раз кастить для индексации, чтобы компилятор не ебал мозг, меня угнетает.
          И да, я пиздец как много потеряю от того, что я не смогу завести больше 2млрд элементов.
          Ответить
          • Да дело даже не в размере массива. Ещё Страуструп говорил, что делать тип unsigned только ради большего размера - не айс. unsigned нужно использовать тогда, когда что-то логически не может быть отрицательным. В частности unsigned избавляет от необходимости проверять положительность индексов/размеров.

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

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

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

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

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

Переведи на "PHP", guest!

    А не использовать ли нам bbcode?


    8