cgv
Loading...
Searching...
No Matches
pipe.hpp
1
28
29#ifndef NOT_ENOUGH_STANDARDS_PIPE
30#define NOT_ENOUGH_STANDARDS_PIPE
31
32#if defined(_WIN32)
33#define NES_WIN32_PIPE
34#ifndef NOMINMAX
35#define NOMINMAX
36#endif
37#ifndef WIN32_LEAN_AND_MEAN
38#define WIN32_LEAN_AND_MEAN
39#endif
40#include <Windows.h>
41#elif defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))
42#define NES_POSIX_PIPE
43#include <unistd.h>
44#include <fcntl.h>
45#include <sys/types.h>
46#include <sys/stat.h>
47#else
48#error "Not enough standards does not support this environment."
49#endif
50
51#include <vector>
52#include <algorithm>
53#include <streambuf>
54#include <istream>
55#include <ostream>
56#include <memory>
57#include <cassert>
58#include <utility>
59
60#if defined(NES_WIN32_PIPE)
61
62namespace nes
63{
64 // removed inline keyword to make it C++14 compatible
65 constexpr const char pipe_root[] = "\\\\.\\pipe\\";
66
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();
73
74 template<typename CharT, typename Traits = std::char_traits<CharT>>
75 class basic_pipe_streambuf : public std::basic_streambuf<CharT, Traits>
76 {
77 private:
78 using parent_type = std::basic_streambuf<CharT, Traits>;
79
80 public:
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;
86
87 public:
88 static constexpr std::size_t buf_size{ 1024 };
89
90 public:
91 basic_pipe_streambuf() = default;
92
93 explicit basic_pipe_streambuf(const std::string& name, std::ios_base::openmode mode)
94 {
95 open(name, mode);
96 }
97
98 virtual ~basic_pipe_streambuf()
99 {
100 close();
101 }
102
103 basic_pipe_streambuf(const basic_pipe_streambuf&) = delete;
104 basic_pipe_streambuf& operator=(const basic_pipe_streambuf&) = delete;
105
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{}) }
111 {
112
113 }
114
115 basic_pipe_streambuf& operator=(basic_pipe_streambuf&& other) noexcept
116 {
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);
121
122 return *this;
123 }
124
125 bool open(const std::string& name, std::ios_base::openmode mode)
126 {
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.");
128
129 close();
130
131 const auto native_name{ to_wide(pipe_root + name) };
132 DWORD native_mode{ mode & std::ios_base::in ? GENERIC_READ : GENERIC_WRITE };
133
134 HANDLE handle = CreateFileW(std::data(native_name), native_mode, 0, nullptr, OPEN_EXISTING, 0, nullptr);
135 if (handle == INVALID_HANDLE_VALUE)
136 {
137 if (GetLastError() == ERROR_FILE_NOT_FOUND)
138 {
139 native_mode = mode & std::ios_base::in ? PIPE_ACCESS_INBOUND : PIPE_ACCESS_OUTBOUND;
140
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)
143 return false;
144
145 if (!ConnectNamedPipe(handle, nullptr))
146 {
147 CloseHandle(handle);
148 return false;
149 }
150 }
151 }
152
153 m_buffer.resize(buf_size);
154 parent_type::setp(std::data(m_buffer), std::data(m_buffer) + buf_size);
155 m_handle = handle;
156 m_mode = mode;
157
158 return true;
159 }
160
161 bool is_open() const noexcept
162 {
163 return m_handle != INVALID_HANDLE_VALUE;
164 }
165
166 void close()
167 {
168 if (is_open())
169 {
170 sync();
171
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);
176 }
177 }
178
179 private:
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>();
182
183 basic_pipe_streambuf(HANDLE handle, std::ios_base::openmode mode)
184 :m_handle{ handle }
185 , m_mode{ mode }
186 {
187 m_buffer.resize(buf_size);
188 parent_type::setp(std::data(m_buffer), std::data(m_buffer) + buf_size);
189 }
190
191 protected:
192 virtual int sync() override
193 {
194 if (m_mode & std::ios_base::out)
195 {
196 const std::ptrdiff_t count{ parent_type::pptr() - parent_type::pbase() };
197
198 DWORD written{};
199 if (!WriteFile(m_handle, reinterpret_cast<const CHAR*>(std::data(m_buffer)), static_cast<DWORD>(count) * sizeof(char_type), &written, nullptr))
200 return -1;
201
202 parent_type::setp(std::data(m_buffer), std::data(m_buffer) + buf_size);
203 }
204
205 return 0;
206 }
207
208 virtual int_type overflow(int_type c = traits_type::eof()) override
209 {
210 assert(m_mode & std::ios_base::out && "Write operation on a read only pipe.");
211
212 if (traits_type::eq_int_type(c, traits_type::eof()))
213 {
214 DWORD written{};
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();
217
218 parent_type::setp(std::data(m_buffer), std::data(m_buffer) + buf_size);
219 }
220 else
221 {
222 *parent_type::pptr() = traits_type::to_char_type(c);
223 parent_type::pbump(1);
224 }
225
226 return traits_type::not_eof(c);
227 }
228
229 virtual std::streamsize xsputn(const char_type* s, std::streamsize count) override
230 {
231 assert(m_mode & std::ios_base::out && "Write operation on a read only pipe.");
232
233 DWORD written{};
234 if (!WriteFile(m_handle, reinterpret_cast<const CHAR*>(s), static_cast<DWORD>(count) * sizeof(char_type), &written, nullptr))
235 return 0;
236
237 return static_cast<std::streamsize>(written);
238 }
239
240 virtual int_type underflow() override
241 {
242 assert(m_mode & std::ios_base::in && "Read operation on a write only pipe.");
243
244 if (parent_type::gptr() == parent_type::egptr())
245 {
246 DWORD readed{};
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();
249
250 parent_type::setg(std::data(m_buffer), std::data(m_buffer), std::data(m_buffer) + (readed / sizeof(char_type)));
251 }
252
253 return traits_type::to_int_type(*parent_type::gptr());
254 }
255
256 virtual std::streamsize xsgetn(char_type* s, std::streamsize count) override
257 {
258 assert(m_mode & std::ios_base::in && "Read operation on a write only pipe.");
259
260 DWORD readed{};
261 if (!ReadFile(m_handle, reinterpret_cast<CHAR*>(s), static_cast<DWORD>(count) * sizeof(char_type), &readed, nullptr))
262 return 0;
263
264 return static_cast<std::streamsize>(readed / sizeof(char_type));
265 }
266
267 private:
268 std::wstring to_wide(std::string path)
269 {
270 assert(std::size(path) < 0x7FFFFFFFu && "Wrong path.");
271
272 if (std::empty(path))
273 return {};
274
275 std::transform(std::begin(path), std::end(path), std::begin(path), [](char c) {return c == '/' ? '\\' : c; });
276
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)));
279
280 if (!MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, std::data(path), static_cast<int>(std::size(path)),
281 &out_path[0], // replaced std::data(out_path) with this to avoid pointer to be to const Elem*
282 static_cast<int>(std::size(out_path))))
283 throw std::runtime_error{ "Failed to convert the path to wide." };
284
285 return out_path;
286 }
287
288 private:
289 std::vector<CharT> m_buffer{};
290 HANDLE m_handle{ INVALID_HANDLE_VALUE };
291 std::ios_base::openmode m_mode{};
292 };
293
294
295 template<typename CharT, typename Traits = std::char_traits<CharT>>
296 class basic_pipe_istream : public std::basic_istream<CharT, Traits>
297 {
298 private:
299 using parent_type = std::basic_istream<CharT, Traits>;
300
301 public:
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;
307
308 public:
309 basic_pipe_istream() = default;
310
311 explicit basic_pipe_istream(const std::string& name, std::ios_base::openmode mode = std::ios_base::in)
312 :parent_type{ nullptr }
313 {
314 parent_type::rdbuf(m_buffer.get());
315 open(name, mode);
316 }
317
318 virtual ~basic_pipe_istream() = default;
319
320 basic_pipe_istream(const basic_pipe_istream&) = delete;
321 basic_pipe_istream& operator=(const basic_pipe_istream&) = delete;
322
323 basic_pipe_istream(basic_pipe_istream&& other) noexcept
324 :parent_type{ std::move(other) }
325 {
326 std::swap(m_buffer, other.m_buffer);
327 parent_type::rdbuf(m_buffer.get());
328 }
329
330 basic_pipe_istream& operator=(basic_pipe_istream&& other) noexcept
331 {
332 parent_type::operator=(std::move(other));
333 std::swap(m_buffer, other.m_buffer);
334
335 parent_type::rdbuf(m_buffer.get());
336
337 return *this;
338 }
339
340 void open(const std::string& name, std::ios_base::openmode mode = std::ios_base::in)
341 {
342 m_buffer->open(name, mode);
343 parent_type::clear(m_buffer->is_open() ? std::ios_base::goodbit : std::ios_base::failbit);
344 }
345
346 bool is_open() const noexcept
347 {
348 return m_buffer->is_open();
349 }
350
351 void close()
352 {
353 m_buffer->close();
354 }
355
356 basic_pipe_streambuf<char_type, traits_type>* rdbuf() const noexcept
357 {
358 return m_buffer.get();
359 }
360
361 private:
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>();
364
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)) }
368 {
369 parent_type::rdbuf(m_buffer.get());
370 }
371
372 private:
373 std::unique_ptr<basic_pipe_streambuf<char_type, traits_type>> m_buffer{ std::make_unique<basic_pipe_streambuf<char_type, traits_type>>() };
374 };
375
376 template<typename CharT, typename Traits = std::char_traits<CharT>>
377 class basic_pipe_ostream : public std::basic_ostream<CharT, Traits>
378 {
379 private:
380 using parent_type = std::basic_ostream<CharT, Traits>;
381
382 public:
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;
388
389 public:
390 basic_pipe_ostream() = default;
391
392 explicit basic_pipe_ostream(const std::string& name, std::ios_base::openmode mode = std::ios_base::out)
393 :parent_type{ nullptr }
394 {
395 parent_type::rdbuf(m_buffer.get());
396 open(name, mode);
397 }
398
399 virtual ~basic_pipe_ostream() = default;
400
401 basic_pipe_ostream(const basic_pipe_ostream&) = delete;
402 basic_pipe_ostream& operator=(const basic_pipe_ostream&) = delete;
403
404 basic_pipe_ostream(basic_pipe_ostream&& other) noexcept
405 :parent_type{ std::move(other) }
406 {
407 std::swap(m_buffer, other.m_buffer);
408 parent_type::rdbuf(m_buffer.get());
409 }
410
411 basic_pipe_ostream& operator=(basic_pipe_ostream&& other) noexcept
412 {
413 parent_type::operator=(std::move(other));
414 std::swap(m_buffer, other.m_buffer);
415
416 parent_type::rdbuf(m_buffer.get());
417
418 return *this;
419 }
420
421 void open(const std::string& name, std::ios_base::openmode mode = std::ios_base::out)
422 {
423 m_buffer->open(name, mode);
424 parent_type::clear(m_buffer->is_open() ? std::ios_base::goodbit : std::ios_base::failbit);
425 }
426
427 bool is_open() const noexcept
428 {
429 return m_buffer->is_open();
430 }
431
432 void close()
433 {
434 m_buffer->close();
435 }
436
437 basic_pipe_streambuf<char_type, traits_type>* rdbuf() const noexcept
438 {
439 return m_buffer.get();
440 }
441
442 private:
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>();
445
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)) }
449 {
450 parent_type::rdbuf(m_buffer.get());
451 }
452
453 private:
454 std::unique_ptr<basic_pipe_streambuf<char_type, traits_type>> m_buffer{ std::make_unique<basic_pipe_streambuf<char_type, traits_type>>() };
455 };
456
457 template<typename CharT, typename Traits>
458 std::pair<basic_pipe_istream<CharT, Traits>, basic_pipe_ostream<CharT, Traits>> make_anonymous_pipe()
459 {
460 HANDLE input{};
461 HANDLE output{};
462
463 if (!CreatePipe(&input, &output, nullptr, 0))
464 throw std::runtime_error{ "Failed to create pipe" };
465
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}});
468 }
469
470 using pipe_streambuf = basic_pipe_streambuf<char>;
471 using pipe_istream = basic_pipe_istream<char>;
472 using pipe_ostream = basic_pipe_ostream<char>;
473
474}
475
476#elif defined(NES_POSIX_PIPE)
477
478
479namespace nes
480{
481 // removed inline keyword to make it C++14 compatible
482 constexpr const char pipe_root[] = "/tmp/";
483
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();
490
491 template<typename CharT, typename Traits = std::char_traits<CharT>>
492 class basic_pipe_streambuf : public std::basic_streambuf<CharT, Traits>
493 {
494 private:
495 using parent_type = std::basic_streambuf<CharT, Traits>;
496
497 public:
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;
503
504 public:
505 static constexpr std::size_t buf_size{ 1024 };
506
507 public:
508 basic_pipe_streambuf() = default;
509
510 explicit basic_pipe_streambuf(const std::string& name, std::ios_base::openmode mode)
511 :parent_type{ nullptr }
512 {
513 open(name, mode);
514 }
515
516 virtual ~basic_pipe_streambuf()
517 {
518 close();
519 }
520
521 basic_pipe_streambuf(const basic_pipe_streambuf&) = delete;
522 basic_pipe_streambuf& operator=(const basic_pipe_streambuf&) = delete;
523
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{}) }
529 {
530
531 }
532
533 basic_pipe_streambuf& operator=(basic_pipe_streambuf&& other) noexcept
534 {
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);
539
540 return *this;
541 }
542
543 bool open(const std::string& name, std::ios_base::openmode mode)
544 {
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.");
546
547 close();
548
549 const auto native_name{ pipe_root + name };
550 if (mkfifo(std::data(native_name), 0660) != 0 && errno != EEXIST)
551 return false;
552
553 const int native_mode{ mode & std::ios_base::in ? O_RDONLY : O_WRONLY };
554 int handle = ::open(std::data(native_name), native_mode);
555 if (handle < 0)
556 return false;
557
558 m_buffer.resize(buf_size);
559 parent_type::setp(std::data(m_buffer), std::data(m_buffer) + buf_size);
560 m_handle = handle;
561 m_mode = mode;
562
563 return true;
564 }
565
566 bool is_open() const noexcept
567 {
568 return m_handle;
569 }
570
571 void close()
572 {
573 if (is_open())
574 {
575 sync();
576
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);
581 }
582 }
583
584 private:
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>();
587
588 basic_pipe_streambuf(int handle, std::ios_base::openmode mode)
589 :m_handle{ handle }
590 , m_mode{ mode }
591 {
592 m_buffer.resize(buf_size);
593 parent_type::setp(std::data(m_buffer), std::data(m_buffer) + buf_size);
594 }
595
596 protected:
597 virtual int sync() override
598 {
599 if (m_mode & std::ios_base::out)
600 {
601 const std::ptrdiff_t count{ parent_type::pptr() - parent_type::pbase() };
602
603 if (write(m_handle, reinterpret_cast<const char*>(std::data(m_buffer)), count * sizeof(char_type)) < 0)
604 return -1;
605
606 parent_type::setp(std::data(m_buffer), std::data(m_buffer) + buf_size);
607 }
608
609 return 0;
610 }
611
612 virtual int_type overflow(int_type c = traits_type::eof()) override
613 {
614 assert(m_mode & std::ios_base::out && "Write operation on a read only pipe.");
615
616 if (traits_type::eq_int_type(c, traits_type::eof()))
617 {
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();
620
621 parent_type::setp(std::data(m_buffer), std::data(m_buffer) + buf_size);
622 }
623 else
624 {
625 *parent_type::pptr() = traits_type::to_char_type(c);
626 parent_type::pbump(1);
627 }
628
629 return traits_type::not_eof(c);
630 }
631
632 virtual std::streamsize xsputn(const char_type* s, std::streamsize count) override
633 {
634 assert(m_mode & std::ios_base::out && "Write operation on a read only pipe.");
635
636 const auto written = write(m_handle, reinterpret_cast<const char*>(s), count * sizeof(char_type));
637 if (written < 0)
638 return 0;
639
640 return static_cast<std::streamsize>(written);
641 }
642
643 virtual int_type underflow() override
644 {
645 assert(m_mode & std::ios_base::in && "Read operation on a write only pipe.");
646
647 if (parent_type::gptr() == parent_type::egptr())
648 {
649 const auto readed = read(m_handle, reinterpret_cast<char*>(std::data(m_buffer)), buf_size * sizeof(char_type));
650 if (readed <= 0)
651 return traits_type::eof();
652
653 parent_type::setg(std::data(m_buffer), std::data(m_buffer), std::data(m_buffer) + (readed / sizeof(char_type)));
654 }
655
656 return traits_type::to_int_type(*parent_type::gptr());
657 }
658
659 virtual std::streamsize xsgetn(char_type* s, std::streamsize count) override
660 {
661 assert(m_mode & std::ios_base::in && "Read operation on a write only pipe.");
662
663 const auto readed = read(m_handle, reinterpret_cast<char*>(s), count * sizeof(char_type));
664 if (readed < 0)
665 return 0;
666
667 return static_cast<std::streamsize>(readed / sizeof(char_type));
668 }
669
670 private:
671 std::vector<CharT> m_buffer{};
672 int m_handle{};
673 std::ios_base::openmode m_mode{};
674 };
675
676 template<typename CharT, typename Traits = std::char_traits<CharT>>
677 class basic_pipe_istream : public std::basic_istream<CharT, Traits>
678 {
679 private:
680 using parent_type = std::basic_istream<CharT, Traits>;
681
682 public:
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;
688
689 public:
690 basic_pipe_istream() = default;
691
692 explicit basic_pipe_istream(const std::string& name, std::ios_base::openmode mode = std::ios_base::in)
693 :parent_type{ nullptr }
694 {
695 parent_type::rdbuf(m_buffer.get());
696 open(name, mode);
697 }
698
699 virtual ~basic_pipe_istream() = default;
700
701 basic_pipe_istream(const basic_pipe_istream&) = delete;
702 basic_pipe_istream& operator=(const basic_pipe_istream&) = delete;
703
704 basic_pipe_istream(basic_pipe_istream&& other) noexcept
705 :parent_type{ std::move(other) }
706 {
707 std::swap(m_buffer, other.m_buffer);
708 parent_type::rdbuf(m_buffer.get());
709 }
710
711 basic_pipe_istream& operator=(basic_pipe_istream&& other) noexcept
712 {
713 parent_type::operator=(std::move(other));
714 std::swap(m_buffer, other.m_buffer);
715
716 parent_type::rdbuf(m_buffer.get());
717
718 return *this;
719 }
720
721 void open(const std::string& name, std::ios_base::openmode mode = std::ios_base::in)
722 {
723 m_buffer->open(name, mode);
724 parent_type::clear(m_buffer->is_open() ? std::ios_base::goodbit : std::ios_base::failbit);
725 }
726
727 bool is_open() const noexcept
728 {
729 return m_buffer->is_open();
730 }
731
732 void close()
733 {
734 m_buffer->close();
735 }
736
737 basic_pipe_streambuf<char_type, traits_type>* rdbuf() const noexcept
738 {
739 return m_buffer.get();
740 }
741
742 private:
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>();
745
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)) }
749 {
750 parent_type::rdbuf(m_buffer.get());
751 }
752
753 private:
754 std::unique_ptr<basic_pipe_streambuf<char_type, traits_type>> m_buffer{ std::make_unique<basic_pipe_streambuf<char_type, traits_type>>() };
755 };
756
757 template<typename CharT, typename Traits = std::char_traits<CharT>>
758 class basic_pipe_ostream : public std::basic_ostream<CharT, Traits>
759 {
760 private:
761 using parent_type = std::basic_ostream<CharT, Traits>;
762
763 public:
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;
769
770 public:
771 basic_pipe_ostream() = default;
772
773 explicit basic_pipe_ostream(const std::string& name, std::ios_base::openmode mode = std::ios_base::out)
774 :parent_type{ nullptr }
775 {
776 parent_type::rdbuf(m_buffer.get());
777 open(name, mode);
778 }
779
780 virtual ~basic_pipe_ostream() = default;
781
782 basic_pipe_ostream(const basic_pipe_ostream&) = delete;
783 basic_pipe_ostream& operator=(const basic_pipe_ostream&) = delete;
784
785 basic_pipe_ostream(basic_pipe_ostream&& other) noexcept
786 :parent_type{ std::move(other) }
787 {
788 std::swap(m_buffer, other.m_buffer);
789 parent_type::rdbuf(m_buffer.get());
790 }
791
792 basic_pipe_ostream& operator=(basic_pipe_ostream&& other) noexcept
793 {
794 parent_type::operator=(std::move(other));
795 std::swap(m_buffer, other.m_buffer);
796
797 parent_type::rdbuf(m_buffer.get());
798
799 return *this;
800 }
801
802 void open(const std::string& name, std::ios_base::openmode mode = std::ios_base::out)
803 {
804 m_buffer->open(name, mode);
805 parent_type::clear(m_buffer->is_open() ? std::ios_base::goodbit : std::ios_base::failbit);
806 }
807
808 bool is_open() const noexcept
809 {
810 return m_buffer->is_open();
811 }
812
813 void close()
814 {
815 m_buffer->close();
816 }
817
818 basic_pipe_streambuf<char_type, traits_type>* rdbuf() const noexcept
819 {
820 return m_buffer.get();
821 }
822
823 private:
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>();
826
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)) }
830 {
831 parent_type::rdbuf(m_buffer.get());
832 }
833
834 private:
835 std::unique_ptr<basic_pipe_streambuf<char_type, traits_type>> m_buffer{ std::make_unique<basic_pipe_streambuf<char_type, traits_type>>() };
836 };
837
838 template<typename CharT, typename Traits>
839 std::pair<basic_pipe_istream<CharT, Traits>, basic_pipe_ostream<CharT, Traits>> make_anonymous_pipe()
840 {
841 int fd[2];
842
843 if (pipe(fd))
844 throw std::runtime_error{ "Failed to create pipe" };
845
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}});
848 }
849
850 using pipe_streambuf = basic_pipe_streambuf<char>;
851 using pipe_istream = basic_pipe_istream<char>;
852 using pipe_ostream = basic_pipe_ostream<char>;
853
854}
855
856#endif
857
858#endif