• Fork.h
  • #pragma once
    #include <any>
    #include <cstddef>
    #include <cstdio>
    #include <exception>
    #include <iostream>
    #include <memory>
    #include <sstream>
    #include <string>
    #include <type_traits>
    #include <unistd.h>
    #include <wait.h>
    #include "Functional.h"
    
    namespace Fork {
    
        namespace Internal {
            using pipe_t = int[2]; 
            class base_exception : public std::exception {
                protected:
                std::string msg; 
    
                public:
                base_exception(const char * msg) : msg(msg) {}
                base_exception(std::string msg) : msg(msg) {}
                base_exception() : msg("exception occurred.") {}
    
                const char * what() {
                    return msg.c_str();
                }
            };
    
            class pipe_exception : public base_exception {
                public:
                pipe_exception() : base_exception("pipe exception occurred.") {}
                pipe_exception(const char * msg) : base_exception(msg) {}
                pipe_exception(std::string msg) : base_exception(msg) {}
            };
    
            class fork_exception : public base_exception {
                public:
                fork_exception() : base_exception("pipe exception occurred.") {}
                fork_exception(const char * msg) : base_exception(msg) {}
                fork_exception(std::string msg) : base_exception(msg) {}
            };
    
            class base_fork {
                protected:
                pid_t pid = -1;
    
                void fork_() {
                    pid = fork(); 
                    if(pid == -1) {
                        throw fork_exception(); 
                    }
                }
    
                public: 
                template <typename ReturnType1, typename... Args1, typename ReturnType2, typename... Args2>
                ReturnType1 Invoke(Functional::BoundArgFunction<ReturnType1, Args1...> main, Functional::BoundArgFunction<ReturnType2, Args2...> child) {
                    fork_(); 
    
                    std::any res;
                    if (pid == 0) {
                        child();
                        exit(1);
                    } else {
                        if constexpr(!std::is_same<ReturnType1, void>::value)
                            res = main();
                        main();
                    }
                    if constexpr (std::is_same<ReturnType1, void>::value) {
                        return;
                    }
                    return std::any_cast<ReturnType1>(res);
                }
    
                template<typename ReturnType, typename... Args>
                void Invoke(Functional::BoundArgFunction<ReturnType, Args...> child) {
                    pid = fork();
                    if (pid == -1) {
                        std::cerr << "Fork error" << std::endl;
                    } else if (pid == 0) {
                        child();
                        exit(1);
                    }
                }
            }; 
        }
    
        class Fork : public Internal::base_fork {
        public:
            Fork() {}
            
            template <typename ReturnType1, typename... Args1, typename ReturnType2, typename... Args2>
            ReturnType1 operator()(Functional::BoundArgFunction<ReturnType1, Args1...> main, Functional::BoundArgFunction<ReturnType2, Args2...> child) {
                if constexpr (std::is_same_v<ReturnType1, void>) return; 
                return Internal::base_fork::Invoke(main, child);
            }
    
            template<typename ReturnType, typename... Args>
            void operator()(Functional::BoundArgFunction<ReturnType, Args...> child) {
                Internal::base_fork::Invoke(child);
            }
        };
    
        template<size_t buffer_size = 1024>
        class PipedFork : public Internal::base_fork {
            private: 
            std::string pipe_result;
            std::unique_ptr<char[]> buffer {new char[buffer_size]}; 
            Internal::pipe_t fd;
    
            public: 
    
            template<typename ReturnType, typename... Args>
            PipedFork<buffer_size>& Invoke(Functional::BoundArgFunction<ReturnType, Args...> child) {
                if(pipe(fd) == -1) {
                    throw Internal::pipe_exception("pipe() failed.");
                }
                
                fork_(); 
    
                if(pid == 0) {
                    close(fd[0]);
                    dup2(fd[1], STDOUT_FILENO); 
                    close(fd[1]);
    
                    child(); 
    
                    exit(1); 
                } else {
                    close(fd[1]); 
                    int bytes_read; 
                    while((bytes_read = read(fd[0], buffer.get(), buffer_size - 1)) > 0) {
                        buffer[bytes_read] = '\0';
                        pipe_result.append(buffer.get(), bytes_read);
                    }
    
                    close(fd[0]);
                    wait(NULL); 
                }
    
                return *this; 
            }
    
            template<typename ReturnType, typename... Args>
            PipedFork<buffer_size>& operator()(Functional::BoundArgFunction<ReturnType, Args...> child) {
                PipedFork::Invoke(child);
                return *this;
            }
    
            std::string PipeResult() {
                const auto data = pipe_result;
                pipe_result.clear();
                return data; 
            }
    
            template<typename Ret_>
            Ret_ PipeResultTo() {
                std::istringstream ss(pipe_result); 
                Ret_ result; 
                ss >> result; 
                return result; 
            }
    
        }; 
    
    };