Pergunta

Eu estou apenas começando com ctypes e gostaria de usar uma classe C ++ que eu exportado em um arquivo DLL de dentro python usando ctypes. Então, vamos dizer que o meu C ++ olhares de código algo como isto:

class MyClass {
  public:
    int test();
...

eu gostaria de saber criar um arquivo .dll que contém essa classe e, em seguida, carregar o arquivo .dll em python usando ctypes. Agora como eu ia criar um objeto do tipo MyClass e chamar sua função de teste? Isso é possível com ctypes? Alternativamente eu consideraria usando SWIG ou Boost.Python mas ctypes parece ser a opção mais fácil para pequenos projetos.

Foi útil?

Solução

A história curta é que não há nenhuma interface binária padrão para C ++ no caminho que há para C. Diferentes compiladores de saída diferentes binários para o mesmo C ++ bibliotecas dinâmicas, devido à desconfiguração do nome e maneiras diferentes de lidar com a pilha entre biblioteca chamadas de função.

Então, infelizmente, não há realmente uma maneira portátil para acessar as bibliotecas C ++ em geral . Mas, para um compilador de cada vez, não é nenhum problema.

este post também tem um breve resumo de por que este trabalho atualmente não. Talvez depois C ++ 0x vem de fora, nós vamos ter um ABI padrão para C ++? Até então, você provavelmente não vai ter qualquer forma de acesso classes C ++ através ctypes do Python.

Outras dicas

Além Boost.Python (que é provavelmente uma solução mais amigável para projetos maiores que requerem mapeamento um-para-um de classes C ++ para as classes Python), você poderia fornecer no C ++ lado de uma interface C. É uma solução de muitos por isso tem as suas próprias soluções de compromisso, mas vou apresentá-lo para o benefício daqueles que não estão familiarizados com a técnica. Para a divulgação completa, com esta abordagem não seria uma interface C ++ para pitão, mas C ++ para C para Python. Abaixo eu incluí um exemplo que atenda às suas necessidades para mostrar-lhe a idéia geral do externo "c" facilidade de compiladores C ++.

//YourFile.cpp (compiled into a .dll or .so file)
#include <new> //For std::nothrow
//Either include a header defining your class, or define it here. 

extern "C"  //Tells the compile to use C-linkage for the next scope.
{
    //Note: The interface this linkage region needs to use C only.  
    void * CreateInstanceOfClass( void )
    {
        // Note: Inside the function body, I can use C++. 
        return new(std::nothrow) MyClass;
    }

    //Thanks Chris. 
    void DeleteInstanceOfClass (void *ptr)
    {
         delete(std::nothrow) ptr; 
    }

    int CallMemberTest(void *ptr)
    {

        // Note: A downside here is the lack of type safety. 
        // You could always internally(in the C++ library) save a reference to all 
        // pointers created of type MyClass and verify it is an element in that
        //structure. 
        //
        // Per comments with Andre, we should avoid throwing exceptions.  
        try
        {
            MyClass * ref = reinterpret_cast<MyClass *>(ptr);
            return ref->Test();
        }
        catch(...)
        {
           return -1; //assuming -1 is an error condition. 
        }
    }

} //End C linkage scope.

Você pode compilar esse código com

gcc -shared -o test.so test.cpp
#creates test.so in your current working directory.

Em seu código python que você poderia fazer algo assim (interativo prompt de de 2,7 mostrado):

>>> from ctypes import cdll
>>> stdc=cdll.LoadLibrary("libc.so.6") # or similar to load c library
>>> stdcpp=cdll.LoadLibrary("libstdc++.so.6") # or similar to load c++ library
>>> myLib=cdll.LoadLibrary("/path/to/test.so")
>>> spam = myLib.CreateInstanceOfClass()
>>> spam
[outputs the pointer address of the element]
>>> value=CallMemberTest(spam)
[does whatever Test does to the spam reference of the object] 

Eu tenho certeza Boost.Python faz algo semelhante sob o capô, mas talvez compreender os conceitos níveis mais baixos é útil. Eu ficaria mais animado com este método se você estivesse tentando funcionalidade de acesso de uma biblioteca C ++ e um mapeamento um-para-um não era necessária.

Para obter mais informações sobre verificação de interação C / C ++ esta página da Sun: http : //dsc.sun.com/solaris/articles/mixing.html#cpp_from_c

A resposta por AudaAero é muito bom, mas não completo (pelo menos para mim).

No meu sistema (Debian estiramento x64 com GCC e G ++ 6.3.0, Python 3.5.3) Tenho segfaults, logo tem que chamo uma função membro que acesso um valor membro da classe. I diagnosticado imprimindo valores de ponteiro na saída padrão que o ponteiro * vazio codificada em 64 bits de invólucros está representado em 32 bits em Python. Assim grandes problemas ocorre quando ele é passado de volta para um invólucro de função de membro.

A solução que encontrei é a mudança:

spam = myLib.CreateInstanceOfClass()

Em

Class_ctor_wrapper = myLib.CreateInstanceOfClass
Class_ctor_wrapper.restype = c_void_p
spam = c_void_p(Class_ctor_wrapper())

Assim, duas coisas estavam faltando:. Definindo o tipo de retorno para c_void_p (o padrão é int) e , em seguida, criar um objeto c_void_p (e não apenas um inteiro)

Eu gostaria de poder ter escrito um comentário mas eu ainda não têm 27 pontos rep.

Esta é uma breve explicação sobre como usar C e C ++ com C_types em python. Como escrever um DLL / SO em C ++ para Python

de AudaAero e Gabriel Devillers responder, eu iria completar a criação da instância objeto de classe por: stdc=c_void_p(cdll.LoadLibrary("libc.so.6")) utilizando o tipo de dados ctypes c_void_p assegura a representação adequada do ponteiro objecto de classe dentro do pitão.

Certifique-se também que a gestão de memória do dll ser tratado pelo dll (memória alocada na DLL deve ser desalocada também na dll, e não em python)!

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top