別プロセスとパイプでやり取りする

特にWindowsのパイプが特殊らしいので少し試しました。

# Unix

```c:pipe.c
#include <unistd.h>

int
main (int argc, char **argv)
{
        char c;
        while (read (0, &c, 1) == 1) {
                c++;
                write (1, &c, 1);
        }
}
```

これは簡単な変換を行うフィルターです。これを、標準入出力をそれぞれ別々のパイプでつないでGUIアプリケーションから実行して、GUI frontendを作りたいと思います。

GUIの部分はQtで適当に作りまして、問題はパイプのところです。以下のようなクソみたいなクラスを作って、これを使うことにしました。エラーチェックは省略してあります。

```cpp:
class hoge {
    int pipe0, pipe1;
public:
    hoge ()
    {
        int fd[4];
        pipe (&fd[0]);
        pipe (&fd[2]);
        pipe0 = fd[1];
        pipe1 = fd[2];
        if (!fork ()) {
            close (fd[1]);
            close (fd[2]);
            if (fd[0] != 0) {
                dup2 (fd[0], 0);
                close (fd[0]);
            }
            if (fd[3] != 1) {
                dup2 (fd[3], 1);
                close (fd[3]);
            }
            execl ("/tmp/pipe", "pipe", NULL);
            exit (1);
        }
        close (fd[0]);
        close (fd[3]);
    }
    ~hoge ()
    {
        close (pipe0);
        close (pipe1);
    }
    void w (char c)
    {
        write (pipe0, &c, 1);
    }
    char r ()
    {
        char c;
        if (read (pipe1, &c, 1) != 1)
            c = 0;
        return c;
    }
};
```

fork()が使えるUnix互換環境であればこれでほぼ動くはずです。

![a.png](https://qiita-image-store.s3.amazonaws.com/0/103733/af7214cd-1f73-32ff-c101-ed950081c9eb.png)

# Windows

Windowsの場合、fork()がありませんのでこのままでは動かせません。MSDNによれば、DOSを思い出すようなspawn系の関数を使うのと、元の標準入出力をとっておいて、先にdup2してspawnして、その後標準入出力を元に戻すようです。

```c++:
class hoge {
    int pipe0, pipe1;
public:
    hoge ()
    {
        int fd[4];
#ifdef __WIN32__
        _pipe (&fd[0], 512, _O_BINARY | _O_NOINHERIT);
        _pipe (&fd[2], 512, _O_BINARY | _O_NOINHERIT);
        pipe0 = fd[1];
        pipe1 = fd[2];
        int fd0 = _dup (0);
        int fd1 = _dup (1);
        if (fd[0] != 0) {
            _dup2 (fd[0], 0);
            _close (fd[0]);
        }
        if (fd[3] != 1) {
            _dup2 (fd[3], 1);
            _close (fd[3]);
        }
        _spawnl (P_NOWAIT, "pipe", "pipe", NULL);
        _dup2 (fd0, 0);
        _dup2 (fd1, 1);
        _close (fd0);
        _close (fd1);
#else
        pipe (&fd[0]);
        pipe (&fd[2]);
        pipe0 = fd[1];
        pipe1 = fd[2];
        if (!fork ()) {
            close (fd[1]);
            close (fd[2]);
            if (fd[0] != 0) {
                dup2 (fd[0], 0);
                close (fd[0]);
            }
            if (fd[3] != 1) {
                dup2 (fd[3], 1);
                close (fd[3]);
            }
            execl ("/tmp/pipe", "pipe", NULL);
            exit (1);
        }
        close (fd[0]);
        close (fd[3]);
#endif
    }
    ~hoge ()
    {
        close (pipe0);
        close (pipe1);
    }
    void w (char c)
    {
        write (pipe0, &c, 1);
    }
    char r ()
    {
        char c;
        if (read (pipe1, &c, 1) != 1)
            c = 0;
        return c;
    }
};
```

こんな感じで、Wineで試すといい感じに動きます。じゃあWindowsでは? というと、動きますが、pipeプログラム用のコマンドプロンプトウインドウが開いてしまいます。対策を試します。

対策その1: pipeプログラム側でFreeConsole()を呼びます。すると、|дﾟ)ﾁﾗｯとウインドウが見えますがすぐに消え、パイプは引き続き使用できます。

対策その2: pipeプログラムを-mwindowsでコンパイルします。するとGUIアプリケーションとなりウインドウは出ませんが、パイプによるやり取りはできるようです。

CUIでのpipeプログラム使用を許したければ対策その1にして、必要に応じてFreeConsole()を呼ぶというのが良さそうです。CUIで使う機会なんか無いよという場合は対策その2が見た目がすっきりします。