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

    +7

    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
    #include <iostream>
    #include <alloca.h>
    #include <stdlib.h>
    #include <new>
    using namespace std;
    
    int main(void) {
      const size_t N = 5+rand()%4;
      char* arr = ::new (alloca(N)) char[N]{1,2,3,4};
      for(size_t i=0; i<N; ++i)
        cout<<(int)arr[i]<<endl;
      cout<<"ok";
      return 0;
    }

    http://ideone.com/pax1TF

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

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

    • Гыгы, Бивис. Кресты не умеют в массивы на стеке переменной длины.
      Ответить
      • Не умеют ;) alloca нет в стандарте крестов, и с ней очень легко словить граблями по яйцам.
        Ответить
        • А где calloca?
          Ответить
        • > alloca нет в стандарте крестов
          А как std::dynarray работает?
          Ответить
          • А он где-то уже работает? :)

            After reviewing national body comments to n3690, this library component was voted out from C++14 working paper into a separate Technical Specification. This container is not a part of the draft C++14 as of n3797

            P.S. И где там написано, что он обязан выделять буфер на стеке?
            Ответить
            • http://en.cppreference.com/w/cpp/container/dynarray/dynarray

              > This way, additional optimization opportunities are possible, for example, using stack-based allocation
              Ответить
              • Otherwise, the memory is allocated from an unspecified source, which may, or may not, invoke the global operator new.

                Но гарантий никаких :)
                Ответить
                • > Но гарантий никаких
                  Я прикупил кроличью лапку.
                  Ответить
          • Кстати через alloca ты его и не запилишь.
            Максимум что получится - std::dyn_array<int> a(alloca(sizeof(int)*N));

            Тут нужно больше магии...
            Ответить
            • алокатор на алоку перепиши
              Ответить
              • Нук, перепиши, продемонстрируй мне чудо ;)
                Ответить
                • Не нукай мне тут. Что сложного то алокатор написать?
                  template< class Alloc >
                   dynarray( std::initializer_list<T> init, const Alloc& alloc );
                  Ответить
                  • А при создании не забывать ему передать результат alloc'и? :)
                    Ответить
                    • Ну если в стандарт добавили, то наверное то можно пользоваться? Почему это не должно работать? Аллокатор пишешь, что в allocate вызывает alloca
                      Ответить
                      • > Ну если в стандарт добавили
                        Ты упоролся? Покажи мне строчку, в которой alloca() добавили в стандарт крестов.

                        > Аллокатор пишешь, что в allocate вызывает alloca
                        Лол. Ну напиши, потестируй.
                        Ответить
                        • Объяснишь почему не будет работать? Где я тебе гцц с новым стандартом то найду чтобы написать и протестировать?
                          Ответить
                          • > Где я тебе гцц с новым стандартом то найду
                            В новом стандарте разве есть alloca? Там, судя по всему, даже std::dynarray по пизде пошел. Покажи мне строчку драфта, в которой описана alloca и ее новое поведение.

                            std::dynarray нельзя реализовать через alloca т.к. выделенная в его конструкторе память сразу же грохнется на выходе из него. А если конструктор все-таки удастся заинлайнить (что, емнип, компиляторы никогда не делают с функциями, в которых есть alloca) - то цикл с std::dynarray будет пожирать стек, т.к. alloca не понимает скопов.

                            Поэтому для реализации std::dynarray нужна совсем другая магия, и без допилки компилятора ты его не сделаешь.
                            Ответить
                            • Я сейчас запощу код с алокой. Только когда у тебя пукан разорвет - ты не плач. Модификация компилятора ему видите ли нужна. ко ко ко
                              Ответить
                              • В дефолтовом аргументе что-ли?

                                Удачи в использовании этой хуйни в цикле, скопам то ты аллоку все равно не научишь ;)

                                Впрочем, кидай код, мой пукан уже в ожидании... ко-ко-ко
                                Ответить
                                • #include <iostream>
                                  #include <alloca.h>
                                  #include <algorithm>
                                  using namespace std;
                                  
                                  template<class T>
                                  struct TMyDynkanMaklaudArray
                                  {
                                  	T* const f, * const l;
                                  	__attribute__((always_inline)) TMyDynkanMaklaudArray(const size_t Size, const T& v=T()):f((T*)alloca(Size*sizeof(T))), l(f+Size){fill(f,l,v);}
                                  	__attribute__((always_inline)) TMyDynkanMaklaudArray(initializer_list<T> ls):
                                  	  f((T*)alloca(
                                  	  	distance(ls.begin(), ls.end() )
                                  	  	)
                                  	  ),
                                  	  l(copy(ls.begin(), ls.end(), f))
                                  	{}
                                  	T* begin(){return f;}
                                  	T* end(){return l;}
                                  	const T* begin() const{return f;}
                                  	const T* end() const{return l;}
                                  };
                                  
                                  #define pi_tux(a)do{for(auto& i : a)cout<<i<<endl;}while(0)
                                  int main() {
                                  	TMyDynkanMaklaudArray<int> ko1{5,6,7,8,9};
                                  	TMyDynkanMaklaudArray<int> ko(5, 10);
                                  	pi_tux(ko1);
                                  	pi_tux(ko);
                                  	return 0;
                                  }

                                  http://ideone.com/eSJ1F5
                                  Ответить
                                  • >always_inline
                                    читер ;)
                                    Ответить
                                    • > читер ;)
                                      А ты хотел что ли чтобы я ушел с порванным анусом после такого? ;)

                                      Это есть почти во всех компиляторах. Только написать дифайн на разных компилях подставляемый по разному. С введением атрибутов в С++11 впредь появятся стандартные атрибуты для этого.
                                      Ответить
                                      • > А ты хотел что ли чтобы я ушел с порванным анусом после такого? ;)

                                        Процитирую себя: А если конструктор все-таки удастся заинлайнить (что, емнип, компиляторы никогда не делают с функциями, в которых есть alloca) - то цикл с std::dynarray будет пожирать стек, т.к. alloca не понимает скопов

                                        Короче юзать то можно, как и саму alloca, если понимаешь последствия. Но в стандарте такой хуйне не место, там и без этого UB'ов хватает. Потом будет куча разорванных попок, кричащих "а почему у меня std::dynarray в цикле сегфолтится?".

                                        А еще такую реализацию нельзя юзать на куче или в статике:
                                        http://ideone.com/cEw6Hg

                                        > Модификация компилятора ему видите ли нужна.
                                        Для нормальной реализации - нужна.
                                        Ответить
                                        • Да ты же гений. Выделил на стеке когда стека не было! С таким же успехом скажи, что у тебя батхерт от указателей, так как их можно сначала выделить, потом инкрементировать, потом удалить, а потом записать по указателю.
                                          Ответить
                                          • В цикле хорошо себя показало:
                                            #include <iostream>
                                            #include <alloca.h>
                                            #include <algorithm>
                                            using namespace std;
                                            
                                            template<class T>
                                            struct TMyDynkanMaklaudArray
                                            {
                                            	T* const f, * const l;
                                            	__attribute__((always_inline)) TMyDynkanMaklaudArray(const size_t Size, const T& v=T()):f((T*)alloca(Size*sizeof(T))), l(f+Size){fill(f,l,v);}
                                            	__attribute__((always_inline)) TMyDynkanMaklaudArray(initializer_list<T> ls):
                                            	  f((T*)alloca(
                                            	  	distance(ls.begin(), ls.end() )
                                            	  	)
                                            	  ),
                                            	  l(copy(ls.begin(), ls.end(), f))
                                            	{}
                                            	T* begin(){return f;}
                                            	T* end(){return l;}
                                            	const T* begin() const{return f;}
                                            	const T* end() const{return l;}
                                            	size_t size() const {return distance(f, l);}
                                            };
                                            
                                            #define pi_tux(a)do{for(auto& i : a)cout<<i<<endl;}while(0)
                                            int main() {
                                            	TMyDynkanMaklaudArray<int> ko1{5,6,7,8,9};
                                            	for(size_t i=0; i<ko1.size(); ++i)
                                            	{
                                            		//[i](){
                                            			TMyDynkanMaklaudArray<int> ko(i, i);
                                            			pi_tux(ko);//}();
                                            	}
                                            	pi_tux(ko1);
                                            	return 0;
                                            }

                                            В крайнем случае можно создать фрейм стека.
                                            http://ideone.com/j13ahX
                                            Ответить
                                            • > В цикле хорошо себя показало:
                                              Цикл маловат ;) В каком-нибудь while (true) или просто достаточно большом цикле оно покажет себя с лучшей стороны.

                                              > Да ты же гений. Выделил на стеке когда стека не было!
                                              Ну так std::dynarray по стандарту должен был быть безопасным, и выделять память не на стеке, если объект размещается не на стеке (+ еще одно магическое свойство этого класса, которое потребует поддержки от компилера или рантайма). Где там написано, что я не могу его засунуть в другой класс, или создать в куче, или поюзать в большом цикле?

                                              P.S. Короче теперь я понял, почему этот класс не включили в с++14 и послали нахуй в std::experimental.
                                              Ответить
                                              • > Цикл маловат
                                                http://ideone.com/mkXjpG

                                                #include <iostream>
                                                #include <alloca.h>
                                                #include <algorithm>
                                                using namespace std;
                                                
                                                template<class T>
                                                struct TMyDynkanMaklaudArray
                                                {
                                                	T* const f, * const l;
                                                	__attribute__((always_inline)) TMyDynkanMaklaudArray(const size_t Size, const T& v=T()):f((T*)alloca(Size*sizeof(T))), l(f+Size){fill(f,l,v);}
                                                	__attribute__((always_inline)) TMyDynkanMaklaudArray(initializer_list<T> ls):
                                                	  f((T*)alloca(
                                                	  	distance(ls.begin(), ls.end() )
                                                	  	)
                                                	  ),
                                                	  l(copy(ls.begin(), ls.end(), f))
                                                	{}
                                                	T* begin(){return f;}
                                                	T* end(){return l;}
                                                	const T* begin() const{return f;}
                                                	const T* end() const{return l;}
                                                	size_t size() const {return distance(f, l);}
                                                };
                                                
                                                #define pi_tux(a)do{for(auto& i : a)cout<<i<<endl;}while(0)
                                                int main() {
                                                	TMyDynkanMaklaudArray<int> ko1{5,6,7,8,9};
                                                	size_t i, j=0;
                                                	for(i=0; i<10005000; ++i)
                                                	{
                                                		[&j](){
                                                			TMyDynkanMaklaudArray<int> ko{1,1,-1,1,};
                                                			j = accumulate(ko.begin(), ko.end(), j);
                                                			}();
                                                	}
                                                	cout<<j<<endl;
                                                	pi_tux(ko1);
                                                	return 0;
                                                }
                                                Никаких переполнений стека.
                                                Ответить
                                                • > Признайся, ты не осилил кресты.
                                                  Да я не о том, что это обойти нельзя. Можно конечно.

                                                  Я все-таки о том, что абстракция у этого класса течет сплошным потоком... В крестах и так UB'ов полно, а ты еще такое чудо хочешь туда добавить.

                                                  А слабо переделать класс так, чтобы я не мог разместить его инстансы в куче или статике? :)
                                                  Ответить
                                                  • Если бы я писал std::dynarray, то пока не вошли в мэйн проверяем не выставленный флаг и выделяем память из кучи. Если мы вошли в маин и флаг выставился, то если у нас просят слишком большой размер, то выделяем из кучи, если в пределах вменяемого, то выделяем через alloca. Все операторы new пометить как =delete.
                                                    Ответить
                                                    • > Если бы я писал std::dynarray <...>
                                                      Вот от такого не спасет :)
                                                      class wrapper {
                                                          std::dynarray<something> array;
                                                          int other;
                                                      };
                                                      Абстракция дырявая даже если убить new :(
                                                      Ответить
                                                      • > kill new

                                                        Это да... :(
                                                        Ну это особый контейнер и требует особого отношения, очевидно. Так что
                                                        И пугаться нет причины,
                                                        Если вы еще мужчины

                                                        и знаете кресты
                                                        Ответить
                                                        • > Ну это особый контейнер и требует особого отношения, очевидно.
                                                          А что если юзать простой и безопасный вариант?
                                                          fast_vector<int, 10> m;
                                                          Если размер меньше 10 интов - буфер будет размещен в самом объекте. Если больше - в куче. Всегда жрет 10*sizeof(int) байт, но и пофиг, зато потребление стека всегда детерминировано, адекватно работает со скопами, не требует никаких нестандартных функций и атрибутов, нет ограничений по размещению...
                                                          Ответить
                                                        • > kill new

                                                          аборт
                                                          Ответить
                            • >В новом стандарте разве есть alloca?
                              стандарт - для анскильных крестопитухов.
                              На gcc работает - все нормально
                              Ответить
                      • >Аллокатор пишешь, что в allocate вызывает alloca
                        Я писал. Не работает. Хотел заменить std::allocator. Внутри функции allocate выделяет память, а когда выходит из функции удаляет память. Функция возвращает мусор.
                        Ответить
        • > с ней очень легко словить граблями по яйцам.
          Это что там можно словить? Всегда пользовался для оптимизации. Код вылетает редко.
          Ответить
          • Жесткое разрушение без вызова деструкторов (хотя для POD'ов - сойдет).
            Можно порвать стек, если массив окажется слишком толстым.
            Есть не во всех компиляторах (хотя мне такие не попадались).
            Возможны баги с циклами (т.к. она не понимает скопов).
            Ответить
            • > Можно порвать стек

              Стек! Порвали стек!
              Кукарек, кукарек, кукарек...
              Ответить
            • А чего жесткое разрушение? Если я поправлю свой дунька арей, то должно нормально вызывать деструктор
              Ответить
              • Ну это да, подергать деструкторы в цикле никто не запрещает.
                Ответить
    • Притча во языцах ваша alloca.
      http://stackoverflow.com/a/3410689/3083709
      Ответить
    • Вечер крестобесия на говнокоде!
      Тред подтолкнул меня нагуглить boost::pool_allocator, который решает обходным путем задачу без дефекации в стек, который не резиновый.
      Ответить
      • А как его применить для массива? Будет ведь та же самая фигня, что и с кучей - фрагментация, выделение/освобождение не за O(1) и прочая питушня.
        Ответить
        • std::vector - вполне себе массив
          Ответить
          • Я тут имел в виду то, что вектор у пула будет просить непрерывные куски. И будет та же самая фигня, что и с кучей. Ну правда масштабы питушни будут поменьше, т.к. все-таки пулы для других размеров это не заденет.

            Все-таки пул, имхо, идеален для одиночных небольших объектов, а не для массивов.
            Ответить
            • Бери std::deque. Будет как смазка для ануса. А вообще ты прав. Ничего не выйдет. boost::pool_allocator - говно и это ничего не изменит. Он не пригоден кроме как для каких-нибудь std::shared_ptr или std::list
              Ответить
            • > вектор же у пула будет просить непрерывные куски
              Таким же образом у стека массив тоже забирает непрерывный кусок памяти. К тому же, напомнило: http://govnokod.ru/11495#comment148787

              > И будет та же самая фигня, что и с кучей.
              Фрагментация, если я правильно понял? Таки да, есть такие проблемы. Если бы std::array или boost::array предоставляли возможность подменить аллокатор, то проблема бы сводилась к написанию аллокатора с аналогичной стеку стратегией выделения памяти из пула. ИМХО это все равно лучше, чем вся эта питушня с alloca. Если б я увидел на работе какой-нибудь "my_super_alloca_array_do_not_touch_magi c" - обиделся бы на всю жизнь, т.к. мне предоставлялась честь прибирать за одним таким изобретателем.
              Ответить
              • > Фрагментация, если я правильно понял?
                Да там не только она...
                - Надо думать о потокобезопасности пула и терять время на блокировки, если мы не делаем thread-local pool'ы (а если делаем - теряем память).
                - У пула повышается сложность освобождения от O(1) до O(n) из-за того, что ему надо сортировать пустые блоки во free list'е (иначе он он пропустит половину подходящих мест под массив).

                Короче пул превращается в самую обычную кучу. Разве что изолированную.

                > проблема бы сводилась к написанию аллокатора с аналогичной стеку стратегией выделения памяти из пула
                Дополнительный thread-local стек? А что, это идея! Только его аллокатором оформлять не стоит, т.к. его будут абузить для других структур и расстраиваться - realloc же возможен только у самого свежего, и удаляться все должно строго в обратном порядке... Имхо лучше отдельный спецкласс под такое.

                Пока вижу одну проблему - та же фигня, что и с кодом лиспговна - если такой массив запилить в куче, он испортит всю синхронизацию стеков.
                Ответить
                • > Надо думать о потокобезопасности пула
                  Хех, о таких вещах я даже не задумывался: код в топике - о "возне" в стеке, а я не представляю себе ситуацию, при которой один поток начинает модифицировать стек другого.

                  > повышается сложность освобождения от O(1) до O(n)
                  Пару лет назад я встречал STL-совместимые set и map для диапазонов значений. Я не математик, но мне кажется, что O(n) можно улучшить до O(log n). Ну, это так, догадки. Похоже, я слишком серьезно отнесся к коду, который написан just for lulz :)
                  Ответить
                  • > один поток начинает модифицировать стек другого
                    Не-не-не. Сам пул же не в стеке создается, он общий... Вот потоки и подерутся за него.

                    > можно улучшить до O(log n)
                    Вполне возможно. Я про конкретную реализацию free list'ов в бустовском пуле. Там прям так и написано - хочешь непрерывные чанки аля массивы - включай сортировку при освобождении и имей O(n).
                    Ответить
                    • А зачем делать этот пул общим? Пусть у каждого треда будет свой. Тем более, если делать аллокатор как предлагает Xom94ok - то есть FIFO-only - несколько тредов вместе с ним работать нормально ну никак не будут. Либо будет все падать, либо они будут вечно ждать друг друга.
                      Ответить
                      • Ну так надо читать тред до конца :)

                        Про идею хомячка обсуждали чуть выше, где я предложил не оформлять его аллокатором, т.к. далеко не все контейнеры адекватно смогут с таким аллокатором работать. А тут я пишу про конкретный бустовский пул...
                        Ответить
                    • Ты с вектором из нескольких тредов работаешь без синхронизации работаешь? Тогда тебе стоит прикупить кроличью лапку. Нет? С синхронизацией? То то. Значит ололокатору личная синхронизация не нужна.
                      Ответить
    • крестопроблемы && дельфепроблемы
      я для такой хуйни стек тупо эмулирую
      выделение-освобождение за O(1), ассерты проверяют, чтобы освобождалось строго в обратном порядке
      я ради такого даже в своём контейнере в деструкторе порядок уничтожения перевернул
      Ответить
      • Ассемблер и сишка нам поможет. Там этой проблемы нет. Сам взял и вызвал деструкторы в правильном порядке
        Ответить
        • А в крестах где эта проблема? Насколько помню, порядок вызова конструкторов-деструкторов там везде вполне определен.
          Ответить
          • проблема в том, что он может отличаться от порядка, требуемого эмулятором стека
            Ответить
            • Да почему, вроде бы всегда в обратном созданию. А эмулятору стека это и надо.
              Ответить
              • Это я к своему контейнеру, который вручную дёргает конструкторы и деструкторы. В нём пришлось руками перевернуть цикл деструкторов.
                Ответить
                • Кстати, так вот почему delete дергает все деструкторы в обратном порядке, от конца массива...
                  Ответить
                  • Наверное привычка связанная со старым добрым LIFO

                    Если я правильно понимаю ++ (а я не понимаю) то порядок значения не имеет?
                    Ответить
                    • В крестах порядок вызова деструкторов чётко определён и он очень важен.
                      Объекты всегда разрушаются в порядке, обратном созданию. И это правильно. Пример:
                      A a;
                      B b(a);
                      b использует a. Если разрушить a до разрушения b, это может помешать нормальному разрушению b, т.к. b может захотеть что-то сделать с a в деструкторе. Аналогично с наследованием: если B наследует A, это реализуется в виде подобъекта a внутри b. Нельзя разрушать внутренности объекта, пока сам объект не разрушен.
                      Ответить
                      • ну так в массиве все объекты одного уровня приоритета.
                        Если массив линкованный - тогда ясен пень с конца.
                        Ответить
    • http://home.roadrunner.com/~hinnant/stack_alloc.h
      Ответить

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