
![]() |
Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
|
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[216.73.216.60] |
![]() |
|
![]() |
|
|
Здравствауте!
Подскажите, пожалуйста, из-за чего эта ошибка и как ее исправить?: Ошибка LNK2019 ссылка на неразрешенный внешний символ __imp__invalid_parameter в функции "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > __cdecl std::operator+<char,struct std::char_traits<char>,class std::allocator<char> >(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > &&,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > &&)" (??$?HDU?$char_traits@D@std@@V?$allocator@D@1@@std@@YA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@0@$$QEAV10@0@Z). school D:\Programming\VS\source\repos\school\school\school.obj 1 ![]() ![]() #include <iostream> #include <memory> #include <mysql_driver.h> #include <mysql_connection.h> #include <cppconn/driver.h> #include <cppconn/exception.h> #include <cppconn/resultset.h> #include <cppconn/statement.h> #include <cppconn/prepared_statement.h> //using namespace std; class MySQLConnection { private: std::unique_ptr<sql::Connection> connection; public: MySQLConnection(const std::string& host, int port, const std::string& user, const std::string& password, const std::string& database) { sql::mysql::MySQL_Driver* driver = sql::mysql::get_mysql_driver_instance(); std::string connectionString = "tcp://" + host + ":" + std::to_string(port); connection.reset(driver->connect(connectionString, user, password)); connection->setSchema(database); } void executeQuery(const std::string& query) { std::unique_ptr<sql::Statement> stmt(connection->createStatement()); std::unique_ptr<sql::ResultSet> res(stmt->executeQuery(query)); while (res->next()) { // Обработка результатов std::cout << "Результат: " << res->getString(1) << std::endl; } } bool isConnected() { return connection.get() != nullptr && !connection->isClosed(); } }; int main() { try { MySQLConnection db( "your_remote_server_ip", 3306, "your_username", "your_password", "your_database" ); if (db.isConnected()) { std::cout << "Подключение установлено успешно!" << std::endl; db.executeQuery("SELECT VERSION()"); } } catch (sql::SQLException& e) { std::cerr << "MySQL Error: " << e.what() << std::endl; } return 0; } |
Сообщ.
#2
,
|
|
|
Если у кого-то есть желание, посмотрите проект: https://disk.yandex.ru/d/1Jpu8d2JXhMkgw
|
Сообщ.
#3
,
|
|
|
DDim1000, привет!
От всей души тебя благодарю за "вчерашний кроссворд" ![]() Вот такая получилась Преамбула. "А почему с Майкрософтом почти дел не имел всё это время?" - возникает резонный вопрос. Отвечу честно: не знаю! Какое-то внутреннее отвращение. Но буквально месяц назад я для себя решил. Надо перебороть себя, поставить студию, и попробовать ее в действии. Даже если блевать тянет. И ТАК Моя "первичная" установка "Microsoft Visual Studio Enterprise 2022" не встала как надо, я её нахрен снёс, даже стандартных заголовков типа math.h не видела. Мать-её-перемать, удалил и поставил "Microsoft Visual Studio Community 2022". И вот тут стало уже интереснее: 1) Стандартное консольное приложение "Хэлоу ворлд!" заработало 2) Я скопировал код твой проги "сканнар". Понятное дело, даже на первом этапе в редакторе не виделись заголовки, но стало ещё интереснее СЕКРЕТНЫЙ СЕКРЕТ Скрытый текст А он заключается в том, что все пути твоего проекта - не соответствуют путям моего проекта в данном ответе. И с этим нужно смириться, но извлечь из этого полезную инфу как-то надо. Уверяю - надо! Просто нужно заценить аналогию и внести правки в свой проект. Это как два байта об асфальт, ну или одно из двух? РЕШАЕМ 1) Прописываем "пути заголовок в MySQL" в опциях проекта. Т.е. добавляем: "Свойства->Каталоги С++->Включаемые каталоги путь. В моём случае "E:\Documents\3.Projects\C++\TestMySQL\mysql\include\jdbc" 2) Прописываем "пути либ MySQL" в опциях проекта. Т.е. добавляем: "Свойства->Каталоги С++->Каталоги библиотек. В моём случае "E:\Documents\3.Projects\C++\TestMySQL\mysql\lib64"и E:\Documents\3.Projects\C++\TestMySQL\mysql\lib64\vs14 3) Прописываем конкретные добавляемые либы. Тут опять, понятное дело, у тебя свое размещение, у меня - своё. Поэтому - ориентируйся! "Свойства->Компоновщик->Все параметры->Дополнительные зависимости" У меня получилось прописать дополнительно: После всего сказанного - проект (солюшен) собрался успешно. В ОБЩЕМ В общем - была проблема сборки (линкинга). Не добавили и не указали нужные либы и их пути для сборки как должно. |
![]() |
Сообщ.
#4
,
|
|
![]() ![]() template <typename Ch, typename Tr, typename Al> std::basic_string<Ch, Tr, Al> operator+(std::basic_string<Ch, Tr, Al>&&, std::basic_string<Ch, Tr, Al>&&); Прикреплённая картинка
Она сообщает, что когда оба параметра оператора являются rvalue ref, реализация вправе считать, что они оба уникальны и не могут совпадать. И это логично, т.к. rvalue ref формально являются временными объектами и получить более одной ссылки на один и тот же временный объект невозможно иначе, как спецом написать кривой код. Я этого добился, явно переместив lvalue с помощью std::move() дважды в одном выражении, в итоге попробовав сконкатенировать некую строку со строкой, которая хранит невесть что. Как бы там ни было, в релизе _invalid_parameter() не используется. Вообще ничего отладочного не используется, что конечно не означает, что баг в программе сам собой вылечивается. Почему линкер не нашёл этого символа, ябеспонятия. Первое, что приходит в голову – отсутствие #include <string>, что само по себе является ошибкой, ведущей к undefined behavior. Но не исключено, что это баг мэйкера проекта, который не добавил нужную DLL. Точнее, либу импорта к ней. Я бы попробовал отключить динамическую RTL и использовать статическую. Если баг линковки исчезнет, то это баг сборки самой динамической RTL, и об этом нужно сообщить в саппорт Студии |
![]() |
|
|
Цитата Majestio @ DDim1000, привет! От всей души тебя благодарю за "вчерашний кроссворд" ![]() Вот такая получилась Преамбула. "А почему с Майкрософтом почти дел не имел всё это время?" - возникает резонный вопрос. Отвечу честно: не знаю! Какое-то внутреннее отвращение. Но буквально месяц назад я для себя решил. Надо перебороть себя, поставить студию, и попробовать ее в действии. Даже если блевать тянет. И ТАК Моя "первичная" установка "Microsoft Visual Studio Enterprise 2022" не встала как надо, я её нахрен снёс, даже стандартных заголовков типа math.h не видела. Мать-её-перемать, удалил и поставил "Microsoft Visual Studio Community 2022". И вот тут стало уже интереснее: 1) Стандартное консольное приложение "Хэлоу ворлд!" заработало 2) Я скопировал код твой проги "сканнар". Понятное дело, даже на первом этапе в редакторе не виделись заголовки, но стало ещё интереснее СЕКРЕТНЫЙ СЕКРЕТ Скрытый текст А он заключается в том, что все пути твоего проекта - не соответствуют путям моего проекта в данном ответе. И с этим нужно смириться, но извлечь из этого полезную инфу как-то надо. Уверяю - надо! Просто нужно заценить аналогию и внести правки в свой проект. Это как два байта об асфальт, ну или одно из двух? РЕШАЕМ 1) Прописываем "пути заголовок в MySQL" в опциях проекта. Т.е. добавляем: "Свойства->Каталоги С++->Включаемые каталоги путь. В моём случае "E:\Documents\3.Projects\C++\TestMySQL\mysql\include\jdbc" 2) Прописываем "пути либ MySQL" в опциях проекта. Т.е. добавляем: "Свойства->Каталоги С++->Каталоги библиотек. В моём случае "E:\Documents\3.Projects\C++\TestMySQL\mysql\lib64"и E:\Documents\3.Projects\C++\TestMySQL\mysql\lib64\vs14 3) Прописываем конкретные добавляемые либы. Тут опять, понятное дело, у тебя свое размещение, у меня - своё. Поэтому - ориентируйся! "Свойства->Компоновщик->Все параметры->Дополнительные зависимости" У меня получилось прописать дополнительно: После всего сказанного - проект (солюшен) собрался успешно. В ОБЩЕМ В общем - была проблема сборки (линкинга). Не добавили и не указали нужные либы и их пути для сборки как должно. Majestio, большое, большое спасибо!!! Я 2 дня седел на разных форумах, но никто не мог толком ничего подсказать. А ты разобрался, и все так четко объяснил! |
![]() |
|
|
Проект компилируеться, но возникла еще одна ошибка.
При выполнении программы выскакивает сообщение: Не удается продолжить выполнение кода, поскольку система не обноружила mysqlcppconn-10-vs14.dll. Для устранеия этой пролемы попробуйте переустанвить программу. Но когда я прописыва путь к mysqlcppconn-10-vs14.dll, при сборки проета выходит ошибка: Ошибка LNK1107 недопустимый или поврежденный файл: не удается прочитать по 0x388 school D:\Programming\VS\source\repos\school\school\MySQL\lib64\mysqlcppconn-10-vs14.dll Подскажите, пожалуста, как исправить ошибку? Прикреплённая картинка
P.S. Как здесь изображения прикреплять? |
Сообщ.
#7
,
|
|
|
Цитата DDim1000 @ Не удается продолжить выполнение кода, поскольку система не обноружила mysqlcppconn-10-vs14.dll. Для устранеия этой пролемы попробуйте переустанвить программу. Обычно для загрузки внешних DLL-либ ничего прописывать не нужно. Нужно лишь обеспечить их загрузку. Самым простым образом это решается двумя способами: Я бы посоветовал первый способ. А вообще лучше глянь страничку на моём сайте, и сам реши для себя как тебе лучше. |
Сообщ.
#8
,
|
|
|
Цитата DDim1000 @ Majestio, большое, большое спасибо!!! Я 2 дня седел на разных форумах, но никто не мог толком ничего подсказать. А ты разобрался, и все так четко объяснил! Это супер! Хорошо, что ты пришёл на наш форум. Оставайся тут, сделай закладочку в своём браузере на наш форум. Через месяц-два это уже будет не "наш" а "твой" форум. Да, конечно "трафик общения" тут не большой. Но, уверяю тебя, тут очень много интересных и толковых людей! Хотя ... о чём я ![]() |
Сообщ.
#9
,
|
|
|
Сообщ.
#10
,
|
|
|
Ну тут уже проще, явная ошибка в коде твоей программы. Самый простой вариант ее найти - пошаговое исполнение в отладчике. Кода у тебя там мало, найдешь достаточно быстро.
|
Сообщ.
#11
,
|
|
|
Ошибка происходит вот на эти строках:
![]() ![]() connection.reset(driver->connect(connectionString, user, password)); connection->setSchema(database); Вот видео: https://rutube.ru/video/private/4a2d373fed2...P7X9cVc0_Tu7uZw |
![]() |
Сообщ.
#12
,
|
|
Очень подозреваю, что SQL собрана версией Студии, отличной от твоей. Попытка сконструировать SQLString где-то изнутри DLL наталкивается на совершенно дикие значения размеров памяти, типа 435626798759 байт. Что очень похоже на нарушение ODR.
Думаю, нужно либо выяснить, какой версией собирались твои DLLки и использовать ту же, либо пересобрать самому из сырцов. Добавлено Но возможно, что причина в другом. Всё-таки ![]() ![]() KernelBase.dll!RaiseException() Нет данных vcruntime140.dll!_CxxThrowException(void * pExceptionObject, const _s__ThrowInfo * pThrowInfo) Строка 80C++ mysqlcppconn-10-vs14.dll!__scrt_throw_std_bad_alloc() Строка 35C++ > mysqlcppconn-10-vs14.dll!operator new(unsigned __int64 size) Строка 48C++ [Внедренный фрейм] mysqlcppconn-10-vs14.dll!std::_Default_allocate_traits::_Allocate(const unsigned __int64) Строка 79C++ mysqlcppconn-10-vs14.dll!std::_Allocate_manually_vector_aligned<std::_Default_allocate_traits>(const unsigned __int64 _Bytes) Строка 134C++ [Внедренный фрейм] mysqlcppconn-10-vs14.dll!std::_Allocate(const unsigned __int64) Строка 229C++ [Внедренный фрейм] mysqlcppconn-10-vs14.dll!std::allocator<char>::allocate(const unsigned __int64 _Count) Строка 801C++ [Внедренный фрейм] mysqlcppconn-10-vs14.dll!std::string::_Construct_lv_contents(const std::string &) Строка 2908C++ mysqlcppconn-10-vs14.dll!std::string::basic_string<char,std::char_traits<char>,std::allocator<char>>(const std::string & _Right) Строка 2420C++ [Внедренный фрейм] mysqlcppconn-10-vs14.dll!sql::SQLString::{ctor}(const sql::SQLString &) Строка 66C++ [Внедренный фрейм] mysqlcppconn-10-vs14.dll!sql::Variant::{ctor}(const sql::SQLString &) Строка 277C++ mysqlcppconn-10-vs14.dll!sql::mysql::MySQL_Connection::MySQL_Connection(sql::Driver * _driver, sql::mysql::NativeAPI::NativeConnectionWrapper & _proxy, const sql::SQLString & hostName, const sql::SQLString & userName, const sql::SQLString & password) Строка 162C++ mysqlcppconn-10-vs14.dll!sql::mysql::MySQL_Driver::connect(const sql::SQLString & hostName, const sql::SQLString & userName, const sql::SQLString & password) Строка 130C++ school.exe!MySQLConnection::MySQLConnection(const std::string & host, int port, const std::string & user, const std::string & password, const std::string & database) Строка 26C++ school.exe!main() Строка 47C++ school.exe!invoke_main() Строка 79C++ school.exe!__scrt_common_main_seh() Строка 288C++ school.exe!__scrt_common_main() Строка 331C++ school.exe!mainCRTStartup(void * __formal) Строка 17C++ kernel32.dll!BaseThreadInitThunk() Нет данных ntdll.dll!RtlUserThreadStart() Нет данных |
Сообщ.
#13
,
|
|
|
Цитата Qraizer @ Очень подозреваю, что SQL собрана версией Студии, отличной от твоей. ... Что это значит? Версия библиотеки не та? |
![]() |
Сообщ.
#14
,
|
|
Ну... наверное. Косвенно это также ещё следует из названия "mysqlcppconn-10-vs14.dll", что скорее всего «но это неточно©» означает сборку на 14-ой версии RTL. Нынешняя ВизуалСтудия2022 имеет 17-ую версию.
Добавлено Технически пересобрать не должно быть неподъёмным процессом. Весь мир опенсорса так работает. На пересборке пререквизитов в конечной точке, в смысле. Но нынче MySQL под Oracle-ом, а его отношение к опенсорс довольно невнятное. Добавлено Ещё есть мысль: попробовать отключить отладочную RTL и заюзать релизную вместо. Кстати, вполне может помочь, т.к. отладочная реализация 100пудово отличается от релизной, откуда и может следовать нарушение ODR. Т.е. SQL собрана с релизной RTL, а твоё приложение с отладочной, и это и есть источник проблемы. Добавлено P.S. Просто переключи Конфигурацию решения в Release: ![]() ![]() MySQL Error: Unable to connect to your_remote_server_ip:3306 |
Сообщ.
#15
,
|
|
|
Когда я переключаю на Release, то сразу вот эти строки подсвечиваются красной чертой. То есть библиотеки становятся не достуаны.
#include <mysql_driver.h> #include <mysql_connection.h> #include <cppconn/driver.h> #include <cppconn/exception.h> #include <cppconn/resultset.h> #include <cppconn/statement.h> #include <cppconn/prepared_statement.h> |
![]() |
Сообщ.
#16
,
|
|
Вероятно, когда ты правил проект по заветам Majestio, ты отредактировал только одну, текущую конфигурацию. Надо было бы все, чтоб не возвращаться к этому, но ничего страшного, просто повтори ещё раз.
|
Сообщ.
#17
,
|
|
|
Кстати во избежании будущих проблем с переносом проекта на другой диск или папку рекомендую использовать не абсолютные, а относительные пути до этих библиотек и заголовочных путей в настройках проекта (раз уж они внутри проекта) и использовать макросы подстановки $(SolutionDir) $(ProjectDir) и т.д.
$(SolutionDir)MySQL\lib64\vs14 Добавлено Далее.. Почему вы линкуете mysqlcppconnx-static.lib? У вас же динамическая линковка, а не статическая. Должно быть mysqlcppconnx.lib вернее mysqlcppconn.lib (потому что используете именно mysqlcppconn-10-vs14.dll, а не mysqlcppconnx-2-vs14.dll) Ну и после исправлений тестовый пример работает нормально. P.S. 14 это не версия студии, а версия toolchain (так написано в доке с MySQL connector). так что нарушений ODR быть не должно. 22 студия так же использует 14 тулчейн (v14.3), так что все работает и на новых студиях |
Сообщ.
#18
,
|
|
|
И в Debug билде надо убрать STATIC_CONCPP в настройках препроцессора. Раз уж решили работать именно с dll.
|
![]() |
Сообщ.
#19
,
|
|
Цитата sharky72 @ Никогда не задумывался, что это действительно можно тоже назвать тулчейном, как принято в embedded. Но отсутствие нарушений ODR всё ж не факт. Счас вот глянул, у меня их две версии, 14.16.27023 и 14.44.35207, отличаются минорами, ибо апдейты Студии они такие. Понятно, что первая уже неактуальна и не используется, но минор версии, на которой был собран SQL, неизвестно какой, и даже если в пределах мажора 14 ничего не должно поменяться, поменяться-таки могло, т.к. баги совместимости никто не отменял. Поэтому я всегда после наката нового апдейта рабочие проекты собираю rebuild all. Так, на всякий случай. P.S. 14 это не версия студии, а версия toolchain (так написано в доке с MySQL connector). так что нарушений ODR быть не должно. 22 студия так же использует 14 тулчейн (v14.3), так что все работает и на новых студиях Добавлено Но тут всё проще: приложение собирается DEBUG, т.е. с отладочной версией RTL, а SQL был собран с релизной версией. По-хорошему надо где-то там взять отладочную SQL и в дебажные конфигурации своих проектов втыкать их. |
Сообщ.
#20
,
|
|
|
Цитата Majestio @ У меня получилось прописать дополнительно: E:\Documents\3.Projects\C++\TestMySQL\mysql\lib64\vs14\libcrypto.lib E:\Documents\3.Projects\C++\TestMySQL\mysql\lib64\vs14\libssl.lib E:\Documents\3.Projects\C++\TestMySQL\mysql\lib64\vs14\mysqlcppconn.lib E:\Documents\3.Projects\C++\TestMySQL\mysql\lib64\vs14\mysqlcppconn-static.lib E:\Documents\3.Projects\C++\TestMySQL\mysql\lib64\vs14\mysqlcppconnx.lib E:\Documents\3.Projects\C++\TestMySQL\mysql\lib64\vs14\mysqlcppconnx-static.lib Вот тут я написал неправильно! ![]() ![]() Ах, да, еще ... где-то в "глубоких водах" GNU я вроде встречал такой термин как "гибридная" или "смешанная" линковка. Это когда часть либ линкуется статически, а часть динамически. ChatGPT утверждает, что и Мелкософтовские линкеры тоже владеют таким кунг-фу. Думаю, тоже стоит внимание обратить на эту шляпу. А то мало ли ... Добавлено Цитата Qraizer @ Никогда не задумывался, что это действительно можно тоже назвать тулчейном, как принято в embedded. Это пробел в знаниях. Несущественный, но всё же. В embedded это принято ровно на столько, на сколько принято в не embedded. Ибо термин toolchain (инструментальная цепочка) не привязан исключительно к embedded-системам, а имеет более общий смысл в разработке программного обеспечения. Тулчейн - это набор инструментов, используемых для создания исполняемых файлов, библиотек или других артефактов для определённой целевой платформы (таргета). Он включает в себя компиляторы, ассемблеры, компоновщики (линкеры), отладчики и другие утилиты, необходимые для сборки и, возможно, отладки кода. |
Сообщ.
#21
,
|
|
|
Кстати. С понятием 'toolchain' я столкнулся уже достаточно давно, в районе 2011 года, когда познакомился с проектом MXE. Но именно "кристальное" его воплощение я заметил в языке программирования Rust. Увы и ах, этот язык мне не зашел ввиду слишком узкого диапазона применений. К примеру, лепить GUI-морды сегодня на базе GTK2, когда уже есть GTK4 - считаю просто кощунственным. И тем не менее, заглянул в структуру каталогов когда-то установленного Раста, я наблюдаю:
![]() ![]() X:\Tools\Rust\rust\toolchains ├─ stable-i686-pc-windows-gnu ├─ stable-i686-pc-windows-msvc ├─ stable-x86_64-pc-windows-gnu └─ stable-x86_64-pc-windows-msvc Правда прекрасно? ![]() |
Сообщ.
#22
,
|
|
|
Цитата Qraizer @ Нет. Проект отлично собирается в DEBUG c релизной dll (pdb есть, функции в стеке посмотреть можно). И работает. В исходном vcxproj для debug конфигурации указана статическая конфигурация в переменных препроцессора для подключения mysqlconnector. Плюс указана неправильная либа для статической линковки, хотя сам проект собирается как /MDd (Multithreaded debug DLL). Короче там в конфигурации каша. Если все указать правильно то ошибок и эксепшнов не возникает. На 2022 студии. Ну и настройки релизной конфигурации вобще отсутствует. В любом случае можно скачать и отладочную версию. на dev.mysql.com/downloads/connector/cpp |
Сообщ.
#23
,
|
|
|
У меня возник вопрос: может я вообще делаю ни так? В общем, у меня есть задача: нужно подключиться к базе данных котора находиться на веб-хостинге. Дак, у меня правильная логика программы, или нет?
|
Сообщ.
#24
,
|
|
|
Цитата DDim1000 @ Дак, у меня правильная логика программы, или нет? Вроде бы и правильная ![]() |
Сообщ.
#25
,
|
|
|
Цитата DDim1000 @ У меня возник вопрос: может я вообще делаю ни так? В общем, у меня есть задача: нужно подключиться к базе данных котора находиться на веб-хостинге. Дак, у меня правильная логика программы, или нет? В смысле работы с Mysql? Ну да. Создали коннектор. Задали строку соединения и логин/пароль. Вызвали метод соединения, обработали исключение в случае ошибки. Только я бы все таки не стал вызывать соединение из конструктора. Можно, но некрасиво из за того что там бросаются исключения в случае ошибок и isConnected() в данном случае не нужен ![]() P.S. Я до сих пор использую dbForge Studio for MySql 9 (2020) пока она была бесплатной для некоммерческого использования |
![]() |
Сообщ.
#26
,
|
|
Цитата Majestio @ Почему пробел. Я в курсе. Просто почему-то до этого не задумывался, что версии RTL можно так называть.Это пробел в знаниях. Несущественный, но всё же. В embedded это принято ровно на столько, на сколько принято в не embedded. Цитата sharky72 @ Не нет, а да. В проекте используются подставляемые функции (inline которые) с внешним связыванием (external linkage), и по Стандарту они обязаны (не ограничиваясь): и неважно, как именно единицы трансляции линкуются в результирующее приложение, посредством статической линковки или динамической. Если конкретно у тебя смешение inline с внешним связыванием — обсуждаемый operator+() является non-static function template, что ставит его в один ряд с inline — работает, то лишь потому, что ожидаемое поведение является частным случаем неопределённого. Вот у нас с ТС, к примеру, не работает. И в отладчике легко видно, почему: в итоге где-то изнутри mysqlcppconn-10-vs14.dll управление попадает в sqlstring.h, конкретно в конструктор SQLString, и контент его параметра const std::string & other не совпадает с определением std::string в приложении, т.б. разные последовательности токенов на лицо. Почему так? Та потому, что линкеру приспичило обработать внешние имена в объектных файлах и библиотеках вот в каком-то таком порядке. И pdb тут совершенно ни причём, на линковку он никак не влияет. Как раз наоборот, создаётся в процессе линковки. Так-то он лишь предоставляет информацию для отладчика, и при нарушении ODR способен даже запутать, т.к. отладчик может в сырцах показывать совсем не то, что в исполняемом коде. Нет. Проект отлично собирается в DEBUG c релизной dll (pdb есть, функции в стеке посмотреть можно). И работает. |
Сообщ.
#27
,
|
|
|
Не буду спорить. В любом случае решение есть. На сайте есть дебажная версия dll и lib. Правильней будет использовать их в отладочном билде. Решается так же прописыванием путей к нужной либе.
По крайней мере я проверил на своем mysql. Код работает. Единственное пришлось еще доложить папку plugins Добавлено Ну и чтобы не тащить все это в проект, и облегчить настройку подключения mysqlconnector автору рекомендую посмотреть в сторону vcpkg. |
Сообщ.
#28
,
|
|
|
Цитата Qraizer @ Почему пробел. Я в курсе. Просто почему-то до этого не задумывался, что версии RTL можно так называть. Ну как почему. Потому ![]() Цитата Qraizer @ что версии RTL можно так называть Вот тут ты не прав - тулчейны, это не просто разные RTL, это полные разные инфраструктуры сопутствующих инструментариев. И более того! Вспомни многообразие форматов исполняемых файлов - это связка "операционная система +архитектура". И не известно кто тут главный, а кто ведомый. Хотя "архитектура" конечно главнее по факту, т.к. она определяет его бинарный код, его внутренности. Но не факт. Тут сложно сказать, сложно что определить, что более первично - "сосуд" или его "содержимое" ![]() |
![]() |
Сообщ.
#29
,
|
|
Цитата Majestio @ Варишься в твоих в своих мелкософтах и свету белого не видишь. ![]() |
Сообщ.
#30
,
|
|
|
Цитата Qraizer @ Тебе что, назвать количество разных процессоров, с которыми работал и работаю? Из них только около половины используют GNU той или иной разновидности. И ОСей в них нет, там сплошной freestanding Ага, вот ты и попался! ![]() ![]() |
Сообщ.
#31
,
|
|
|
![]() |
![]() |
|
|
Цитата Majestio @ Цитата DDim1000 @ Дак, у меня правильная логика программы, или нет? Вроде бы и правильная ![]() Качнул и установил Navicat, и вот что он говорит при попытки подключиться к БД: "1130 - Host '169...' is not allowed to connect to this MySQL server". То есть, получатся, что, сам сервер не разрешает подключиться? |
Сообщ.
#33
,
|
|
|
Цитата DDim1000 @ То есть, получатся, что, сам сервер не разрешает подключиться? Ну да, получается что так. Поработай с настройками сервера, постарайся получить коннект с помощью Navicat. И только после этого возвращайся к кодированию. |
Сообщ.
#34
,
|
|
|
Кстати ... если у тебя проблемсы с MySQL-сервером, а они явно у тебя есть - рекомендую тебе просто ужасно-прекрасную софтину Open Server. Да, сия софтина обычно используется для web-разработок. Тем не менее, в её составе есть полноценные версии MySQL и PostgreSQL, практически готовые сразу после установки. Очень очень рекомендую. Сам пользую уже лет 10, и вааще доволен.
|
Сообщ.
#35
,
|
|
|
Цитата DDim1000 @ 1130 - Host '169...' is not allowed to connect to this MySQL server 1. Смотри есть ли права доступа у данного пользователя на подключение к конкретному хосту или к любому (в таблице mysql.user). Если нет - то дай права через grant all priveleges 2. Проверь что mysql забинден на нужный интерфейс. Или слушает все. в bind-address = 0.0.0.0 |
Сообщ.
#36
,
|
|
|
Цитата sharky72 @ Цитата DDim1000 @ У меня возник вопрос: может я вообще делаю ни так? В общем, у меня есть задача: нужно подключиться к базе данных котора находиться на веб-хостинге. Дак, у меня правильная логика программы, или нет? В смысле работы с Mysql? Ну да. Создали коннектор. Задали строку соединения и логин/пароль. Вызвали метод соединения, обработали исключение в случае ошибки. Только я бы все таки не стал вызывать соединение из конструктора. Можно, но некрасиво из за того что там бросаются исключения в случае ошибок и isConnected() в данном случае не нужен ![]() P.S. Я до сих пор использую dbForge Studio for MySql 9 (2020) пока она была бесплатной для некоммерческого использования Установил локальный сервер, установил Navicat Premium, создал БД. Но мая программа не может подключиться к базе данны, пишет: MySQL Error: Unable to connect to HчЇФи:3306 Что можно сделать? ![]() ![]() #include <iostream> #include <memory> #include <mysql_driver.h> #include <mysql_connection.h> #include <cppconn/driver.h> #include <cppconn/exception.h> #include <cppconn/resultset.h> #include <cppconn/statement.h> #include <cppconn/prepared_statement.h> //using namespace std; class MySQLConnection { private: std::unique_ptr<sql::Connection> connection; public: MySQLConnection(const std::string& host, int port, const std::string& user, const std::string& password, const std::string& database) { sql::mysql::MySQL_Driver* driver = sql::mysql::get_mysql_driver_instance(); std::string connectionString = "tcp://" + host + ":" + std::to_string(port); connection.reset(driver->connect(connectionString, user, password)); connection->setSchema(database); } void executeQuery(const std::string& query) { std::unique_ptr<sql::Statement> stmt(connection->createStatement()); std::unique_ptr<sql::ResultSet> res(stmt->executeQuery(query)); while (res->next()) { // Обработка результатов std::cout << "Результат: " << res->getString(1) << std::endl; } } bool isConnected() { return connection.get() != nullptr && !connection->isClosed(); } }; int main() { setlocale(LC_ALL, "Russian"); try { MySQLConnection db( "locolhost", // host 3306, "root", // your_username "", // your_password "school" // your_database ); if (db.isConnected()) { std::cout << "Подключение установлено успешно!" << std::endl; db.executeQuery("SELECT VERSION()"); } } catch (sql::SQLException& e) { std::cerr << "MySQL Error: " << e.what() << std::endl; } return 0; } |
Сообщ.
#37
,
|
|
|
Самое простое попробуйте. Наберите в браузере http://HчЇФи:3306 и посмотрите на ответ. Если сервер работает по этому адресу, тогда вы увидите ошибку в протоколе рукопожатия в браузере, а если нет -- страницу: нет доступа.
В зависимости от этого и надо будет решать проблему. 1) Ошибка в браузере есть т.е. сервер работает и отвечает. Тогда надо смотреть на то, чтобы отправляет ему коннектор. 2) Ошибка в браузере, что нет сервера. Тогда проверяем настройки у mysql. Обычно там стоит строго 127.0.0.1:3306 и к нему не подключиться даже через v6 |
![]() |
|
|
Цитата macomics @ Самое простое попробуйте. Наберите в браузере http://HчЇФи:3306 и посмотрите на ответ. Если сервер работает по этому адресу, тогда вы увидите ошибку в протоколе рукопожатия в браузере, а если нет -- страницу: нет доступа. В зависимости от этого и надо будет решать проблему. 1) Ошибка в браузере есть т.е. сервер работает и отвечает. Тогда надо смотреть на то, чтобы отправляет ему коннектор. 2) Ошибка в браузере, что нет сервера. Тогда проверяем настройки у mysql. Обычно там стоит строго 127.0.0.1:3306 и к нему не подключиться даже через v6 Сообщние в браузере: Не удается получить доступ к сайту Проверьте, нет ли опечаток в имени хоста xn--h-ptb9al4f. Если все правильно, воспользуйтесь инструментом "Диагностика сетей Windows". DNS_PROBE_FINISHED_NXDOMAIN |
Сообщ.
#39
,
|
|
|
А вы все сделали как вам советовали?
1. Как сказал Qraizer запустить в релизе 2. Как советовал я - поставить отладочную версию mysql сonnector и запустить в дебаге. Ибо похоже дебаг проект и релиз dll у вас не дружат. 3. "locolhost" это что-то жутко интересное... ![]() ЗЫ. QTешников не спрашивали ![]() ![]() |
Сообщ.
#40
,
|
|
|
Цитата DDim1000 @ Проверьте, нет ли опечаток в имени хоста xn--h-ptb9al4f. Это означает, что по внешнему ip порт не доступен (2 вариант). Значит проверяйте настройки сервера mysql. Скорее всего, к нему можно подключиться строго через 127.0.0.1:3306 Добавлено Хотя надо еще попробовать локальный ip компьютера вместо dns |
![]() |
|
|
Цитата sharky72 @ А вы все сделали как вам советовали? 1. Как сказал Qraizer запустить в релизе 2. Как советовал я - поставить отладочную версию mysql сonnector и запустить в дебаге. Ибо похоже дебаг проект и релиз dll у вас не дружат. 3. "locolhost" это что-то жутко интересное... ![]() ЗЫ. QTешников не спрашивали ![]() ![]() Когда я преключаю на Release, сразу появляются куча ошибок. https://disk.yandex.ru/i/JL2VqnRFCuZPYA |
![]() |
Сообщ.
#42
,
|
|
Цитата macomics @ К слову: 127.0.0.1 отнюдь не то же самое, что локальный IP. Хотя надо еще попробовать локальный ip компьютера вместо dns |
Сообщ.
#43
,
|
|
|
Цитата sharky72 @ ЗЫ. QTешников не спрашивали Они вообще не в зуб ногой в кроме своего фреймворка ![]() |
Сообщ.
#44
,
|
|
|
Цитата Qraizer @ К слову: 127.0.0.1 отнюдь не то же самое, что локальный IP. Именно потому, что это разные адреса я про это и сказал. Доступ по dns может отсутствовать из-за недоступности локального ip за роутером. Но вот если и с локальным ip будет тот же ответ, тогда значит, что mysql слушает только 127.0.0.1 (localhost, но только по v4). |
![]() |
|
|
А вот через Комадную строку все работает.
![]() ![]() Microsoft Windows [Version 10.0.19045.6332] (c) Корпорация Майкрософт (Microsoft Corporation). Все права защищены. C:\Windows\system32>D: D:\>cd Programming\Servers\xampp\mysql\bin D:\Programming\Servers\xampp\mysql\bin>mysql.exe -h localhost --port=3306 -u root --password Enter password: Welcome to the MariaDB monitor. Commands end with ; or \g. Your MariaDB connection id is 9 Server version: 10.4.32-MariaDB mariadb.org binary distribution Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. MariaDB [(none)]> SELECT NOW(); +---------------------+ | NOW() | +---------------------+ | 2025-09-28 20:11:06 | +---------------------+ 1 row in set (0.000 sec) MariaDB [(none)]> |
Сообщ.
#46
,
|
|
|
Ну так вы же через командную строку используете localhost, а не dns или ip вашей сетевой на компьютере. Вот и проверяйте подключение к серверу по тем же параметрам в программе или через браузер, как я показал ранее.
|
Сообщ.
#47
,
|
|
|
Цитата DDim1000 @ Цитата sharky72 @ А вы все сделали как вам советовали? Когда я преключаю на Release, сразу появляются куча ошибок. https://disk.yandex.ru/i/JL2VqnRFCuZPYA Ну вам же Qraizer написал что для Release так же НУЖНО настроить пути и библиотеки |
Сообщ.
#48
,
|
|
|
Переключил на Release, теперь такая ошибка:
![]() ![]() MySQL Error: Authentication plugin 'mysql_native_password' cannot be loaded: Не найден указанный модуль. D:\Programming\VS\source\repos\school\x64\Release\school.exe (процесс 7780) завершил работу с кодом 0 (0x0). Нажмите любую клавишу, чтобы закрыть это окно: Скопировал файл mysql_native_password.dll в папку с exe-файлом, но ошибка осталась. |
Сообщ.
#49
,
|
|
|
надо было скопировать полностью папку plugin. Он ищет именно в папке.
Почему так - надо спрашивать у разрабов MySQL connector ЗЫ. Qraizer таки был прав использовть релизную dll для дебага нельзя. Посыпаю голову пеплом. https://dev.mysql.com/doc/dev/connector-cpp...test/usage.html On Windows it is not possible to mix code built in release and debug modes. И там же по поводу папки plugin (можно решить через переменную среды). В общем в доках все есть. Нужно только их прочитать. MySQL client library plugins The classic API is implemented using the MySQL client library which might need to load plugins during some operations (mainly during authentication). In case of system-wide installation the required plugins (and their dependencies) should be also installed system-wide and available at the default location where the client library looks for them. If however Connector/C++ was installed to a local directory the plugins (and their dependencies) are installed together with the connector at ${mysql-concpp_DIR}/lib64/plugins but the client library will not know how to find them. To fix this pass their location as PLUGIN_DIR connection option that is recognized by the classic API. Note Client library plugins must be installed and PLUGIN_DIR must be correctly set (if needed) on the target host on which the application is run. This might be different from the environment in which application was built. Note that client library plugins are loaded dynamically at run-time and they are not required during build process. Client library plugins can have further dependencies. For example authentication_kerberos_client plugin depends on Kerberos libraries and when used these libraries must be found on the target system. If client library plugins are not available the connector will still work correctly provided that it does not invoke any functionality that requires these plugins. For example if the server to which it connects does not have any accounts that use advanced authentication mechanisms that would require use of client library authentication plugins. In case of a local installation the mysql-concpp module sets the MYSQL_CONCPP_PLUGIN_DIR variable to the location where bundled client library plugins (and their dependencies) are located inside connector installation directory. Depending on the deployment strategy this information can be used, for example, to bundle the available plugins with the final application. In case of system-wide installation the plugins are not bundled with the connector and MYSQL_CONCPP_PLUGIN_DIR variable is not set. |
![]() |
|
|
Заработало!!!
Большое спасибо за помощь! ![]() ![]() Подключение установлено успешно! Результат: 10.4.32-MariaDB |
Сообщ.
#51
,
|
|
|
Здравствуйте!
Возник вопрос: как сделать, чтобы русcкие буквы отображались корректно? ![]() ![]() Подключение установлено успешно! |1|Р"РчР?РёС?| |2|Р>С'Р?С?| |
Сообщ.
#52
,
|
|
|
Цитата DDim1000 @ Возник вопрос: как сделать, чтобы русcкие буквы отображались корректно? Лучший вариант (но я еще сам не пробовал) - использовать ConEmu в качестве терминала, настроенным на UTF-8 по умолчанию + в студии использовать расширение ConEmu Integration 2017. Подчёркиваю, я сам этим пока не пользовался, но интересно, и я скоро сам попробую. В этом большой бонус - полноценный терминал с UTF-8 кодировкой. Второй вариант - писать свою функцию преобразования перед печатью на стандартный терминал со стандартной кодировкой cp866. Должно получиться что-то типа: ![]() ![]() #include <string> #include <iostream> #include <windows.h> std::string utf8_to_cp866(const std::string& utf8_str) { // 1. UTF-8 -> UTF-16 int wide_size = MultiByteToWideChar(CP_UTF8, 0, utf8_str.c_str(), -1, nullptr, 0); std::wstring wide_str(wide_size, 0); MultiByteToWideChar(CP_UTF8, 0, utf8_str.c_str(), -1, &wide_str[0], wide_size); // 2. UTF-16 -> cp866 int cp866_size = WideCharToMultiByte(866, 0, wide_str.c_str(), -1, nullptr, 0, nullptr, nullptr); std::string cp866_str(cp866_size, 0); WideCharToMultiByte(866, 0, wide_str.c_str(), -1, &cp866_str[0], cp866_size, nullptr, nullptr); return cp866_str; } int main() { std::string utf8_str = u8"Привет, ворлд!"; std::string cp866_str = utf8_to_cp866(utf8_str); std::cout << cp866_str << std::endl; return 0; } С кириллицей должно прокатить. Но, сам понимаешь, символы юникода, которым нет соответствия в cp866, выведены не будут. |
Сообщ.
#53
,
|
|
|
Цитата Majestio @ Второй вариант - писать свою функцию преобразования перед печатью на стандартный терминал со стандартной кодировкой cp866. Должно получиться что-то типа: Третий вариант. Использовать WSL или вовсе перейти на Linux. Там давно уже UTF8 и нету этих приколов. |
Сообщ.
#54
,
|
|
|
Четвертый вариант. Использовать либу boost
![]() ![]() ![]() #include <boost/locale.hpp> #include <string> #include <iostream> int main() { std::string utf8_str = u8"Привет, васьминогам!"; std::cout << boost::locale::conv::from_utf(utf8_str, "CP866") << std::endl; return 0; } |
![]() |
Сообщ.
#55
,
|
|
Команда в консоли "chcp 65001", програмно SetConsoleOutputCP(), конкретно в Плюсах std::locale(). Проще свои сырцы уже представлять компилятору к нужной кодировке. И нужен соответствующий шрифт, естественно.
Только вот вопрос: а тебе этот UTF-8 вообще зачем? Многобайтная кодировка – это жестокая боль, что б тебе не советовали из никсов. |
Сообщ.
#56
,
|
|
|
Цитата Qraizer @ Команда в консоли "chcp 65001", програмно SetConsoleOutputCP(), конкретно в Плюсах std::locale(). Qraizer, вот скажи, зачем ты так издеваешься над человеком этим своим советом? ![]() С каких это пор виндовая стандартная консоль дружила с многобайтовыми кодировками? Не, ну серьёзно. Ну покажи код, как в наш век гомоглобальной экзистенции модуляции синтеза, на примере вывода UTF-8 строк, сделать печать прямым выводом, без перекодировки в cp866 и вывести юникод в эту всратую консоль, чисто одной сменой локали... Цитата Qraizer @ Проще свои сырцы уже представлять компилятору к нужной кодировке. Дело не в одних сырцах. Есть ещё и данные из БД MySQL, которые туда нужно вписать, и оттуда ещё и читать. И я тебе, как родному, скажу: лучше это будет, если таблицы будут там созданы в кодировке utf8mb4 вместе с современными правилами сортировки ("collation" — так это называется на языке западных кровавых империалистов) utf8mb4_unicode_520_ci. Вот тогда, и именно тогда, и только тогда — никакая внешняя индустриальная интервенция (особенно китайская) нам не страшна!!! Вот именно тогда мы будем готовы кодить в жёстком стиле, по всем фронтам, но соблюдая современные международные нормы. Свободу восьминогам! ![]() |
![]() |
Сообщ.
#57
,
|
|
![]() ![]() #include <iostream> #include <string> #include <locale> int main() { std::string str = u8"Хеллоу, уорлд!"; std::locale locRusUTF8("russian.utf8"); std::cout.imbue(locRusUTF8); std::cout << str << std::endl; } ![]() ![]() D:\Work\DelMe>chcp 65001 Active code page: 65001 D:\Work\DelMe>delMe.exe Хеллоу, уорлд! D:\Work\DelMe> Добавлено UTF-8 вам в одну руку и флаг в другую, уважаемые уважатели суррогатных байт. А я вот не люблю, когда не могу сделать ![]() ![]() std::replace(str.begin(), str.end(), u8'л', u8'l'); когда str.length() возвращает 25 вместо правильного 14 и тем более когда str[6] вместо u8',' возвращает мне некое '\xD0' |
Сообщ.
#58
,
|
|
|
Просто ты не распознал всей вкусности юникода!
Да, пришли времена когда печатаемый символ может/должен занимать более одного байта. Смирись с этим, или проиграешь!!! ![]() |
Сообщ.
#59
,
|
|
|
![]() |
|
|
Подскажите, пожалуйста, почему ошибка?:
это объявление не содержит класс хранения или спецификатор типа. Ст. 15. На 15 ст. находится вызов функции system("chcp 65001");. ![]() ![]() #include "MySQLConnection.h" #include <cstdlib> // system() #include <iostream> #include <memory> #include <mysql_driver.h> #include <mysql_connection.h> #include <cppconn/driver.h> #include <cppconn/exception.h> #include <cppconn/resultset.h> #include <cppconn/statement.h> #include <cppconn/prepared_statement.h> namespace school { system("chcp 65001"); MySQLConnection::MySQLConnection(const std::string& host, int port, const std::string& user, const std::string& password, const std::string& database) { sql::mysql::MySQL_Driver* driver = sql::mysql::get_mysql_driver_instance(); std::string connectionString = "tcp://" + host + ":" + std::to_string(port); connection.reset(driver->connect(connectionString, user, password)); // Настройка кодировки UTF-8 std::unique_ptr<sql::Statement> stmt(connection->createStatement()); stmt->execute("SET NAMES 'utf8'"); stmt->execute("SET CHARACTER SET utf8"); connection->setSchema(database); } void MySQLConnection::executeQuery(const std::string& query) { std::unique_ptr<sql::Statement> stmt(connection->createStatement()); std::unique_ptr<sql::ResultSet> res(stmt->executeQuery(query)); while (res->next()) { // Обработка результатов std::cout << "Результат: " << res->getString(1) << std::endl; } } bool MySQLConnection::isConnected() { return connection.get() != nullptr && !connection->isClosed(); } } |
Сообщ.
#61
,
|
|
|
Цитата DDim1000 @ Вы вкрячили непонятно что внутри namespace. Это и ни объявление функции, ни её реализация. Поэтому компилятор вас и не понял. system() или SetConsoleOutputCP() надо вызывать в main(). В добавок system() вам выдаст "мусор" в консоль "Active codepage xxxx", потому что это запуск команды cmd. Тогда уж system("chcp 65001 >nul"). |
![]() |
Сообщ.
#62
,
|
|
Цитата Majestio @ Ещё как вкусил. Главная проблема юникода не в много...символьности (UTF-16 тоже имеет суррогатные пары). Главная его проблема в том, что вместо того, чтобы разобраться с зоопарком мультикультурности и устранить связанные с ним неудобства, он его стандартизировал и т.с. эти неудобства узаконил.Просто ты не распознал всей вкусности юникода! Цитата Majestio @ Их никогда и не было. Но люди умели писать код в многобайтных кодировках. Проблема не в самой многобайтовости, а в отсутствии абстракции "символ". Это уже не к юникоду, конечно, а к std. Если уж ввели char8_t, префикс u8 и класс std::u8string, почему они по-прежнему отражают абстракцию "байт"?Да, пришли времена когда печатаемый символ может/должен занимать более одного байта Мне несложно написать свои алгоритмы и функции для "символов" и таскать их из проекта в проект. Более того, в C++11 сделали некий шаг в эту сторону, предоставив фасеты и классы. Пусть шаг и неудобный, т.к. отделён от самой абстракции "символ", но вот прошло 6 лет, а воз не то что и ныне там, а этот шаг в принципе отменён, и ничего не предоставлено взамен. Причём формулировка причины, типа, "ой, мы ниасилили сделать это безопасно с точки зрения обработки апшибков" и "ай-яй-яй, эти фасеты локале-независимы, поэтому им нечего делать в <locale>". Оттакъот. Юникоду 35 лет, к слову, и <locale> появился аж в C++98, в первой редакции Стандарта. |
![]() |
Сообщ.
#63
,
|
|
Цитата DDim1000 @ Не надо system(). Я ж упомянул программное средство:![]() ![]() system("chcp 65001"); ![]() ![]() SetConsoleOutputCP(65001); |
Сообщ.
#64
,
|
|
|
Здравствуйте!
У меня снова возникла ошибка: "ссылка на неразрешенный внешний символ", другом проекте. ссылка на неразрешенный внешний символ "public: void __cdecl school::TabTest::run(void)" (?run@TabTest@school@@QEAAXXZ) в функции main. Еще такая ошибка: неразрешенных внешних элементов: 1 h-file: ![]() ![]() #pragma once #include <iostream> #include <sstream> #include <string> #include <vector> #include <locale> namespace school { // Класс-обертка для преобразования в строку class StringConverter { public: template<typename T> static std::wstring toString(T val); }; // Класс для работы со строками class MyString : public std::wstring { public: MyString(); MyString(std::wstring s); MyString(const wchar_t* s); MyString& operator=(const std::wstring& a); template<typename T> MyString& operator=(const T a); }; // Класс для работы с вектором template<typename T> class MyVector : public std::vector<T> { public: T& operator[](const size_t i); void resize(size_t new_size, const T& value = T()); }; // Класс для форматирования таблицы class TableFormatter { private: MyVector<MyVector<MyString>>& m; MyVector<MyString>& h; size_t& CMAX; public: TableFormatter(size_t& cmax, MyVector<MyVector<MyString>>& data, MyVector<MyString>& header); void format(); private: void calculateMaxColumns(); void resizeRows(); void addRowNumbers(); void calculateColumnWidths(); void alignColumns(); void alignHeaders(); }; // Класс для вывода таблицы class TablePrinter { private: MyVector<MyVector<MyString>>& m; MyVector<MyString>& h; size_t& CMAX; public: TablePrinter( size_t& cmax, MyVector<MyVector<MyString>>& data, MyVector<MyString>& header); void print(std::wostream& o); private: std::wstring createLine(); void printHeader(std::wostream& o); void printRows(std::wostream& o); }; // Основной класс таблицы class Tab { private: MyVector<MyVector<MyString>> m; MyVector<MyString> h; size_t CMAX = 0; public: Tab(); MyVector<MyString>& operator[](const size_t i); template<typename ... T> void head(T ... t); void formater(); friend std::wostream& operator<<(std::wostream& o, Tab& t); }; // Объявление дружественной функции std::wostream& operator<<(std::wostream& o, Tab& t); // Класс для тестирования class TabTest { public: void run(); }; } // namespace school cpp-file: ![]() ![]() #include "DataTable.h" #define l(v) std::wcout << #v << " = " << (v) << "\n"; // Класс-обертка для преобразования в строку class StringConverter { public: template<typename T> static std::wstring toString(T val) { return (std::wostringstream() << val).str(); } }; // Класс для работы со строками class MyString : public std::wstring { public: MyString() : std::wstring() {} MyString(std::wstring s) : std::wstring(s) {} MyString(const wchar_t* s) : std::wstring(s) {} MyString& operator=(const std::wstring& a) { return *static_cast<std::wstring*>(this) = a, *this; } template<typename T> MyString& operator=(const T a) { return *this = StringConverter::toString(a); } }; // Класс для работы с вектором template<typename T> class MyVector : public std::vector<T> { public: T& operator[](const size_t i) { if (i >= this->size()) this->resize(i + 1); return *(this->begin() + i); } void resize(size_t new_size, const T& value = T()) { std::vector<T>::resize(new_size, value); } }; // Класс для форматирования таблицы class TableFormatter { private: MyVector<MyVector<MyString>>& m; MyVector<MyString>& h; size_t& CMAX; public: TableFormatter(size_t& cmax, MyVector<MyVector<MyString>>& data, MyVector<MyString>& header) : m(data), h(header), CMAX(cmax) { } void format() { calculateMaxColumns(); resizeRows(); addRowNumbers(); calculateColumnWidths(); alignColumns(); alignHeaders(); } private: void calculateMaxColumns() { for (const auto& e : m) if (CMAX < e.size()) CMAX = e.size(); } void resizeRows() { for (auto& e : m) if (CMAX > e.size()) e.resize(CMAX); if (CMAX > h.size()) h.resize(CMAX); } void addRowNumbers() { for (size_t r = 0; r < m.size(); ++r) { m[r][0] = StringConverter::toString(r + 1); } } void calculateColumnWidths() { std::vector<size_t> n(CMAX, 0); // Инициализация ширинами заголовков for (size_t i = 0; i < h.size() && i < CMAX; ++i) { n[i] = h[i].size(); } // Нахождение максимальных ширин for (size_t c = 0; c < CMAX; ++c) { for (size_t r = 0; r < m.size(); ++r) { if (n[c] < m[r][c].size()) n[c] = m[r][c].size(); } } // Выравнивание столбцов for (size_t c = 0; c < CMAX; ++c) { for (size_t r = 0; r < m.size(); ++r) { if (n[c] > m[r][c].size()) m[r][c].resize(n[c], ' '); } } // Выравнивание заголовков for (size_t c = 0; c < CMAX; ++c) { if (h[c].size() < m[0][c].size()) h[c].resize(m[0][c].size(), ' '); } } void alignColumns() {} // Заглушка для совместимости с логикой void alignHeaders() {} // Заглушка для совместимости с логикой }; // Класс для вывода таблицы class TablePrinter { private: MyVector<MyVector<MyString>>& m; MyVector<MyString>& h; size_t& CMAX; public: TablePrinter( size_t& cmax, MyVector<MyVector<MyString>>& data, MyVector<MyString>& header) : CMAX(cmax), m(data), h(header) { } void print(std::wostream& o) { std::wstring line = createLine(); o << line << '\n'; printHeader(o); o << '\n' << line << '\n'; printRows(o); o << line << '\n' << '\n'; } private: std::wstring createLine() { std::wstring line; for (size_t c = 0; c < CMAX; ++c) { line += std::wstring(h[c].size() + 3, '='); } return line; } void printHeader(std::wostream& o) { for (size_t c = 0; c < CMAX; ++c) { o << ' ' << h[c] << " |"; } } void printRows(std::wostream& o) { for (const auto& r : m) { for (const auto& e : r) { o << ' ' << e << " |"; } o << '\n'; } } }; // Основной класс таблицы class Tab { private: MyVector<MyVector<MyString>> m; MyVector<MyString> h; size_t CMAX = 0; public: Tab() = default; MyVector<MyString>& operator[](const size_t i) { if (i >= m.size()) m.resize(i + 1); return *(m.begin() + i); } template<typename ... T> void head(T ... t) { (h.push_back(StringConverter::toString(t)), ...); } void formater() { TableFormatter formatter(CMAX, m, h); formatter.format(); } friend std::wostream& operator<<(std::wostream&, Tab&); }; std::wostream& operator<<(std::wostream& o, Tab& t) { t.formater(); TablePrinter printer(t.CMAX, t.m, t.h); printer.print(o); return o; } ///----------------------------------------------------------------------------| /// Тест. ///----------------------------------------------------------------------------: class TabTest { public: void run() { ///-----------------------| /// Создаём табулятор. | ///-----------------------: Tab tab; ///-----------------------| /// Заголовок. | ///-----------------------: tab.head(L"NN", L"Первый", L"Второй", 3); ///-----------------------| /// Как угодно заполняем. | ///-----------------------: tab[1][1] = 2020; tab[2][2] = "qwerty"; tab[4][3] = 3.14; tab[0][4] = L"Яша + Оля"; ///-----------------------| /// Вывод на экран/файл. | ///-----------------------: std::wcout << tab; tab[2][4] = 2025; tab[3][5] = 'A'; std::wcout << tab; } }; source.cpp: ![]() ![]() #include "DataTable.h" using namespace school; ///----------------------------------------------------------------------------| /// Старт. ///----------------------------------------------------------------------------: int main() { setlocale(0, ""); TabTest test; test.run(); return 0; } |
![]() |
Сообщ.
#65
,
|
|
Ну так класс TabTest объявлен в пространстве имён school. Тот, что определён в глобальном, то другой, получается, они не совпадают.
|
![]() |
|
|
Цитата Qraizer @ Ну так класс TabTest объявлен в пространстве имён school. Тот, что определён в глобальном, то другой, получается, они не совпадают. То есть, и заголовки и реализаци дожны находиться в одном прострастве имен? |
![]() |
|
|
Я разместил код в прострастве имен school, и у меня вылезло куча ошибо. Почемиу?
![]() ![]() #include "DataTable.h" namespace school { #define l(v) std::wcout << #v << " = " << (v) << "\n"; // Класс-обертка для преобразования в строку class StringConverter { public: template<typename T> static std::wstring toString(T val) { return (std::wostringstream() << val).str(); } }; // Класс для работы со строками class MyString : public std::wstring { public: MyString() : std::wstring() {} MyString(std::wstring s) : std::wstring(s) {} MyString(const wchar_t* s) : std::wstring(s) {} MyString& operator=(const std::wstring& a) { return *static_cast<std::wstring*>(this) = a, *this; } template<typename T> MyString& operator=(const T a) { return *this = StringConverter::toString(a); } }; // Класс для работы с вектором template<typename T> class MyVector : public std::vector<T> { public: T& operator[](const size_t i) { if (i >= this->size()) this->resize(i + 1); return *(this->begin() + i); } void resize(size_t new_size, const T& value = T()) { std::vector<T>::resize(new_size, value); } }; // Класс для форматирования таблицы class TableFormatter { private: MyVector<MyVector<MyString>>& m; MyVector<MyString>& h; size_t& CMAX; public: TableFormatter(size_t& cmax, MyVector<MyVector<MyString>>& data, MyVector<MyString>& header) : m(data), h(header), CMAX(cmax) { } void format() { calculateMaxColumns(); resizeRows(); addRowNumbers(); calculateColumnWidths(); alignColumns(); alignHeaders(); } private: void calculateMaxColumns() { for (const auto& e : m) if (CMAX < e.size()) CMAX = e.size(); } void resizeRows() { for (auto& e : m) if (CMAX > e.size()) e.resize(CMAX); if (CMAX > h.size()) h.resize(CMAX); } void addRowNumbers() { for (size_t r = 0; r < m.size(); ++r) { m[r][0] = StringConverter::toString(r + 1); } } void calculateColumnWidths() { std::vector<size_t> n(CMAX, 0); // Инициализация ширинами заголовков for (size_t i = 0; i < h.size() && i < CMAX; ++i) { n[i] = h[i].size(); } // Нахождение максимальных ширин for (size_t c = 0; c < CMAX; ++c) { for (size_t r = 0; r < m.size(); ++r) { if (n[c] < m[r][c].size()) n[c] = m[r][c].size(); } } // Выравнивание столбцов for (size_t c = 0; c < CMAX; ++c) { for (size_t r = 0; r < m.size(); ++r) { if (n[c] > m[r][c].size()) m[r][c].resize(n[c], ' '); } } // Выравнивание заголовков for (size_t c = 0; c < CMAX; ++c) { if (h[c].size() < m[0][c].size()) h[c].resize(m[0][c].size(), ' '); } } void alignColumns() {} // Заглушка для совместимости с логикой void alignHeaders() {} // Заглушка для совместимости с логикой }; // Класс для вывода таблицы class TablePrinter { private: MyVector<MyVector<MyString>>& m; MyVector<MyString>& h; size_t& CMAX; public: TablePrinter( size_t& cmax, MyVector<MyVector<MyString>>& data, MyVector<MyString>& header) : CMAX(cmax), m(data), h(header) { } void print(std::wostream& o) { std::wstring line = createLine(); o << line << '\n'; printHeader(o); o << '\n' << line << '\n'; printRows(o); o << line << '\n' << '\n'; } private: std::wstring createLine() { std::wstring line; for (size_t c = 0; c < CMAX; ++c) { line += std::wstring(h[c].size() + 3, '='); } return line; } void printHeader(std::wostream& o) { for (size_t c = 0; c < CMAX; ++c) { o << ' ' << h[c] << " |"; } } void printRows(std::wostream& o) { for (const auto& r : m) { for (const auto& e : r) { o << ' ' << e << " |"; } o << '\n'; } } }; // Основной класс таблицы class Tab { private: MyVector<MyVector<MyString>> m; MyVector<MyString> h; size_t CMAX = 0; public: Tab() = default; MyVector<MyString>& operator[](const size_t i) { if (i >= m.size()) m.resize(i + 1); return *(m.begin() + i); } template<typename ... T> void head(T ... t) { (h.push_back(StringConverter::toString(t)), ...); } void formater() { TableFormatter formatter(CMAX, m, h); formatter.format(); } friend std::wostream& operator<<(std::wostream&, Tab&); }; std::wostream& operator<<(std::wostream& o, Tab& t) { t.formater(); TablePrinter printer(t.CMAX, t.m, t.h); printer.print(o); return o; } ///----------------------------------------------------------------------------| /// Тест. ///----------------------------------------------------------------------------: class TabTest { public: void run() { ///-----------------------| /// Создаём табулятор. | ///-----------------------: Tab tab; ///-----------------------| /// Заголовок. | ///-----------------------: tab.head(L"NN", L"Первый", L"Второй", 3); ///-----------------------| /// Как угодно заполняем. | ///-----------------------: tab[1][1] = 2020; tab[2][2] = "qwerty"; tab[4][3] = 3.14; tab[0][4] = L"Яша + Оля"; ///-----------------------| /// Вывод на экран/файл. | ///-----------------------: std::wcout << tab; tab[2][4] = 2025; tab[3][5] = 'A'; std::wcout << tab; } }; }; Прикреплённая картинка
|
Сообщ.
#68
,
|
|
|
Вам же Qraizer сказал.
Объявление и реализация должны быть в одном namespace. в cpp файле так же должно быть указано namespace school { Тут должна быть вся реализация и объявления классов } Зы. Таки все объявления классов StringConverter, MyString, MyVector и т.д. я бы перенес в .h Выглядит очень некрасиво. |
Сообщ.
#69
,
|
|
|
Цитата sharky72 @ Вам же Qraizer сказал. Объявление и реализация должны быть в одном namespace. А у меня разве не в одном namespace? |
Сообщ.
#70
,
|
|
|
Цитата DDim1000 @ Цитата sharky72 @ Вам же Qraizer сказал. Объявление и реализация должны быть в одном namespace. А у меня разве не в одном namespace? Неа. Я не вижу namespace school {) в cpp и внутри все что должно ему принадлежать .h ![]() ![]() namespace mynamespace { class classname { void method1(); void method2(); } void function(); } .cpp ![]() ![]() namespace mynamespace { void classname::method1(){} void classname::method2(){} void function(){} } |
![]() |
|
|
![]() |
Сообщ.
#72
,
|
|
Так не нужно же дублировать определения. Отдельно, в .cpp, определять нужно только то, что в .h было только объявлено. (В свою очередь в .h определять нужно типы и inline, остальное должно быть только объявлено, а определено в .cpp)
|
Сообщ.
#73
,
|
|
|
Ну я же не вижу где находится ваш класс Tab и судя по ошибке его нет в данном namespace. Не говоря о том что по скрину MyString и MyVector, StringConverter определены как в .h так и в .cpp поэтому ошибка двойного определения С2011
Не надо выкладывать скрины. Либо выкладывайте проект, либо заворачивайте в /code. С телефона смотреть не всегда удобно, не говоря о том что скрин показывает только часть кода. Как должны выглядеть объявление и реализация я вам написал. |
![]() |
Сообщ.
#74
,
|
|
Например, класс TabTest не содержит inline методов, поэтому определён правильно: метод TabTest::run() только объявлен. Но определение в .cpp должно быть только для метода:
![]() ![]() namespace school { /* ... */ ///----------------------------------------------------------------------------| /// Тест. ///----------------------------------------------------------------------------: void TabTest::run() { /* ... */ } } Добавлено P.S. Если ранее писал на Джаве (Шарпы, мэйби?), то в Плюсах так тоже можно, но не приветствуется. Только в отдельных случаях |
Сообщ.
#75
,
|
|
|
Я так понял, что у меня пробле с загаловками? Потому что в одном файле все работает.
Source.cpp: ![]() ![]() #include <iostream> #include <sstream> #include <string> #include <vector> #include <locale> #define l(v) std::wcout << #v << " = " << (v) << "\n"; // Класс-обертка для преобразования в строку class StringConverter { public: template<typename T> static std::wstring toString(T val) { return (std::wostringstream() << val).str(); } }; // Класс для работы со строками class MyString : public std::wstring { public: MyString() : std::wstring() {} MyString(std::wstring s) : std::wstring(s) {} MyString(const wchar_t* s) : std::wstring(s) {} MyString& operator=(const std::wstring& a) { return *static_cast<std::wstring*>(this) = a, *this; } template<typename T> MyString& operator=(const T a) { return *this = StringConverter::toString(a); } }; // Класс для работы с вектором template<typename T> class MyVector : public std::vector<T> { public: T& operator[](const size_t i) { if (i >= this->size()) this->resize(i + 1); return *(this->begin() + i); } void resize(size_t new_size, const T& value = T()) { std::vector<T>::resize(new_size, value); } }; // Класс для форматирования таблицы class TableFormatter { private: MyVector<MyVector<MyString>>& m; MyVector<MyString>& h; size_t& CMAX; public: TableFormatter(size_t& cmax, MyVector<MyVector<MyString>>& data, MyVector<MyString>& header) : m(data), h(header), CMAX(cmax) { } void format() { calculateMaxColumns(); resizeRows(); addRowNumbers(); calculateColumnWidths(); alignColumns(); alignHeaders(); } private: void calculateMaxColumns() { for (const auto& e : m) if (CMAX < e.size()) CMAX = e.size(); } void resizeRows() { for (auto& e : m) if (CMAX > e.size()) e.resize(CMAX); if (CMAX > h.size()) h.resize(CMAX); } void addRowNumbers() { for (size_t r = 0; r < m.size(); ++r) { m[r][0] = StringConverter::toString(r + 1); } } void calculateColumnWidths() { std::vector<size_t> n(CMAX, 0); // Инициализация ширинами заголовков for (size_t i = 0; i < h.size() && i < CMAX; ++i) { n[i] = h[i].size(); } // Нахождение максимальных ширин for (size_t c = 0; c < CMAX; ++c) { for (size_t r = 0; r < m.size(); ++r) { if (n[c] < m[r][c].size()) n[c] = m[r][c].size(); } } // Выравнивание столбцов for (size_t c = 0; c < CMAX; ++c) { for (size_t r = 0; r < m.size(); ++r) { if (n[c] > m[r][c].size()) m[r][c].resize(n[c], ' '); } } // Выравнивание заголовков for (size_t c = 0; c < CMAX; ++c) { if (h[c].size() < m[0][c].size()) h[c].resize(m[0][c].size(), ' '); } } void alignColumns() {} // Заглушка для совместимости с логикой void alignHeaders() {} // Заглушка для совместимости с логикой }; // Класс для вывода таблицы class TablePrinter { private: MyVector<MyVector<MyString>>& m; MyVector<MyString>& h; size_t& CMAX; public: TablePrinter( size_t& cmax, MyVector<MyVector<MyString>>& data, MyVector<MyString>& header) : CMAX(cmax), m(data), h(header) { } void print(std::wostream& o) { std::wstring line = createLine(); o << line << '\n'; printHeader(o); o << '\n' << line << '\n'; printRows(o); o << line << '\n' << '\n'; } private: std::wstring createLine() { std::wstring line; for (size_t c = 0; c < CMAX; ++c) { line += std::wstring(h[c].size() + 3, '='); } return line; } void printHeader(std::wostream& o) { for (size_t c = 0; c < CMAX; ++c) { o << ' ' << h[c] << " |"; } } void printRows(std::wostream& o) { for (const auto& r : m) { for (const auto& e : r) { o << ' ' << e << " |"; } o << '\n'; } } }; // Основной класс таблицы class Tab { private: MyVector<MyVector<MyString>> m; MyVector<MyString> h; size_t CMAX = 0; public: Tab() = default; MyVector<MyString>& operator[](const size_t i) { if (i >= m.size()) m.resize(i + 1); return *(m.begin() + i); } template<typename ... T> void head(T ... t) { (h.push_back(StringConverter::toString(t)), ...); } void formater() { TableFormatter formatter(CMAX, m, h); formatter.format(); } friend std::wostream& operator<<(std::wostream&, Tab&); }; std::wostream& operator<<(std::wostream& o, Tab& t) { t.formater(); TablePrinter printer(t.CMAX, t.m, t.h); printer.print(o); return o; } ///----------------------------------------------------------------------------| /// Тест. ///----------------------------------------------------------------------------: class TabTest { public: void run() { ///-----------------------| /// Создаём табулятор. | ///-----------------------: Tab tab; ///-----------------------| /// Заголовок. | ///-----------------------: tab.head(L"NN", L"Первый", L"Второй", 3); ///-----------------------| /// Как угодно заполняем. | ///-----------------------: tab[1][1] = 2020; tab[2][2] = L"qwerty"; tab[4][3] = 3.14; tab[0][4] = L"Яша + Оля"; ///-----------------------| /// Вывод на экран/файл. | ///-----------------------: std::wcout << tab; tab[2][4] = 2025; tab[3][5] = L'A'; std::wcout << tab; } }; ///----------------------------------------------------------------------------| /// Старт. ///----------------------------------------------------------------------------: int main() { setlocale(0, "ru"); TabTest test; test.run(); return 0; } |
Сообщ.
#76
,
|
|
|
У вас проблема в том что вы и в заголовок и в cpp файл впихнули одно и то же.
Вы уж определитесь нужен ли вам в данном случае вообще заголовок. Если больше никто этот ваш код использовть не будет и нигде кроме этого файла эти классы не понадобятся - то да, можно и так. Если же вы хотите написать нормально и не копировать код в другом файле - то в заголовок размещается объявление классов, функций, дефайнов (если они влияют на несколько единиц трансляции), а вся реализация - выносится в cpp файл. |
![]() |
|
|
Цитата sharky72 @ ... Если же вы хотите написать нормально и не копировать код в другом файле - то в заголовок размещается объявление классов, функций, дефайнов (если они влияют на несколько единиц трансляции), а вся реализация - выносится в cpp файл. Я так и хочу сделать, заголовки разместить в h-файле, а реализацию в cpp-файл. Но проблема в том, что я не понимаю как в данном случае вынести заголовки. |
Сообщ.
#78
,
|
|
|
Вы понимаете разницу между объявлением и реализацией?
Я же вам привел пример объявления класса в .h и реализации его методов в .cpp Как исправить ошибку: "LNK2019 ссылка на неразрешенный внешний символ"? Ну почитайте хотя бы основы C++ https://www.learncpp.com/cpp-tutorial/class...d-header-files/ |
![]() |
|
|
Я отредактировал код, но одна ошибка осталась: идентификатор "run" не определен.
Подскажите, пожалуйста, как исправить ошибку? Файл дикларации: ![]() ![]() #pragma once #include <iostream> #include <sstream> #include <string> #include <vector> #include <locale> namespace school { #define l(v) std::wcout << #v << " = " << (v) << "\n"; // Класс-обертка для преобразования в строку class StringConverter { public: template<typename T> static std::wstring toString(T val) { return (std::wostringstream() << val).str(); } }; // Класс для работы со строками class MyString : public std::wstring { public: MyString(); MyString(std::wstring s); MyString(const wchar_t* s); template<typename T> MyString& operator=(const T a) { return *this = StringConverter::toString(a); }; }; // Класс для работы с вектором template<typename T> class MyVector : public std::vector<T> { public: T& operator[](const size_t i) { if (i >= this->size()) this->resize(i + 1); return *(this->begin() + i); } void resize(size_t new_size, const T& value = T()) { std::vector<T>::resize(new_size, value); } }; // Класс для форматирования таблицы class TableFormatter { private: MyVector<MyVector<MyString>>& m; MyVector<MyString>& h; size_t& CMAX; public: TableFormatter(size_t& cmax, MyVector<MyVector<MyString>>& data, MyVector<MyString>& header); void format(); private: void calculateMaxColumns(); void resizeRows(); void addRowNumbers(); void calculateColumnWidths(); void alignColumns(); // Заглушка для совместимости с логикой void alignHeaders(); // Заглушка для совместимости с логикой }; // Класс для вывода таблицы class TablePrinter { private: MyVector<MyVector<MyString>>& m; MyVector<MyString>& h; size_t& CMAX; public: TablePrinter( size_t& cmax, MyVector<MyVector<MyString>>& data, MyVector<MyString>& header); void print(std::wostream& o); private: std::wstring createLine(); void printHeader(std::wostream& o); void printRows(std::wostream& o); }; // Основной класс таблицы class Tab { private: MyVector<MyVector<MyString>> m; MyVector<MyString> h; size_t CMAX = 0; public: // Tab() = default Tab(); // объявление оператора[] MyVector<MyString>& operator[](const size_t i); template<typename ... T> void head(T ... t) { (h.push_back(StringConverter::toString(t)), ...); } void formater(); // Объявляем дружественные классы friend class TableFormatter; friend class TablePrinter; friend std::wostream& operator<<(std::wostream& o, Tab& t); }; std::wostream& operator<<(std::wostream& o, Tab& t); ///----------------------------------------------------------------------------| /// Тест. ///----------------------------------------------------------------------------: class TabTest { public: void run(); }; } // namespace school Файл реализации: ![]() ![]() #include "DataTable.h" namespace school { // Реализация методов MyString MyString::MyString() : std::wstring() {} MyString::MyString(std::wstring s) : std::wstring(s) {} MyString::MyString(const wchar_t* s) : std::wstring(s) {} // Явная инстанциация шаблонов template class MyVector<MyString>; template class MyVector<MyVector<MyString>>; // Реализация TableFormatter TableFormatter::TableFormatter(size_t& cmax, MyVector<MyVector<MyString>>& data, MyVector<MyString>& header) : m(data), h(header), CMAX(cmax) { } void TableFormatter::format() { calculateMaxColumns(); resizeRows(); addRowNumbers(); calculateColumnWidths(); alignColumns(); alignHeaders(); } void TableFormatter::calculateMaxColumns() { for (const auto& e : m) if (CMAX < e.size()) CMAX = e.size(); } void TableFormatter::resizeRows() { for (auto& e : m) if (CMAX > e.size()) e.resize(CMAX); if (CMAX > h.size()) h.resize(CMAX); } void TableFormatter::addRowNumbers() { for (size_t r = 0; r < m.size(); ++r) { m[r][0] = StringConverter::toString(r + 1); } } void TableFormatter::calculateColumnWidths() { std::vector<size_t> n(CMAX, 0); // Инициализация ширинами заголовков for (size_t i = 0; i < h.size() && i < CMAX; ++i) { n[i] = h[i].size(); } // Нахождение максимальных ширин for (size_t c = 0; c < CMAX; ++c) { for (size_t r = 0; r < m.size(); ++r) { if (n[c] < m[r][c].size()) n[c] = m[r][c].size(); } } // Выравнивание столбцов for (size_t c = 0; c < CMAX; ++c) { for (size_t r = 0; r < m.size(); ++r) { if (n[c] > m[r][c].size()) m[r][c].resize(n[c], ' '); } } // Выравнивание заголовков for (size_t c = 0; c < CMAX; ++c) { if (h[c].size() < m[0][c].size()) h[c].resize(m[0][c].size(), ' '); } } void TableFormatter::alignColumns() {} void TableFormatter::alignHeaders() {} // Реализация TablePrinter TablePrinter::TablePrinter( size_t& cmax, MyVector<MyVector<MyString>>& data, MyVector<MyString>& header) : CMAX(cmax), m(data), h(header) { } void TablePrinter::print(std::wostream& o) { std::wstring line = createLine(); o << line << '\n'; printHeader(o); o << '\n' << line << '\n'; printRows(o); o << line << '\n' << '\n'; } std::wstring TablePrinter::createLine() { std::wstring line; for (size_t c = 0; c < CMAX; ++c) { line += std::wstring(h[c].size() + 3, '='); } return line; } void TablePrinter::printHeader(std::wostream& o) { for (size_t c = 0; c < CMAX; ++c) { o << ' ' << h[c] << " |"; } } void TablePrinter::printRows(std::wostream& o) { for (const auto& r : m) { for (const auto& e : r) { o << ' ' << e << " |"; } o << '\n'; } } // Реализация Tab //Tab::Tab() {}; Tab::Tab() = default; MyVector<MyString>& Tab::operator[](const size_t i) { if (i >= m.size()) m.resize(i + 1); return *(m.begin() + i); } void Tab::formater() { TableFormatter formatter(CMAX, m, h); formatter.format(); } // Реализация оператора вывода std::wostream& operator<<(std::wostream& o, Tab& t) { t.formater(); TablePrinter printer(t.CMAX, t.m, t.h); printer.print(o); return o; } // Реализация TabTest void TabTest::run() { ///-----------------------| /// Создаём табулятор. | ///-----------------------: Tab tab; ///-----------------------| /// Заголовок. | ///-----------------------: tab.head(L"NN", L"Первый", L"Второй", 3); ///-----------------------| /// Как угодно заполняем. | ///-----------------------: tab[1][1] = 2020; tab[2][2] = "qwerty"; tab[4][3] = 3.14; tab[0][4] = L"Яша + Оля"; ///-----------------------| /// Вывод на экран/файл. | ///-----------------------: std::wcout << tab; tab[2][4] = 2025; tab[3][5] = 'A'; std::wcout << tab; }; } Файл Source.cpp: ![]() ![]() #include "DataTable.h"; using namespace school; int main() { setlocale(0, "ru"); TabTest test; test,run(); return 0; } Добавлено Все, нашел ошибку. ![]() ![]() TabTest test; test,run(); Теперь программа компилируется, запускается, но на консоль ничего не выводится,кроме этого сообщения: ![]() ![]() D:\Programming\VS\source\repos\Test\x64\Debug\Test.exe (процесс 30896) завершил работу с кодом -1073741571 (0xc00000fd). Нажмите любую клавишу, чтобы закрыть это окно: Почему? |
![]() |
Сообщ.
#80
,
|
|
Цитата DDim1000 @ 0xc00000fd – переполнение стека, похоже. ![]() ![]() D:\Programming\VS\source\repos\Test\x64\Debug\Test.exe (процесс 30896) завершил работу с кодом -1073741571 (0xc00000fd). Добавлено P.S. Ты на предупреждения не смотришь? ![]() ![]() DataTable.h(36) : warning C4717: school::MyString::operator=<std::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t> > >: рекурсия на всех путях выполнения, функция вызовет переполнение стека |
Сообщ.
#81
,
|
|
|
Цитата Qraizer @ P.S. Ты на предупреждения не смотришь? Никаких предупреждений не вижу. Сборка проходит без ошибок и предупреждений. Прикреплённая картинка
Добавлено Все, увидил. 1>D:\Programming\VS\source\repos\Test\Test\DataTable.h(36,1): warning C4717: school::MyString::operator=<std::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t> > >: рекурсия на всех путях выполнения, функция вызовет переполнение стека Почему так проиходит? |
Сообщ.
#82
,
|
|
|
Решение:
![]() ![]() // ... // Класс для работы со строками class MyString : public std::wstring { public: MyString(); MyString(std::wstring s); MyString(const wchar_t* s); template<typename T> MyString& operator=(const T a) { std::wstring temp = StringConverter::toString(a); this->assign(temp); // используем базовую реализацию assign из wstring return *this; }; }; // ... |
![]() |
Сообщ.
#83
,
|
|
Цитата DDim1000 @ Потому что template<typename T> MyString& operator=(const T a) принимает любой T, в частности и std::wstring тоже. Поэтому operator=() в строке *this = StringConverter::toString(a) после преобразования любого T в std::wstring снова вызывает сам себя, но уже с T, выведенным как std::wstring, что в свою очередь опять вызовет StringConverter::toString() с аргументом std::wstring в качестве T, т.к. template<typename T> std::wstring StringConverter::toString(T val) опять-таки принимает любой T.Почему так проиходит? Чтобы этого не происходило, нужно отвязать MyString::operator=() от std::wstring, предоставив более специализированную реализацию, или использовать какой-нибудь operator=(), не принадлежащий MyString. Добавлено Я бы написал ![]() ![]() std::wstring temp = StringConverter::toString(a); this->assign(std::move(temp)); // используем базовую реализацию assign из wstring P.S. Если с семантикой перемещения пока не знаком, не бери в голову. Больше путаться будешь, чем выигрывать от оптимизации. Добавлено P.P.S. Ты реализовал второе решение разрыва рекурсии, с использованием методов, не принадлежащих MyString. Альтернативный вариант с более специализированным operator=() может быть таким: просто добавить ещё один operator=(), который перегрузит шаблонный: ![]() ![]() MyString& operator=(const std::wstring& a) { this->std::wstring::operator=(a); /* как вариант: *static_cast<std::wstring*>(this) = a; сработает, т.к. std::wstring является базовым для MyString, и в нём есть свой operator=(std::wstring) */ return *this; } Добавлено Позволь совет на будущее: если программа собирается и даже запускается, это ещё не означает, что с ней всё в порядке. Это должно быть и так понятно, конечно, иначе бы программы после успешной сборки всегда работали идеально. Компилятор не может залезть тебе в голову и понять, что ты хотел написать, поэтому компилит ровно то, что ты написал, а не что хотел написать. Но предупреждения компиляторам выдумали не просто так. Он всё-таки пытается понять написанное в рамках своей скудной разумности, и если увидит что-то непротиворечащее правилам Языка, однако с его точки зрения странное, код-то он соберёт, но предупредит, что тут что-то с его точки зрения не так. Предупреждениям компиляторы не зря научили, к ним всегда стоит присматриваться. Многие этого не понимают и просто их игнорируют, мол, собралось же. Однако даже самое незначительное на первый взгляд, типа "локальная переменная определена и не используется" иногда может указывать на серьёзную ошибку: вместо неё в программе заюзана одноимённая глобальная. Всегда стоит стараться избавляться от предупреждений, причём ни в коем случае не подавлением (кроме ситуаций, когда это зависит не от тебя, и ты просто ничего не можешь сделать; типичный пример – ругательства компилятора на несоответствие знаковости типов внутри какого-нибудь <sys/socket.h>, ибо не ты писал этот идиотский API, а Беркли), а путём исправления кода, иначе за простынёй неважных предупреждений запросто пропустишь реально важные. |
Сообщ.
#84
,
|
|
|
Именно поэтому на всех серьезных проектах ставят warning as error и warning level 4.
|
Сообщ.
#85
,
|
|
|
Цитата sharky72 @ Именно поэтому на всех серьезных проектах ставят warning as error и warning level 4. Это что такое, warning as error и warning level 4? |
![]() |
Сообщ.
#86
,
|
|
Уровни предупреждений от 1 и до... обычно 4, но бывает и больше (у Intel compiler, например, 5) определяют степень важности предупреждения, по какой-то там шкале. Чем больше цифра, тем менее важное. У каждого предупреждения есть некий уровень, и он фиксирован для него. Когда выставляешь желаемый уровень диагностики, предупреждения с большим уровнем компилятор подавляет. Например «warning C4018: <: несоответствие типов со знаком и без знака» у Студии имеет уровень 3, а «warning C4389: ==: несоответствие типов со знаком и без знака» уровень 4, поэтому выставив желаемый уровень 3, увидишь в диагностике первые, но не увидишь вторых.
У разных компиляторов может быть по-разному. Даже распределение предупреждений по уровням, не говоря уже о списке странностей, которые способен обнаружить. По умолчанию уровень обычно 1, когда ставишь 4, компилер будет рассказывать о твоём коде больше, как правило всё, до чего сможет докопаться. Чтобы побить себя по рукам, можно попросить компилятор расценивать любые предупреждения как ошибки, тогда он при обнаружении хотя бы одного, даже самого с его точки зрения некритичного, даже при отсутствии ошибок, будет отказываться собирать код как при ошибках. Довольно кардинально, на спорю. Студия в качестве соломонова решения умеет рассматривать как ошибки только предупреждения с неким указанным уровнем и строже, те, что помягче, не будут стопорить сборку. |
Сообщ.
#87
,
|
|
|
Цитата Qraizer @ Уровни предупреждений от 1 и до... обычно 4, но бывает и больше (у Intel compiler, например, 5) определяют степень важности предупреждения, по какой-то там шкале. Чем больше цифра, тем менее важное. У каждого предупреждения есть некий уровень, и он фиксирован для него. Когда выставляешь желаемый уровень диагностики, предупреждения с большим уровнем компилятор подавляет. Например «warning C4018: <: несоответствие типов со знаком и без знака» у Студии имеет уровень 3, а «warning C4389: ==: несоответствие типов со знаком и без знака» уровень 4, поэтому выставив желаемый уровень 3, увидишь в диагностике первые, но не увидишь вторых. У разных компиляторов может быть по-разному. Даже распределение предупреждений по уровням, не говоря уже о списке странностей, которые способен обнаружить. По умолчанию уровень обычно 1, когда ставишь 4, компилер будет рассказывать о твоём коде больше, как правило всё, до чего сможет докопаться. Чтобы побить себя по рукам, можно попросить компилятор расценивать любые предупреждения как ошибки, тогда он при обнаружении хотя бы одного, даже самого с его точки зрения некритичного, даже при отсутствии ошибок, будет отказываться собирать код как при ошибках. Довольно кардинально, на спорю. Студия в качестве соломонова решения умеет рассматривать как ошибки только предупреждения с неким указанным уровнем и строже, те, что помягче, не будут стопорить сборку. Ой, Qraizer, ну ты же знаешь как я тебя уважаю! Но ты часто бываешь такой "душный" =) Ну чтобы просто сказать, мол "увеличение уровня предупреждений увеличивает детализацию сообщений об ошибках". Это всё. Обнять и плакать ![]() |
![]() |
Сообщ.
#88
,
|
|
У меня пунктик: не просто выдать факт, а объяснить его, идеально – почему так, а не как-нибудь иначе. Связи концепций лучше усваиваются и дают понимание системы, а не просто её знание.
|
Сообщ.
#89
,
|
|
|
![]() ![]() |