<P>这是我最近开发的一个C++友好解决方案。</p>
<p>我在我的博客上解释了其中的一些细节:<a href="http://mateusz.loskot.net/post/2011/12/01/python-sys-stdout-redirection-in-cpp/" rel="noreferrer">Python sys.stdout redirection in C++</a>其中我还指向GitHub上的存储库,在那里可以找到最新的版本。
以下是发布此答案时基于当前代码的完整示例:</p>
<pre><code>#include <functional>
#include <iostream>
#include <string>
#include <Python.h>
namespace emb
{
typedef std::function<void(std::string)> stdout_write_type;
struct Stdout
{
PyObject_HEAD
stdout_write_type write;
};
PyObject* Stdout_write(PyObject* self, PyObject* args)
{
std::size_t written(0);
Stdout* selfimpl = reinterpret_cast<Stdout*>(self);
if (selfimpl->write)
{
char* data;
if (!PyArg_ParseTuple(args, "s", &data))
return 0;
std::string str(data);
selfimpl->write(str);
written = str.size();
}
return PyLong_FromSize_t(written);
}
PyObject* Stdout_flush(PyObject* self, PyObject* args)
{
// no-op
return Py_BuildValue("");
}
PyMethodDef Stdout_methods[] =
{
{"write", Stdout_write, METH_VARARGS, "sys.stdout.write"},
{"flush", Stdout_flush, METH_VARARGS, "sys.stdout.flush"},
{0, 0, 0, 0} // sentinel
};
PyTypeObject StdoutType =
{
PyVarObject_HEAD_INIT(0, 0)
"emb.StdoutType", /* tp_name */
sizeof(Stdout), /* tp_basicsize */
0, /* tp_itemsize */
0, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_reserved */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT, /* tp_flags */
"emb.Stdout objects", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
Stdout_methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
0, /* tp_new */
};
PyModuleDef embmodule =
{
PyModuleDef_HEAD_INIT,
"emb", 0, -1, 0,
};
// Internal state
PyObject* g_stdout;
PyObject* g_stdout_saved;
PyMODINIT_FUNC PyInit_emb(void)
{
g_stdout = 0;
g_stdout_saved = 0;
StdoutType.tp_new = PyType_GenericNew;
if (PyType_Ready(&StdoutType) < 0)
return 0;
PyObject* m = PyModule_Create(&embmodule);
if (m)
{
Py_INCREF(&StdoutType);
PyModule_AddObject(m, "Stdout", reinterpret_cast<PyObject*>(&StdoutType));
}
return m;
}
void set_stdout(stdout_write_type write)
{
if (!g_stdout)
{
g_stdout_saved = PySys_GetObject("stdout"); // borrowed
g_stdout = StdoutType.tp_new(&StdoutType, 0, 0);
}
Stdout* impl = reinterpret_cast<Stdout*>(g_stdout);
impl->write = write;
PySys_SetObject("stdout", g_stdout);
}
void reset_stdout()
{
if (g_stdout_saved)
PySys_SetObject("stdout", g_stdout_saved);
Py_XDECREF(g_stdout);
g_stdout = 0;
}
} // namespace emb
int main()
{
PyImport_AppendInittab("emb", emb::PyInit_emb);
Py_Initialize();
PyImport_ImportModule("emb");
PyRun_SimpleString("print(\'hello to console\')");
// here comes the ***magic***
std::string buffer;
{
// switch sys.stdout to custom handler
emb::stdout_write_type write = [&buffer] (std::string s) { buffer += s; };
emb::set_stdout(write);
PyRun_SimpleString("print(\'hello to buffer\')");
PyRun_SimpleString("print(3.14)");
PyRun_SimpleString("print(\'still talking to buffer\')");
emb::reset_stdout();
}
PyRun_SimpleString("print(\'hello to console again\')");
Py_Finalize();
// output what was written to buffer object
std::clog << buffer << std::endl;
}
</code></pre>
<>这允许用任何类型的可调用C++实体截取^ {< CD1>}输出:自由函数、类成员函数、命名函数对象或匿名函数,如上面我使用<a href="http://en.wikipedia.org/wiki/C++11" rel="noreferrer">C++11 lambda</a>的例子。</p>
<p>注意,这是一个简单的例子来展示基本概念。在产品级代码中,它当然需要更多关注<code>PyObject</code>的引用计数、摆脱全局状态等等。</p>