16

I want to ban use of iostreams in a code base I have (for various reasons). Is there a way I can inspect symbol files or force the compiler to emit an error when that API is used?

13
  • Kind of crazy answer for a kind of crazy question: temporarily move the header file for iostream before invoking the compiler. Move it back when the compiler finishes. (This could all be stuffed into the Makefile.) Commented Aug 12, 2013 at 1:38
  • 8
    Okay, here's a real solution. Use the methods here: stackoverflow.com/questions/2726993/… to add a new path to your g++ linking list. Include an iostream header there but make sure there's a syntax error (or something) in that header. Whenever it's included it'll blow up compilation. Commented Aug 12, 2013 at 1:40
  • 3
    To improve @skishore solution with headers: use #error preprocessor directive to give friendly error message. Commented Aug 12, 2013 at 1:42
  • 2
    Only iostreams or istreams and ostreams as well? Just the cin and cout objects (as well as cerr and the wide stream variants) or even instances from the stringstream and fstream variants? Commented Aug 12, 2013 at 2:09
  • 1
    What if <string> includes <iostream> ? C++ has no restrictions on that, unlike C, and skishore's solution would break that. Commented Aug 12, 2013 at 8:15

5 Answers 5

35

A simple approach is provide a dummy iostream implementation that does nothing but throw a compile-time error.

The following example assumes a GCC toolchain - I imagine the process is similar with other compilers.

First, create your dummy iostream file:

#error 'Use of iostream is prohibited' 

Some dummy application code to demonstrate:

#include <iostream> int main (int argc, char** argv) { std::cout << "foo!"; return 0; } 

Compile as follows (assuming the dummy iostream and main.cpp are in the working directory):

g++ -I. main.cpp 

Compilation fails with the following errors:

In file included from main.cpp:2:0: ./iostream:1:2: error: #error 'Use of iostream is prohibited' main.cpp: In function 'int main(int, char**)': main.cpp:4:2: error: 'cout' is not a member of 'std' 

Added bonus: symbols usually declared in that file (e.g. cout here) are undefined, and so get flagged in the compiler output as well. As such, you also get pointers to exactly where you're using your prohibited API.

UPDATE: Instructions for Visual C++ 2012.

As @RaymondChen points out in the comments below, a solution tailored to Visual C++ is likely more useful to the OP. As such, the following outlines the process I went through to achieve the same as the above under Visual C++ 2012.

First, create a new console project, using the above C++ code. Also create the dummy iostream header I described above, and place it in a directory somewhere easy to find (I put mine in the main project source directory).

Now, in the Solution Explorer, right click on the project node and select "Properties" from the drop-down list. In the dialog that appears, select "VC++ Directories" from the tree on the left. Prepend the directory containing the dummy iostream file into the list of include directories that appears on the right, separated from the other directories with a semicolon:

Adding new path to include directories list

Here, my project was called TestApp1, and I just prepended its main directory to the $(IncludePath) that was already there. Note that it is important to prepend rather than append - the order of the directories in the list determines the search order, so if $(IncludePath) appears before your custom directory, the system header will be used in preference to your dummy header - not what you want.

Click OK, and rebuild the project.

For me, doing so resulted in the following errors in the VC++ console (edited slightly for brevity):

error C1189: #error : 'Use of iostream is prohibited' IntelliSense: #error directive: 'Use of iostream is prohibited' IntelliSense: namespace "std" has no member "cout" 

Note that IntelliSense also picks up the (now) illegal use of cout - it is highlighted with an error mark in the editor.

Sign up to request clarification or add additional context in comments.

9 Comments

-1 for no std:: before cout. And seriously, please don't add a using.
This would generate false positives in code which includes iostream but doesn't use any streams. It also fails to catch code which manually declares std::cout.
@DeadMG: really? A -1 for something completely irrelevant to the question? You're right, it should be there (and is, now), but I hardly think its worth giving up on the whole answer for.
The compiler is not required to implement standard header file inclusions via files at all - in fact #include <iostream> doesn't even require that a file called iostream exist or even be used. The compiler could, in theory, sprinkle the magic sauce from a database, or the Internet, or via telepathy.
@NikBougalis: I'm very aware that a compiler is not required to implement standard includes however it wants. That's why, when I gave my example in GCC, I added a disclaimer indicating that it may not work in exactly the same way with other compilers. For what it's worth, this absolutely does work with GCC - I did test it before I posted it.
|
7

This is a nasty hack, but it should work.

The C standard (and consequently the C++ standard as well) allows preprocessor tokens in #include directives. This is also known as "computed includes".

Thus, adding something like -Diostream to CFLAGS inside your makefile (or to compiler options in your IDE's project settings) should reliably break the build if someone tries to use iostream.

Of course, with an empty macro, the error message will not be very informative, but you could instead use something like -Diostream=DUDE_DONT_USE_IOSTREAM, which will show an error like: DUDE_DONT_USE_IOSTREAM: file not found.

It's also something that you can turn off again without much hassle if you change your mind later. Just remove the build option.

5 Comments

-1 for compiler-specific hack when the OP doesn't mention his compiler.
Really, DeadMG? Sure enough your compiler understands /D if it doesn't understand -D. Are you trying to find a more ridiculous downvote than the one above in the answer by @Mac or what's the issue with you today? :)
Even if the compiler doesn't support neither -D not /D then you can just put #define iostream DUDE_DONT_USE_IOSTREAM on top of file if I understand the answer correctly.
@MaciejPiechotka: Yes indeed. :) But of course then you would have to do it in every file, using -D in the makefile (or in project settings) is much nicer, less intrusive (although it still uses the preprocessor) and more comfortable.
@Damon I was more replying to the DeadMG complain that this can be done in 'portable' way.
5

Your idea to inspect symbol files is feasible and very realistic. virtual ~ios_base(); is a single method that all streams will inherit, and which can't easily be inlined since it's virtual and non-trivial. Its presence in an object file is therefore a very strong indication of IOstream use.

Comments

0

In addition to compiler-assist method mentioned by Mac you can use generic search functions. For example (I assume zsh shell - for bash doesn't have ** and on Windows you need to find how to do it with Powershell):

 # Find all mentioning on `iostream` `cin` in all files ending in cc in all subdirectories of current directory grep iostream **/*.c grep cin **/*.cc 

If you don't want to/can't use command line you can use your favourite editor and search for unwanted symbols.

I usually combine both methods:

  • Compilation, especially of large project with large number of templates, is slow while searching is fast so you're more productive with search
  • On the other hand search operates is not exact and might miss something. So I'd use header tricks to verify solution done in previous step
  • As final verification you can search for symbols after compilation. It is especially useful if you compile with no optimization. You can use objdump or similar (depending on platform) and watch for imported symbol (this works if you don't say link statically to something using iostreams).

1 Comment

@DeadMG: And? C++ is in text files, grep operates on grep files ergo grep can operate on C++. In similar way git's not C++ yet I use it when working on C++ project.
-12

No, not at all. For a very limited subset, you could provide your own definitions, causing the linker to error at the duplicates. This would be very undefined behaviour though. And a good portion is templates that aren't susceptible to this. Without doing drastic things like deleting the iostream header, or using a compiler like Clang and modifying the source code, there's really nothing you can do.

2 Comments

Haven't downvoted you, but AFAIK disabling features built-in to the language is already out of C++ standard's scope, so mentioning that doing X to disable Y built-in into language may be UB is sort of counter-productive. Also, this answer seems to be more like a long comment rather than an actual answer.
You gave a stupid reason to downvote each answer in this question, just so people would upvote your answer. Well, I think that backfired a little bit.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.