2010-11-29 6 views
21

私は、実行中に時々いくつかのタスクを実行するためにpythonを呼び出す必要があるプログラムを持っています。私はPythonを呼び出す関数が必要で、はpythons stdoutをキャッチし、それをいくつかのファイルに置きます。 これは私の問題は、与えられたコマンド(pythonInput)についてすべてのpython出力をキャッチすることです関数の宣言C++コードでpython stdoutをキャッチする方法

pythonCallBackFunc(const char* pythonInput) 

です。 私はPython APIに関する経験がなく、私はこれを行う正しい技法が何か分かりません。 私が試した最初のことはPythonのsdtoutとstderrをPy_run_SimpleStringを使ってリダイレクトすることです これは私が書いたコードのいくつかの例です。

#include "boost\python.hpp" 
#include <iostream> 

void pythonCallBackFunc(const char* inputStr){ 

    PyRun_SimpleString(inputStr); 
} 


int main() { 
    ... 
    //S0me outside functions does this 
    Py_Initialize(); 
    PyRun_SimpleString("import sys"); 
    PyRun_SimpleString("old_stdout = sys.stdout"); 
    PyRun_SimpleString("fsock = open('python_out.log','a')"); 
    PyRun_SimpleString("sys.stdout = fsock"); 
    ... 

    //my func 
    pythonCallBackFunc("print 'HAHAHAHAHA'"); 
    pythonCallBackFunc("result = 5"); 
    pythonCallBackFunc("print result"); 

    pythonCallBackFunc("result = 'Hello '+'World!'"); 
    pythonCallBackFunc("print result"); 

    pythonCallBackFunc("'KUKU '+'KAKA'"); 
    pythonCallBackFunc("5**3"); 

    pythonCallBackFunc("prinhghult"); 

    pythonCallBackFunc("execfile('stdout_close.py')"); 
    ... 

    //Again anothers function code 
    PyRun_SimpleString("sys.stdout = old_stdout"); 
    PyRun_SimpleString("fsock.close()"); 

    Py_Finalize(); 
    return 0; 
} 

これを行うより良い方法はありますか? PyRun_SimpleString( "5 ** 3")は何も表示しません(python conlsulは結果を出力します:125)

多分、私は視覚的に使用していますスタジオ2008年 おかげで、私はマークの提案に従って作った


変更 アレックス

#include <python.h> 
    #include <string> 

    using namespace std; 

    void PythonPrinting(string inputStr){ 
    string stdOutErr = 
    "import sys\n\ 
    class CatchOut:\n\ 
     def __init__(self):\n\ 
      self.value = ''\n\ 
     def write(self, txt):\n\ 
      self.value += txt\n\ 
    catchOut = CatchOut()\n\ 
    sys.stdout = catchOut\n\ 
    sys.stderr = catchOut\n\ 
    "; //this is python code to redirect stdouts/stderr 

    PyObject *pModule = PyImport_AddModule("__main__"); //create main module 
    PyRun_SimpleString(stdOutErr.c_str()); //invoke code to redirect 

    PyRun_SimpleString(inputStr.c_str()); 
    PyObject *catcher = PyObject_GetAttrString(pModule,"catchOut"); 

    PyObject *output = PyObject_GetAttrString(catcher,"value"); 
    printf("Here's the output: %s\n", PyString_AsString(output)); 
    } 

    int main(int argc, char** argv){ 
     Py_Initialize(); 

    PythonPrinting("print 123"); 
    PythonPrinting("1+5"); 
    PythonPrinting("result = 2"); 
     PythonPrinting("print result"); 

     Py_Finalize(); 
     return 0; 
    } 

私はメイン実行した後に取得する出力:

​​

それは私のために良いですが、唯一の問題は、それは私がなぜ知っているが、このコマンドを実行した後いけない

Here's the output: 123 

Here's the output: 6 

Here's the output: 
Here's the output: 2 

する必要があります:PythonPrintingを( "1 + 5")、PyString_AsString(出力)コマンドは6の代わりに空の文字列(char *)を返す... :(私はこの出力を失わないようにすることができますか?

Thaks、 アレックス

+0

プログラミングに関する質問はStackOverflowにあります。 –

答えて

16

私が正しくあなたの質問を読んでいる場合、あなたはC内の変数に標準出力/標準エラー出力をキャプチャしたい++? stdout/stderrをpython変数にリダイレクトして、この変数をC++に問い合わせることで、これを行うことができます。私が最近開発したC++のフレンドリーな解決法は、以下の通りです:

#include <Python.h> 
#include <string> 

int main(int argc, char** argv) 
{ 
    std::string stdOutErr = 
"import sys\n\ 
class CatchOutErr:\n\ 
    def __init__(self):\n\ 
     self.value = ''\n\ 
    def write(self, txt):\n\ 
     self.value += txt\n\ 
catchOutErr = CatchOutErr()\n\ 
sys.stdout = catchOutErr\n\ 
sys.stderr = catchOutErr\n\ 
"; //this is python code to redirect stdouts/stderr 

    Py_Initialize(); 
    PyObject *pModule = PyImport_AddModule("__main__"); //create main module 
    PyRun_SimpleString(stdOutErr.c_str()); //invoke code to redirect 
    PyRun_SimpleString("print(1+1)"); //this is ok stdout 
    PyRun_SimpleString("1+a"); //this creates an error 
    PyObject *catcher = PyObject_GetAttrString(pModule,"catchOutErr"); //get our catchOutErr created above 
    PyErr_Print(); //make python print any errors 

    PyObject *output = PyObject_GetAttrString(catcher,"value"); //get the stdout and stderr from our catchOutErr object 

    printf("Here's the output:\n %s", PyString_AsString(output)); //it's not in our C++ portion 

    Py_Finalize(); 


    return 0; 

} 
+0

こんにちはマーク、それは非常に役に立つです。いくつか説明してください。 まず、キャッサーはどのように機能するのですか、第2に、あなたの提案に基づいて私が行った質問の変更に投稿しました。 メイン(PythonPrinting( "1 + 5");)で2番目のコマンドを実行すると、PyString_AsString(出力)関数が空の文字列を返す、meanin、元のPython出力が失われています: 変更私はこれを失うことはありませんか? もう一度ありがとう... :) – alexpov

+1

@alexpov、キャッチャーは単純にPythonのstdoutとstderrを変数にリダイレクトして動作します。あなたは "1 + 5"の出力を見ません。なぜなら、pythonはそのような場合にstdoutに何も送信しないからです。あなたは "print(1 + 5)"を使うべきです。また、あなたのコードをリファクタリングして、PyImport_AddModuleに複数の呼び出しを行うべきではありません。 – Mark

+0

こんにちは、私の場合、単純にCからpythonコマンドを実行して、すべてのpythons出力をキャプチャする方法です。私はそれがどのコマンドになるか分からず、pythonをsdtoutまたはstderrまたは "1 + 1"コマンドに印刷するコマンドです。私はすべての命令を印刷物で包むことはできません。リダイレクション(または何か他のもの)を行う方法を知っていますので、私はそれらの出力を捕まえることができますか? (ここではPythonはその出力をどこに送りますか?) PyImport_AddModuleについて、一度呼び出すと、catcherの "value"はすべての以前のoutputs.Howを保持していますが、PythonPrintingを呼び出すたびにこの値を空の文字列に初期化しますか? ありがとうございました Alex – alexpov

24

私は自分のブログでそれについていくつかの説明をしています:Python sys.stdout redirection in C++最新のバージョンが見つかる私のGitHubでリポジトリを指しています。ここ この回答を投稿する時に現在のコードに基づいて完全な例です:

#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; 
} 

これは、呼び出し可能なC++のエンティティのいずれかの種類でsys.stdout.write出力を傍受することができます:free関数、クラスのメンバ関数、名前の関数オブジェクトや私がC++11 lambdaを使用している上記の例のような匿名の関数でさえも。

これは必須概念を示すための最小限の例です。プロダクション対応のコードでは、参照カウントをPyObjectにしたり、グローバル状態を取り除いたりするなど、注意が必要です。

+1

これは印象的です。実際に古いもの:) –

3

私はこの質問が古いですけど、質問の一部がまだ回答されていません。

「直接のように、パイソンのstdoutに書き込みをしないコマンドの出力をキャッチする方法: 1 + 1?ここ

(Pythonの3.4の場合)の手順は次のとおりPythonソースコードからhttps://stackoverflow.com/a/4307737/1046299

  • コピー機能PyRun_InteractiveOneObject(FILE *fp, PyObject *filename, PyCompilerFlags *flags):マルコ溶液を用い/ stderrのPythonの変数に

    1. リダイレクトSTDOUT。それは、代わりにFILE*の最初のパラメータとしてファイルpythonrun.c

    2. 新しい機能がconst char*(あなたのコマンドを)とるようにPyRun_InteractiveOneObject関数名と署名を変更するに位置しています。関数の実装でPyParser_ASTFromFileObjectの代わりにPyParser_ASTFromStringObjectを使用する必要があります。関数内で呼び出されるので、run_modをPythonののようにコピーする必要があることに注意してください。

    3. コマンドで新しい機能を呼び出します(例:1+1)。 Stdoutは出力2を受け取るはずです。

  • +0

    しかし、ありがとう – alexpov

    関連する問題