From ad77653f517e1bbac2f0076d7fc49b84e4630ed3 Mon Sep 17 00:00:00 2001 From: Alexander Grund Date: Tue, 4 Feb 2020 17:23:00 +0100 Subject: [PATCH] Avoid casting to super class during ctor run It is undefined behavior to cast to a type for which the constructor did not finish yet. So make the impl class inherit from the std class as all calls for which a cast to the super class were required are actually to the std class. This allows moving the initialization of the std class to the impl further reducing duplicate code. --- include/boost/nowide/fstream.hpp | 114 ++++++++++++++++--------------- 1 file changed, 60 insertions(+), 54 deletions(-) diff --git a/include/boost/nowide/fstream.hpp b/include/boost/nowide/fstream.hpp index cfd7556..5941145 100644 --- a/include/boost/nowide/fstream.hpp +++ b/include/boost/nowide/fstream.hpp @@ -22,22 +22,35 @@ namespace nowide { { static std::ios_base::openmode mode() { return std::ios_base::in; } static std::ios_base::openmode mode_modifier() { return mode(); } + template + struct stream_base{ + typedef std::basic_istream type; + }; }; struct StreamTypeOut { static std::ios_base::openmode mode() { return std::ios_base::out; } static std::ios_base::openmode mode_modifier() { return mode(); } + template + struct stream_base{ + typedef std::basic_ostream type; + }; }; struct StreamTypeInOut { static std::ios_base::openmode mode() { return std::ios_base::in | std::ios_base::out; } static std::ios_base::openmode mode_modifier() { return std::ios_base::openmode(); } + template + struct stream_base{ + typedef std::basic_iostream type; + }; }; // clang-format on /// CRTP Base class for all basic_*fstream classes /// Contains basic_filebuf instance so its pointer can be used to construct basic_*stream - /// Provides common functions to reduce boilerplate code + /// Provides common functions to reduce boilerplate code including inheriting from + /// the correct std::basic_[io]stream class and initializing it /// \tparam T_StreamType One of StreamType* above. /// Class used instead of value, because openmode::operator| may not be constexpr template @@ -52,31 +65,26 @@ namespace nowide { /// \brief Same as std::basic_ifstream but accepts UTF-8 strings under Windows /// template > - class basic_ifstream : public detail::fstream_impl, detail::StreamTypeIn>, - public std::basic_istream + class basic_ifstream : public detail::fstream_impl, detail::StreamTypeIn> { typedef detail::fstream_impl fstream_impl; - typedef std::basic_istream stream_base; public: - basic_ifstream() : stream_base(&this->buf_) + basic_ifstream() {} - explicit basic_ifstream(const char* file_name, std::ios_base::openmode mode = std::ios_base::in) : - stream_base(&this->buf_) + explicit basic_ifstream(const char* file_name, std::ios_base::openmode mode = std::ios_base::in) { open(file_name, mode); } #ifdef BOOST_WINDOWS - explicit basic_ifstream(const wchar_t* file_name, std::ios_base::openmode mode = std::ios_base::in) : - stream_base(&this->buf_) + explicit basic_ifstream(const wchar_t* file_name, std::ios_base::openmode mode = std::ios_base::in) { open(file_name, mode); } #endif - explicit basic_ifstream(const std::string& file_name, std::ios_base::openmode mode = std::ios_base::in) : - stream_base(&this->buf_) + explicit basic_ifstream(const std::string& file_name, std::ios_base::openmode mode = std::ios_base::in) { open(file_name, mode); } @@ -84,8 +92,7 @@ namespace nowide { template explicit basic_ifstream( const Path& file_name, - typename detail::enable_if_path::type mode = std::ios_base::in) : - stream_base(&this->buf_) + typename detail::enable_if_path::type mode = std::ios_base::in) { open(file_name, mode); } @@ -100,37 +107,31 @@ namespace nowide { /// template > - class basic_ofstream : public detail::fstream_impl, detail::StreamTypeOut>, - public std::basic_ostream + class basic_ofstream : public detail::fstream_impl, detail::StreamTypeOut> { typedef detail::fstream_impl fstream_impl; - typedef std::basic_ostream stream_base; public: - basic_ofstream() : stream_base(&this->buf_) + basic_ofstream() {} - explicit basic_ofstream(const char* file_name, std::ios_base::openmode mode = std::ios_base::out) : - stream_base(&this->buf_) + explicit basic_ofstream(const char* file_name, std::ios_base::openmode mode = std::ios_base::out) { open(file_name, mode); } #ifdef BOOST_WINDOWS - explicit basic_ofstream(const wchar_t* file_name, std::ios_base::openmode mode = std::ios_base::out) : - stream_base(&this->buf_) + explicit basic_ofstream(const wchar_t* file_name, std::ios_base::openmode mode = std::ios_base::out) { open(file_name, mode); } #endif - explicit basic_ofstream(const std::string& file_name, std::ios_base::openmode mode = std::ios_base::out) : - stream_base(&this->buf_) + explicit basic_ofstream(const std::string& file_name, std::ios_base::openmode mode = std::ios_base::out) { open(file_name, mode); } template explicit basic_ofstream( const Path& file_name, - typename detail::enable_if_path::type mode = std::ios_base::out) : - stream_base(&this->buf_) + typename detail::enable_if_path::type mode = std::ios_base::out) { open(file_name, mode); } @@ -149,40 +150,34 @@ namespace nowide { /// \brief Same as std::basic_fstream but accepts UTF-8 strings under Windows /// template > - class basic_fstream : public detail::fstream_impl, detail::StreamTypeInOut>, - public std::basic_iostream + class basic_fstream : public detail::fstream_impl, detail::StreamTypeInOut> { typedef detail::fstream_impl fstream_impl; - typedef std::basic_iostream stream_base; public: - basic_fstream() : stream_base(&this->buf_) + basic_fstream() {} explicit basic_fstream(const char* file_name, - std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) : - stream_base(&this->buf_) + std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) { open(file_name, mode); } #ifdef BOOST_WINDOWS explicit basic_fstream(const wchar_t* file_name, - std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) : - stream_base(&this->buf_) + std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) { open(file_name, mode); } #endif explicit basic_fstream(const std::string& file_name, - std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) : - stream_base(&this->buf_) + std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) { open(file_name, mode); } template explicit basic_fstream(const Path& file_name, typename detail::enable_if_path::type mode = - std::ios_base::in | std::ios_base::out) : - stream_base(&this->buf_) + std::ios_base::in | std::ios_base::out) { open(file_name, mode); } @@ -218,21 +213,32 @@ namespace nowide { // Implementation namespace detail { + /// Holds an instance of T + /// Required to make sure this is constructed first before passing it to sibling classes + template + struct buf_holder + { + T buf_; + }; template class T_Basic_FStream, typename CharType, typename Traits, typename T_StreamType> class fstream_impl, T_StreamType> + : private buf_holder >, // must be first due to init order + public T_StreamType::template stream_base::type { - typedef T_Basic_FStream Underlying; typedef basic_filebuf internal_buffer_type; + typedef typename T_StreamType::template stream_base::type stream_base; - Underlying* underlying() - { - return static_cast(this); - } + public: + using stream_base::setstate; + using stream_base::clear; protected: + fstream_impl() : stream_base(rdbuf()) + {} + void open(const std::string& file_name, std::ios_base::openmode mode = T_StreamType::mode()) { open(file_name.c_str(), mode); @@ -245,40 +251,40 @@ namespace nowide { } void open(const char* file_name, std::ios_base::openmode mode = T_StreamType::mode()) { - if(!buf_.open(file_name, mode | T_StreamType::mode_modifier())) - underlying()->setstate(std::ios_base::failbit); + if(!rdbuf()->open(file_name, mode | T_StreamType::mode_modifier())) + setstate(std::ios_base::failbit); else - underlying()->clear(); + clear(); } #ifdef BOOST_WINDOWS void open(const wchar_t* file_name, std::ios_base::openmode mode = T_StreamType::mode()) { - if(!buf_.open(file_name, mode | T_StreamType::mode_modifier())) - underlying()->setstate(std::ios_base::failbit); + if(!rdbuf()->open(file_name, mode | T_StreamType::mode_modifier())) + setstate(std::ios_base::failbit); else - underlying()->clear(); + clear(); } #endif bool is_open() { - return buf_.is_open(); + return rdbuf()->is_open(); } bool is_open() const { - return buf_.is_open(); + return rdbuf()->is_open(); } void close() { - if(!buf_.close()) - underlying()->setstate(std::ios_base::failbit); + if(!rdbuf()->close()) + setstate(std::ios_base::failbit); } internal_buffer_type* rdbuf() const { - return const_cast(&this->buf_); + return const_cast(&buf_.buf_); } - internal_buffer_type buf_; + buf_holder buf_; }; /// Trait to heuristically check for a *::filesystem::path