2
0
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:
Alexander Grund
2020-02-04 17:23:00 +01:00
parent dbac66606d
commit ad77653f51

View File

@@ -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