使用ctypes.cdll.LoadLibrary从Python加载库时,ELF标头无效

时间:2011-11-25 01:01:54

标签: c++ python g++ ctypes

我刚开始使用Linux上的gcc。我正在学习教程here,除了我正在使用g ++编译器。

hello_fn.cpp

#include <stdio.h>
#include "hello.h"

void 
hello (const char * name)
{
  printf ("Hello, %s!\n", name);
}

bye_fn.cpp

#include <stdio.h>
#include "hello.h"

void 
bye (void)
{
  printf ("Goodbye!\n");
}

hello.h

void hello (const char * name);
void bye (void);

然后我在shell中运行以下命令:

$ g++ -Wall -c hello_fn.cpp
$ g++ -Wall -c bye_fn.cpp
$ ar cr libhello.a hello_fn.o bye_fn.o

然后我从python尝试以下内容:

Python 2.7.1+ (r271:86832, Apr 11 2011, 18:05:24) 
[GCC 4.5.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import ctypes 
>>> test = ctypes.cdll.LoadLibrary(r'/home/oob/development/libtest/libhello.a')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.7/ctypes/__init__.py", line 431, in LoadLibrary
    return self._dlltype(name)
  File "/usr/lib/python2.7/ctypes/__init__.py", line 353, in __init__
    self._handle = _dlopen(self._name, mode)
OSError: /home/jeff/development/libtest/libhello.a: invalid ELF header

我的想法是用c ++编写一些函数并用Python调用它们。有什么想法吗?

更新:我能够让事情变得“有效”。根据Cat Plus Plus的说法,我可能不会为新代码指明这个方向,但是我能够使用一个从Windows移植到Linux的大型遗留c ++库。我们需要一个前端来从这个库调用一些长时间运行的函数,所以我认为Python可能是最简单的。这些函数创建了大量输出并且只返回一个整数返回码,所以也许我可以避免Cat Plus Plus所说的“痛苦”的东西。

这就是我所做的。

修改了hello_fn.cpp

#include <stdio.h>
#include "hello.h"

extern "C" int 
hello (void)
{
  return 16;
}                                                                                

修改by_fn.cpp

#include <stdio.h>
#include "hello.h"

extern "C" void 
bye (void)
{
  printf ("Goodbye!\n");
}

修改了hello.h

extern "C" int hello (void);
extern "C" void bye (void);

buildscript.sh

#!/bin/bash

rm *.o
rm *.so

g++ -fpic -g -c -Wall hello_fn.cpp
g++ -fpic -g -c -Wall bye_fn.cpp
#make a shared library, not a static library (thanks cat plus plus)
g++ -shared -o libhello.so hello_fn.o bye_fn.o

test.py

#!/usr/bin/python

import ctypes

c = ctypes.cdll.LoadLibrary(r'/home/jeff/development/libtest/libhello.so')
a = c.hello()
print 'hello was ' + str(a)
c.bye()

在终端尝试....

oob@ubuntu:~/development/libtest$ ./build_script.sh 
oob@ubuntu:~/development/libtest$ python test.py 
hello was 16
Goodbye!

我们的遗留库并没有真正使用任何特定于Windows的c ++内容(感谢编写该代码的人),因此它是一个非常简单的端口。我们有几个函数使用extern“C”来公开函数。对于端口,我做了以下更改:

#ifdef LINUX
#define __stdcall
#endif
#ifdef WINDOWS
#define __stdcall __stdcall
#endif

对于我们的一个功能,我可以保持不变,例如:

extern "C" long __stdcall reform_proj {
    //do a bunch of stuff
    return 0;
}

1 个答案:

答案 0 :(得分:4)

ctypes用于加载共享库。 ar创建目标文件的存档,也称为静态库。您无法使用ctypes加载该文件,只有链接器才能理解它。

另一个问题是,通过ctypes使用C ++共享库是痛苦的,如果不是完全不可能的话。只是不要。请改用Cython,并编写一个与C ++代码接口的正确Python扩展(然后您可以静态或动态地链接它,并且它将起作用)。

另一个选项是Boost.Python,但它的文档记录较少,但有一个好处就是直接在C ++代码中定义Python模块,而不是使用另一种语言编写的包装器。

第三是SWIG,但我从未使用它,所以无法告诉你它在实践中的效果如何。