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

Я пытаюсь каким-то образом отключить/пометить как устаревшую отвратительную перегрузку 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++?
  • если нет "общего" (= применимого к любому методу, любому классу, любой функции, ...) решения, есть ли что-то, что мы могли бы применить к этому конкретному случаю (= отключить метод класса шаблона, возможно, даже просто конкретный экземпляр)?

person Matteo Italia    schedule 18.11.2015    source источник
comment
У вас есть забавное определение deprecated, если вы пытаетесь использовать статическое утверждение или удаленную функцию.   -  person Jonathan Wakely    schedule 18.11.2015
comment
@JonathanWakely: устарело или полностью отключено; но, насколько я понимаю, это по существу то же самое, я бы, вероятно, использовал -Werror в таком предупреждении в любом случае, нет веской причины использовать этот оператор присваивания (точно так же, как нет причин использовать, скажем, gets или строковый литерал =› char * преобразование помимо совместимости с устаревшим кодом).   -  person Matteo Italia    schedule 18.11.2015


Ответы (3)


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

$ 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 в комментарии.

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

person rodrigo    schedule 18.11.2015
comment
@SergeBallesta: Ну, с -O0 это работает. С -O1 и выше это не так. - person rodrigo; 18.11.2015
comment
Не делай этого! Совершенно неочевидно, что эта ошибка связана с неправильно использованным оператором присваивания. Через несколько лет этот флаг компоновщика сам по себе может стать трудно отслеживаемой ошибкой. - person Richard; 05.08.2016

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

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

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

person Serge Ballesta    schedule 18.11.2015
comment
Первое предложение немного спорное; возьмите любой динамический язык на ваш выбор (Javascript, Python, Ruby, ...), там есть стандартная библиотека, но вы можете крутить ее самыми причудливыми способами; кроме того, стандартная библиотека C++ предоставляет точки настройки (как аргументы шаблона или как открытые функции, подумайте std::hash), POSIX и Win32 допускают условное включение материала с помощью макросов, плюс все конкретные реализации C++ позволяют использовать флаги или настройки для стандартной библиотеки, и даже к самому языку (как переключатели времени компиляции для компилятора, как определяемые макросы,...). - person Matteo Italia; 18.11.2015

Это специфично для 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 позволяет вам делать такие вещи, здорово.

person Jon Chesterfield    schedule 19.11.2015