Я нахожусь в миграции исходного кода, и программа-конвертер не конвертировала конкатенацию встроенных строк с целыми числами. Теперь у меня есть много кода с такими выражениями:
f("some text" + i);
Так как C / C ++ будет интерпретировать это как индекс массива, f
получит "some text"
, или же "ome text"
, или же "me text"
…
Мой исходный язык конвертирует конкатенацию строки с int как конкатенацию строки. Теперь мне нужно построчно просмотреть исходный код и вручную изменить предыдущее выражение на:
f("some text" + std::to_string(i));
Конверсионной программе удалось конвертировать локальнуюString
«переменные к»std::string
«, в результате чего выражения:
std::string some_str = ...;
int i = ...;
f(some_str + i);
Это было легко исправить, потому что с такими выражениями компилятор C ++ выдает ошибку.
Есть ли инструмент для автоматического поиска таких выражений в исходном коде?
Легко! Просто замените все +
с -&
:
find . -name '*.cpp' -print0 | xargs -0 sed -i '' 's/+/-\&/g'
При попытке скомпилировать ваш проект вы увидите, между другими ошибками, что-то вроде этого:
foo.cpp:9:16: error: 'const char *' and 'int *' are not pointers to compatible types
return f(s -& i);
~ ^~~~
(Я использую clang, но другие компиляторы должны выдавать похожие ошибки)
Так что вам просто нужно отфильтровать выходные данные компилятора, чтобы сохранить только эти ошибки:
clang++ foo.cpp 2>&1 | grep -F "error: 'const char *' and 'int *' are not pointers to compatible types"
И вы получите:
foo.cpp:9:16: error: 'const char *' and 'int *' are not pointers to compatible types
foo.cpp:18:10: error: 'const char *' and 'int *' are not pointers to compatible types
Ты можешь попробовать кремень, Lint-программа с открытым исходным кодом для C ++, разработанная и используемая на Facebook. Имеет черную последовательность токенов (checkBlacklistedSequences
). Вы можете добавить свою последовательность токенов в checkBlacklistedSequences
функция и flint
сообщит о них.
в checkBlacklistedSequences
функция, я добавил последовательность string_literal + number
BlacklistEntry([tk!"string_literal", tk!"+", tk!"number"],
"string_literal + number problem!\n",
true),
затем скомпилируйте и протестируйте
$ cat -n test.cpp
1 #include <iostream>
2 #include <string>
3
4 using namespace std;
5
6 void f(string str)
7 {
8 cout << str << endl;
9 }
10
11 int main(int argc, char *argv[])
12 {
13 f("Hello World" + 2);
14
15 f("Hello World" + std::to_string(2));
16
17 f("Hello World" + 2);
18
19 return 0;
20 }
$ ./flint test.cpp
test.cpp(13): Warning: string_literal + number problem!
test.cpp(17): Warning: string_literal + number problem!
flint
имеет две версии (старая версия разработана на C ++ и новая версия на языке D), я внес свои изменения в версию D.
Я не знаком со многими инструментами, которые могут это сделать, но я думаю, grep
может быть полезным в какой-то мере.
В корневом каталоге вашего исходного кода попробуйте:
grep -rn '".\+"\s*+\s*' .
, который может узнать все файлы, которые содержат строку вроде "xxxxx" +
Надеюсь, это поможет вам найти все нужные вам строки.
Если все целые числа постоянны, вы можете изменить эксперимент grep следующим образом:
grep -rn '".\+"\s*+\s*[0-9]*' .
И вы также можете включить (
перед строковой константой:
grep -rn '(".\+"\s*+\s*[0-9]*' .
Это может быть не «правильный» ответ, но я надеюсь, что это поможет вам.
Вам может не понадобиться внешний инструмент. Вместо этого вы можете воспользоваться преимуществом правила однопользовательского преобразования C ++. По сути, вам нужно изменить аргумент вашего f
функция от const char*
/std::string
к типу, который неявно конвертируется только из строкового литерала (const char[size]
) или std::string
экземпляр (что вы получаете, когда добавляете std::to_string
в выражении).
#include <string>
#include <iostream>
struct string_proxy
{
std::string value;
string_proxy(const std::string& value) : value(value) {}
string_proxy(std::string&& value) : value(std::move(value)) {}
template <size_t size>
string_proxy(const char (&str)[size]) : value(str) {}
};
void f(string_proxy proxy)
{
std::cout << proxy.value << std::endl;
}
int main()
{
f("this works"); // const char[size]
f("this works too: " + std::to_string(10)); // std::string
f("compile error!" + 10); // const char*
return 0;
}
Обратите внимание, что это не собираюсь работать на MSVC, по крайней мере, не в версии 2012 года; это, вероятно, ошибка, так как предупреждение также не выдается. Он отлично работает в g ++ и clang (вы можете быстро это проверить Вот).
Я нашел очень простой способ обнаружить эту проблему. Регулярное выражение и ворс не будут соответствовать более сложным выражениям, подобным следующему:
f("Hello " + g(i));
Что мне нужно, так это как-то сделать вывод типа, так что я позволяю компилятору сделать это. Используя std::string
вместо буквальной строки возникает ошибка, поэтому я написал простой конвертер исходного кода перевести все строковые литералы в завернутые std::string
версия, как это:
f(std::string("Hello ") + g(i));
Затем, после перекомпиляции проекта, я увижу все ошибки. Исходный код находится на GitHub, в 48 строках кода Python:
Если ваш случай именно так
"some text in quotations" + a_numeric_variable_or_constant
тогда Powergrep или аналогичные программы позволят вам сканировать все файлы на наличие
("[^"]+")\s*\+\s*(\w+)
и заменить на
\1 + std::to_string(\2)
Это принесет вам возможные совпадения, но я настоятельно рекомендую сначала просмотреть то, что вы заменяете. Потому что это также заменит строковые переменные.
Регулярные выражения не могут понять семантику вашего кода, поэтому они не могут быть уверены, что если они целые числа. Для этого вам нужна программа с парсером типа CDT или статическими анализаторами кода. Но, к сожалению, я не знаю никого, кто мог бы это сделать. Подводя итог, я надеюсь, что регулярное выражение помогает 🙂
PS: В худшем случае, если переменные не являются числовыми, компилятор выдаст ошибку, потому что to_string
Функция не принимает ничего, кроме числовых значений. Может быть позже, тогда вы можете вручную заменить только их, что я могу только надеяться, больше не будет.
PS 2: Некоторые могут подумать, что Powergrep стоит дорого. Вы можете использовать пробную версию на 15 дней с полной функциональностью.
Вы можете попробовать подключаемый модуль Map-Reduce Clang.
Инструмент был разработан в Google для проведения такого рода рефакторинга, смешивая строгую проверку типов и регулярное выражение.
(см. видео презентацию Вот ).
Вы можете использовать оператор набора типов C ++ & создайте новый класс, который может перегрузить оператор + в соответствии с вашими потребностями. Вы можете заменить int на новый класс «Integer» & выполнить необходимую перегрузку. Это не требует никаких изменений или замены слова в вызове основной функции.
class Integer{
long i;
std::string formatted;
public:
Integer(int i){i = i;}
operator char*(){
return (char*)formatted.c_str();}
friend Integer operator +( char* input, Integer t);
};
Integer operator +( char* input, Integer integer) {
integer.formatted = input + std::to_string(integer.i);
return integer;
}
Integer i = ....
f("test" + i); //executes the overloaded operator