I have a function in C++
code which I want to call from Python with ctypes
. The shared library (libRunaphys.so, I'm on Linux) contains a lot of other functions but I only need to use one function. The function is called advance_runaway_population()
and it is not part of any class. I wrote the following after the function descritptions in
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);}
}
where plasma_local
and module_struct
are both custom structures and rate_values
is an array of 4 doubles. My header file looks like
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_ */
I wrote a Python script in which I want to test the C++ function:
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)
I managed to make structures from Python, though I'm not sure wheter they are suitable for C++ as well. Also I found another question regarding passing strings and I made my MODULE
struct accordingly.
The Python code runs fine until the C++
function call, where it exits with Segmentation fault. I've been searching and I found that my problem is most likely with the passing of pointers between Python and C++
. Since my C++
function requires pretty specific structures and isn't part of a class I couldn't find a tutorial to use. I was reading the ctypes
documentation too but I couldn't apply those examples to my usecase either. I primarily work in Python so I'm not too familiar with C++
, C
and ctypes
so please point out the trivial mistakes too.
How can I pass pointers to C
structures (made in Python) from Python to C++
?
If possible I would like to use ctypes
because the code should be as portable as possible, but if there is a much simpler way for me to be able to call this C++
function I am open to that solution as well.