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

    −1

    1. 1
    2. 2
    3. 3
    Если у меня есть приватный метод, то зачем мне его объявлять в .h-файле?
    Хочу только в .cpp написать вспомогательный метод и только там его использовать.
    А вынужден копипастить сигнатуру ещё и в .h.

    Запостил: 3_dar, 10 Сентября 2021

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

    • Ну что, никто не знает?
      Ответить
      • Никто. Это потеряное знание, давно исчезнувшее из памяти живущих.
        Ответить
        • Ну так-то он прав: значение имеют только ПЕРЕМЕННЫЕ (потому что занимают место) и публичные методы.

          ООП гоавно
          Ответить
    • Используй pimpl, люк
      Ответить
      • Фу. Говно, в котором нужно будет дублировать вызовы паблик методов? И из-за которого утяжеляется навигация из-за JAWA-подобной лапши, которая нихуя не делает? Нет уж, спасибо.
        Ответить
        • Наверное можешь сделать рядом класс Util, объявить его другом, и вынести код туда
          Ответить
          • И в него передавать this? Хуйня какая-то.
            Ответить
            • Ну да, а что?

              Я бы вообще вынес всё в свободные функции и передавл зис, если не нужно приватных полей.


              Ладно, давай дожемся Борманда и ISO
              Ответить
              • Давайте подождём j123123 и он расскажет, что в «С» никаких методов нет и this всегда передавался явно.
                Ответить
                • И будет прав. В "си" такой проблемы нет в принципе.

                  Пока ООПушки решают какие-то выдуманные проблемы, сишники пишут код
                  Ответить
        • А зачем дублировать? Ты не обязан проксировать вызовы в импл, читай поля импла да и всё. А импл будет тупо для данных и приватных методов.
          Ответить
        • создай класс а верни интерфейс
          Ответить
      • Пимпл от хорошей жизни не юзают... Он нужен либо ради стабильного ABI либо чтобы сишное наследие с макроснёй и прочими ужасами всем клиентам не прилетало.
        Ответить
        • Pimple это прыщ, кстати. Справедливо ли утверждение, что в этом треде мы решаем надуманные прыщепроблемы?
          Ответить
    • Ка на счет этого?

      class my_genius_class_impl;
      class my_genius_class
      {
      ...
      private:
      my_genius_class_impl *_pimpl;
      ...
      }

      Все приватное пихаешь в my_genius_class_impl и описываешь и используешь в скрытых cpp и hpp.

      Ого, я наговнокодил в комментах!))
      Ответить
      • так пимпл и есть же
        Ответить
        • Да, я как истинный говнокодер сначала пишу коммент, а потом читаю мировой опыт выше.)
          Ответить
      • А не использовать ли нам bbcode?
        http://govnokod.ru/page/bbcode
        Ответить
    • Можешь в хедерах писать интерфейс, а реализацию полностью убрать в цпп, вместе с полями и прочими приватными кишками.

      Оп, оп, джава-стайл.

      Минус такой же как у пимпла -- надо кучу и лишняя индирекция.
      Ответить
      • Чем это от пимпла отличается? Приведи реальный пример.
        Я ненавижу pimpl не за кучу, а за ненужную вложенность вызовов.
        Ответить
        • В пимпле у тебя класс косплеит value type, а кишки прячет в куче.

          С интерфейсами класс надо таскать по указателю, как в джаве. Зато кишки не разбросаны и можно сделать несколько реализаций.
          Ответить
          • Так Инью и говорит, что с затратами на кучу он еще может смириться, а писать много буков ему не нравится
            Ответить
        • > вложенность вызовов

          А ты не вкладывай. Считай, что импл -- это просто структурка с полями и хелперами. Никто не заставляет делать "красиво" и проксировать вызовы в импл.
          Ответить
          • (вонишд)
            Ответить
          • // roost.h
            struct Roost {
                void kukarek();
            private:
                void kok();
            };
            
            
            // roost.cpp
            #include "roost.h"
            
            void Roost::kukarek() {
                kok();
            }
            void Roost::kok() {
                cout << "kok\n";
            }



            Переведи на то, что ты предлагаешь. Я не понимаю.
            Ответить
            • Приват с полями в RoostImpl у которой всё публичное. Roost держит RoostImpl по unique_ptr. Roost::kukarek зовет m_impl->kok() или лезет к полям m_impl сразу и делает всю работу если она несложная.

              Impl не обязан быть классом с полноценной инкапсуляцией, он наружу не торчит
              Ответить
              • > Impl не обязан быть классом с полноценной инкапсуляцией, он наружу не торчит

                Ясно. Для меня то это один хуй.
                Ответить
                • А мне вообще похуй на всякие инкапсуляции, я даже слов таких не знаю!
                  Ответить
                  • Ну как видишь Борманжу тоже похуй: он предлагает сделать полностбю публичного питуха и обернуть его питухом-интерфейсом

                    Именно потому я за "питон" (нет)
                    Ответить
                    • Я считаю инкапсуляцию инструментом, а не смыслом жизни.

                      В общем-то даже в джаве не парятся и разрешают доступ из всех соседей по пакету.
                      Ответить
                      • К приватным не разрешают:)

                        Но вообще я бы так сказал: приватно всё, что не документировано. Насиловать язык чтобы сэмулировать приватность и правда не всегда нужно
                        Ответить
                  • Инкапсуляция — это хорошо, на ней интернет держится. А оопитухи-угнетатили просто культурно аппроприировали наш термин.
                    У нас в ``Erlang'', кстати, правильная инкапсуляция.
                    Ответить
      • Почему вообще существует эта проблема?

        Чтобы я случайно в чужой класс не влез?
        Ответить
        • Потому что труп страуса не решился запилить модули в своё время, оставил сишные хедера, в которых все кишками наружу.

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


            Почему не разрещить .cpp файлу добавить любой метод, если в .h файле явно указать его как имеюшщий на такое право?
            Ответить
            • Потому что "свой" и "чужой" цпп невозможно отличить (это НЕ модули, это просто подстановка текста, нет никакой связи между цпп и аш), вся суть private теряется.
              Ответить
              • Так я и предлагаю ввести физгармонию, позволяющую это делать. Чтобы "h" мог явно сказать "вот мой главный cpp"
                Ответить
            • используй TypeScript компилятор - там можно все
              Ответить
          • используй DLL с одним доступным методом :)
            Ответить
          • > Потому что труп страуса не решился запилить модули в своё время, оставил сишные хедера, в которых все кишками наружу.

            Переходи на "D".
            Ответить
      • это не жаба стайл. LLVM так делает
        Ответить
    • на .xyz нельзя написать "прив*тный метод"
      какой багор
      Ответить
    • А вообще, можешь по заветам d++ ебашить хедер онли и течь.

      Никакого дублирования, топовый пирфоманс, никаких проблем с разбиением на аш и цпп.
      Ответить
      • >Никакого дублирования
        кроме кода в .o?
        Ответить
        • Линкер не человек и ему предстоит трудная ночь.
          Ответить
          • Так это понятно, но файлы-то насруца
            И скомпилируются два раза
            Ответить
            • А зачем второй раз, кстати? В труъ хедер онли у тебя один main.cpp, всё остальное в хедерах.
              Ответить
              • Ну если я напишу код в .h файле и подключу его в два места, то у меня же он два раза скомпилируется и насрётся в два .o файла, не?

                > тебя один main.cpp, всё остальное в хедерах.
                аааа, гггг
                ну ок
                Ответить
        • Ебашь всё в одном файле. Никакого дублирования.
          Ответить
          • Вот именно по этому ротоёб и за "рты"


            За одно нет проблемы с системой сборки: не нужно никакие сложные файлы писать
            Ответить
      • Тоже вариант. Я так делал в своём личном проекте. Но на работе не получится, иначе по 3 часа собираться будет на каждое изменение.
        Ответить
        • А еще приятно когда его шесть человек поправили одновременно, и случился мёрдж конфликт
          Ответить
          • hpp файлов может быть сколько угодно.
            Ответить
            • main.cpp-то всё равно нужен чтобы их подключать
              Ответить
              • А зачем тебе менять «main.cpp» ради правки отдельных файлов? Питух 1 правит «1.hpp», питух 2 — «2.hpp», «main.cpp» не меняется. Никаких «мёрдж-конфликтов».
                Ответить
              • А зачем тебе менять «main.cpp» ради правки отдельных файлов? Питух 1 правит «1.hpp», питух 2 — «2.hpp», «main.cpp» не меняется. Никаких «мёрдж-конфликтов».
                Ответить
                • mootools
                  Ответить
                • А в это время четвертый питух подключает petuh4, а пятый тоже свой petuh4, и пиздец.

                  Хотя проблема конечно несколько надуманная, я согласен.

                  Можно ли подключить автоматически все .hpp файлы в папке?
                  Ответить
              • В нём просто триггеришь разворечивание мегашаблона и всё.
                Ответить
          • > его

            Его? В бусте вон тыщи хедеров которым не нужны цппшки. Никто не мешает разбивать код по разным хедерам.
            Ответить
            • main.cpp-то всё равно нужен чтобы их подключать
              Ответить
              • А зачем тебе менять «main.cpp» ради правки отдельных файлов? Питух 1 правит «1.hpp», питух 2 — «2.hpp», «main.cpp» не меняется. Никаких «мёрдж-конфликтов».
                Ответить
                • mootools
                  Ответить
                • А в это время четвертый питух подключает petuh4, а пятый тоже свой petuh4, и пиздец.

                  Хотя проблема конечно несколько надуманная, я согласен.

                  Можно ли подключить автоматически все .hpp файлы в папке?
                  Ответить
                  • Без кастомных систем сборок — нет.
                    Ответить
                    • Жалко.

                      Можно сделать такой API для плагинов, и писать их в виде hpp
                      Ответить
                      • > Можно сделать такой API для плагинов, и писать их в виде hpp

                        Это ваш мозг на крестах.
                        Ответить
              • А зачем тебе менять «main.cpp» ради правки отдельных файлов? Питух 1 правит «1.hpp», питух 2 — «2.hpp», «main.cpp» не меняется. Никаких «мёрдж-конфликтов».
                Ответить
                • mootools
                  Ответить
                • А в это время четвертый питух подключает petuh4, а пятый тоже свой petuh4, и пиздец.

                  Хотя проблема конечно несколько надуманная, я согласен.

                  Можно ли подключить автоматически все .hpp файлы в папке?
                  Ответить
                  • > Можно ли подключить автоматически все .hpp файлы в папке?
                    Используя PHP в качестве препроцессора — да.
                    Ответить
                • А в это время четвертый питух подключает petuh4, а пятый тоже свой petuh4, и пиздец.

                  Хотя проблема конечно несколько надуманная, я согласен.

                  Можно ли подключить автоматически все .hpp файлы в папке??
                  Ответить
                  • > Можно ли подключить автоматически все .hpp файлы в папке??

                    find -name "*.h" -exec echo '#include "{}"' \; >main.cpp
                    Ответить
                    • find -name "*.h" -printf '#include "%p"\n' >main.cpp
                      Ответить
                    • sed не нужен
                      find .  -name '*.h*' -exec echo "#include<"{}">" > main.cpp \;
                      Ответить
                      • exec тоже нинужен, см. выше
                        Ответить
                        • да, так еще пижже

                          find вообще мощная питушня очень
                          Ответить
                          • > мощная питушня

                            Правда я вообще не помню его опции, обычно просто высираю список файлов и другими тулами обрабатываю (i.e. find | grep). Поэтому -printf сразу в голову не пришёл.
                            Ответить
                            • Советую иногда освежевать знания)

                              Он умеет несколько условий по OR или AND объединять например
                              Ответить
                      • Можно вот так ещё, чтобы на лету без генереций:
                        gcc main.cpp `find -name "*.h" -printf '-include %p '`
                        Главное чтобы хедер с бармин-инъекцией не прилетел.
                        Ответить
              • В нём просто триггеришь разворечивание мегашаблона и всё.
                Ответить
          • Смысле? У меня много .h файлов и 1 .cpp. На каждое изменение троттлица проц и Clion зависает всё это говно парсить.
            Ответить
    • Вспомнил про два стула:
      https://gcc.gnu.org/onlinedocs/gcc/Template-Instantiation.html
      Ответить
    • «Pimpl» не обязан использовать кучу.
      #include <string>
      #include <cstdio>
      
      // peetooh-kishki.hpp
      class PeetoohImpl {
      public:
          PeetoohImpl(std::string name) : name(std::move(name)) {}
      
          void private_kukareku() const
          {
              std::printf("%s: kukareku!\n", name.data());
          }
      
          static PeetoohImpl *cast(void *impl) noexcept
          {
              return reinterpret_cast<PeetoohImpl*>(impl);
          }
      
          static const PeetoohImpl *cast(const void *impl) noexcept
          {
              return reinterpret_cast<const PeetoohImpl*>(impl);
          }
      
          std::string name;
      };
      
      constexpr size_t PEETOOH_IMPL_SIZE = sizeof(PeetoohImpl);
      constexpr size_t PEETOOH_IMPL_ALIGN = alignof(PeetoohImpl);
      
      // peetooh-public.hpp
      // #include "peetooh-kishki.hpp"
      class Peetooh {
      public:
          Peetooh(std::string name);
          void public_kukareku() const;
      
          std::aligned_storage_t<PEETOOH_IMPL_SIZE, PEETOOH_IMPL_ALIGN> impl;
      };
      
      // peetooh-public-impl.cpp
      Peetooh::Peetooh(std::string name)
      {
          new (&impl) PeetoohImpl(std::move(name));
      }
      
      void Peetooh::public_kukareku() const
      {
          PeetoohImpl::cast(&impl)->private_kukareku();
      }
      
      int main()
      {
          auto peetooh = Peetooh("Petya");
          peetooh.public_kukareku();
          
          return EXIT_SUCCESS;
      }

      https://gcc.godbolt.org/z/hv33eT7jd
      Единственная проблема — прокинуть в публичный интерфейс размер и выравнивание приватного питуха. А так — полный зирокост.
      Ответить
      • эм
        а не придеца потом пересобирать всё когда размер внутреннего питуха изменица?
        Ответить
        • Придётся. Но тут уж или рыбку съесть…
          Собственно, если ты не используешь такой «pimpl» и изменяешь публичный класс — тебе всё равно придётся всё пересобирать.
          Ответить
          • Я думал смысл пимпла в том, чтобы в тайне от клиентов ковырять внутренние кишки, а так ABI всё равно поломается же

            Впрочем, API не сломается, так что выпускать новый .h не придется
            Но клиента нужно будет перебрать

            или нет?
            Ответить
            • Оригинального, который через кучу — да. А вот такой зирокост-пимпл работает только для скрытия кишков, но не абстрагирования от них.

              API тоже сломается: размер публичного-то петуха тоже изменится.
              Ответить
      • Почитай тред. Мне похуй на кучу/не кучу. Мне чтобы кода меньше было.
        Ответить
        • > чтобы кода меньше было.
          Тогда хуярь всё в .hpp.
          Ответить
      • > reinterpret_cast
        > std::aligned_storage_t
        > new (&impl)
        > alignof
        > void*
        > noexcept

        Выебнулся, молодец. В моём коде такой хуйни нет.
        Ответить
        • Ну тогда кучу соси

          Без плейсмент нью ты никак не
          Ответить
          • > Ну тогда кучу соси
            1505407
            Ответить
          • Лол. Просто вместо указателя делаешь не указатель.
            Ответить
            • А какой тогда смысл в отделении имплементации? Тебе её придётся демонстрировать всем желающим.
              Ответить
              • Ты точно понял что мне нужно?

                https://govnokod.xyz/_27655/#comment-658861

                Здеся не объявлять kok() в h-нике.

                Можно сделать pimpl как с указателем, так и без него. Тогда приватные методы не нужно описывать в .h. Но исходную проблему (количество кода) это не решает.
                Ответить
                • Ну да, а я что предложил?
                  // peetooh-public.hpp
                  // #include "peetooh-kishki.hpp" <- как избавляться от этого — вопрос другой
                  class Peetooh {
                  public:
                      Peetooh(std::string name);
                      void public_kukareku() const;
                  
                      std::aligned_storage_t<PEETOOH_IMPL_SIZE, PEETOOH_IMPL_ALIGN> impl;
                  };

                  «private_kukareku()» — это и есть твой «kok».

                  > количество кода
                  Если твоя проблема именно в количестве кода — то тебе действительно нужно описывать весь класс в .hpp. Меньше ты в любом случае никак не сделаешь.
                  Ответить
                  • Меня бы устроило описывать только public методы в h. Вопрос был в том почему крестопетухи не предоставили такой возможности. Одному мне это не нравится?
                    Я меняю сигнатуру приватного метода и мне нужно подправить это в двух местах, когда этого можно было избежать.
                    Ответить
                    • > Вопрос был в том почему крестопетухи не предоставили такой возможности
                      Потому что компилятор должен иметь возможность скомпилировать твой класс исключительно по его объявлению. Грубо говоря, .h-файл — это не только API, а ещё и ABI. Без объявления приватных членов ABI объявить невозможно без грязных хаков.
                      Ответить
                      • Чтобы в таблице виртуальных методов были правильные смещения? А если вместо приватных методов и полей в хедере написать foo, bar, baz, чтобы секреты не раскрывать?
                        Ответить
                        • > Чтобы в таблице виртуальных методов были правильные смещения?
                          В том числе, да.

                          > А если вместо приватных методов и полей в хедере написать foo, bar, baz, чтобы секреты не раскрывать?
                          Какая обфусцированная «Java» )))
                          Ответить
                        • А если мои приватные методы не виртуальны?
                          Ответить
                  • Нахуй так сложно? Почему не
                    class Pituh {
                        PituhImpl impl;
                    public:
                        void kukarek() {
                            impl.kukarek();
                        }
                    };

                    ?
                    Ответить
                    • Потому что я привёл реальный пример именно каноничного «pimpl» в статической форме. Твой пример, разумеется, тоже будет работать, и, возможно, решать твою задачу (я не знаю, подходит ли это тебе), но это не «pimpl».

                      P.S. А ещё у меня там тупая утечка — помнил о ней, но забыл поправить перед постингом. Какой багор )))
                      Ответить
                      • Чем мой вариант хуже?
                        Ответить
                        • В контексте разговора — тем, что он не решает задачу, для которой создан «pimpl» — полное сокрытие приватной реализации. Справедливости ради, «pimpl» через кучу ещё решает задачу бинарного отвязывания публичного интерфейса от приватного — у статического с этим похуже.

                          Для твоей задачи он, возможно, и лучше.
                          Ответить
                          • > полное сокрытие приватной реализации

                            Чем мой пример не подходит?
                            Ответить
                            • Тем, что для того, чтобы сделать «PituhImpl impl» — тебе в любом случае придётся сделать #include «PituhImpl.hpp», в котором лежат твои приватные методы. В моём варианте это не обязательно (хотя с инклудом и проще).
                              Ответить
                              • Не подумал. В моём примере нужно где-то в отдельном файле объявить PituhImpl и все его методы (в т.ч приватные).
                                И тут мы опять приходим к вопросу - писать в одном impl.hpp, или разносить в .cpp. Тогда нахуй оно надо?
                                Ответить
                              • В твоём примере тоже нужно инклудить PituhImpl.hpp. Иначе как ты его методы вызывать собрался без хедера?
                                Ответить
                                • В моём примере иклудить его надо только в .cpp, что позволяет скомпилировать реализации обоих классов, упаковать в «.dll» и выдать клиенту заголовочный файл, содержащий исключительно публичные методы, и скомпилированную библиотеку, обеспечивая тем самым полное* сокрытие реализации.

                                  * Против «IDA Pro» нет приёма.
                                  Ответить
                                  • Теперь понятно. Я указал, что на стеке у меня будет вот эта вот структура (заинклудя её).
                                    А ты указал, что у тебя тут будет некое говно вручную предпосчитанного размера (без инклуда).
                                    Ответить
                                    • Да, всё так.
                                      Ответить
                                      • То есть мой пример неюзабельный. Я смог наебать самого ISO!
                                        Ответить
                                      • А как ты размер узнаешь? Вручную захардкодишь, а в cpp static_assert заебенишь? )
                                        Ответить
                                        • Ну да. В скомпилированной библиотеке он точно меняться не будет.
                                          Ответить
                                          • Нахуй так ебаться. Уж лучше укокозатель.
                                            Ответить
                                  • > * Против «IDA Pro» нет приёма.

                                    Прыжок в середину инструкции.

                                    ** Такой прыжок невозможен в RISC-процессорах и в процессорах, требующих выравнивания адреса перехода.
                                    Ответить
                                    • > ** Такой прыжок невозможен в RISC-процессорах***

                                      *** Но у некоторых RISC-процессоров, таких как "ARM" с набором инструкций "thumb-2" таки возможен. Правда х.з. получится ли там что-то разумное и полезное.
                                      Ответить
                                      • Такие трюки на ARM будут сопоставимы с программированием на Malbolge. Одна только запись immediate чего стоит!
                                        Ответить
                                        • addi    s0,sp,688        ;1d00
                                          auipc   zero,0x1d000     ;1d000017
                                          
                                          addi   tp,a4,1           ;00170213
                                          
                                          addi    a2,sp,708        ;05d0                	
                                          li      a7,42            ;05d00842

                                          Два реальных примера коллизий.
                                          Ответить
                                      • > Но у некоторых RISC-процессоров, таких как "ARM" с набором инструкций "thumb-2"
                                        И у MIPS16, и даже у тех, который имеют слово RISC прямо в названии.
                                        Реальный пример:
                                        add(x):
                                         lui	a1,0x30   ;000305b7
                                         add	a0,a0,a1  ;952e
                                         ret              ; 8082
                                        Ответить
                                      • > *** Но у некоторых RISC-процессоров, таких как "ARM" с набором инструкций "thumb-2" таки возможен. Правда х.з. получится ли там что-то разумное и полезное.

                                        https://govnokod.ru/20245#comment335899

                                        я об этом писал.
                                        Ответить
                                    • Идея с прыжком без асмовставки: заставляем компилятор сгенерировать инструкцию mov регистр, константа. Например, вызвать какую-нибудь функцию с соглашением fastcall/sysvabi/msabi или оператор return. В бинарнике для x86_64 будет байт b8...bf, за которым следует константа. Нужно взять адрес метки оператора, увеличить на единицу и сделать computed goto. Тогда мы выполним не mov, а опкод, записанный в константе. Правда, можем соснуть, если запустим компилятор с другими ключами оптимизации.

                                      «IDA Pro» дизассемблирует только одну из веток: либо исходную функцию с mov, либо сгенерированный нами опкод.
                                      Ответить
                                      • > «IDA Pro» дизассемблирует только одну из веток

                                        Но у неё в названии не просто так есть буква "I". Так что в отличие от автоматических дизасмов ты всегда можешь переключиться между этими ветками.
                                        Ответить
                                      • > «IDA Pro» дизассемблирует только одну из веток: либо исходную функцию с mov, либо сгенерированный нами опкод.

                                        Это проблема «IDA Pro». Дизассемблировать можно по всем возможным смещениям, и визуализировать это как-то так https://i.imgur.com/LvuS6Zt.png
                                        Ответить
                    • Потому что изменение размера impl приведет к нарушению ABI
                      Ответить
      • Откуда в методах класса Peetooh берётся &impl?
        Ответить
    • https://cdn1.img.sputnik.tj/img/103108/85/1031088598_0:0:1200:813_600x0_80_0_0_102 a18d44db907d95717cee8bd52569f.jpg
      Ответить
    • Переходи на PHP. При желании там можно писать полностью процедурным стилем, безо всяких приватных, публичных, статических, хуических методов. Тупо прописываешь функцию и течёшь.
      Ответить
      • Всегда так делаю, Именно поэтому я против «C#» и «Java», в которых даже хеллоуворлд нельзя написать без классов.
        Ответить
    • #define private public
      Ответить

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