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

    +23

    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
    #ifndef SAFE_RELEASE
    #define SAFE_RELEASE(x) \
       if(x != NULL)        \
       {                    \
          x->Release();     \
          x = NULL;         \
       }
    #endif
    
    #define SAFE_DELETE(a) if( (a) != NULL ) delete (a); (a) = NULL;
    
    #ifndef SAFE_ARRAY_DELETE
    #define SAFE_ARRAY_DELETE(x) \
       if(x != NULL)             \
       {                         \
          delete[] x;            \
          x = NULL;              \
       }
    #endif
    
    #define SAFE_FREE( p )      if( p ) { free( p ) ; p=NULL ; }

    Я вот все никак не могу забыть старый код из доков макрософт по COM, а также из книги Андре Ла Мота.

    Два макроса до сих пор висят среди доков на сайте мс (по коду догадаетесь какие):

    http://msdn.microsoft.com/ru-RU/library/windows/desktop/dd743946(v=vs.85).aspx

    Запостил: LispGovno, 01 Июня 2013

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

    • Микрософт начинет исправлятся, но пока не достаточно:
      http://msdn.microsoft.com/en-us/library/windows/desktop/dd940435(v=vs.85).aspx
      template <class T> void SafeRelease(T **ppT)
      {
          if (*ppT)
          {
              (*ppT)->Release();
              *ppT = NULL;
          }
      }
      Ответить
      • Мне сейчас 100% заминусуют, но эта куйня ничем не лучше макроса.
        Ответить
        • > ничем не лучше макроса
          Лучше. Она хотя бы не так забагована, как макросы, перечисленные выше ;)
          Ответить
      • Зачем указатель на указатель заместо ссылки?
        Ответить
        • Это чтобы занулять рилизнутый указатель
          Ответить
          • Имелась ввиду ссылка на указатель, я думаю.

            template <typename T> void SafeRelease(T *&pT) {
                if (pT) {
                    pT->Release();
                    pT = NULL;
                }
            }
            Ответить
            • А в COM такая форма передачи выходных параметров используется для совместимости с Си, так что это правильно. Хотя не вижу никакого удовольствия писать под COM на си.
              Ответить
            • Если уж писать на крестах - то лучше сделать нормальный смартпоинтер. А от ссылки никакого толку нет - код перестанет быть совместимым с си, а удобства не прибавится.
              Ответить
              • Причем примитивный смартпоинтер наваять - дело пяти минут:
                template <class T> class GovnoPtr {
                public:
                    GovnoPtr() : ptr(0) {}
                    GovnoPtr(T * p) : ptr(p) {
                        if (ptr)
                            ptr->AddRef();
                    }
                    GovnoPtr(const GovnoPtr &other) : ptr(other.ptr) {
                        if (ptr)
                            ptr->AddRef();
                     }
                    ~GovnoPtr() {
                        if (ptr)
                            ptr->Release();
                    }
                    T * operator = (T *p) {
                        if (p)
                            p->AddRef();
                        if (ptr)
                            ptr->Release();
                        ptr = p;
                        return p;
                    }
                    T * operator -> () { return ptr; }
                    T & operator * () { return *ptr; }
                    operator bool () { return ptr != 0; }
                private:
                    T * ptr;
                };
                Ответить
                • P.S. По идее в конструкторе от T * не нужен AddRef() т.к. у свежесозданного объекта уже единичка на счетчике.
                  Ответить
                  • >По идее в конструкторе от T * не нужен AddRef()
                    да, также как и в T * operator = (T *p) {
                    Ответить
                    • Да, я тупанул. Этот оператор должен был принимать ссылку на GovnoPtr. Тогда AddRef нужен. А здесь - не нужен.
                      Ответить
                  • An inneeligltt point of view, well expressed! Thanks!
                    Ответить
                • explicit добавь в GovnoPtr(T * p)
                  поддержки const корректности не хватает
                  T * operator = (T *p) { не безопасен с точки зрения исключений и не хваает такого же оператора, только копирования
                  Ответить
                  • > explicit
                    > поддержки const корректности
                    Логично

                    > не безопасен с точки зрения исключений
                    Зачем AddRef и Release будут кидать исключения? Они ведь даже не крестоблядские...
                    Ответить
                    • P.S. COM'овский метод, имхо, вообще не может кидать крестоблядские исключения, т.к. это нарушит интероперабельность с другими языками.
                      Ответить
                      • DLL поидеи тоже не может кидать исключения, но на деле что для DLL, что COM разрешают кидать исключения, когда пишут в рамках этих технологий, но на одном компиляторе и настройках в одном проекте. Не спрашивай зачем, возможно для модульности. Вообщем лучше от таких питушков уберечься, так как это почти ничего не стоит.
                        Ответить
                        • > DLL поидеи тоже не может кидать исключения
                          Моя DLL, че хочу то и делаю. Соглашения о вызовах и исключениях не зафиксированы, поэтому я всегда могу написать свои и следовать им.

                          А вот COM это не та вещь, в которой допустимы вольности. Не нравятся правила COM'а - мути свою аналогичную технологию, но не создавай людям батхерты, называя это COM'ом.

                          К тому же "Exceptions aren't allowed to flow across a COM interface boundary. Because there is no binary contract for C++ exceptions, COM cannot marshal them from one thread to another.". Т.е. с COM объектом из соседнего процесса так уже не поработаешь.

                          P.S. Исключение в Release равносильно исключению в деструкторе.
                          Ответить
                          • Поддерживаю по всем пунктам, но лучше написать тк, чтобы не было потом мучительно больно за ...
                            Ответить
                • А что это вообще за AddRef() и Release()? Что-то из винапи?
                  Ответить
                  • COM же. Методы интерфейса IUnknown, от которого порождены все остальные интерфейсы.

                    AddRef увеличивает счетчик использований на 1.
                    Release уменьшает, и удаляет объект, если счетчик достиг нуля.
                    QueryInterface позволяет получить другой интерфейс по его GUID'у.
                    Ответить
      • msdn-страннота~
        http://msdn.microsoft.com/en-us/library/windows/desktop/bb761722(v=vs.85).aspx
        pDragInfo = (LPARAM)(LPDRAGLISTINFO) lParam;
        Ответить
    • Как нормальный образованный человек может написать такое идиотское говно?
      if (nothingElseToDo) SAFE_FREE(resources);
      else doSomething();
      Почему говнокодеры так любят макросы и одновременно с этим ну никак не могут в них?
      Ответить
      • потому что те, кто могут в макросы - понимют что они не нужны
        Ответить
        • > понимют что они не нужны в большинстве ситуаций (но не всегда)
          fxd
          Ответить
      • где ты это нашел?
        Ответить
        • Он не нашел, он привел пример, на котором этот макрос поведет себя неадекватно - else уйдет внутрь макроса, а не к тому ифу, к которому должно относиться.

          Основы макроёбства - не жалеть скобок в выражениях и do { ... } while (0) в стейтментах, чтобы потом об этом не жалеть ;)
          Ответить
          • А почему {...} просто не достаточно вокруг макроса?
            Ответить
            • if ... else опять же ломается
              #define RELEASE(x) { if (x) x->Release(); }
              if (itIsTime) RELEASE(resources); // semicolon at the end finishes `if` statement
              else doSmth();
              Ответить
            • Макрос завернутый в do { ... } while (0) ведет себя неотличимо от функции в том плане, что требует ставить после него ";". А с голым блоком { ... } получается наоборот, что не всегда приятно.

              Сравни
              if (a)
                  SAFE_FREE(x)
              else
                  SAFE_FREE(y)
              и
              if (a)
                  SAFE_FREE(x);
              else
                  SAFE_FREE(y);
              Второе, естественно, смотрится привычней и интуитивней.
              Ответить
              • > do { ... } while (0)
                Лучше так:
                if(0){}else{...макро...}
                Ответить
                • > Лучше так
                  Нет, не лучше. Этот вариант забагуется на примере Романа, равно как и просто { ... }. Конструкцию с if(0){}else{...} юзают не в таком случае, а когда макросом пытаются эмулировать какой-нибудь foreach или особый if.

                  Емнип других способов замутить макрос, ведущий себя как вызов void функции кроме do { ... } while(0) и нет.
                  Ответить
                  • > макросом пытаются эмулировать какой-нибудь foreach или особый if.
                    Пример использования if(0){}else для foreach и\или if ? if(0){}else - если это не работает, то зачем так делают?
                    Ответить
                    • > если это не работает, то зачем так делают
                      Оно работает, но семантика получается как у любой другой управляющей конструкции - if/for/while и т.п. Вот для эмуляции новых подобных конструкций оно юзабельно. Для эмуляции void функций - нет.

                      > Пример использования if(0){}else для foreach
                      В Qt'шной реализации foreach'а for завернут в if(0){}else чтобы переменные, описанные в for'е не вылезали наружу из-за ебанутого скопинга в msvc6. Если интересно, а искать исходники кютихи влом - могу выложить сюда этот кусочек.
                      Ответить
                      • >Если интересно, а искать исходники кютихи влом - могу выложить сюда этот кусочек.
                        Не, спасибо. Я видел. Тот щё гонокод. Хуже только BOOST_FOREACH
                        Ответить
              • Естественнее смотрится Паскаль, в котором перед else точка с запятой никогда не ставится, потому что точка с запятой будет означать конец всего оператора if.

                А вообще же тот, кто уже обжёгся, ставит фигурные скобки при каждом удобном случае, даже вокруг единственного оператора:
                if (a) {
                    SAFE_FREE(x);
                } else {
                    SAFE_FREE(y);
                }
                Разве так не надёжнее?
                Ответить
                • Я всегда ставлю скобки. Но весьма многие не ставят их там, где они не обязательны.
                  Ответить
                  • > Я всегда ставлю скобки.
                    А я ставлю их в нетривиальных случаях - более одного уровня вложенности. Примерно так:
                    // здесь без скобок
                    if (x)
                        x->release();
                    
                    // если одна из веток сложна - скобки пишу в обе стороны
                    if (x) {
                        x->show();
                    } else {
                        y->show();
                        z->show();
                    }
                    
                    // два и более уровней вложенности - тоже скобки
                    for (int i=0; i<10; i++) {
                        for (int j=0; j<10; j++) {
                            printf("%d %d\n", i, j);
                            printf("%d %d\n", i, j);
                        }
                    }
                    
                    // но
                    for (int i=0; i<10; i++)
                        for (int j=0; j<10; j++)
                            a[i][j] = 5;
                    Ответить
                    • Ненене, я скобки ставлю вообще всегда. Мне когда-то в код влезли:

                      if (someVar != nil)
                           // log("куча текста");
                      
                      release(someVar);


                      А я потом понять не мог, почему у меня память течь начала, да и ещё в промышленных масштабах, по 3 мб на одну отрисовку
                      Ответить
                • > Разве так не надёжнее?
                  Скобки то само-собой надежнее. Но согласитесь: лучше написать макрос (а лучше вообще не писать их лишний раз без причины ;) так, чтобы он адекватно работал во всех контекстах, чем объяснять в документации как именно нужно его юзать, и надеяться, что ее будут читать...

                  > А вообще же тот, кто уже обжёгся, ставит фигурные скобки при каждом удобном случае
                  Обжегшись на молоке дуют на воду... Проблема то в том, что макрос через жопу написан. А пофиксить пытаются последствия.
                  Ответить
    • Из всех макросов полезен только SAFE_RELEASE. От остальных польза разве что в запихивании NULL'а. Но в любом случае для с++ они как-то не особо идиоматичны.

      P.S. Кстати макросы написаны неправильно, нужно больше do { ... } while(0).
      Ответить
      • Коллеги, работавшие в мотороле, рассказывают слезливые истории про их нестандартные функции освобождения памяти, вызывающие ребут телефона при передаче им NULL. Десткая психика не выдержала травмы, никак не могу отучить их писать эти проверки.
        Ответить
        • > вызывающие ребут телефона при передаче им NULL
          Ужоснах какой-то. В чем смысл такой функции? Оптимизация путем устранения лишней проверки? Так один хер все в страхе будут проверять, и проверка вернется, правда вместо одного места она будет размазана по всему коду ровным слоем...
          Ответить
          • я не знаю, но победосный зохват моторолы рынка мобильных аппаратов как-бы намекает нам
            Ответить
        • >вызывающие ребут телефона при передаче им NULL
          Это фича!
          Ответить
    • Apalnertpy this is what the esteemed Willis was talkin' 'bout.
      Ответить

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