10

私はどうにかして恐ろしい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 ++固有のものはありますか?
  • 「一般的な」(= 任意のメソッド、任意のクラス、任意の関数などに適用できる) 解決策がない場合、この特定のケースに適用できるものはありますか (= テンプレート クラスのメソッドを無効にします。特定のインスタンス化)?
4

3 に答える 3

4

次のコンパイラ/リンカー オプションを使用できます。

$ 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がコメントで指摘したように、インライン化がある場合、リンカのトリックは機能しません。

ちょっとしたハックかもしれませんが、うまくいきます!

于 2015-11-18T11:14:49.933 に答える
1

残念ながら、標準ライブラリは... standardであることを意図しており、開発者がそれを微調整できるようにするためのフックを提供していません。

醜い方法 (使用することをお勧めしますとは決して言いません;-) ) は、標準ライブラリのヘッダーが単なるテキスト ファイルであるため、ローカルの開発環境で簡単に変更できるという事実を利用することです。おそらくそれほど悪くない方法は、変更されたヘッダーを除く元のヘッダーへのリンクを含むフォルダーをセットアップし、システムヘッダーにそのフォルダーを使用するようにコンパイラーに指示することです。

そうすれば、好きなように変更できますが...移植性と保守性...それは本当に絶望的な解決策です...

于 2015-11-18T11:29:16.877 に答える
0

これは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 を 1 回呼び出すだけでかまいません。フラグを渡して clang フロントエンド経由で選択することはおそらく可能ですが、その方法はすぐにはわかりません。

全体として、rodrigo の提案に従い、時々 O0 で製品全体を構築することはおそらくより良い計画ですが、clang がこのようなことを可能にすることは刺激的です。

于 2015-11-19T11:08:46.430 に答える