Kald af C/C++ kode fra Python

I dette blog-indlæg viser jeg hvordan man fra Python kan kalde C++ kode på en Linux-maskine. Det er ikke så svært og meget anvendeligt, fordi jeg ofte med fordel kan kode ”tung” talknusnings-kode i C++, mens Python ofte er mit foretrukne valg til scripts. Roder du ikke med Python og C/C++ så læs hellere et af de andre blog-indlæg på version2.dk :-)

Til eksemplet skal jeg bruge swig, g++ og Python 2.6. På Debian klares afhængighederne således fra et terminal-vindue:

sudo apt-get install python python-dev swig g++

I det følgende kommer mit eksempel og anvisning til hvordan jeg oversætter en C++ klasse “number.h/cxx” så den kan kaldes fra Python. Eksemplet kommer delvist fra bogen Programming Python, men i denne bog var fokus på Windows. Jeg oversætter eksemplet på Linux.

C++ header filen number.h:

// number.h
class Number
{
  public:
    Number(int a);
    void add(int a);
    void display();
  private:
    int value;
};

Selve klasse-funktionerne er implementeret i number.cxx

// number.cxx
#include <iostream>
#include "number.h"
 
Number::Number(int a)
{
    value = a;
}
 
void Number::add(int a)
{
    value += a;
}
 
void Number::display()
{
    std::cout << "Value = "<< value << std::endl;
}

Fra Python vil jeg gerne kunne oprette et “Number” og og kunne anvende “add()” og “display()” funktionerne. Koden ovenfor er med vilje holdt meget simpel - i praksis vil jeg have meget større kodemængder, men pointen i dette blog-indlæg er sammenkoblingen af C++ og Python. Tricket er nu at anvende oversætteren “swig

Først skal der laves en interface-fil “number.i”, som laver et number-modul ud fra header-filen “number.h”. Den skal se således ud:

%module number
 
%{
#include "number.h"
%}
 
%include number.h

Swig kan oversætte number.i til number.py og number_wrap.cxx dvs. en C++ wrapper, som skal efterfølgende oversættes med g++. Til sidst laver jeg et shared library “_number.so” som følgende fire kommandoer:

  swig -c++ -python number.i
  g++ -fPIC number_wrap.cxx -c -g -I/usr/include/python2.6/
  g++ -fPIC number.cxx -c -g
  g++ -shared number_wrap.o number.o -o _number.so 

Nu har jeg en number.py klasse-fil, som jeg kan anvende from et Python-script “test.py”:

from number import Number
num= Number(7)
num.add(10)
num.display()

Og kører jeg “python test.py” får jeg som ventet kaldt min Number constructor i C++ klassen, der lægger 7 ind i “value”, dernæst adderer 10 med “num.add(10) og endelig viser resultatet med “num.display()”

python test.py 
Value = 17

Koden ovenfor kan checkes ud med Mercurial

hg clone https://bitbucket.org/petertoft/pythoncpp

Titlen lover at C-kode også bør være med i dette blog-indlæg. Det er næsten samme metode som ovenstående. Dog skal swig ikke have argumentet "-c++" og gcc anvendes i stedet for g++.
Koden kan hentes med

hg clone https://bitbucket.org/petertoft/pythonc

Anvisninger er i begge bitbucket-eksempler i filen README.md.

Kommentarer er meget velkomne.

/pto

Kommentarer (21)
sortSortér kommentarer
  • Ældste først
  • Nyeste først
  • Bedste først
Lars Tørnes Hansen

Man kan også kigge på Cython, der er en fork af Pyrex.
Cython er et programmeringssprog, der er Python + noget mere (mest at man kan angive en variabels type).
Der er nogle få former for Python kode der ikke virker i Cython.

Cython kode oversættes af en Cython compileren til C kode, som så kan oversættes af en C compiler til et Python modul, der bor i en en lib.so/.dll/*.dynlib fil afhængig af hvad platform der bruges.

http://cython.org/

Dokumentation:
http://docs.cython.org/

David Rechnagel Udsen

Hvilket også er en af grundene til, at når folk sammenligner hastighed mellem sprog, så har sprog Python et uretfærdigt trick i ærmet. Jeg er ikke modstander af at kode moduler i Python i C/C++ der skal være hurtige, men en sammenligning kan få folk til at tro, at Python er hurtigt, når det ikke er tilfældet. Især sammenlignet med sprog hvor man spiser sin egen kage.

Kenneth Geisshirt

Min erfaring er at det er bedre at håndkode extension til Python. På den både er det muligt at få en extension til at være mere Python-agtigt (for .. in ... og with ... er jo herlige konstruktioner). Genererede extension ender med at være C- eller C++-agtige.

Noget andet er debugging. Python 3 har support for at gdb kan skifte sprog - jeg har ikke så meget praktisk erfaring med det.

Bygger du din Python-fortolker selv er det en fordel at gøre det med valgrind-support. På den måde kan du lettere tjekke om din extension har memory leaks. Jeg har haft stor nytte af at tjekke mine extension på den måde.

Robert Larsen
David Rechnagel Udsen

Prøv at uddyb (udover drilleriet i det)

Der er intet at uddybe. Hr. Nejsum ignorerer bevist mit indlæg og begynder at tale om udviklingsprocessen i Python, hvilket han godt ved ikke er hvad jeg taler om (se min parentes i det indlæg du citerede).

Jeg taler om at selve kørselshastigheden kan være misvisende, når man sammenligner Python med sprog, hvor man ikke implementerer standardbiblioteket i C eller C++.

Rene Nejsum

Måske kunne jeg godt gætte at nogle tænker hastighed som hvor hurtigt man kan afvikle en for-loop fra 0 til 1000000.

En gang imellem er der nok nogen der får værdi af at det går hurtigt, men hos mine kunder er der langt imellem nogen der tænker på det.

Stadig uden at drille, så tænk på at CPython (standard Python fortolkeren) er skrevet i C, alligevel er CPython på mange områder er langsommere end PyPy, der er skrevet i Python. Faktisk er både Jython (skrevet i Java) og IronPython (C#) ofte hurtigere end CPython.

Så både på den ene og den anden måde er det lidt for nemt/simpelt at sige at C er hurtigere end fx. Python.

Adam Tulinius

Undskyld meget, men dit indlæg er en mærkelig sludder for en sladder, der ignorerer virkeligheden groft, og til tider er faktuelt forkert.

alligevel er CPython på mange områder er langsommere end PyPy, der er skrevet i Python

PyPy er skrevet i RPython, hvilket er en meget begrænset udgave af Python, og oversættes til noget mere low level snask. Ud over de syntaktiske ligheder mellem RPython og Python er der altså en kæmpe stor forskel på de to sprog.

Det er, forøvrigt, i det store og hele irrelevant hvilket sprog noget er skrevet i, og mere interessant hvad det oversættes til, og hvilken platform det afvikles på.

Grunden til at PyPy kan være hurtigere end CPython er at PyPy bruger jit. Det er, mig bekendt, fravalgt i CPython for at holde CPython simplere. Alt i alt siger det altså intet om den performance man kan få ud af C-kode.

Rene Nejsum

Diskussioner om hastighed i sprog - uden præmisser - kan kun blive en sludder for en sladder. (omend en god én af slagsen :-))

Den største forskel mellem Python og RPython, er at man ikke må re-assigne variable til andre typer. (a=10 må ikke bagefter blive a="Hello"). Så "meget begrænset" er jeg ikke enig i.

Til gengæld er det en gang grundig vrøvl at C-programmer kan JIT optimeres. JIT kan kun gøres i en VM. (Som PyPy, Java og CLR)

Men, jeg er med på at vi er OT og det snart er weekend, så lad os bare lade sladderen ligge for nu...

Adam Tulinius

Til gengæld er det en gang grundig vrøvl at C-programmer kan JIT optimeres. JIT kan kun gøres i en VM. (Som PyPy, Java og CLR)

Det har jeg heller aldrig påstået. Jeg påpegede blot at Guido har fravalgt at implementere en jit'ed vm i CPython.

Den største forskel mellem Python og RPython, er at man ikke må re-assigne variable til andre typer. (a=10 må ikke bagefter blive a="Hello"). Så "meget begrænset" er jeg ikke enig i.

Ja, de har selvfølgelig fjernet alle stumper der gør det svært at analysere kode. Det er tit også den slags der koster mange CPU-cykler.
I den forbindelse er http://blog.headius.com/2012/10/so-you-want-to-optimize-ruby.html en spændende gennemgang af hvilke dele af Ruby der gør det svært at lave en hurtig implementation; gad vide om der findes en tilsvarende artikel omhandlende Python?

Mogens Hansen

Til gengæld er det en gang grundig vrøvl at C-programmer kan JIT optimeres. JIT kan kun gøres i en VM. (Som PyPy, Java og CLR)

Hvad i C (eller C++) gør at det ikke kan JIT compileres ?

Det står ikke i sprog specifikationerne at programmerne skal oversættes til platform specifik assembler instruktioner.

Man kan godt JIT compilere C (og C++) programmer - og det bliver gjort.
Tag et kig på Clang + LLVM arkitekturen (http://www.aosabook.org/en/llvm.html).
Clang parser C eller C++ koden, og kan generere LLVM IR, der så kan distribueres og JIT compileres og eksekveres af LLVM toolet lli (http://llvm.org/docs/CommandGuide/lli.html)

Log ind eller Opret konto for at kommentere