Использую в программе систему перевода с применением функции 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
(Надеюсь, нигде не напутал. Пусть гуру шелла поправят.)