Changes between Initial Version and Version 1 of Programming/Cpp/StandardLibraryGotchaIncludeHeadersExplicitly


Ignore:
Timestamp:
Feb 19, 2016, 4:01:30 PM (8 years ago)
Author:
Vijay Varadan
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • Programming/Cpp/StandardLibraryGotchaIncludeHeadersExplicitly

    v1 v1  
     1= C++ Standard Library Gotcha: Include Headers Explicitly =
     2Jan 24 2016
     3
     4[[Image(htdocs:images/cpp/tcpppl.jpg, alt="tcpppl", align=center)]]
     5
     6I was using the `boost::log` library component and when compiling my code, I ran into a rather odd error converting a derived shared pointer to a base class shared pointer, i.e. implicitly converting a **`shared_ptr<std::ofstream>`** to a **`const shared_ptr<std::ostream> &amp;`**. Here's the code snippet, with the line causing the compiler error highlighed. Note that code has been simplified for clarity.
     7
     8{{{#!cpp
     9using namespace boost::log;
     10
     11typedef sinks::asynchoronous_sink<
     12    sinks::text_ostream_backend> TextSink;
     13
     14bool Initialize(const std::string &amp;logFile)
     15{
     16    bool didSucceed = false;
     17
     18    try
     19    {
     20        auto sink = boost::make_shared<TextSink>();
     21        auto lfstream 
     22            = boost::make_shared<std::ofstream>(logFile);
     23        sink->locked_backend()->add_stream(lfstream); // <-- COMPILER ERROR HERE
     24        core::get()->add_sink(sink);
     25        BOOST_LOG(lgr_gtl) << "LogSystem initialized...";
     26        core::get()->flush();
     27 
     28        didSucceed = true;
     29    }
     30    catch (const std::exception& ex)
     31    {
     32        std::cout
     33            << "LogSystem::Initialize() failed."
     34            << std::endl;
     35        std::cout << "Details:" << std::endl;
     36        std::cout << ex.what() << std::endl;
     37    }
     38    return didSucceed;
     39}
     40}}}
     41
     42The compiler, Visual Studio 2015 Update 1, spat out the error pointing to the line above marked with
     43the comment `COMPILER ERROR HERE`:
     44
     45{{{#!cpp
     46error C2664: 'void boost::log::v2s_mt_nt6::sinks::basic_text_ostream_backend<char>::add_stream(const boost::shared_ptr<std::basic_ostream<char,std::char_traits<char>>> &amp;)': cannot convert argument 1 from 'boost::shared_ptr<T>' to 'const boost::shared_ptr<std::basic_ostream<char,std::char_traits<char>>> &amp;'
     47    with
     48    [
     49        T=std::ofstream
     50    ]
     51note: Reason: cannot convert from 'boost::shared_ptr<T>' to 'const boost::shared_ptr<std::basic_ostream<char,std::char_traits<char>>>'
     52    with
     53    [
     54        T=std::ofstream
     55    ]
     56note: No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
     57}}}
     58
     59I was flummoxed. `std::ofstream` is a typedef for `std::basic_ofstream`, which is a class derived from `basic_ostream`. So, there should be no reason why the error should be thrown. The conversion from non-const to const shouldn't be an issue either, though it's a red herring and would cause most folks to look at that first and maybe add a const qualifier. Adding the const qualifier won't make a jot of difference.
     60
     61I looked up the definition of `std::ofstream` and sure enough, it looked like this:
     62
     63{{{#!cpp
     64typedef basic_ofstream<char, char_traits<char> > ofstream;
     65}}}
     66
     67and a little further up in the same file is a forward declaration for basic_ofstream, which looks like this:
     68
     69{{{#!cpp
     70template<class _Elem, class _Traits = char_traits<_Elem> >
     71    class basic_ofstream;
     72}}}
     73
     74The one odd thing was that Intellisense found this in the `iosfwd` header file and was a ''forward declaration''. But I wanted the ''definition'', which Intellisense stubbornly refused to show me. So, I tried finding all references which only yielded the earlier mentioned ofstream definition basic_ostream forward declaration. Even more odd.
     75
     76The standard says `std::ofstream` is defined in the `fstream` header. So, I cracked that open to find the definition of `std::basic_ofstream`, which was just as I had expected:
     77
     78{{{#!cpp
     79template<class _Elem, class _Traits> class basic_ofstream
     80    : public basic_ostream<_Elem, _Traits>
     81{ // output stream associated with a C stream
     82...
     83...
     84...
     85};
     86}}}
     87
     88Now, I didn't `#include <iosfwd>` explicitly anywhere. It was getting included by one of the standard C++ headers or one of the boost headers. MSDN says, of iosfwd:
     89
     90>    ''Declares forward references to several template classes used throughout iostreams. All such template classes are defined in other standard headers. You include this header explicitly only when you need one of its declarations, but not its definition.''
     91
     92I figured, more along the lines of "Let me throw this at the wall and see if it sticks.", that I should include the fstream header explicitly. In hindsight, seeing that Intellisense could only show me the forward declaration, it should've been the first thing to try.
     93
     94So, I explicitly added a `#include <fstream>` to my code. Voilà! The error disappeared and the code compiled just fine.
     95
     96It's a tad irritating (Who am I kidding? It's outright frustrating.) when the compiler throws out an error like the one above, but the definitions and declarations seem to be present - yeah, in this case a forward declaration only, but I would expect the compiler to complain that it didn't have enough information (in this case the ''definition'') to determine whether `ofstream` / `basic_ofstream` was derived from `basic_ostream`.
     97
     98Lesson learned: Explicitly include headers that contain the definitions, do not rely on indirect inclusions.