Страницы

Поиск по вопросам

вторник, 7 января 2020 г.

Получить параметры функций

#cpp #функции


Использую в программе систему перевода с применением функции Lang("Text"), которая
возвращает найденный перевод строки, либо переданный текст. По ходу вызовов Lang()
программа заносит новые слова в файл.
Как бы получить сразу все тексты во всех Lang()? Подозреваю, вариант с применением
#define и хитрым кодом. Хотя как вариант можно прогнать код программы через парсер.    


Ответы

Ответ 1



Для Windows, я бы решил эту проблему аналогично, как в ситуации: "Построение цепочки потомков для каждого скомпилированного символа", при условии, что макрос _D выглядел бы как-то так: #define ___D( x ) #x #define __D( marker, x ) ___D( marker ## x ) #define _D( x ) __D( LangMarker, x ) при том, что marker более ни где по коду в тексте встречаться не будет. И, соответственно, использовался бы он так: Lang( _D( someone save us ) ); Хотя, именно для такой задачи, "овчинка выделки не стоит". Можно даже вырезать из всех этих строк сам маркер! Т.е. маркер будет выступать в роли compile-time-атрибута, предписывающего размещение строки в таком массиве, но это реализовать уже сложнее, т.к. потребует модификации всех ссылок на эти строки. Можно в самом маркере закодировать переменную, в которую будет сливаться такой массив. Т.е, на самом деле, вариантов, как это реализовать в compile-time достаточно много и может ограничиваться только фантазией и возможностями самого языка. Мои комментарии везде позаканчивались, поэтому я выбрал из своих ответов тот, который бы решал поставленную задачу. Некоторые зависимые от него комментарии я так же переместил в тело ответа, чтобы была понятна их связь. Следите, пожалуйста, за моими новыми комментариями при редактировании ответа. К сожалению, более подходящего решения оставлять сообщения в форуме я пока не вижу. Думаю, стоит этот вопрос вынести на рассмотрение администрации. Комментарии: как это решение привязать к моей задаче @mikelsv, это решение включает в себя "посткомпилятор", который найдет все строки в объектных файлах и выделит из них только те, которые помечены маркером. Организует из них список или вектор и поместит адрес его первого элемента в какую-нибудь статическую переменную. Все это он сделает на этапе компиляции, поэтому в программе вектор/список строк будет известен заранее, можно даже знать его размер заранее. Если вы говорите о синтаксисе времени выполнения, то не гарантировано, что любая из конструкций Lang() вообще когда-либо сработает или вообще произведёт код Я не говорю о синтаксисе времени выполнения, я говорю о compile-time выражениях, которые допустимы в statement'е параметра функции. компиляция может до макроса Lang вообще не добраться. Например: #ifdef SOMETHING Lang("1") #else Lang("2") #endif Тогда это будет зависимая конструкция. Значит, в реальной программе эта конструкция и не встретится никогда без перекомпиляции с SOMETHING. Так что рантайм и компиляция не кажется мне адекватным решением Не уверен, что в адекватности дело. @mega: не, так нельзя рассуждать. Потому что если я перед функцией main напишу int doexit() { exit(0); return 0; } int i = doexit(); то рантайм ни до одного Lang не доберётся. Чем это отличается от #ifdef? Ничем, просто другой синтаксис. @VladD, как это соотносится с условием задачи? От #ifdef это отличается тем, что до Lang исполнение просто не доберется. Поэтому, если компилятор учитывает особенности ОСи, он и формировать не будет "недосягаемый" код. Ergo: в рантайме вы не можете гарантированно добраться до содержимого Lang. У меня другой вопрос: а зачем Вам "недосягаемый" Lang? Ведь Вы можете написать: #define MyLng( x ) Lang( x ) и везде в программе использовать MyLng, что в таком случае будете делать? Очевидно, будете править свой фильтр, "раздувать" regexp для Lang, и возможно, все будет хорошо, если не забудете, о каком-нибудь еще аналоге MyLng :) Или можете поступить интереснее: #define __Depend( x ) #x #define _Depend( x, y ) x ##<-depend <-## y #define Depend( x, y ) __Depend( _Depend( x, y ) ) Lang( Depend( head, neck ) ) Lang( Depend( head, _Depend( neck, body ) ) ) Очевидно, в такой ситуации очередное "раздувание" regexp будет частично включать код самого макроса Depend, все его возможные вложения и, соответственно все зависимости, которые из него вытекают. Т.е., у Вас и с досягаемым-то кодом уже проблемы, а Вы о недосягаемом думаете, странно :) По моему, один парсер ( а в нашем случае, лучше сказать - "сканер" ) эту задачу не решит. Вернее, суть его решения всегда будет заключаться в том, как бы переписать фильтр под конкретный код. А это означает, что человек, который его использует, при изменении кода, всегда будет сталкиваться с исходной проблемой, т.е. он ее так и не решит. Периодически, я с такими решениями тоже сталкиваюсь. Тот же doxygen - полезнейший продукт, но абсолютно "тупой" парсер. Вот и ему приходится расписывать в PREDEFINED десятки элементарных фильтров и следить за их обновлениями. Не знаю, как с макросами. Насколько я понял их надо вставить всюду в текст программы внуть Lang? Такая идея мало кому из пишущих код понравится. @avp, макрос нужен только для фильтрации строк, т.е., чтобы в массиве были только определенные константные строки, а не все, что были скомпилированы. Ну, допустим, самый простой вариант: все строки в Lang будут начинаться с ##, например: Lang( "##someone save us" ). Т.е. нужна какая-то идентификация самих строк. Если она уже есть, значит и код менять не потребуется. Либо, как вариант: все эти строки будут находиться в определенном сегменте памяти (секции), тогда модификации кода будут вообще минимальны. Я сейчас не могу сказать, в каком классе данных находятся константные строки VS, надо разобрать для примера какой-нибуть *.obj, со строками. Но в любом случае, это контролируется прагмой data_seg или code_seg, поэтому, заключив функцию/класс/заголовок и т.п., который содержит массовые вызовы Lang в определенный сегмент, его можно будет использовать в качестве фильтра, который будет отделять обычные строки от искомых. Поэтому без парсера тут не обойтись. Во во, и я о том же. А что может быть лучше парсера от VS? Наверное только парсер от VS, его-то мы и используем. Нам ведь не сам парсер нужен. Нам нужны результаты его работы. В *.obj и находятся результаты его работы. @mega: какой из примеров вы имеете в виду? #define MyLng( x ) Lang( x )? Это противоречит заданию -- искать надо текст Lang(...) MyLng как раз и делает вызов Lang(...). Как это может противоречить заданию? Но представьте себе, что у нас есть код: if (random(4) < 0) { cout << Lang("1") } Компилятор знает, что значение random положительное, и выкидывает код при оптимизации. Завтра приходит разработчик, находит ошибку и исправляет её. Запускать анализ снова? Даже если представить, что Компилятор делает какие-то предположения по поводу этой функции. Разработчик, исправив ошибку запустит процесс сборки заново. Т.е. запустит всю цепочку утилит компоновки, в которую входят все задействованные компиляторы, мой "посткомпилятор" (или "прелинкер"), и, непосредственно линковщик. Что с того? Будет массив на один элемент длиннее, что от этого изменится? Или Вы переживаете на счет того, что количество "билдов" программы увеличится? Не совсем я понимаю Вашу позицию. Вы ее проясните пожалуйста, а то, подозреваю, ее не только я не понимаю.

Ответ 2



Если я все-таки правильно понял задачу. Сделать синглетон-обертку для контейнера, например, множества std::set (или наследовать его) и заносить туда в функции Lang() все тексты, которые были переданы ей. Всегда можно прочитать содержимое этого контейнера.

Ответ 3



Вы под Юниксом? Попробуйте просто grep -rh 'Lang(' projectDir/ | sed 's/Lang(/\n\0/g' | grep 'Lang(' | sed 's/^.*Lang(\([^)]*\)).*$/\1/' | sort -u >out.txt (Надеюсь, нигде не напутал. Пусть гуру шелла поправят.)

Комментариев нет:

Отправить комментарий