29#ifndef NOT_ENOUGH_STANDARDS_PIPE
30#define NOT_ENOUGH_STANDARDS_PIPE
37#ifndef WIN32_LEAN_AND_MEAN
38#define WIN32_LEAN_AND_MEAN
41#elif defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))
48#error "Not enough standards does not support this environment."
60#if defined(NES_WIN32_PIPE)
65 constexpr const char pipe_root[] =
"\\\\.\\pipe\\";
67 template<
typename CharT,
typename Traits>
68 class basic_pipe_istream;
69 template<
typename CharT,
typename Traits>
70 class basic_pipe_ostream;
71 template<
typename CharT =
char,
typename Traits = std::
char_traits<CharT>>
72 std::pair<basic_pipe_istream<CharT, Traits>, basic_pipe_ostream<CharT, Traits>> make_anonymous_pipe();
74 template<
typename CharT,
typename Traits = std::
char_traits<CharT>>
75 class basic_pipe_streambuf :
public std::basic_streambuf<CharT, Traits>
78 using parent_type = std::basic_streambuf<CharT, Traits>;
81 using char_type = CharT;
82 using traits_type = Traits;
83 using int_type =
typename Traits::int_type;
84 using pos_type =
typename Traits::pos_type;
85 using off_type =
typename Traits::off_type;
88 static constexpr std::size_t buf_size{ 1024 };
91 basic_pipe_streambuf() =
default;
93 explicit basic_pipe_streambuf(
const std::string& name, std::ios_base::openmode mode)
98 virtual ~basic_pipe_streambuf()
103 basic_pipe_streambuf(
const basic_pipe_streambuf&) =
delete;
104 basic_pipe_streambuf& operator=(
const basic_pipe_streambuf&) =
delete;
106 basic_pipe_streambuf(basic_pipe_streambuf&& other) noexcept
107 :parent_type{ other }
108 , m_buffer{ std::move(other.m_buffer) }
109 , m_handle{ std::exchange(other.m_handle, INVALID_HANDLE_VALUE) }
110 , m_mode{ std::exchange(other.m_mode, std::ios_base::openmode{}) }
115 basic_pipe_streambuf& operator=(basic_pipe_streambuf&& other)
noexcept
117 parent_type::operator=(other);
118 m_buffer = std::move(other.m_buffer);
119 m_handle = std::exchange(other.m_handle, m_handle);
120 m_mode = std::exchange(other.m_mode, m_mode);
125 bool open(
const std::string& name, std::ios_base::openmode mode)
127 assert(!((mode & std::ios_base::in) && (mode & std::ios_base::out)) &&
"nes::basic_pipe_streambuf::open called with mode = std::ios_base::in | std::ios_base::out.");
131 const auto native_name{ to_wide(pipe_root + name) };
132 DWORD native_mode{ mode & std::ios_base::in ? GENERIC_READ : GENERIC_WRITE };
134 HANDLE handle = CreateFileW(std::data(native_name), native_mode, 0,
nullptr, OPEN_EXISTING, 0,
nullptr);
135 if (handle == INVALID_HANDLE_VALUE)
137 if (GetLastError() == ERROR_FILE_NOT_FOUND)
139 native_mode = mode & std::ios_base::in ? PIPE_ACCESS_INBOUND : PIPE_ACCESS_OUTBOUND;
141 handle = CreateNamedPipeW(std::data(native_name), native_mode, PIPE_READMODE_BYTE | PIPE_WAIT, 1, buf_size, buf_size, 0,
nullptr);
142 if (handle == INVALID_HANDLE_VALUE)
145 if (!ConnectNamedPipe(handle,
nullptr))
153 m_buffer.resize(buf_size);
154 parent_type::setp(std::data(m_buffer), std::data(m_buffer) + buf_size);
161 bool is_open() const noexcept
163 return m_handle != INVALID_HANDLE_VALUE;
172 m_mode = std::ios_base::openmode{};
173 CloseHandle(std::exchange(m_handle, INVALID_HANDLE_VALUE));
174 parent_type::setp(
nullptr,
nullptr);
175 parent_type::setg(
nullptr,
nullptr,
nullptr);
180 friend class process;
181 friend std::pair<basic_pipe_istream<char_type, traits_type>, basic_pipe_ostream<char_type, traits_type>> make_anonymous_pipe<char_type, traits_type>();
183 basic_pipe_streambuf(HANDLE handle, std::ios_base::openmode mode)
187 m_buffer.resize(buf_size);
188 parent_type::setp(std::data(m_buffer), std::data(m_buffer) + buf_size);
192 virtual int sync()
override
194 if (m_mode & std::ios_base::out)
196 const std::ptrdiff_t count{ parent_type::pptr() - parent_type::pbase() };
199 if (!WriteFile(m_handle,
reinterpret_cast<const CHAR*
>(std::data(m_buffer)),
static_cast<DWORD
>(count) *
sizeof(char_type), &written,
nullptr))
202 parent_type::setp(std::data(m_buffer), std::data(m_buffer) + buf_size);
208 virtual int_type overflow(int_type c = traits_type::eof())
override
210 assert(m_mode & std::ios_base::out &&
"Write operation on a read only pipe.");
212 if (traits_type::eq_int_type(c, traits_type::eof()))
215 if (!WriteFile(m_handle,
reinterpret_cast<const CHAR*
>(std::data(m_buffer)),
static_cast<DWORD
>(buf_size) *
sizeof(char_type), &written,
nullptr))
216 return traits_type::eof();
218 parent_type::setp(std::data(m_buffer), std::data(m_buffer) + buf_size);
222 *parent_type::pptr() = traits_type::to_char_type(c);
223 parent_type::pbump(1);
226 return traits_type::not_eof(c);
229 virtual std::streamsize xsputn(
const char_type* s, std::streamsize count)
override
231 assert(m_mode & std::ios_base::out &&
"Write operation on a read only pipe.");
234 if (!WriteFile(m_handle,
reinterpret_cast<const CHAR*
>(s),
static_cast<DWORD
>(count) *
sizeof(char_type), &written,
nullptr))
237 return static_cast<std::streamsize
>(written);
240 virtual int_type underflow()
override
242 assert(m_mode & std::ios_base::in &&
"Read operation on a write only pipe.");
244 if (parent_type::gptr() == parent_type::egptr())
247 if (!ReadFile(m_handle,
reinterpret_cast<CHAR*
>(std::data(m_buffer)),
static_cast<DWORD
>(buf_size *
sizeof(char_type)), &readed,
nullptr) || readed == 0)
248 return traits_type::eof();
250 parent_type::setg(std::data(m_buffer), std::data(m_buffer), std::data(m_buffer) + (readed /
sizeof(char_type)));
253 return traits_type::to_int_type(*parent_type::gptr());
256 virtual std::streamsize xsgetn(char_type* s, std::streamsize count)
override
258 assert(m_mode & std::ios_base::in &&
"Read operation on a write only pipe.");
261 if (!ReadFile(m_handle,
reinterpret_cast<CHAR*
>(s),
static_cast<DWORD
>(count) *
sizeof(char_type), &readed,
nullptr))
264 return static_cast<std::streamsize
>(readed /
sizeof(char_type));
268 std::wstring to_wide(std::string path)
270 assert(std::size(path) < 0x7FFFFFFFu &&
"Wrong path.");
272 if (std::empty(path))
275 std::transform(std::begin(path), std::end(path), std::begin(path), [](
char c) {
return c ==
'/' ?
'\\' : c; });
277 std::wstring out_path{};
278 out_path.resize(
static_cast<std::size_t
>(MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, std::data(path),
static_cast<int>(std::size(path)),
nullptr, 0)));
280 if (!MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, std::data(path),
static_cast<int>(std::size(path)),
282 static_cast<int>(std::size(out_path))))
283 throw std::runtime_error{
"Failed to convert the path to wide." };
289 std::vector<CharT> m_buffer{};
290 HANDLE m_handle{ INVALID_HANDLE_VALUE };
291 std::ios_base::openmode m_mode{};
295 template<
typename CharT,
typename Traits = std::
char_traits<CharT>>
296 class basic_pipe_istream :
public std::basic_istream<CharT, Traits>
299 using parent_type = std::basic_istream<CharT, Traits>;
302 using char_type = CharT;
303 using traits_type = Traits;
304 using int_type =
typename Traits::int_type;
305 using pos_type =
typename Traits::pos_type;
306 using off_type =
typename Traits::off_type;
309 basic_pipe_istream() =
default;
311 explicit basic_pipe_istream(
const std::string& name, std::ios_base::openmode mode = std::ios_base::in)
312 :parent_type{ nullptr }
314 parent_type::rdbuf(m_buffer.get());
318 virtual ~basic_pipe_istream() =
default;
320 basic_pipe_istream(
const basic_pipe_istream&) =
delete;
321 basic_pipe_istream& operator=(
const basic_pipe_istream&) =
delete;
323 basic_pipe_istream(basic_pipe_istream&& other) noexcept
324 :parent_type{ std::move(other) }
326 std::swap(m_buffer, other.m_buffer);
327 parent_type::rdbuf(m_buffer.get());
330 basic_pipe_istream& operator=(basic_pipe_istream&& other)
noexcept
332 parent_type::operator=(std::move(other));
333 std::swap(m_buffer, other.m_buffer);
335 parent_type::rdbuf(m_buffer.get());
340 void open(
const std::string& name, std::ios_base::openmode mode = std::ios_base::in)
342 m_buffer->open(name, mode);
343 parent_type::clear(m_buffer->is_open() ? std::ios_base::goodbit : std::ios_base::failbit);
346 bool is_open() const noexcept
348 return m_buffer->is_open();
356 basic_pipe_streambuf<char_type, traits_type>* rdbuf() const noexcept
358 return m_buffer.get();
362 friend class process;
363 friend std::pair<basic_pipe_istream<char_type, traits_type>, basic_pipe_ostream<char_type, traits_type>> make_anonymous_pipe<char_type, traits_type>();
365 basic_pipe_istream(basic_pipe_streambuf<char_type, traits_type> buffer)
366 :parent_type{ nullptr }
367 , m_buffer{ std::make_unique<basic_pipe_streambuf<char_type, traits_type>>(std::move(buffer)) }
369 parent_type::rdbuf(m_buffer.get());
373 std::unique_ptr<basic_pipe_streambuf<char_type, traits_type>> m_buffer{ std::make_unique<basic_pipe_streambuf<char_type, traits_type>>() };
376 template<
typename CharT,
typename Traits = std::
char_traits<CharT>>
377 class basic_pipe_ostream :
public std::basic_ostream<CharT, Traits>
380 using parent_type = std::basic_ostream<CharT, Traits>;
383 using char_type = CharT;
384 using traits_type = Traits;
385 using int_type =
typename Traits::int_type;
386 using pos_type =
typename Traits::pos_type;
387 using off_type =
typename Traits::off_type;
390 basic_pipe_ostream() =
default;
392 explicit basic_pipe_ostream(
const std::string& name, std::ios_base::openmode mode = std::ios_base::out)
393 :parent_type{ nullptr }
395 parent_type::rdbuf(m_buffer.get());
399 virtual ~basic_pipe_ostream() =
default;
401 basic_pipe_ostream(
const basic_pipe_ostream&) =
delete;
402 basic_pipe_ostream& operator=(
const basic_pipe_ostream&) =
delete;
404 basic_pipe_ostream(basic_pipe_ostream&& other) noexcept
405 :parent_type{ std::move(other) }
407 std::swap(m_buffer, other.m_buffer);
408 parent_type::rdbuf(m_buffer.get());
411 basic_pipe_ostream& operator=(basic_pipe_ostream&& other)
noexcept
413 parent_type::operator=(std::move(other));
414 std::swap(m_buffer, other.m_buffer);
416 parent_type::rdbuf(m_buffer.get());
421 void open(
const std::string& name, std::ios_base::openmode mode = std::ios_base::out)
423 m_buffer->open(name, mode);
424 parent_type::clear(m_buffer->is_open() ? std::ios_base::goodbit : std::ios_base::failbit);
427 bool is_open() const noexcept
429 return m_buffer->is_open();
437 basic_pipe_streambuf<char_type, traits_type>* rdbuf() const noexcept
439 return m_buffer.get();
443 friend class process;
444 friend std::pair<basic_pipe_istream<char_type, traits_type>, basic_pipe_ostream<char_type, traits_type>> make_anonymous_pipe<char_type, traits_type>();
446 basic_pipe_ostream(basic_pipe_streambuf<char_type, traits_type> buffer)
447 :parent_type{ nullptr }
448 , m_buffer{ std::make_unique<basic_pipe_streambuf<char_type, traits_type>>(std::move(buffer)) }
450 parent_type::rdbuf(m_buffer.get());
454 std::unique_ptr<basic_pipe_streambuf<char_type, traits_type>> m_buffer{ std::make_unique<basic_pipe_streambuf<char_type, traits_type>>() };
457 template<
typename CharT,
typename Traits>
458 std::pair<basic_pipe_istream<CharT, Traits>, basic_pipe_ostream<CharT, Traits>> make_anonymous_pipe()
463 if (!CreatePipe(&input, &output,
nullptr, 0))
464 throw std::runtime_error{
"Failed to create pipe" };
466 return std::make_pair(basic_pipe_istream<CharT, Traits>{basic_pipe_streambuf<CharT, Traits>{input, std::ios_base::in}},
467 basic_pipe_ostream<CharT, Traits>{basic_pipe_streambuf<CharT, Traits>{output, std::ios_base::out}});
470 using pipe_streambuf = basic_pipe_streambuf<char>;
471 using pipe_istream = basic_pipe_istream<char>;
472 using pipe_ostream = basic_pipe_ostream<char>;
476#elif defined(NES_POSIX_PIPE)
482 constexpr const char pipe_root[] =
"/tmp/";
484 template<
typename CharT,
typename Traits>
485 class basic_pipe_istream;
486 template<
typename CharT,
typename Traits>
487 class basic_pipe_ostream;
488 template<
typename CharT =
char,
typename Traits = std::
char_traits<CharT>>
489 std::pair<basic_pipe_istream<CharT, Traits>, basic_pipe_ostream<CharT, Traits>> make_anonymous_pipe();
491 template<
typename CharT,
typename Traits = std::
char_traits<CharT>>
492 class basic_pipe_streambuf :
public std::basic_streambuf<CharT, Traits>
495 using parent_type = std::basic_streambuf<CharT, Traits>;
498 using char_type = CharT;
499 using traits_type = Traits;
500 using int_type =
typename Traits::int_type;
501 using pos_type =
typename Traits::pos_type;
502 using off_type =
typename Traits::off_type;
505 static constexpr std::size_t buf_size{ 1024 };
508 basic_pipe_streambuf() =
default;
510 explicit basic_pipe_streambuf(
const std::string& name, std::ios_base::openmode mode)
511 :parent_type{ nullptr }
516 virtual ~basic_pipe_streambuf()
521 basic_pipe_streambuf(
const basic_pipe_streambuf&) =
delete;
522 basic_pipe_streambuf& operator=(
const basic_pipe_streambuf&) =
delete;
524 basic_pipe_streambuf(basic_pipe_streambuf&& other) noexcept
525 :parent_type{ std::move(other) }
526 , m_buffer{ std::move(other.m_buffer) }
527 , m_handle{ std::exchange(other.m_handle, 0) }
528 , m_mode{ std::exchange(other.m_mode, std::ios_base::openmode{}) }
533 basic_pipe_streambuf& operator=(basic_pipe_streambuf&& other)
noexcept
535 parent_type::operator=(std::move(other));
536 m_buffer = std::move(other.m_buffer);
537 m_handle = std::exchange(other.m_handle, m_handle);
538 m_mode = std::exchange(other.m_mode, m_mode);
543 bool open(
const std::string& name, std::ios_base::openmode mode)
545 assert(!((mode & std::ios_base::in) && (mode & std::ios_base::out)) &&
"nes::basic_pipe_streambuf::open called with mode = std::ios_base::in | std::ios_base::out.");
549 const auto native_name{ pipe_root + name };
550 if (mkfifo(std::data(native_name), 0660) != 0 && errno != EEXIST)
553 const int native_mode{ mode & std::ios_base::in ? O_RDONLY : O_WRONLY };
554 int handle = ::open(std::data(native_name), native_mode);
558 m_buffer.resize(buf_size);
559 parent_type::setp(std::data(m_buffer), std::data(m_buffer) + buf_size);
566 bool is_open() const noexcept
577 m_mode = std::ios_base::openmode{};
578 ::close(std::exchange(m_handle, 0));
579 parent_type::setp(
nullptr,
nullptr);
580 parent_type::setg(
nullptr,
nullptr,
nullptr);
585 friend class process;
586 friend std::pair<basic_pipe_istream<char_type, traits_type>, basic_pipe_ostream<char_type, traits_type>> make_anonymous_pipe<char_type, traits_type>();
588 basic_pipe_streambuf(
int handle, std::ios_base::openmode mode)
592 m_buffer.resize(buf_size);
593 parent_type::setp(std::data(m_buffer), std::data(m_buffer) + buf_size);
597 virtual int sync()
override
599 if (m_mode & std::ios_base::out)
601 const std::ptrdiff_t count{ parent_type::pptr() - parent_type::pbase() };
603 if (write(m_handle,
reinterpret_cast<const char*
>(std::data(m_buffer)), count *
sizeof(char_type)) < 0)
606 parent_type::setp(std::data(m_buffer), std::data(m_buffer) + buf_size);
612 virtual int_type overflow(int_type c = traits_type::eof())
override
614 assert(m_mode & std::ios_base::out &&
"Write operation on a read only pipe.");
616 if (traits_type::eq_int_type(c, traits_type::eof()))
618 if (write(m_handle,
reinterpret_cast<const char*
>(std::data(m_buffer)), std::size(m_buffer) *
sizeof(char_type)))
619 return traits_type::eof();
621 parent_type::setp(std::data(m_buffer), std::data(m_buffer) + buf_size);
625 *parent_type::pptr() = traits_type::to_char_type(c);
626 parent_type::pbump(1);
629 return traits_type::not_eof(c);
632 virtual std::streamsize xsputn(
const char_type* s, std::streamsize count)
override
634 assert(m_mode & std::ios_base::out &&
"Write operation on a read only pipe.");
636 const auto written = write(m_handle,
reinterpret_cast<const char*
>(s), count *
sizeof(char_type));
640 return static_cast<std::streamsize
>(written);
643 virtual int_type underflow()
override
645 assert(m_mode & std::ios_base::in &&
"Read operation on a write only pipe.");
647 if (parent_type::gptr() == parent_type::egptr())
649 const auto readed = read(m_handle,
reinterpret_cast<char*
>(std::data(m_buffer)), buf_size *
sizeof(char_type));
651 return traits_type::eof();
653 parent_type::setg(std::data(m_buffer), std::data(m_buffer), std::data(m_buffer) + (readed /
sizeof(char_type)));
656 return traits_type::to_int_type(*parent_type::gptr());
659 virtual std::streamsize xsgetn(char_type* s, std::streamsize count)
override
661 assert(m_mode & std::ios_base::in &&
"Read operation on a write only pipe.");
663 const auto readed = read(m_handle,
reinterpret_cast<char*
>(s), count *
sizeof(char_type));
667 return static_cast<std::streamsize
>(readed /
sizeof(char_type));
671 std::vector<CharT> m_buffer{};
673 std::ios_base::openmode m_mode{};
676 template<
typename CharT,
typename Traits = std::
char_traits<CharT>>
677 class basic_pipe_istream :
public std::basic_istream<CharT, Traits>
680 using parent_type = std::basic_istream<CharT, Traits>;
683 using char_type = CharT;
684 using traits_type = Traits;
685 using int_type =
typename Traits::int_type;
686 using pos_type =
typename Traits::pos_type;
687 using off_type =
typename Traits::off_type;
690 basic_pipe_istream() =
default;
692 explicit basic_pipe_istream(
const std::string& name, std::ios_base::openmode mode = std::ios_base::in)
693 :parent_type{ nullptr }
695 parent_type::rdbuf(m_buffer.get());
699 virtual ~basic_pipe_istream() =
default;
701 basic_pipe_istream(
const basic_pipe_istream&) =
delete;
702 basic_pipe_istream& operator=(
const basic_pipe_istream&) =
delete;
704 basic_pipe_istream(basic_pipe_istream&& other) noexcept
705 :parent_type{ std::move(other) }
707 std::swap(m_buffer, other.m_buffer);
708 parent_type::rdbuf(m_buffer.get());
711 basic_pipe_istream& operator=(basic_pipe_istream&& other)
noexcept
713 parent_type::operator=(std::move(other));
714 std::swap(m_buffer, other.m_buffer);
716 parent_type::rdbuf(m_buffer.get());
721 void open(
const std::string& name, std::ios_base::openmode mode = std::ios_base::in)
723 m_buffer->open(name, mode);
724 parent_type::clear(m_buffer->is_open() ? std::ios_base::goodbit : std::ios_base::failbit);
727 bool is_open() const noexcept
729 return m_buffer->is_open();
737 basic_pipe_streambuf<char_type, traits_type>* rdbuf() const noexcept
739 return m_buffer.get();
743 friend class process;
744 friend std::pair<basic_pipe_istream<char_type, traits_type>, basic_pipe_ostream<char_type, traits_type>> make_anonymous_pipe<char_type, traits_type>();
746 basic_pipe_istream(basic_pipe_streambuf<char_type, traits_type> buffer)
747 :parent_type{ nullptr }
748 , m_buffer{ std::make_unique<basic_pipe_streambuf<char_type, traits_type>>(std::move(buffer)) }
750 parent_type::rdbuf(m_buffer.get());
754 std::unique_ptr<basic_pipe_streambuf<char_type, traits_type>> m_buffer{ std::make_unique<basic_pipe_streambuf<char_type, traits_type>>() };
757 template<
typename CharT,
typename Traits = std::
char_traits<CharT>>
758 class basic_pipe_ostream :
public std::basic_ostream<CharT, Traits>
761 using parent_type = std::basic_ostream<CharT, Traits>;
764 using char_type = CharT;
765 using traits_type = Traits;
766 using int_type =
typename Traits::int_type;
767 using pos_type =
typename Traits::pos_type;
768 using off_type =
typename Traits::off_type;
771 basic_pipe_ostream() =
default;
773 explicit basic_pipe_ostream(
const std::string& name, std::ios_base::openmode mode = std::ios_base::out)
774 :parent_type{ nullptr }
776 parent_type::rdbuf(m_buffer.get());
780 virtual ~basic_pipe_ostream() =
default;
782 basic_pipe_ostream(
const basic_pipe_ostream&) =
delete;
783 basic_pipe_ostream& operator=(
const basic_pipe_ostream&) =
delete;
785 basic_pipe_ostream(basic_pipe_ostream&& other) noexcept
786 :parent_type{ std::move(other) }
788 std::swap(m_buffer, other.m_buffer);
789 parent_type::rdbuf(m_buffer.get());
792 basic_pipe_ostream& operator=(basic_pipe_ostream&& other)
noexcept
794 parent_type::operator=(std::move(other));
795 std::swap(m_buffer, other.m_buffer);
797 parent_type::rdbuf(m_buffer.get());
802 void open(
const std::string& name, std::ios_base::openmode mode = std::ios_base::out)
804 m_buffer->open(name, mode);
805 parent_type::clear(m_buffer->is_open() ? std::ios_base::goodbit : std::ios_base::failbit);
808 bool is_open() const noexcept
810 return m_buffer->is_open();
818 basic_pipe_streambuf<char_type, traits_type>* rdbuf() const noexcept
820 return m_buffer.get();
824 friend class process;
825 friend std::pair<basic_pipe_istream<char_type, traits_type>, basic_pipe_ostream<char_type, traits_type>> make_anonymous_pipe<char_type, traits_type>();
827 basic_pipe_ostream(basic_pipe_streambuf<char_type, traits_type> buffer)
828 :parent_type{ nullptr }
829 , m_buffer{ std::make_unique<basic_pipe_streambuf<char_type, traits_type>>(std::move(buffer)) }
831 parent_type::rdbuf(m_buffer.get());
835 std::unique_ptr<basic_pipe_streambuf<char_type, traits_type>> m_buffer{ std::make_unique<basic_pipe_streambuf<char_type, traits_type>>() };
838 template<
typename CharT,
typename Traits>
839 std::pair<basic_pipe_istream<CharT, Traits>, basic_pipe_ostream<CharT, Traits>> make_anonymous_pipe()
844 throw std::runtime_error{
"Failed to create pipe" };
846 return std::make_pair(basic_pipe_istream<CharT, Traits>{basic_pipe_streambuf<CharT, Traits>{fd[0], std::ios_base::in}},
847 basic_pipe_ostream<CharT, Traits>{basic_pipe_streambuf<CharT, Traits>{fd[1], std::ios_base::out}});
850 using pipe_streambuf = basic_pipe_streambuf<char>;
851 using pipe_istream = basic_pipe_istream<char>;
852 using pipe_ostream = basic_pipe_ostream<char>;