Текущий Стандарт С++ и перспективы его развития
![]() |
Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
|
| ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
| [216.73.216.167] |
|
|
| Страницы: (81) « Первая ... 77 78 [79] 80 81 ( Перейти к последнему сообщению ) |
Текущий Стандарт С++ и перспективы его развития
|
Сообщ.
#1171
,
|
|
|
|
Почему это? Не будет. \, предшествующий ", маскирует конец литерала и означает просто символ " как его элемент, тогда как если этому \ предшествует ещё один \, то это означает просто символ \ как элемент литерала, и если следом идёт ", то это его граница. Если мы видим \\\, то первые два означают просто \, а последний является модификатором следующего символа, и какой-то смысл они имеют только совместно. \\\" означает два обычных символа \ и ", не специальных, \\\t означает \ и TAB итп
|
|
Сообщ.
#1172
,
|
|
|
|
Проверю.
![]() ![]() $ cat test.cpp #include <iostream> int main() { std::cout << "Test \\\"; return 0; } $ g++ -o test test.cpp test.cpp:4:22: предупреждение: отсутствует завершающий символ " 4 | std::cout << "Test \\\"; | ^ test.cpp:4:22: ошибка: отсутствует терминирующий символ " 4 | std::cout << "Test \\\"; | ^~~~~~~~~~~ test.cpp: In function «int main()»: test.cpp:5:9: ошибка: expected primary-expression before «return» 5 | return 0; | ^~~~~~ $ cat test.cpp #include <iostream> int main() { std::cout << "Test \\\\"; return 0; } $ g++ -o test test.cpp $ ./test Test \\$ |
|
Сообщ.
#1173
,
|
|
|
|
Да, это проблемы раскрашивателя кода. Он не учитывает, что комментарии не могут начинаться, и не могут заканчиваться внутри строковых и символьных литералов. Цитата macomics @ Судя по этому утверждению, тогда "\\\" должен быть завершенной строкой. Но не будет. Стоило сказать про нечетное количество \. Проще не заморачиваться количеством или чётностью. А просто определиться с обработкой. Символ \ может участвовать: Экранирование Работает только внутри литералов. Символ \ может экранировать " или ' или \ Escape-преобразования Работают только внутри литералов. Всем известные \n \r \t \a \0 \x42 .... Компилятор сканирует код посимвольно. Встречая символы, которые могут иметь и обычное, и управляющее значение. Компилятор, в процессе сканирования, "назначает" встретившему символу "роль" (обычный или управляющий) в зависимости от ряда состояний. |
|
Сообщ.
#1174
,
|
|
|
|
Цитата Qraizer @ Не всё, что внутри /**/ или //EOL, является комментом, а только то, что не в строковом литерале. Крайне подозрительное утверждение. Внутри комментариев - не может быть литералов. И если мы что-то решили закомментировать (я имею ввиду по правилам), то любые литералы автоматом превращаются в комментарии. |
|
Сообщ.
#1175
,
|
|
|
|
Majestio, ну да. Это и означает, что
![]() ![]() std::cout << "Тут я не должна заменяться, даже /* если я размещу её */ вот так"; // в отличие от "Тут я должна быть \ заменена", потому что это всё ещё коммент |
|
Сообщ.
#1176
,
|
|
|
|
Qraizer, склейка строк не спасет от неправильности твоего этого утверждения.
Хотя нет, не от неправильности. Твое это утверждение отчасти верно. Просто оно записано так, что несколько ломает причинно-следственные связи А именно ... как бы это правильно сказать: литералы все же первичны, конструкции комментариев вторичны. Т.е., выражаясь академическим языком, и чтобы не туманить мозги С++ неофитам, ты должен был выразить мысль в более "календарном" смысле, в обратном порядке:Заметь! 2 потом 3, а не 3 потом 2. Если я не ошибаюсь, это что-то типа раздел 5.2 "Phases of translation" текущего Стандарта. |
|
Сообщ.
#1177
,
|
|
|
|
В чём именно ты видишь неправильность? Я не собирался описывать алгоритм, хошь сырцов?, они есть. Я описал спектр учитываемых аспектов. Естественно их не следует применять в описанном порядке. Давайте не будем душнить. Плз.
|
|
Сообщ.
#1178
,
|
|
|
|
Цитата Qraizer @ В чём именно ты видишь неправильность? Ну я же написал выше. Не неправильность, а скорее запутанность. Проще написать, что внутри литералов не может быть комментов, равно как и внутри комментов не может быть литералов. Тогда всё ясно и понятно. И да, давай не будем душнить =) |
|
Сообщ.
#1179
,
|
|
|
|
Qraizer, тебе квест!
Цитата Qraizer @ Как-то пришлось писать утилю, которая заменяла все я на Я, потому как используемая тулза (инструментатор исходного кода под: сбор структурного покрытия; сбор информации WCET, анализ потоков данных и/или управления; оценка наихудшего использования стека) на ASCII символах == 0xFF бажила. (Дело не в русской букве, а именно коде символа. В европейских кодировках на этом символе тот же баг.) Так это было целое приключение, т.к. заменять всё было нельзя, только в комментах. А то, что в строках, нужно оставить нетронутым, иначе меняется поведение кода, благо на таких тулза не бажила. В итоге это вылилось в весьма нетривиальный алгоритм. Я уверен, что сей велосипед ты навелосипедил на все 100% правильно. Но ты тогда, imho, пошел самым упоротым путем. Ибо для таких задач уже давно существовали специальные решения. А именно связка либ - bison (1985) + flex (1987). Генераторы парсеров и лексические анализаторы в лице вышеупомянутых либ. Слабо изучить матчасть и перевести свой код под эти "платформы"? Скрытый текст заодно и мы поучимся ... а? |
|
Сообщ.
#1180
,
|
|
|
|
Зачем такое старьё, если есть boost::spirit
? |
|
Сообщ.
#1181
,
|
|
|
|
Bison + Flex однозначно лучше, когда требуется максимальная производительность парсинга для обработки больших входных данных, а грамматика подходит для LALR, так как они генерируют оптимизированный C-код, который работает быстрее, чем парсеры, созданные с помощью Boost::Spirit.
|
|
Сообщ.
#1182
,
|
|
|
|
Ну я бы не был так категоричен на предмет производительности. Другое дело, компиляция...
Та и в целом, меня напрягает любой автоматически непонятно кем непонятно как генерируемый код, вставляемый в мой проект. Никсоидам может быть и нормуль, когда make генерит makefile, по которому make генерит три makefile, по которым make генерит N makefiles для сборки N/3 пререквизитов, по которым рекурсивно процесс повторяется для сборки пререквизитов, ещё один собирает собственно проект с пререквизитами и ещё один конфигурит и инсталит, и поэтому им нормуль, когда половина пререквизитов автогенерится своими makefile, точнее, их половиной, тогда как вторая половина их собственно собирает... а, не, половина от N/3... ойфсё. И да, в Cях метакодить иначе никак. Спасибо, я всё ж лучше метакодить по Стандарту буду |
|
Сообщ.
#1183
,
|
|
|
|
Цитата Qraizer @ Никсоидам может быть и нормуль, когда make генерит makefile, по которому make генерит три makefile, по которым make генерит N makefiles для сборки N/3 пререквизитов, по которым рекурсивно процесс повторяется для сборки пререквизитов, ещё один собирает собственно проект с пререквизитами и ещё один конфигурит и инсталит, и поэтому им нормуль, когда половина пререквизитов автогенерится своими makefile, точнее, их половиной, тогда как вторая половина их собственно собирает... а, не, половина от N/3... ойфсё. Вот тут про никсоидов было очень обидно сказано! Нагнал жути Спецом побеседовал с ChatGPT по этой теме, т.к. практического использования bison+flex не имел. Ради прикола собрал парсер С++ кода, который из кода извлекает все литералы и распечатывает их. Заметь, я не использовал make, а сделал это современными и отчасти удобными средствами сборки cmake+ninja. А вот сам пример (да, понимаю, что сильно упрощенный - но наглядный) lexer.l ![]() ![]() %{ #include "parser.tab.h" // Включаем заголовок от Bison для токенов %} %option noyywrap /* Состояния */ %x SINGLE_COMMENT %x MULTI_COMMENT %% /* Пропуск пробелов и другого кода (мы фокусируемся только на литералах и комментариях) */ [ \t\n]+ ; // Игнорируем whitespace /* Начало однострочного комментария */ "//" { BEGIN(SINGLE_COMMENT); } <SINGLE_COMMENT>[^\n]* ; // Потребляем до конца строки <SINGLE_COMMENT>\n { BEGIN(INITIAL); } // Возврат в нормальное состояние /* Начало многострочного комментария */ "/*" { BEGIN(MULTI_COMMENT); } <MULTI_COMMENT>[^*]* ; // Потребляем всё, кроме * <MULTI_COMMENT>"*"+[^*/]* ; // Потребляем * не за которыми / <MULTI_COMMENT>"*"+"/" { BEGIN(INITIAL); } // Конец комментария /* Строковый литерал (упрощённо, с escape) */ \"(\\.|[^\\"])*\" { yylval.str = strdup(yytext); return STRING_LITERAL; } /* Символьный литерал (упрощённо) */ \'(\\.|[^\\'])*\' { yylval.str = strdup(yytext); return CHAR_LITERAL; } /* Другие символы (пропускаем, так как не интересуют) */ . ; // Игнорируем остальной код %% parser.y ![]() ![]() %{ #include <iostream> #include <vector> #include <string> #include <cstring> extern int yylex(); void yyerror(const char* s); std::vector<std::string> literals; // Глобальный список для собранных литералов %} %union { char* str; } %token <str> STRING_LITERAL %token <str> CHAR_LITERAL %start program %% program: statements ; statements: statement | statements statement ; statement: STRING_LITERAL { literals.push_back($1); free($1); } | CHAR_LITERAL { literals.push_back($1); free($1); } | /* пусто */ { /* Игнорируем другие токены или пустые */ } ; %% void yyerror(const char* s) { std::cerr << "Ошибка парсинга: " << s << std::endl; } main.cpp ![]() ![]() #include <iostream> #include <vector> #include <string> #include <windows.h> // Для Windows API #include "parser.tab.h" extern std::vector<std::string> literals; extern FILE* yyin; // Функция для вывода строки в CP1251 через WriteConsoleW void print_in_cp1251(const std::string& str) { // Конвертируем UTF-8 или ASCII строку в UTF-16 (Windows использует UTF-16) int wstr_size = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, nullptr, 0); std::wstring wstr(wstr_size, 0); MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, &wstr[0], wstr_size); // Выводим в консоль с CP1251 (Windows автоматически конвертирует UTF-16 в CP1251 для консоли) HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); WriteConsoleW(hConsole, wstr.c_str(), wstr.size() - 1, nullptr, nullptr); } int main(int argc, char** argv) { // Устанавливаем кодировку консоли на CP1251 SetConsoleOutputCP(1251); if (argc > 1) { yyin = fopen(argv[1], "r"); if (!yyin) { print_in_cp1251("Не удалось открыть файл: " + std::string(argv[1]) + "\n"); return 1; } } yyparse(); // Запускаем парсер // Выводим собранные литералы print_in_cp1251("Собранные строковые и символьные литералы (вне комментариев):\n"); for (const auto& lit : literals) { std::cout << lit << std::endl; // Литералы в ASCII, их можно выводить через std::cout } if (yyin != stdin) fclose(yyin); return 0; } CMakeLists.txt ![]() ![]() cmake_minimum_required(VERSION 3.10) project(CPPScanner LANGUAGES C CXX) # Стандарт C++17 set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) # Включаем статическую линковку set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static -static-libgcc") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static") # Найти Flex и Bison find_package(BISON REQUIRED) find_package(FLEX REQUIRED) # Генерация парсера из parser.y (с подавлением предупреждений Bison) BISON_TARGET(MyParser parser.y ${CMAKE_CURRENT_BINARY_DIR}/parser.tab.c DEFINES_FILE ${CMAKE_CURRENT_BINARY_DIR}/parser.tab.h COMPILE_FLAGS "-Wnone") # Генерация лексера из lexer.l FLEX_TARGET(MyLexer lexer.l ${CMAKE_CURRENT_BINARY_DIR}/lex.yy.c) # Зависимость: парсер зависит от лексера ADD_FLEX_BISON_DEPENDENCY(MyLexer MyParser) # Собрать исполняемый файл add_executable(scanner main.cpp ${BISON_MyParser_OUTPUT_SOURCE} # parser.tab.c ${FLEX_MyLexer_OUTPUTS} # lex.yy.c ) # Указываем, что генерированные .c файлы — это C++ set_source_files_properties(${BISON_MyParser_OUTPUT_SOURCE} ${FLEX_MyLexer_OUTPUTS} PROPERTIES LANGUAGE CXX) # Подавляем предупреждение о deprecated для Clang set_source_files_properties(${BISON_MyParser_OUTPUT_SOURCE} ${FLEX_MyLexer_OUTPUTS} PROPERTIES COMPILE_FLAGS "-Wno-deprecated") # Включить директории для заголовков target_include_directories(scanner PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) # Добавить путь к библиотекам clang64 target_link_directories(scanner PRIVATE E:/Tools/MSys64/clang64/lib) # Линковка со статическими библиотеками C++ и unwind target_link_libraries(scanner PRIVATE -static libc++.a libunwind.a) Процесс сборки: ![]() ![]() mkdir build && cd build cmake -G Ninja .. ninja ![]() ![]() -- The C compiler identification is Clang 20.1.8 -- The CXX compiler identification is Clang 20.1.8 -- Detecting C compiler ABI info -- Detecting C compiler ABI info - done -- Check for working C compiler: E:/Tools/MSys64/clang64/bin/cc.exe - skipped -- Detecting C compile features -- Detecting C compile features - done -- Detecting CXX compiler ABI info -- Detecting CXX compiler ABI info - done -- Check for working CXX compiler: E:/Tools/MSys64/clang64/bin/c++.exe - skipped -- Detecting CXX compile features -- Detecting CXX compile features - done -- Found BISON: E:/Tools/MSys64/usr/bin/bison.exe (found version "3.8.2") -- Found FLEX: E:/Tools/MSys64/usr/bin/flex.exe (found version "2.6.4") -- Configuring done (1.7s) -- Generating done (0.0s) -- Build files have been written to: E:/Tools/MSys64/home/Majestio/work/bison_flex/build [6/6] Linking CXX executable scanner.exe Проверяем на файле input.cpp следующего содержимого: ![]() ![]() // Это комментарий с "строкой", которую нужно пропустить int main() { std::string s = "hello world"; // Строка для сбора char c = 'a'; // Символ для сбора /* Многострочный комментарий с 'b' и "test", которые пропустить */ return 0; } ![]() ![]() E:\Tools\MSys64\home\Majestio\work\bison_flex\build>scanner.exe input.cpp Собранные строковые и символьные литералы (вне комментариев): "hello world" 'a' По-моему не всё так страшно-ужасно как ты расписал |
|
Сообщ.
#1184
,
|
|
|
|
Цитата Majestio @ Тут ты ошибся.Заметь! 2 потом 3, а не 3 потом 2 Сперва из текста программы удаляются комментарии и производится склейка строк. Потом производится разбор текста на лексемы, включая и строки с директивами препроцессора. И уже после разбора на лексемы работает препроцессор, удаляя куски уже разобранного текста и заменяя макросы. Причём всё это время лексемы остаются привязанными к исходному тексту программы, и результат препроцессирования может быть выдан в виде текста так, будто препроцессор работает непосредственно с текстом. В ранних компиляторах C (K&R), состоящих из отдельных программ, каждая из которых выполняла свой проход, порядок был другой. Препроцессор выполнялся до разбора на лексемы, но после удаления комментариев (собственно он их и удалял). |
|
Сообщ.
#1185
,
|
|
|
|
amk, привет, дружище!!!
Вообще без проблем поговорить по этому вопросу и набраться современных знаний - тема есть, но наука то не стоит! Будь добр, обеспечить свои утверждения линками-пруфами по этим темам, чисто для ознакомления. |