Версия для печати
Нажмите сюда для просмотра этой темы в оригинальном формате |
Форум на Исходниках.RU > C/C++: Общие вопросы > Структрура проектов C++ |
Автор: tumanovalex 19.07.23, 14:53 |
В проекте файл cppProcFiles.cpp, который нормально компилируется. Я хотел бы научиться грамотно разбивать проекты на файлы и включать в файлы с исходным кодом заголовочные файлы, чтобы не было дублирования и конфликтов (чтобы в будущем не переделывать более сложные проекты, надеюсь, что до этого дорасту). Сделал файл cppTest.cpp с main и файл procfile.h с функциями обработки файлов. В результате получаю 27 ошибок. У меня возникли следующие вопросы: 1. Как грамотно включать в файлы заголовочные файлы, если они повторяются в каждом из файлов. В проекте только один файл cpp, но по мере освоения С++ проекты будут с несколькими файлами cpp. 2. Можно ли включать в заголовочные файлы и файлы cpp строки namespace fs = filesystem; using namespace std; одновременно или нужно в заголовочных файлах все включать полностью. 3. Какие ошибки я сделал при создании проекта с несколькими файлами. 4. Посоветуйте, пожалуйста, источники, в которых хорошо изложены принципы построения проектов C++ и есть ответы на эти вопросы. cppTest.zip (, : 29) |
Автор: Feldsher 19.07.23, 17:34 |
Мы начинаем так <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> #ifndef NOOB_ONLINE #define NOOB_ONLINE class Class1{ }; #endif |
Автор: Betelgeuse 19.07.23, 18:55 |
Есть еще <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> #pragma once |
Автор: Qraizer 19.07.23, 20:00 |
Автор: Betelgeuse 20.07.23, 06:33 |
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 если они есть значит функция не доработана и ее можно сделать лучше. Комментарий у меня только в начале модуля чтобы знать что это за модуль - если придется напечатать или что-то еще. Комментарии удобно использовать в модуле над которым ведется работа - копируешь блок закомментировал переделываешь под свое, либо что-то нужно временно выключить. Либо найти вхождение глобальной переменной в любом месте программы - закомментировал в объявлении и пожалуйста компилятор как ошибки укажет все вхождения глобальной переменной. |
Автор: Majestio 20.07.23, 08:37 |
Что-то мутное! Virustotal на Program.exe: <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> 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 20.07.23, 08:58 |
Если программа с вирусами, значит не нужно ее запускать. Вы совершенно правы |
Автор: tumanovalex 20.07.23, 09:05 |
Была дурацкая ошибка: using namespace std; после namespace fs = filesystem; Из-за этого столько ошибок возникало. Программа после исправления ошибки заработала. Спасибо всем ответившим. Мой вопрос был преждевременным, поскольку в проекте не было нескольких файлов, а только один заголовочный и один файл исходного кода. Посмотрю рекомендации в книгах по C++. |
Автор: ЫукпШ 20.07.23, 10:10 |
Утечка ресурса - использована функция RegisterClassA, не использована функция UnregisterClassA. --- Цитата 2. В файле Project.cpp находится точка входа WinMain() я бы назвал этот файл WinMain.cpp И далее так по всем классам - имя класса (или имя процедуры) это имена *.h и *.cpp файлов --- Класс - сабклассинг: Скрытый текст <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> // 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 ) { } // -------------------------------------------------------------------------- |
Автор: Betelgeuse 20.07.23, 11:32 |
Цитата ЫукпШ @ Утечка ресурса - использована функция RegisterClassA, не использована функция UnregisterClassA. За подсказку по сабклассингу - спасибо ) |
Автор: Qraizer 20.07.23, 13:03 |
Добавлено Цитата Betelgeuse @ В общем случае это невозможно, т.к. методы хотят this, который при & над методом (или элементом данных) класса встраивается в сам тип указателя, что приводит к несовместимости типов указателей на элементы класса и указателей на обычные сущности. Так для int A::f() тип указателя будет int (A::*)(), а не int (*)(). И операция разыменования указателя не просто *, а .* или ->*, где слева указывается экземпляр класса. Эти операции позволяют этот экземпляр использовать как конкретный this для указателя. Понятно, что * такого не умеет. Единственный вариант, сохраняющий совместимость типов указателей – это использовать статические методы (или данные), но и тут подводные камни в лице невозможности из таких методов обращаться к нестатическим полям и методам. Причина опять же в том, что неоткуда брать конкретный this.Проблема вот в чем : когда есть CALLBACK функции, особенно субклассинг - как их всунуть в класс ? Но в частных случаях этот this можно куда-нибудь замешать. Многие callback в WinAPI предусматривают дополнительный параметр, который сам WinAPI игнорит, но передаёт в неизменном виде в сам callback. Его легко можно использовать для такого замешивания. Типа такого: <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> Простое использование:class WindowEnumerator { virtual bool currentWindow(HWND) = 0; public: static BOOL CALLBACK enumerator(HWND hWnd, LPARAM param) { return reinterpret_cast<WindowEnumerator*>(param)->currentWindow(hWnd); } }; <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> 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 |
Автор: tumanovalex 22.07.23, 10:21 |
Разбил проект на несколько файлов. В Visual Studio 2022 проект компилируется без ошибок. А в Visual Studio Code при компиляции его с помощью gcс получаю ошибку <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> Видимо, моя разбивка на файлы не универсальная, нужно делать по-другому. Подскажите, пожалуйста, как правильно сделать структуру проекта, чтобы он компилировался правильно различными компиляторами. Проект прикрепил.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 (, : 28) |
Автор: Qraizer 22.07.23, 21:24 |
Твой интерфейс cppProcFiles.h зависит от std::vector<> и std::string, значит должен включить их интерфейсы. Добавь подключение <vector> и <string> в cppProcFiles.h |
Автор: tumanovalex 23.07.23, 08:54 |
Цитата Qraizer @ Я думал, что предпроцессор подставит в cppProcFiles.cpp файл cppProcFiles.h, тогда в объединенном файле уже будет указание на включаемые файлы <vector> и <string> и включать их в файл cppProcFiles.h не надо. Видимо, препроцессор Visual Studio так и понимает, а Visual Studio Code понимает по-другому. Эти ошибки исчезли, но появились другие:Твой интерфейс cppProcFiles.h зависит от std::vector<> и std::string, значит должен включить их интерфейсы. Добавь подключение <vector> и <string> в cppProcFiles.h <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> Откуда появилась ссылка на WinMain в консольном приложение - не понятно. 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 |
Автор: Qraizer 23.07.23, 12:57 |
Цитата tumanovalex @ Рассчитывать, что вокруг тебя создадут требуемое тебе операционное окружение – это неверный подход. И неудобный. Декларируемый интерфейс должен быть самодостаточным и не зависеть от окружения, в котором используется. Если он зависит от других подсистем, будет более чем естественно, если он явно это покажет, сделав подключения необходимых интерфейсов прямо в декларации своего. Иначе как я, будучи её пользователем, смогу понять, что за сущности в нём фигурируют? И более того, я могу вообще не заглядывать вовнутрь cppProcFiles.h и оказаться сильно удивлён при попытке его использовать. Вот как ты сейчас.Я думал, что предпроцессор подставит в cppProcFiles.cpp файл cppProcFiles.h, тогда в объединенном файле уже будет указание на включаемые файлы <vector> и <string> и включать их в файл 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> также подключается где-то ещё, пользователя это не должно заботить. Другими словами, самодостаточность зависимых интерфейсов обоюдна: и декларируемый интерфейс не должен зависеть от окружения, и окружение не должно полагаться на внутренние зависимости подсистем. Добавлено Там какого-то ключика линкеру не хватает, чтоб знал, какой стартовый .o брать. Не могу сказать какого, мингвы счас не стоит нигде. Студия в этом смысле несколько умнее, что сама видит, то линкеру и говорит, но так не всегда было, раньше тоже приходилось ключиком разъяснять, что за приложение я собираю. |
Автор: tumanovalex 23.07.23, 15:18 |
Спасибо за ответ и подробные объяснения. По поводу gcc спрошу в кроссплатформенной теме. |
Автор: Majestio 23.07.23, 18:29 |
Цитата Qraizer @ Там какого-то ключика линкеру не хватает, чтоб знал, какой стартовый .o брать. Не могу сказать какого, мингвы счас не стоит нигде. Студия в этом смысле несколько умнее, что сама видит, то линкеру и говорит, но так не всегда было, раньше тоже приходилось ключиком разъяснять, что за приложение я собираю. тут нужные ключики |
Автор: tumanovalex 24.07.23, 08:31 |
Спасибо! Ошибка исчезла. Но, как я понимаю, проблема в том, что в параметре tasks.json нужно указать, что нужно компилировать не один, а несколько файлов. Буду разбираться с структурой этого файла и попытаюсь исправить его так, чтобы компилировался весь проект, а не один файл. |