2013-04-25 2 views
1

Я пытаюсь использовать Python, чтобы открыть диалоговое окно для ввода ввода в мое приложение на C++.PyObject segfault при вызове функции

Вот очень минимальное представление о том, что я пытаюсь сделать:

#include <iostream> 
#include <Python.h> 

int main() 
{ 
    /* Begin Python Ititialization - only needs to be done once. */ 
    PyObject *ip_module_name = NULL; 
    PyObject *ip_module = NULL; 
    PyObject *ip_module_contents = NULL; 
    PyObject *ip_module_getip_func = NULL; 

    Py_Initialize(); 
    PyEval_InitThreads(); 

    ip_module_name  = PyString_FromString("get_ip"); 
    ip_module   = PyImport_Import(ip_module_name); 
    ip_module_contents = PyModule_GetDict(ip_module); 
    ip_module_getip_func = PyDict_GetItemString(ip_module_contents, "get_ip_address"); 
    /* End Initialization */ 

    PyGILState_STATE state = PyGILState_Ensure(); 
    PyObject *result = PyObject_CallObject(ip_module_getip_func, NULL); 

    if(result == Py_None) 
     printf("None\n"); 
    else 
     printf("%s\n", PyString_AsString(result)); 

    PyGILState_Release(state); 

    /* This is called when the progam exits. */ 
    Py_Finalize(); 
} 

Однако, когда я вызываю функцию с PyObject_CallObject, приложение возвращает ошибку сегментации. Я предполагаю, что это потому, что я использую библиотеку Tk. Я попытался связать свое приложение с _tkinter.lib, tk85.lib, tcl85.lib, tkstub85.lib, tclstub85.lib, и ничто из этого не помогает. Я очень озадачен ...

Вот сценарий:

import Tkinter as tk 
from tkSimpleDialog import askstring 
from tkMessageBox import showerror 

def get_ip_address(): 

    root = tk.Tk() 
    root.withdraw() 

    ip = askstring('Server Address', 'Enter IP:') 

    if ip is None: 
     return None 

    ip = ip.strip() 

    if ip is '': 
     showerror('Error', 'Please enter a valid IP address') 
     return get_ip_address() 

    if len(ip.split(".")) is not 4: 
     showerror('Error', 'Please enter a valid IP address') 
     return get_ip_address() 

    for octlet in ip.split("."): 
     x = 0 

     if octlet.isdigit(): 
      x = int(octlet) 
     else: 
      showerror('Error', 'Please enter a valid IP address') 
      return get_ip_address() 

     if not (x < 256 and x >= 0): 
      showerror('Error', 'Please enter a valid IP address') 
      return get_ip_address() 

    return ip 

Edit: добавлена ​​мою установку поточной

+0

Это segfault немедленно, не делая ничего? Использует ли ваш код C потоки? – user4815162342

+0

Это segfaults прямо в корне = tk.Tk(). Если я удалю код окна и установил ip на что-то вроде «127.0.0.1», он будет работать по назначению. – Neil

+0

Я использую потоки, есть ли что-то, что мне нужно сделать, прежде чем вызывать мою функцию в дополнение к настройке протектора (PyEval_InitThreads() и т. Д. После PyInitialize())? – Neil

ответ

2

Добавить PySys_SetArgv(argc, argv) (наряду с int argc, char **argv параметров main), и ваш код будет работать.

tk.Tk() Доступ к sys.argv, которого не существует, если только PySys_SetArgv не был вызван. Это вызывает исключение, которое распространяется из get_ip и сообщается Python/C по PyObject_CallObject, возвращая NULL. NULL хранится до result и передается в PyString_AsString, что является непосредственной причиной наблюдаемого сбоя.

Несколько замечаний по коду:

  • потребовалось усилие, чтобы отладить это потому, что код не делает никакой проверки вообще ошибка, она слепо проталкивается вперед, пока она не выходит из строя из-за прохождения NULL указатели вокруг. Наименьшее можно сделать, это написать что-то вроде:

    if (!ip_module_name) { 
        PyErr_Print(); 
        exit(1); 
    } 
    // and so on for every PyObject* that you get from a Python API call 
    

    В реальном коде вы не exit() бы, но сделать некоторые очистки и возврата NULL (или поднять C++ - исключение уровня, или что-то подходит).

  • Нет необходимости звонить PyGILState_Ensure в теме, которую вы уже знаете, содержит GIL. Как заявляет documentation of PyEval_InitThreads, он инициализирует GIL, и приобретает. Вам нужно только повторно получить GIL при вызове Python из обратного вызова C, который возникает, например, из цикла событий инструментария, который не имеет ничего общего с Python.

  • Новые ссылки, полученные от Python, должны быть Py_DECREF 'ed раз больше не нужен. Сводный подсчет может быть опущен из минимального примера для краткости, но его всегда следует учитывать.

+0

Охххх хорошо. Да, это заставило его работать. Спасибо огромное! – Neil

+1

@nbsdx Нет проблем. Обратите внимание на дополнительные замечания о стиле кода - ошибка была бы намного легче поймать с хорошими методами кодирования. В любом случае, удача взломала Python/C! – user4815162342

+0

Да, я спешил и раньше не работал с Python или Python/C. Я согласен на 100% с комментариями стиля кода. Я должен делать проверку ошибок, я просто спешил ха-ха. Еще раз спасибо :) – Neil

Смежные вопросы