Cython: call function from external C file - cython

After Cython's "Hello World" and the example of calling a function in the C math libraries here, what I really want to do is to have C code of my own in a separate file and use it from Cython. Following this, I modify the setup.py file:
sourcefiles = ['hello2_caller.pyx', 'hello2.c']
This is hello2.c (main is just there to compile and test it separately---though that product isn't present for the test:
#import <stdio.h>
void f() {
printf("%s", "Hello world!\n");
}
int main(int argc, const char* argv[]) {
f();
return 0;
}
This is hello2_caller.pyx
cdef extern from "hello2.c":
void f()
cpdef myf():
f()
I get:
In file included from hello2_caller.c:219:
hello2.c:3: warning: function declaration isn’t a prototype
So I guess I'm failing to provide a header in some way.. though just feeding setup.py a standard header like 'hello2.h' doesn't work. Can you point me to a working example or explain what I'm doing wrong. Thanks.

Thanks to help from the Cython users' list here.
My writeup here.
Bottom line: this is only a warning, that is not fixed by a declaration of f(), but the compiled .so works. I'm still not sure how you would provide a .h file to Cython or if there is a better way to do this.
And there's a couple of errors: should be #include, and don't list the .c file in sourcfiles.

Related

exposing nonstatic member function of class to chaiscript

I have a project that tries to implement keyboard macro scripting with chaiscript. I am writing a class based on xlib to wrap the xlib code.
I have a member function to add a modifier key to an ignored list, because of a xlib quirk.
how could i do something like the following minimal example.
#include <chaiscript/chaiscript.hpp>
#include <functional>
class MacroEngine{
public:
MacroEngine() = default;
//...
void addIgnoredMod(int modifier){
ignoredMods |= modifier;
}
//...
private:
int ignoredMods;
};
int main(int argc, char *argv[]){
MacroEngine me;
chaiscript::ChaiScript chai;
//...
chai.add(chaiscript::fun(std::bind(&MacroEngine::addIgnoredMod, me, std::placeholders::_1)), "setIgnoredMods");
//...
return 0;
}
I tried bind and failed with the following error message:
In file included from ../deps/ChaiScript/include/chaiscript/dispatchkit/proxy_functions_detail.hpp:24:0,
from ../deps/ChaiScript/include/chaiscript/dispatchkit/proxy_functions.hpp:27,
from ../deps/ChaiScript/include/chaiscript/dispatchkit/proxy_constructors.hpp:14,
from ../deps/ChaiScript/include/chaiscript/dispatchkit/dispatchkit.hpp:34,
from ../deps/ChaiScript/include/chaiscript/chaiscript_basic.hpp:12,
from ../deps/ChaiScript/include/chaiscript/chaiscript.hpp:823,
from ../src/main.cpp:2:
../deps/ChaiScript/include/chaiscript/dispatchkit/callable_traits.hpp: In instantiation of ‘struct chaiscript::dispatch::detail::Callable_Traits<std::_Bind<void (MacroEngine::*(MacroEngine, std::_Placeholder<1>))(unsigned int)> >’:
../deps/ChaiScript/include/chaiscript/language/../dispatchkit/register_function.hpp:45:72: required from ‘chaiscript::Proxy_Function chaiscript::fun(const T&) [with T = std::_Bind<void (MacroEngine::*(MacroEngine, std::_Placeholder<1>))(unsigned int)>; chaiscript::Proxy_Function = std::shared_ptr<chaiscript::dispatch::Proxy_Function_Base>]’
../src/main.cpp:21:95: required from here
../deps/ChaiScript/include/chaiscript/dispatchkit/callable_traits.hpp:99:84: error: decltype cannot resolve address of overloaded function
typedef typename Function_Signature<decltype(&T::operator())>::Signature Signature;
^~~~~~~~~
../deps/ChaiScript/include/chaiscript/dispatchkit/callable_traits.hpp:100:86: error: decltype cannot resolve address of overloaded function
typedef typename Function_Signature<decltype(&T::operator())>::Return_Type Return_Type;
^~~~~~~~~~~
I also tried to make the variable static which worked, but it wont work if I try to make it possible to ignore modifiers on a per hotkey basis.
what am i doing wrong? and how can I fix it?
You can do this instead:
chai.add(chaiscript::fun(&MacroEngine::addIgnoredMod, &me), "addIgnoredMod");
Or use a lambda:
chai.add(chaiscript::fun([&me](int modifier){ me.addIgnoredMod(modifier); }), "addIgnoredMod");
Jason Turner, the creator of Chaiscript, commented on it here: http://discourse.chaiscript.com/t/may-i-use-std-bind/244/4
"There’s really never any good reason to use std::bind. I much better solution is to use a lambda (and by much better, I mean much much better. std::bind adds to compile size, compile time and runtime)."

Cython compiling error: Saying that Array is a Struct

This is a minimally reproducible version of my Cython error. The code runs in C++.
The compiler is telling me error C2088 that "+= is illegal for struct". However, it is being passed an array.
The pyx file:
from libc.stdint cimport uint32_t as Card
from cpython cimport array
import array
cdef extern from "ace_eval.h":
void ACE_addcard(h, Card c)
def create_hand():
cdef array.array h = array.array('I',[0, 0, 0, 0, 0])
ACE_addcard(h, 257)
return h
The function imported from the header is:
#define ACE_addcard(h,c) h[c&7]+=c,h[3]|=c
I have also tried declaring my arrays using
cdef Card h[5]
array.array is a Python object that is ultimately compiled into a struct (so this is what C++ sees). Element access to it is controlled at a Python level by __getitem__ and __setitem__, which are compiled by Cython into C API function calls. When Cython sees code for an array being manipulated it'll generate the appropriate C API function calls. You code using C++ #define statements attempts to manipulate it at C++ compile time and prevents Cython from knowing what's going on.
Ideally you should be using "typed memoryviews" which give Cython quicker access to the array (but will still not work with the C++ #define since this is applied after Cython has processed the file):
cdef int[::1] h = array.array('I',[0, 0, 0, 0, 0]) # you may have to change the type long... I haven't tested it
h[257&7]+=257
h[3]|=257
If you absolutely insist on using macros instead then they need to take something with a C++ array interface. A pointer is probably the easiest option and can be got from:
cdef int* h_ptr = &h[0]
#DavidW 's second way of
cdef Card h[5]
h[:] = [0, 0, 0, 0, 0]
cdef Card* h_ptr = &h[0]
also worked once I also adjusted my cdef like so to accept the pointer. Note that the function in the #define macro is not changed and does not have return type specified.
cdef extern from "ace_eval.h":
void ACE_addcard(Card* h, Card c)
This allowed me to pass any of my arrays over flawlessly.
This is actually what it says in the docs, but it was a bit obtuse to me - hopefully my explanation helps someone else.
https://cython.readthedocs.io/en/latest/src/userguide/external_C_code.html
If the header file defines a function using a macro, declare it as though it were an ordinary function, with appropriate argument and result types.

Return a c array from a code, wrapped by cython, to python,

I am trying to run my C++ code from python, since I want to couple it with an open source software which has python interface, so I used cython for the sake of wrapping . My C++ code can be compiled from python but I have some problems in returning the results which are float vectors. I can return a single float or integer to python but not a vector. I tried to return the pointer, like what is normally done for C++ functions, but python does not recognize the pointer form C++.
here is a simplified code to show what do I mean.
my .hpp file is:
#include "mpi.h"
float my_cppfun(int D);
my .cpp file:
#include <stdio.h>
#include <stdlib.h>
#include <vector>
#include "my_cpp.hpp"
float my_cppfun(int D)
{
float DD[D];
int i;
for (i=0;i<D;i++){
DD[i]=i;
}
return DD[i-1];
//return DD instead of DD[i-1]
}
my .pyx file is:
cdef extern from "my_cpp.hpp":
float my_cppfun(int)
def my_cpp(int D):
return my_cppfun(D)
my .py file is:
from mpi4py import MPI
from pympi import my_cpp
y=my_cpp(3)
print y
There is also setup.py file and it is working at the moment. I am wondering how can I return DD array instead of single float value(DD[i-1]).
Thanks in advance for your help.
You are going to need to make a list and return that.
The documentation for List should help. PyList_New, PyList_SetItem, etc.

Accessing C arrays in cython wrapped C code from Python

I have successfully wrapped some of the api functions of a C library with Cython. I would now like to expose some of the global C arrays used within the library but I am having a tough time figuring out how to do it.
The array pointer is initialized and allocated inside epanet.c:
void initpointers()
{
H = NULL;
}
int allocdata()
{
n = MaxNodes + 1;
H = (double *) calloc(n, sizeof(double));
}
It is made global in a header file (vars.h):
extern double *H;
I can access the values in the array within the C code of course by doing something like:
for (i=1; i<=Nnodes; i++)
{
H[i] = some_value;
}
I would like to write a function in my pyepanet.pyx file like:
def printH(int Nnodes, double *H):
for i in range(1, Nnodes+1):
print H[i]
but, I am missing the necessary connection between C and cython/python. I have searched quite a bit for an answer but with no luck. Please help.
I finally found something that works. It seems that I can declare the pointer without referring to the header file. When I was trying to refer to the header file before, I think I was getting errors due to redefining items. Here is the code from my .pyx file that works now:
cdef:
extern double *H
extern int Nnodes
def printH():
cdef int i
for i in range(1,Nnodes+1):
print round(H[i], 2)

Use Tcl library with C code gets errors

softwarre: ActiveState ActiveTcl 8.5.13.296436/Win7/DEV C++ 5.4.1.
ActiveTcl is installed at D:/TCL/.
error information:
E:\src\c\tcl\main.oIn function `Tcl_AppInit':
8E:\src\c\tcl\main.cundefined reference to `_imp__Tcl_Init'
E:\src\c\tcl\main.oIn function `main':
14E:\src\c\tcl\main.cundefined reference to `_imp__Tcl_Main'
E:\src\c\tcl\collect2.exe[Error] ld returned 1 exit status
26E:\src\c\tcl\Makefile.winrecipe for target 'tcl_test.exe' failed
c source code:
#include <stdio.h>
#include <stdlib.h>
#include <tcl.h>
#include <tk.h>
int Tcl_AppInit(Tcl_Interp *interp)
{
return (Tcl_Init(interp) == (TCL_ERROR))?TCL_ERROR:TCL_OK;
}
int main(int argc, char** argv)
{
printf("--- Tcl Third-Party Shell Start ---\n");
Tcl_Main(argc, argv, Tcl_AppInit);
printf("--- Tcl Third-Party Shell End ---\n");
return 0;
}
In order to access those functions (notably, Tcl_Main) you must link your code against the Tcl DLL (which I think will be tcl85.dll in your installation); it's not a symbol that is exported through Tcl's stub linking mechanism. I don't know exactly how you do that on Windows (nor exactly where it will be located) but instructing your build environment to use the DLL should not be too difficult.
FWIW, Tcl_Init always returns either TCL_OK (i.e., 0) or TCL_ERROR (i.e., 1). You can just return the value directly from your AppInit function if you're not going to install your own commands and functionality after basic initialization.
After playing around a bit I could reproduce and solve this in Visual Studio.
You just have to add D:\Tcl\lib\tcl86.lib to the "Additional Dependencies" under "Linker/Input".
This solved the problem for me.
Edit
You could either pass Tcl_Init to Tcl_Main (If you don't have to do any specific initialization) or just return the result of Tcl_Init like this:
int Tcl_AppInit(Tcl_Interp *interp)
{
return Tcl_Init(interp);
}