Skip to content

process.hpp

Alairion edited this page May 8, 2021 · 17 revisions

Process

Not Enough Standards' process management utilities are defined in header <nes/process.hpp>

Description

This header contains everything you need in order to create and manage child processes, and also get and set informations of the current process.
The class nes::process is used to create child processes.
The namespace nes::this_process is used to get informations or modify the current process.

nes::process is the main class of this header. On construction, this class create a child process that can be detached, waited or killed. The behaviour and interface of this class is similar to std::thread, so if you are used to use std::thread you won't get any trouble using nes::process.

nes::process::id is a trivially copyable type that serves as a unique identifier of a process within the system.

nes::process_options is a bitmask type that specifies additional options for nes::process.

nes::this_process namespace can be used to get the current process id, the current process working directory, and to modify the current process working directory.

Header Dependencies

Header Notes
<nes/pipe.hpp>(optional) If the file <nes/pipe.hpp> is not present then you can not redirect child processes' standard streams.

Synopsis

#if __has_include("pipe.hpp")
    #define NES_PROCESS_PIPE_EXTENSION
    #include "pipe.hpp"
#endif

namespace nes
{

enum class process_options : std::uint32_t
{
    none = 0x00,
#ifdef NES_PROCESS_PIPE_EXTENSION
    grab_stdout = 0x10,
    grab_stderr = 0x20,
    grab_stdin = 0x40 
#endif
};

constexpr process_options operator&(process_options left, process_options right) noexcept;
constexpr process_options& operator&=(process_options& left, process_options right) noexcept;
constexpr process_options operator|(process_options left, process_options right) noexcept;
constexpr process_options& operator|=(process_options& left, process_options right) noexcept;
constexpr process_options operator^(process_options left, process_options right) noexcept;
constexpr process_options& operator^=(process_options& left, process_options right) noexcept;
constexpr process_options operator~(process_options value) noexcept;

class process
{
public:
    using native_handle_type = /*implementation-defined*/;
    using return_code_type = /*implementation-defined*/;
    using id = /*implementation-defined*/;

public:
    constexpr process() noexcept = default;

    explicit process(const std::string& path, const std::string& working_directory);
    explicit process(const std::string& path, process_options options);
    explicit process(const std::string& path, const std::vector<std::string>& args, process_options options);
    explicit process(const std::string& path, const std::string& working_directory, process_options options);
    explicit process(const std::string& path, std::vector<std::string> args = std::vector<std::string>{}, const std::string& working_directory = std::string{}, process_options options = process_options{});
    
    ~process();

    process(const process&) = delete;
    process& operator=(const process&) = delete;

    process(process&& other) noexcept;
    process& operator=(process&& other) noexcept;

    void join();
    bool joinable() const noexcept;
    bool active() const;
    
    void detach();
    bool kill();

    return_code_type return_code() const noexcept;
    native_handle_type native_handle() const noexcept;
    id get_id() const noexcept;

#ifdef NES_PROCESS_PIPE_EXTENSION
    pipe_ostream& stdin_stream() noexcept;
    pipe_istream& stdout_stream() noexcept;
    pipe_istream& stderr_stream() noexcept;
#endif
};


constexpr bool operator==(process::id lhs, process::id rhs) noexcept;
constexpr bool operator!=(process::id lhs, process::id rhs) noexcept;
constexpr bool operator<(process::id lhs, process::id rhs) noexcept;
constexpr bool operator<=(process::id lhs, process::id rhs) noexcept;
constexpr bool operator>(process::id lhs, process::id rhs) noexcept;
constexpr bool operator>=(process::id lhs, process::id rhs) noexcept;

namespace this_process
{

inline process::id get_id() noexcept;
inline std::string working_directory();
inline bool change_working_directory(const std::string& path);

}

}

template<typename CharT, typename Traits>
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os, nes::process::id id);

namespace std
{
    template<>
    struct hash<nes::process::id>;
}

Example

Here is an example in which we create a child process.
main.cpp is the main file of the parent process.
other.cpp is the main file of the child process.
Output is the standard output of the parent process.

main.cpp

#include <iostream>

#include <nes/process.hpp>

int main()
{
    //nes::this_process namespace can be used to modify current process or get informations about it.
    std::cout << "Current process has id " << nes::this_process::get_id() << std::endl; 
    std::cout << "Its current directory is \"" << nes::this_process::working_directory() << "\"" << std::endl;

    //Create a child process
    nes::process other{"other_process", {"Hey!", "\\\"12\"\"\\\\", "\\42\\", "It's \"me\"!"}, nes::process_options::grab_stdout};
    
    //Read the entire standard output of the child process. (nes::process_options::grab_stdout must be specified on process creation)
    std::cout << other.stdout_stream().rdbuf() << std::endl;

    //As a std::thread, a nes::process must be joined if it is not detached.
    if(other.joinable())
        other.join();

    //Once joined, we can check its return code.
    std::cout << "Other process ended with code: " << other.return_code() << std::endl;
}

other.cpp

#include <iostream>

#include <nes/process.hpp>

int main(int argc, char** argv)
{
    //Display some informations about this process
    std::cout << "Hello world! I'm Other!\n";
    std::cout << "You gaved me " << argc << " arguments:";
    for(int i{}; i < argc; ++i)
        std::cout << "[" << argv[i] << "] ";
    std::cout << '\n';
    std::cout << "My working directory is \"" << nes::this_process::working_directory() << "\"" << std::endl;
}

Output

Current process has id 3612
Its current directory is "/..."
Hello world! I'm Other!
You gaved me 5 arguments:[not_enough_standards_other.exe] [Hey!] [\"12""\\\] [\42\] [It's "me"!] 
My working directory is "/..."

Other process ended with code: 0
Clone this wiki locally