На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
Модераторы: Qraizer, Hsilgos
Страницы: (2) [1] 2  все  ( Перейти к последнему сообщению )  
> Структрура проектов C++
    В проекте файл cppProcFiles.cpp, который нормально компилируется. Я хотел бы научиться грамотно разбивать проекты на файлы и включать в файлы с исходным кодом заголовочные файлы, чтобы не было дублирования и конфликтов (чтобы в будущем не переделывать более сложные проекты, надеюсь, что до этого дорасту). Сделал файл cppTest.cpp с main и файл procfile.h с функциями обработки файлов. В результате получаю 27 ошибок. У меня возникли следующие вопросы:
    1. Как грамотно включать в файлы заголовочные файлы, если они повторяются в каждом из файлов. В проекте только один файл cpp, но по мере освоения С++ проекты будут с несколькими файлами cpp.
    2. Можно ли включать в заголовочные файлы и файлы cpp строки namespace fs = filesystem; using namespace std; одновременно или нужно в заголовочных файлах все включать полностью.
    3. Какие ошибки я сделал при создании проекта с несколькими файлами.
    4. Посоветуйте, пожалуйста, источники, в которых хорошо изложены принципы построения проектов C++ и есть ответы на эти вопросы.
    Прикреплённый файлПрикреплённый файлcppTest.zip (4,34 Кбайт, скачиваний: 28)
      Мы начинаем так

      ExpandedWrap disabled
        #ifndef NOOB_ONLINE
        #define NOOB_ONLINE
         
        class Class1{
        };
         
        #endif
        Есть еще
        ExpandedWrap disabled
          #pragma once
          1. Нужно просто правильно понимать, какую роль играют заголовки. Они нужны для документирования интерфейса некой конкретно взятой подсистемы. Взгляни, к примеру, на заголовки стандартного C. Каждый заголовок служит конкретной цели: определить интерфейс той или иной подсистемы библиотеки времени выполнения. stdio, string, malloc, math итп. В свою очередь, подключаешь к единице трансляции заголовок нужной подсистемы, если тебе требуется её функционал, а следовательно и интерфейс к ней. Ничего сложного.
          2. Категорически не стоит иметь ничего подобного в заголовках. Сделав так, ты тем самым не оставишь пользователю твоей подсистемы выбора, он вынужден будет использовать те же подсистемы, которые ты выбрал для реализации функционала своей подсистемы. Это плохой тон, лучше пусть там решает, нужно ли ему оно или он предпочтёт использовать другие интерфейсы.
          3. Пока я никаких нескольких файлов не увидел. Проект просто не слинкуется в таком виде.
          4. Та в принципе любая нормальная книжка должна эти вопросы хорошо освещать. За нормальными, боюсь, не ко мне, я давно перестал следить за подобного рода литературой.
          Сообщение отредактировано: Qraizer -
            Qraizer покритикуйте мой проект
            Правила по которым я строю проект

            1. Файлы идут парами : имяфайла.h имяфайла.cpp
            2. В файле Project.cpp находится точка входа WinMain() и главная процедура WndProc() и все.
            3. В файле .cpp находятся только функции в алфавитном порядке.
            4. В файле .h находятся все глобальные переменные, вкладываю в namespace e{} тогда в любом месте глобальная переменная будет выглядеть e::имяпеременной.
            5. Не люблю подчеркивания поэтому <имя_переменной> не делаю, практика показывает что <имяпеременной> читается не хуже, но зато проект не рябит подчеркиваниями и не создает визуальный шум.
            6. Объявление переменных всегда пишу в начале функции int i,j,k.
            7. Функции ВерблюжьяЗапись() причем первая чать имя модуля где расположена функция.
            8. Глобальные переменные по названию либо должны точно определять ее назначение, либо иметь в названии чать модуля где они более всего используются
            9. Обычно еще есть модуль Temp.h в который как в записную книжку сбрасываю временную информацию, периодически оттуда все удаляю
            10.Комментарии не пишу*
            Скрытый текст

            * Комментарий это как goto если они есть значит функция не доработана и ее можно сделать лучше. Комментарий у меня только в начале модуля чтобы знать что это за модуль - если придется напечатать или что-то еще. Комментарии удобно использовать в модуле над которым ведется работа - копируешь блок закомментировал переделываешь под свое, либо что-то нужно временно выключить. Либо найти вхождение глобальной переменной в любом месте программы - закомментировал в объявлении и пожалуйста компилятор как ошибки укажет все вхождения глобальной переменной.
            Сообщение отредактировано: Betelgeuse -
              Цитата Betelgeuse @
              Прикреплённый файлProgram_graphics.rar (65,07 Кбайт, скачиваний: 1)

              Что-то мутное! :scratch:

              Virustotal на Program.exe:

              ExpandedWrap disabled
                APEX 6.434                   | Malicious
                BitDefenderTheta 7.2.37796.0 | Gen:NN.ZexaE.36318.bqW@ayRumtni
                Bkav 2.0.0.1                 | W32.AIDetectMalware
                MaxSecure 1.0.0.1            | Trojan.Malware.300983.susgen
                Trapmine 4.0.14.90           | malicious.moderate.ml.score
                Если программа с вирусами, значит не нужно ее запускать. Вы совершенно правы
                Сообщение отредактировано: Betelgeuse -
                  Была дурацкая ошибка: using namespace std; после namespace fs = filesystem; Из-за этого столько ошибок возникало. Программа после исправления ошибки заработала. Спасибо всем ответившим. Мой вопрос был преждевременным, поскольку в проекте не было нескольких файлов, а только один заголовочный и один файл исходного кода. Посмотрю рекомендации в книгах по C++.
                    Цитата Betelgeuse @
                    покритикуйте мой проект

                    Утечка ресурса - использована функция RegisterClassA,
                    не использована функция UnregisterClassA.
                    ---
                    Цитата

                    2. В файле Project.cpp находится точка входа WinMain()

                    я бы назвал этот файл WinMain.cpp
                    И далее так по всем классам - имя класса (или имя процедуры) это имена *.h и *.cpp файлов
                    ---
                    Класс - сабклассинг:
                    Скрытый текст

                    ExpandedWrap disabled
                      // h-файл
                      // --------------------------------------------------------------------------
                      // FILE MSubClass.h 21.08.2006
                      // --------------------------------------------------------------------------
                      // класс - саб-классинг
                      // Дает возможность выполнять процедуру как до вызова оригинальной
                      // оконной функции, так и после в том числе и оба варианта сразу.
                      // --------------------------------------------------------------------------
                      #ifndef __MSubClass__H
                      #define __MSubClass__H
                       
                      // --------------------------------------------------------------------------
                      class MSubClass
                      {
                       public:
                      static const int  SUBCLASS_NO_DIALOG   = 0;
                      static const int  SUBCLASS____DIALOG   = 1;
                       
                       private:
                       
                       static LRESULT CALLBACK SubClassStatic
                                            (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
                       
                       protected:
                      virtual  int WINAPI  SubClass
                                            (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
                       
                      virtual void WINAPI  SubClassPast
                                            (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
                       
                                   int      ReturnCode;
                                   int      index;
                                   HWND     hwndSub;
                                   WNDPROC  winrou;
                                   DWORD    err;
                                   LONG_PTR OldUserData;
                                   LRESULT  RetC;
                       
                       public:
                                   WINAPI  MSubClass ();
                           virtual WINAPI  ~MSubClass ();
                              bool WINAPI  InstallSub (HWND hwndSub_, int TipeSub=SUBCLASS_NO_DIALOG);
                       
                           DWORD   WINAPI  LastError(void){return err;}
                      };
                      // --------------------------------------------------------------------------
                      // пример сабклассинга окна редактирования:
                      //
                      /*
                        #define IDCONEDITTEST 0xFFF4
                      MSubClass *pSC=NULL;
                      HWND hConTest;
                       
                        hConTest = ::CreateWindowEx
                        (
                         WS_EX_OVERLAPPEDWINDOW | WS_EX_STATICEDGE,
                         "edit",
                         NULL,
                         WS_CHILD | ES_LEFT | WS_VISIBLE,
                         x+10,
                         y+40,
                         60,
                         30,
                         GetHWND(),//Dlghwnd,
                         (HMENU)IDCONEDITTEST,
                         hInst,
                         NULL
                        );
                       
                        pSC = new MSubClass ();pSC->InstallSub (hConTest);
                      ..
                      ..
                        delete pSC; pSC=NULL;
                       
                      */
                      //
                      // --------------------------------------------------------------------------
                      #endif
                       
                      // cpp файл
                       
                       
                      // --------------------------------------------------------------------------
                      //   FILE MSubClass.cpp 21.08.2006
                      // --------------------------------------------------------------------------
                      #include "stdafx.h"
                      #include "MSubClass.h"
                      // --------------------------------------------------------------------------
                      WINAPI MSubClass::~MSubClass ()
                      {
                       if (hwndSub != NULL)
                       {
                      // попытка сделать это для уже разрушенного окна
                      // не приведет к исключению.
                      // Просто будет выдана ошибка и фсе.
                        ::SetWindowLongPtr (hwndSub,GWLP_USERDATA,OldUserData);
                        ::SetWindowLongPtr (hwndSub,index,(LONG_PTR)winrou);
                       }
                      }
                      // --------------------------------------------------------------------------
                      WINAPI MSubClass::MSubClass ()                   {hwndSub=NULL;ReturnCode=0;}
                      // --------------------------------------------------------------------------
                      bool WINAPI MSubClass::InstallSub (HWND hwndSub_, int TipeSub)
                      {
                      bool Result=true;
                       
                       if (hwndSub == NULL)
                       {
                        hwndSub    = hwndSub_;
                       
                        if (TipeSub == SUBCLASS_NO_DIALOG) {index = GWLP_WNDPROC;}
                        else                               {index = DWLP_DLGPROC;}
                       
                        winrou = (WNDPROC)::GetWindowLongPtr(hwndSub,index);
                       
                      // спасем старый параметр, который может быть нужен окошку
                        OldUserData = ::GetWindowLongPtr(hwndSub,GWLP_USERDATA);
                        ::SetWindowLongPtr (hwndSub,GWLP_USERDATA,(LONG_PTR)this);
                       
                        RetC=::SetWindowLongPtr (hwndSub,index,(LONG_PTR)SubClassStatic);
                        if(RetC==0)
                        {
                         err = ::GetLastError();  if (err != NO_ERROR) Result=false;
                        }
                       }
                       else
                       {
                        Result=false;
                       }
                       return Result;
                      }
                      // --------------------------------------------------------------------------
                      LRESULT CALLBACK MSubClass::SubClassStatic
                                               (HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)
                      {
                      MSubClass *pSC;
                      int retc=10;
                       
                       pSC = (MSubClass *)  ::GetWindowLongPtr (hwnd,GWLP_USERDATA);
                       
                       if (!pSC) return ::DefWindowProc(hwnd, message, wParam, lParam);
                       if (hwnd == pSC->hwndSub)
                       {
                         retc =pSC->SubClass(hwnd,message,wParam,lParam);
                       }
                       switch (retc)
                       {
                        case 0:
                               pSC->RetC = ::CallWindowProc(pSC->winrou, hwnd, message, wParam, lParam);
                               pSC->SubClassPast(hwnd,message,wParam,lParam);
                       
                        return pSC->RetC;
                       
                        case 1:  return   pSC->ReturnCode;
                        default: return ::DefWindowProc(hwnd, message, wParam, lParam);
                       }
                       
                      }
                      // --------------------------------------------------------------------------
                      int WINAPI MSubClass::SubClass
                                            (HWND , UINT , WPARAM , LPARAM )
                      {
                       return 0;
                      }
                      // --------------------------------------------------------------------------
                      // при необходимости может исказить RetC
                      void WINAPI MSubClass::SubClassPast
                                            (HWND , UINT , WPARAM , LPARAM )
                      {
                      }
                      // --------------------------------------------------------------------------

                    Сообщение отредактировано: ЫукпШ -
                      Цитата ЫукпШ @
                      Утечка ресурса - использована функция RegisterClassA,
                      не использована функция UnregisterClassA.

                      За подсказку по сабклассингу - спасибо )
                      Сообщение отредактировано: Betelgeuse -
                        1. Почему нет. Имя реализации интерфейса совпадает с именем публикуемого его заголовка. Но это не всегда удобно.
                        2. :-?
                        3. Зачем так строго? А если есть нечто приватное, не предназначенное для публикации?
                        4. Для чего namespace-то? Не, я знаю, что глобальный скоп иногда преподносит сюрпризы со стандартными типами при ADL, но у тебя явно не тот стиль, когда это скажется.
                        5. Дело вкуса. Подчерк заменяет пробел. Если у тебя родной немецкий, то может быть удобно, но не тем, у кого иначе. Я предпочитаю такую же верблюжьюЗапись, но со строчной в начале. Кроме как если внутри аббревиатуры, они всегда заглавные. Почти, кроме предлогов.
                        6. Опять же, зачем? Определения без инициализации требуются редко, а инициализация в большинстве случаев возможна уже по ходу дела. Другое дело, что я все определения отделяю пустыми строками, так что их всё равно хорошо видно.
                        7. ВерблюжьяЗапись мною используется для типов, в частности классов. Так-то для функций такой же стиль, как для переменных, они всё равно легко различимы по наличию или отсутствию () при использовании.
                        8. :)
                        9. :-?
                        10. Пффф... Если комментировать, что происходит в коде, то такое никому не нужно, да. Комментирование используется не для этого.


                        Добавлено
                        Цитата Betelgeuse @
                        Проблема вот в чем : когда есть CALLBACK функции, особенно субклассинг - как их всунуть в класс ?
                        В общем случае это невозможно, т.к. методы хотят this, который при & над методом (или элементом данных) класса встраивается в сам тип указателя, что приводит к несовместимости типов указателей на элементы класса и указателей на обычные сущности. Так для int A::f() тип указателя будет int (A::*)(), а не int (*)(). И операция разыменования указателя не просто *, а .* или ->*, где слева указывается экземпляр класса. Эти операции позволяют этот экземпляр использовать как конкретный this для указателя. Понятно, что * такого не умеет. Единственный вариант, сохраняющий совместимость типов указателей – это использовать статические методы (или данные), но и тут подводные камни в лице невозможности из таких методов обращаться к нестатическим полям и методам. Причина опять же в том, что неоткуда брать конкретный this.
                        Но в частных случаях этот this можно куда-нибудь замешать. Многие callback в WinAPI предусматривают дополнительный параметр, который сам WinAPI игнорит, но передаёт в неизменном виде в сам callback. Его легко можно использовать для такого замешивания. Типа такого:
                        ExpandedWrap disabled
                          class WindowEnumerator
                          {
                            virtual bool currentWindow(HWND) = 0;
                           
                          public:
                            static BOOL CALLBACK enumerator(HWND hWnd, LPARAM param)
                            {
                              return reinterpret_cast<WindowEnumerator*>(param)->currentWindow(hWnd);
                            }
                          };
                        Простое использование:
                        ExpandedWrap disabled
                          class WindowEnumerator_impl: public WindowEnumerator
                          {
                            virtual bool currentWindow(HWND hWnd)
                            {
                              std::cout << "Found " << hWnd << std::endl;
                              return true;
                            }
                          };
                           
                          /* ... */
                           
                          EnumWindows(&WindowEnumerator::enumerator, reinterpret_cast<LPARAM>(&WindowEnumerator_impl()));


                        Добавлено
                        В твоём случае, когда тебе нужна конкретно WNDPROC, можно ещё проще. Зарезервируй в окне дополнительное место под this, запиши его туда SetWindowLongPtr() и читай GetWindowLongPtr() в WNDPROC
                        Сообщение отредактировано: Qraizer -
                          Разбил проект на несколько файлов. В Visual Studio 2022 проект компилируется без ошибок. А в Visual Studio Code при компиляции его с помощью gcс получаю ошибку
                          ExpandedWrap disabled
                            D:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/13.1.0/../../../../x86_64-w64-mingw32/bin/ld.exe: C:\Users\Admin\AppData\Local\Temp\cc9YPIRm.o: in function `main':
                            D:/MyProgramming/cppProcFiles/cppProcFiles.cpp:42: undefined reference to `CreateVectorsFiles(std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >&, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >&, char const*, char const*)'
                            collect2.exe: error: ld returned 1 exit status
                          Видимо, моя разбивка на файлы не универсальная, нужно делать по-другому. Подскажите, пожалуйста, как правильно сделать структуру проекта, чтобы он компилировался правильно различными компиляторами. Проект прикрепил.
                          Прикреплённый файлПрикреплённый файлcppProcFiles.zip (5,5 Кбайт, скачиваний: 27)
                            Твой интерфейс cppProcFiles.h зависит от std::vector<> и std::string, значит должен включить их интерфейсы. Добавь подключение <vector> и <string> в cppProcFiles.h
                            Сообщение отредактировано: Qraizer -
                              Цитата Qraizer @
                              Твой интерфейс cppProcFiles.h зависит от std::vector<> и std::string, значит должен включить их интерфейсы. Добавь подключение <vector> и <string> в cppProcFiles.h
                              Я думал, что предпроцессор подставит в cppProcFiles.cpp файл cppProcFiles.h, тогда в объединенном файле уже будет указание на включаемые файлы <vector> и <string> и включать их в файл cppProcFiles.h не надо. Видимо, препроцессор Visual Studio так и понимает, а Visual Studio Code понимает по-другому. Эти ошибки исчезли, но появились другие:
                              ExpandedWrap disabled
                                D:\msys64\mingw64\bin\g++.exe -fdiagnostics-color=always -g D:\MyProgramming\cppProcFiles\cppFun.cpp -o D:\MyProgramming\cppProcFiles\cppFun.exe
                                D:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/13.1.0/../../../../x86_64-w64-mingw32/bin/ld.exe: D:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/13.1.0/../../../../lib/libmingw32.a(lib64_libmingw32_a-crtexewin.o): in function `main':
                                C:/M/B/src/mingw-w64/mingw-w64-crt/crt/crtexewin.c:67: undefined reference to `WinMain'
                                collect2.exe: error: ld returned 1 exit status
                              Откуда появилась ссылка на WinMain в консольном приложение - не понятно.
                                Цитата tumanovalex @
                                Я думал, что предпроцессор подставит в cppProcFiles.cpp файл cppProcFiles.h, тогда в объединенном файле уже будет указание на включаемые файлы <vector> и <string> и включать их в файл cppProcFiles.h не надо.
                                Рассчитывать, что вокруг тебя создадут требуемое тебе операционное окружение – это неверный подход. И неудобный. Декларируемый интерфейс должен быть самодостаточным и не зависеть от окружения, в котором используется. Если он зависит от других подсистем, будет более чем естественно, если он явно это покажет, сделав подключения необходимых интерфейсов прямо в декларации своего. Иначе как я, будучи её пользователем, смогу понять, что за сущности в нём фигурируют? И более того, я могу вообще не заглядывать вовнутрь cppProcFiles.h и оказаться сильно удивлён при попытке его использовать. Вот как ты сейчас.
                                Это не значит, что в .h должен фигурировать полный набор, требуемый для реализации. Например, <string.h> никуда не денется от <malloc.h>, т.к. в его составе есть функции, самостоятельно создающие новые ASCIIZ-строки, однако видеть #include <malloc.h> внутри string.h совсем необязательно. Вот какой-нибудь string.с, реализующий интерфейс <string.h>, будет его иметь, но заявлять об этом в декларации самого <string.h> нет необходимости, это никак не повлияет на понимание его контента. А вот увидеть <stddef.h> будет вполне ожидаемым, т.к. вдруг такая функции inline и возвращают NULL, а определён он именно там.
                                То же можно сказать и о пользователе. Он не обязан знать, какие подсистемы используются в декларации, а какие в реализации используемого им интерфейса. Он просто сделал его #include и хочет его использовать. Если ему в программе требуется <vector>, то он не должен выискивать .h, который его подключает или нет, чтобы понять, а нужно ли ему его подключать. Нужно. Раз использует, подключает и не парится. Если <vector> также подключается где-то ещё, пользователя это не должно заботить. Другими словами, самодостаточность зависимых интерфейсов обоюдна: и декларируемый интерфейс не должен зависеть от окружения, и окружение не должно полагаться на внутренние зависимости подсистем.

                                Добавлено
                                Цитата tumanovalex @
                                Откуда появилась ссылка на WinMain в консольном приложение - не понятно.
                                Там какого-то ключика линкеру не хватает, чтоб знал, какой стартовый .o брать. Не могу сказать какого, мингвы счас не стоит нигде. Студия в этом смысле несколько умнее, что сама видит, то линкеру и говорит, но так не всегда было, раньше тоже приходилось ключиком разъяснять, что за приложение я собираю.
                                Сообщение отредактировано: Qraizer -
                                0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                                0 пользователей:


                                Рейтинг@Mail.ru
                                [ Script execution time: 0,0515 ]   [ 20 queries used ]   [ Generated: 27.04.24, 12:32 GMT ]