11

I'm trying to somehow disable/mark as deprecated the hideous std::string::operator=(char) overload (which in my experience is used only when mistakingly assigning an integer to a string, and causes subtle and difficult to track bugs).

I tried with:

  • an explicit specialization with a static assert in it

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

    which fails as <string> already does an explicit instantiation of std::string

  • the [[deprecated]] attribute, applied to a similar declaration as above in various positions; no position I tried seemed to yield any reasonable result;
  • =delete, which fails for reasons similar to above;
  • I thought about using linker tricks (in a similar vein, in the same project we have runtime checks on stray setlocale usages using the --wrap ld linker option), but the fact that this is a template and inline method complicates the matter.

Now to the questions:

  • is there a standard method to somehow disable (as would happen with =delete) any function or method in the standard library (read: in a library where you cannot alter the declarations in the headers)?
  • as above, but, instead of disable, add a warning (as would happen with [[deprecated]]);
  • failing the standard method, is there something g++-specific?
  • if there's no "general" (=applicable to any method, any class, any function, ...) solution, is there something that we could apply to this specific case (=disable a method of a template class, possibly even just a specific instantiation)?
Matteo Italia
  • 123,740
  • 17
  • 206
  • 299
  • 1
    You have a funny definition of "deprecated" if you're trying to use a static assert or a deleted function. – Jonathan Wakely Nov 18 '15 at 11:49
  • @JonathanWakely: "deprecated or disabled altogether"; but as far as I'm concerned, it's essentially the same, I would probably use `-Werror` on such a warning anyway, there's no valid reason to use that assignment operator (exactly as there's no reason to use, say, `gets` or the string literal => `char *` conversion besides legacy code compatibility). – Matteo Italia Nov 18 '15 at 12:23

3 Answers3

4

You can use the following compiler/linker option:

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

Explanation:

The _ZNSsaSEc is the decorated name of your offending function:

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

The -Wl compiler option is to pass options to the linker.

And the --wrap=<symbol> linker option transforms any reference to the given symbol to the alternative __wrap_<symbol>. And since you are (hopefully) not defining a function named __wrap__ZNSsaSEc, you will get a nice linker error:

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

And -O0 is to disable optimizations and prevent the compiler from inlining the function. The linker trick will not work if there is inlining, as @SergeBallesta pointed out in the comment.

Maybe a bit of a hack, but hey, it works!

rodrigo
  • 94,151
  • 12
  • 143
  • 190
  • 1
    @SergeBallesta: Well, with `-O0`, it works. With `-O1` or higher it does not. – rodrigo Nov 18 '15 at 11:21
  • 1
    Don't do this! It is absolutely unobvious that this error is because of a wrongly used assignment operator. A few years down the road this linker flag could be a hard-to-track bug itself. – Richard Aug 05 '16 at 13:41
1

Well, I'm afraid that the standard library is intended to be... standard, and as such does not provide hooks to allow developpers to tweak it.

An ugly way (never say I advise you do use it ;-) ) would be to use the fact that Standard Library headers are just text files, so you can easily change them in you local developpement environment. A possibly less bad way, would be to setup a folder containing links to original headers except for the modified header and instruct compiler to use that folder for system headers.

That way, you can change anything you want, but... portability and maintainability... It's really a desperado solution...

Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
  • The first sentence is a bit of a moot point; take any dynamic language of your choice (Javascript, Python, Ruby, ...), there *is* a standard library, but you can twist it in the most bizarre ways; also, the C++ standard library provides customization points (as template arguments, or as "open functions", think `std::hash`), POSIX and Win32 allow conditional inclusion of stuff through macros, plus all concrete implementations of C++ allow flags or customizations to the standard library, and even to the language itself (as compile-time switches for the compiler, as macros to be defined, ...). – Matteo Italia Nov 18 '15 at 12:37
0

This is clang++ specific, since I don't know what the equivalent functionality is called in the gnu toolchain. It's also somewhat overkill.

Rodrigo's suggestion of using the linker to swap out the symbol is great for non-inlined cases. If you build everything at O0 occasionally that'll suffice.

Otherwise, the llvm (clang) toolchain offers surprising amounts of control over the optimisation pipeline. For example, you can compile without optimisations, then run the optimisations yourself using opt, then convert to an object file.

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

The opt tool is extensible. I can't honestly say it's trivial to extend, but the process is well documented. You can write a compiler pass that warns when your standard library function is seen. The end result could be invoked something like:

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

If you're confident the custom pass is correct (not merely useful) you can use a single invocation of opt. It's probably possible to pass flags through to opt via the clang front end, but it's not immediately obvious how to.

Overall, following rodrigo's suggestion and occasionally building the entire product at O0 is probably a better plan - but it is exciting that clang lets you do things like this.

Jon Chesterfield
  • 2,251
  • 1
  • 20
  • 30