【问题标题】:Segmentation fault when calling a more complex C++ function from Python with ctypes [closed]使用 ctypes 从 Python 调用更复杂的 C++ 函数时出现分段错误 [关闭]
【发布时间】:2021-07-12 19:37:17
【问题描述】:

我在C++ 代码中有一个函数,我想用ctypes 从Python 调用它。共享库(libRunaphys.so,我在 Linux 上)包含许多其他功能,但我只需要使用一个功能。该函数称为advance_runaway_population(),它不属于任何类。我在

中的功能描述之后写了以下内容

control.cpp

extern "C"
{
double Runaphys_advance_runaway_population(const plasma_local &plasma_local, double timestep, double inv_asp_ratio, double rho_tor_norm, module_struct const &modules, double *rate_values){return advance_runaway_population(plasma_local, timestep, inv_asp_ratio, rho_tor_norm, modules, rate_values);}
}

其中plasma_localmodule_struct 都是自定义结构,rate_values 是一个包含 4 个双精度的数组。我的头文件看起来像

control.h

#ifndef CONTROL_H_
#define CONTROL_H_
    
#include <vector>
#include <string>
#include "plasma_structures.h"
    

struct module_struct {
    std::string dreicer_formula;
    bool dreicer_toroidicity;
    std::string avalanche_formula;
    bool avalanche_toroidicity;
    bool hdf5_output;
    double warning_percentage_limit;
    double rho_edge_calculation_limit;
    
};

#ifdef __cplusplus
extern "C" {
#endif

double advance_runaway_population(const plasma_local &plasma_local, double timestep, double inv_asp_ratio, double rho_tor_norm, module_struct const &modules, double *rate_values);
    
#ifdef __cplusplus
}
#endif

int list_parameter_settings(module_struct const &modules);

#endif /* CONTROL_H_ */

我写了一个 Python 脚本,我想在其中测试 C++ 函数:

python_constructor.py

import ctypes as ct
from os import path

class MODULE(ct.Structure):
    _fields_ = [("dreicer_formula", ct.c_char_p),
            ("dreicer_toroidicity", ct.c_bool),
            ("avalanche_formula", ct.c_char_p),
            ("avalanche_toroidicity", ct.c_bool),
            ("hdf5_output", ct.c_bool),
            ("warning_percentage_limit", ct.c_double),
            ("rho_edge_calculation_limit", ct.c_double)]

string1 = "string 1"
string2 = "string 2"
# create byte objects from the strings
dreicer_formula1 = string1.encode('utf-8')
avalanche_formula1 = string2.encode('utf-8')

dreicer_toroidicity = ct.c_bool(True)
avalanche_toroidicity = ct.c_bool(True)
hdf5_output = ct.c_bool(False)
warning_percentage_limit = ct.c_double(1.0)
rho_edge_calculation_limit = ct.c_double(0.85)

modules = MODULE(dreicer_formula1, dreicer_toroidicity, avalanche_formula1, avalanche_toroidicity, hdf5_output, warning_percentage_limit,rho_edge_calculation_limit)

#get the pointer to the structure
modules_pointer = ct.byref(modules)

class PLASMA(ct.Structure):
    _fields_ = [("rho", ct.c_double),
            ("electron_density", ct.c_double),
            ("electron_temperature", ct.c_double),
            ("effective_charge", ct.c_double),
            ("electric_field", ct.c_double),
            ("magnetic_field", ct.c_double),
            ("runaway_density", ct.c_double)]

rho = ct.c_double(1.8)
electron_density = ct.c_double(1e21)
electron_temperature = ct.c_double(1e5)
effective_charge = ct.c_double(1.0)
electric_field = ct.c_double(1.0)
magnetic_field = ct.c_double(2.0)
runaway_density = ct.c_double(3e15)

plasma_local = PLASMA(rho, electron_density, electron_temperature, effective_charge, electric_field, magnetic_field, runaway_density)
plasma_pointer = ct.byref(plasma_local)

basepath = path.dirname("python_constructor.py")
library_path = path.abspath(path.join(basepath, "..", "build/src/libRunaphys.so"))
lib_Runaphys = ct.CDLL(library_path)
adv_RE_pop = lib_Runaphys.Runaphys_advance_runaway_population
adv_RE_pop.restype = ct.c_double

timestep = ct.c_double(1e-3)
inv_asp_ratio = ct.c_double(0.30303)
rho_tor_norm = ct.c_double(0.65)
rate_values_type = ct.c_double * 4
rate_values = rate_values_type(0.,0.,0.,0.)
rate_values_pointer = ct.byref(rate_values)

adv_RE_pop.argtypes = [ct.POINTER(PLASMA), ct.c_double, ct.c_double, ct.c_double, ct.POINTER(MODULE), ct.POINTER(rate_values_type)]

answer = adv_RE_pop(ct.byref(plasma_local), timestep, inv_asp_ratio, rho_tor_norm, ct.byref(modules), ct.byref(rate_values))

print(answer)

我设法用 Python 制作了结构,但我不确定它们是否也适用于 C++。我还发现了另一个关于传递字符串的问题,并相应地创建了 MODULE 结构。

Python 代码运行良好,直到调用 C++ 函数,并在此处退出并出现分段错误。我一直在搜索,我发现我的问题很可能与 Python 和 C++ 之间的指针传递有关。由于我的C++ 函数需要非常特定的结构并且不是课程的一部分,因此我找不到要使用的教程。我也在阅读ctypes 文档,但我也无法将这些示例应用到我的用例中。我主要使用 Python 工作,所以我对C++Cctypes 不太熟悉,所以也请指出一些小错误。

如何将指向C 结构(用Python 制作)的指针从Python 传递到C++

如果可能的话,我想使用ctypes,因为代码应该尽可能可移植,但如果有更简单的方法让我能够调用此C++ 函数,我愿意接受该解决方案,因为嗯。

【问题讨论】:

  • “由于我的 C++ 函数需要非常特定的结构,并且不是类的一部分……” 将它包装成一个适合您与 python 交互的需要的结构。
  • 原来的C++ 库不仅由我使用,而且已经发布。我将不得不在某个时候将我的修改合并到主分支中,但我希望尽可能地保持原始库的轻量级。基于此,您是否仍然认为我最好的选择是将代码包装在C++ 中? (我不介意写一个长的 Python 脚本。)
  • 另一个问题:如何将指向C 结构(用Python 制作)的指针从Python 传递到C++
  • 我相信你应该使用 Python 的 c-API 来做到这一点。要与 c++ 代码交互,最好使用一些帮助框架来编写包装器代码(包装意味着您不需要在该原始库中 merge ,而是将 python 与您自己的)结合起来)使用一些帮助框架,例如boost::python.

标签: python c++ c ctypes


【解决方案1】:

这是一个工作示例。 module_struct std::string 值无法在 Python 中生成,因此使用具有 C 类型的包装器结构从 Python 传递到 C,extern "C" 函数获取该包装器类实例并将其转换为适当的 C++类实例。

此外,您不必将每个参数都包装在 ctypes 类型中。如果.argtypes 声明正确,ctypes“知道”包装器类型。

对于传递rate_valuesct.byref(rate_values) 等价于double(*)[4] 类型。不要使用ct.byrefc_double * 4 数组将作为ct.POINTER(double) 传递。这类似于 C 数组衰减为指针作为参数。

控制.cpp:

#include <string>

#ifdef _WIN32
#   define API __declspec(dllexport)
#else
#   define API
#endif

struct plasma_local
{
    double rho;
    double electron_density;
    double effective_charge;
    double electric_field;
    double magnetic_field;
    double runaway_density;
};

// ctypes-compatible structure
struct module_struct_wrap
{
    const char* dreicer_formula;
    bool dreicer_toroidicity;
    const char* avalanche_formula;
    bool avalanche_toroidicity;
    bool hdf5_output;
    double warning_percentage_limit;
    double rho_edge_calculation_limit;
    
};

// C++ structure
struct module_struct
{
    std::string dreicer_formula;
    bool dreicer_toroidicity;
    std::string avalanche_formula;
    bool avalanche_toroidicity;
    bool hdf5_output;
    double warning_percentage_limit;
    double rho_edge_calculation_limit;
};

// dummy C++ function to validate parameters were passed correctly.
double advance_runaway_population(
        const plasma_local &plasma_local,
        double timestep,
        double inv_asp_ratio,
        double rho_tor_norm,
        module_struct const &modules,
        double *rate_values)
{
    printf("plasma_local(%g,%g,%g,%g,%g,%g)\n",
        plasma_local.rho,
        plasma_local.electron_density,
        plasma_local.effective_charge,
        plasma_local.electric_field,
        plasma_local.magnetic_field,
        plasma_local.runaway_density);
    printf("t=%g,iar=%g,rtn=%g\n",timestep,inv_asp_ratio,rho_tor_norm);
    printf("module_struct(%s,%d,%s,%d,%d,%g,%g)\n",
        modules.dreicer_formula.c_str(),
        modules.dreicer_toroidicity,
        modules.avalanche_formula.c_str(),
        modules.avalanche_toroidicity,
        modules.hdf5_output,
        modules.warning_percentage_limit,
        modules.rho_edge_calculation_limit);
    for(int i = 0; i < 4; ++i)
        printf("rate_values[%d] = %g\n",i,rate_values[i]);
    return 1.0;
}

// ctypes-compatible wrapper function
extern "C" API
double Runaphys_advance_runaway_population(
        const plasma_local &plasma_local,
        double timestep,
        double inv_asp_ratio,
        double rho_tor_norm,
        module_struct_wrap const &modules_wrap,
        double *rate_values)
{
    // convert ctypes-compatible structure to required C++ structure.
    module_struct modules;
    modules.dreicer_formula = modules_wrap.dreicer_formula;
    modules.dreicer_toroidicity = modules_wrap.dreicer_toroidicity;
    modules.avalanche_formula = modules_wrap.avalanche_formula;
    modules.avalanche_toroidicity = modules_wrap.avalanche_toroidicity;
    modules.hdf5_output = modules_wrap.hdf5_output;
    modules.warning_percentage_limit = modules_wrap.warning_percentage_limit;
    modules.rho_edge_calculation_limit = modules_wrap.rho_edge_calculation_limit;
    return advance_runaway_population(plasma_local, timestep, inv_asp_ratio, rho_tor_norm, modules, rate_values);
}

test.py

import ctypes as ct

class MODULE(ct.Structure):
    _fields_ = [("dreicer_formula", ct.c_char_p),
                ("dreicer_toroidicity", ct.c_bool),
                ("avalanche_formula", ct.c_char_p),
                ("avalanche_toroidicity", ct.c_bool),
                ("hdf5_output", ct.c_bool),
                ("warning_percentage_limit", ct.c_double),
                ("rho_edge_calculation_limit", ct.c_double)]

class PLASMA(ct.Structure):
    _fields_ = [("rho", ct.c_double),
            ("electron_density", ct.c_double),
            ("electron_temperature", ct.c_double),
            ("effective_charge", ct.c_double),
            ("electric_field", ct.c_double),
            ("magnetic_field", ct.c_double),
            ("runaway_density", ct.c_double)]

modules = MODULE(b'string 1',True,b'string 2',True,False,1.0,0.85)
plasma_local = PLASMA(1.8,1e21,1e5,1.0,1.0,2.0,3e15)

lib_Runaphys = ct.CDLL('./control')
adv_RE_pop = lib_Runaphys.Runaphys_advance_runaway_population
adv_RE_pop.argtypes = ct.POINTER(PLASMA),ct.c_double,ct.c_double,ct.c_double,ct.POINTER(MODULE),ct.POINTER(ct.c_double)
adv_RE_pop.restype = ct.c_double

rate_values = (ct.c_double * 4)(0.,0.,0.,0.)
answer = adv_RE_pop(ct.byref(plasma_local),1e-3,0.30303,.65,ct.byref(modules),rate_values)
print(answer)

输出:

plasma_local(1.8,1e+21,100000,1,1,2)
t=0.001,iar=0.30303,rtn=0.65
module_struct(string 1,1,string 2,1,0,1,0.85)
rate_values[0] = 0
rate_values[1] = 0
rate_values[2] = 0
rate_values[3] = 0
1.0

【讨论】:

  • 非常感谢您花时间做一个完整的解决方案!它也适用于我。
猜你喜欢
  • 2018-11-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-04
  • 2011-07-02
相关资源
最近更新 更多