Aleph
Aleph

Reputation: 1373

Pybind11 and global C variables

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

Answers (1)

Wim Lavrijsen
Wim Lavrijsen

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

Related Questions