Как пометить * стандартную библиотеку * функцию / метод как устаревшую (или вообще отключенную) в моем проекте?

Я пытаюсь как-то отключить / пометить как осуждаемое отвратительное std::string::operator=(char) перегрузка (которая, по моему опыту, используется только при ошибочном назначении целого числа на строку и вызывает незаметные и трудные для отслеживания ошибки).

Я пробовал с:

  • явная специализация со статическим утверждением в нем

    #include <string>
    #include <type_traits>
    
    template<> std::basic_string<char> &std::basic_string<char>::operator=(char c) {
    static_assert(false, "Don't use this!");
    }
    

    который терпит неудачу как <string> уже делает явную реализацию std::string

  • [[deprecated]] атрибут, применяемый к аналогичной декларации, как указано выше, в различных позициях; никакая позиция, которую я пробовал, не дала разумного результата;
  • =delete, который терпит неудачу по причинам, подобным вышеупомянутым;
  • Я думал об использовании трюков с компоновщиком (в том же духе, в том же проекте у нас есть проверки времени выполнения на случайных setlocale использование с помощью --wrap ld вариант компоновщика), но тот факт, что это шаблон и встроенный метод, усложняет дело.

Теперь к вопросам:

  • есть ли стандартный способ как-то отключить (как было бы с =delete) любая функция или метод в стандартной библиотеке (читай: в библиотеке, где нельзя изменить объявления в заголовках)?
  • как указано выше, но вместо отключения добавьте предупреждение (как это происходит с [[deprecated]]);
  • В случае неудачи стандартного метода, есть ли что-то специфическое для g ++?
  • если нет «общего» (= применимого к какому-либо методу, любому классу, любой функции,…) решения, есть ли что-то, что мы могли бы применить к этому конкретному случаю (= отключить метод шаблонного класса, возможно, даже просто конкретный экземпляр)?

10

Решение

Вы можете использовать следующую опцию компилятора / компоновщика:

$ g++ -O0 test.cpp -Wl,--wrap=_ZNSsaSEc

Объяснение:

_ZNSsaSEc это название вашей оскорбительной функции:

$ echo _ZNSsaSEc | c++filt
std::basic_string<char, std::char_traits<char>, std::allocator<char> >::operator=(char)

-Wl Опция компилятора — передать опции компоновщику.

И --wrap=<symbol> опция компоновщика преобразует любую ссылку на данный символ в альтернативу __wrap_<symbol>, И так как вы (надеюсь) не определяете функцию с именем __wrap__ZNSsaSEc, вы получите хорошую ошибку компоновщика:

test.cpp:(.text+0x26): undefined reference to `__wrap__ZNSsaSEc'

А также -O0 это отключить оптимизацию и запретить компилятору встроить функцию. Трюк с компоновщиком не сработает, если есть встраивание, как указал @SergeBallesta в комментарии.

Может быть, немного взломать, но эй, это работает!

4

Другие решения

Ну, я боюсь, что стандартная библиотека предназначена для … стандарт, и как таковой не предоставляет крючки, позволяющие разработчикам настраивать его.

Уродливый путь (никогда не говори я советую вам его использовать ;-)) было бы использовать тот факт, что заголовки стандартной библиотеки — это просто текстовые файлы, так что вы можете легко изменить их в своей локальной среде разработки. Возможно, менее плохой способ — создать папку, содержащую ссылки на оригинальные заголовки, за исключением измененного заголовка, и дать указание компилятору использовать эту папку для системных заголовков.

Таким образом, вы можете изменить все, что захотите, но … портативность и ремонтопригодность … Это действительно головорез решение…

1

Это специфично для clang ++, так как я не знаю, как называется эквивалентная функциональность в цепочке инструментов gnu. Это также несколько излишне.

Предложение Родриго об использовании компоновщика для замены символа отлично подходит для случаев без подстановки. Если вы все время строите в O0, этого будет достаточно.

В противном случае, цепочка инструментов llvm (clang) предлагает удивительный контроль над конвейером оптимизации. Например, вы можете скомпилировать без оптимизации, затем запустить оптимизацию самостоятельно с помощью opt, а затем преобразовать в объектный файл.

clang++ -c test.cpp -O0 --emit-llvm test.ll
opt -O3 test.bc -o faster.ll
clang++ -c faster.bc -o test.o

Инструмент opt является расширяемым. Я не могу честно сказать, что это тривиально, но процесс хорошо документирован. Вы можете написать пропуск компилятора, который предупреждает, когда ваша стандартная библиотечная функция видна. Конечный результат может быть вызван что-то вроде:

clang++ -c test.cpp -O0 --emit-llvm test.ll
opt --load DeprecationPass.so test.bc -o /dev/null
opt -O3 test.bc -o faster.ll
clang++ -c faster.bc -o test.o

Если вы уверены, что пользовательский проход правильный (не просто полезный), вы можете использовать один вызов opt. Вероятно, можно передать флаги, чтобы выбрать через интерфейс Clang, но не сразу понятно, как это сделать.

В целом, следуя совету Родриго и иногда собирая весь продукт на O0, возможно, это лучший план, но интересно, что clang позволяет вам делать такие вещи.

0
По вопросам рекламы [email protected]