なつねこメモ

主にプログラミング関連のメモ帳 ♪(✿╹ヮ╹)ノ 書いてあるコードは自己責任でご自由にどうぞ。記事本文の無断転載は禁止です。

pybind11 で、 Console Application じゃないものでも print を stdout/err にリダイレクトしたい

C++ から Python を扱うことが出来る (その逆も出来る) pybind11 という C++ ライブラリがあるのですが、それで Python の print をリダイレクトする方法についてのメモです。
問題は、 Windows Desktop Application の場合の対処法。

Windows Desktop Application では、 Console を持っていないため、普通に print("Hello") とすると、 stdout が見つからない (None) というエラーが出力されます。
その場合、一般的な print debug すら困難になるので、それへの対処方法です。

コードとしては以下の通り:

// header
#pragma once

#include "pch.h"

namespace py = pybind11;

class PyStdOutRedirect
{
private:
    pb::object _stdout;

public:
    explicit PyStdOutRedirect()
    {
        const auto sys = py::module::import("sys");
        _stdout = sys.attr("stdout");

        sys.attr("stdout") = py::module::import("redirect_sys_stdout").attr("stdout_redirect");
    }

    ~PyStdOutRedirect()
    {
        const auto sys = py::module::import("sys");
        sys.attr("stdout") = _stdout;
    }
}

// source
#include "pch.h"
#include "PyStdOutRedirect.h"

PYBIND11_EMBEDDED_MODULE(redirect_sys_stdout, m)
{
    struct StdOutRedirect
    {
        StdOutRedirect() = default;
        StdOutRedirect(const StdOutRedirect&) = default;
        StdOutRedirect(StdOutRedirect&&) = default;
    };

    py::class_<StdOutRedirect> StdRedirect(m, "stdout_redirect");

    StdRedirect.def_static("write", [](const py::object buffer)
    {
        std::cout << buffer.cast<std::string>();
    });

    StdRedirect.def_static("flush", []()
    {
        std::cout << std::flush;
    });
}

あとは、これを任意のブロックやクラスで呼ぶことで、インスタンスが有効な間は std::cout へと print の中身が出力されます。
ということで、メモでした。