Reputation: 1373
I don't manage to export a global variable from C to Python using pybind11. The problem can be reproduced from a simple example. Let's say we have a header file (global.h) like this:
#ifndef GLOBAL_H
#define GLOBAL_H
extern int array[];
#endif
The array is defined in a C file (global.c) like this:
#include "global.h"
int array[] = {1, 2, 3, 4};
I want to export this array in a Python module using pybind11 and the following C++ file (pyglobal.cpp):
#include <pybind11/pybind11.h>
extern "C"
{
#include "global.h"
}
PYBIND11_MODULE(pyglobal, m)
{
m.attr("array") = array;
}
Everything works fine when I generate my library with CMake (CMakeLists.txt):
cmake_minimum_required(VERSION 2.8.12)
project(pyglobal)
find_package(pybind11 PATHS ${PYBIND11_DIR} REQUIRED)
pybind11_add_module(pyglobal pyglobal.cpp global.c)
But when I start a python3 shell and type
import pyglobal
I get the following error message:
> Python 3.5.2 (default, Nov 23 2017, 16:37:01)
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import pyglobal
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: AttributeError: array
What am I doing wrong here?
Upvotes: 2
Views: 3037
Reputation: 3788
That assignment is a rather unfortunate implicit cast and hence doesn't do what you think it does. The following is one way of exposing that array, assuming you have numpy installed:
#include <pybind11/pybind11.h>
#include <pybind11/numpy.h>
extern "C"
{
#include "global.h"
}
PYBIND11_MODULE(pyglobal, m)
{
auto dtype = pybind11::dtype(pybind11::format_descriptor<int>::format());
m.attr("array") = pybind11::array(dtype, {3}, {sizeof(int)}, array, nullptr);
}
If you do not know the size, you can use an empty base array and a large (fake) size. Just be sure not to iterate over the array in anything other than a range-restricted manner. Example:
#include <pybind11/pybind11.h>
#include <pybind11/numpy.h>
extern "C"
{
#include "global.h"
}
PYBIND11_MODULE(pyglobal, m)
{
auto dtype = pybind11::dtype(pybind11::format_descriptor<int>::format());
auto base = pybind11::array(dtype, {(unsigned)-1}, {sizeof(uintptr_t)});
m.attr("array") = pybind11::array(dtype, {(unsigned)-1}, {sizeof(int)}, array, base);
}
which can be used like so:
>>> import pyglobal
>>> for i in range(3):
... print(pyglobal.array[i])
...
1
3
0
>>>
but which for example can not be printed, as that would iterate over the full (unsigned)-1
size.
Upvotes: 2