mirror of
https://github.com/boostorg/nowide.git
synced 2026-02-22 03:22:32 +00:00
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.
This commit is contained in:
@@ -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<typename CharType, typename Traits>
|
||||
struct stream_base{
|
||||
typedef std::basic_istream<CharType, Traits> type;
|
||||
};
|
||||
};
|
||||
struct StreamTypeOut
|
||||
{
|
||||
static std::ios_base::openmode mode() { return std::ios_base::out; }
|
||||
static std::ios_base::openmode mode_modifier() { return mode(); }
|
||||
template<typename CharType, typename Traits>
|
||||
struct stream_base{
|
||||
typedef std::basic_ostream<CharType, Traits> 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<typename CharType, typename Traits>
|
||||
struct stream_base{
|
||||
typedef std::basic_iostream<CharType, Traits> 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<typename T_Basic_FStream, typename T_StreamType>
|
||||
@@ -52,31 +65,26 @@ namespace nowide {
|
||||
/// \brief Same as std::basic_ifstream<char> but accepts UTF-8 strings under Windows
|
||||
///
|
||||
template<typename CharType, typename Traits = std::char_traits<CharType> >
|
||||
class basic_ifstream : public detail::fstream_impl<basic_ifstream<CharType, Traits>, detail::StreamTypeIn>,
|
||||
public std::basic_istream<CharType, Traits>
|
||||
class basic_ifstream : public detail::fstream_impl<basic_ifstream<CharType, Traits>, detail::StreamTypeIn>
|
||||
{
|
||||
typedef detail::fstream_impl<basic_ifstream, detail::StreamTypeIn> fstream_impl;
|
||||
typedef std::basic_istream<CharType, Traits> 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<typename Path>
|
||||
explicit basic_ifstream(
|
||||
const Path& file_name,
|
||||
typename detail::enable_if_path<Path, std::ios_base::openmode>::type mode = std::ios_base::in) :
|
||||
stream_base(&this->buf_)
|
||||
typename detail::enable_if_path<Path, std::ios_base::openmode>::type mode = std::ios_base::in)
|
||||
{
|
||||
open(file_name, mode);
|
||||
}
|
||||
@@ -100,37 +107,31 @@ namespace nowide {
|
||||
///
|
||||
|
||||
template<typename CharType, typename Traits = std::char_traits<CharType> >
|
||||
class basic_ofstream : public detail::fstream_impl<basic_ofstream<CharType, Traits>, detail::StreamTypeOut>,
|
||||
public std::basic_ostream<CharType, Traits>
|
||||
class basic_ofstream : public detail::fstream_impl<basic_ofstream<CharType, Traits>, detail::StreamTypeOut>
|
||||
{
|
||||
typedef detail::fstream_impl<basic_ofstream, detail::StreamTypeOut> fstream_impl;
|
||||
typedef std::basic_ostream<CharType, Traits> 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<typename Path>
|
||||
explicit basic_ofstream(
|
||||
const Path& file_name,
|
||||
typename detail::enable_if_path<Path, std::ios_base::openmode>::type mode = std::ios_base::out) :
|
||||
stream_base(&this->buf_)
|
||||
typename detail::enable_if_path<Path, std::ios_base::openmode>::type mode = std::ios_base::out)
|
||||
{
|
||||
open(file_name, mode);
|
||||
}
|
||||
@@ -149,40 +150,34 @@ namespace nowide {
|
||||
/// \brief Same as std::basic_fstream<char> but accepts UTF-8 strings under Windows
|
||||
///
|
||||
template<typename CharType, typename Traits = std::char_traits<CharType> >
|
||||
class basic_fstream : public detail::fstream_impl<basic_fstream<CharType, Traits>, detail::StreamTypeInOut>,
|
||||
public std::basic_iostream<CharType, Traits>
|
||||
class basic_fstream : public detail::fstream_impl<basic_fstream<CharType, Traits>, detail::StreamTypeInOut>
|
||||
{
|
||||
typedef detail::fstream_impl<basic_fstream, detail::StreamTypeInOut> fstream_impl;
|
||||
typedef std::basic_iostream<CharType, Traits> 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<typename Path>
|
||||
explicit basic_fstream(const Path& file_name,
|
||||
typename detail::enable_if_path<Path, std::ios_base::openmode>::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<typename T>
|
||||
struct buf_holder
|
||||
{
|
||||
T buf_;
|
||||
};
|
||||
template<template<typename, typename> class T_Basic_FStream,
|
||||
typename CharType,
|
||||
typename Traits,
|
||||
typename T_StreamType>
|
||||
class fstream_impl<T_Basic_FStream<CharType, Traits>, T_StreamType>
|
||||
: private buf_holder<basic_filebuf<CharType, Traits> >, // must be first due to init order
|
||||
public T_StreamType::template stream_base<CharType, Traits>::type
|
||||
{
|
||||
typedef T_Basic_FStream<CharType, Traits> Underlying;
|
||||
typedef basic_filebuf<CharType, Traits> internal_buffer_type;
|
||||
typedef typename T_StreamType::template stream_base<CharType, Traits>::type stream_base;
|
||||
|
||||
Underlying* underlying()
|
||||
{
|
||||
return static_cast<Underlying*>(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<internal_buffer_type*>(&this->buf_);
|
||||
return const_cast<internal_buffer_type*>(&buf_.buf_);
|
||||
}
|
||||
|
||||
internal_buffer_type buf_;
|
||||
buf_holder<internal_buffer_type> buf_;
|
||||
};
|
||||
|
||||
/// Trait to heuristically check for a *::filesystem::path
|
||||
|
||||
Reference in New Issue
Block a user