Pergunta

Atualização: Problema resolvido! Veja o fundo da postagem

Preciso permitir que os desenvolvedores do Python passem uma variedade de dados embalados (neste caso, vértices) na minha API, que é uma série de interfaces C ++ expostas manualmente através da API do Python C. Minha impressão inicial com isso é usar a classe de estrutura CTYPES para permitir uma interface como esta:

class Vertex(Structure):
_fields_ = [
    ('x', c_float),
    ('y', c_float),
    ('z', c_float),
    ('u', c_float),
    ('v', c_float),
    ('color', c_int)
] 

verts = (Vertex * 3)()
verts[0] = Vertex(0.0, 0.5, 0.0, 0.0, 0.5, 0xFF0000FF)
verts[1] = Vertex(0.5, -0.5, 0.0, 0.5, -0.5, 0x00FF00FF)
verts[2] = Vertex(-0.5, -0.5, 0.0, -0.5, -0.5, 0x0000FFFF)

device.ReadVertices(verts, 3) # This is the interfaces to the C++ object

Para onde a função que estou tentando passar tem a seguinte assinatura:

void Device::ReadVertices(Vertex* verts, int count);

E o invólucro Python se parece mais disso:

static PyObject* Device_ReadVertices(Py_Device* self, PyObject* args)
{
    PyObject* py_verts;
    int count;

    if(!PyArg_ParseTuple(args, "Oi", &py_verts, &count)) 
        return NULL;

    // This Doesn't Work!
    Vertex* verts = static_cast<Vertex*>(PyCObject_AsVoidPtr(py_verts));

    self->device->ReadVertices(verts, count);

    Py_RETURN_NONE;
}

Obviamente, o maior problema que tenho é o seguinte: posso recuperar o PyObject para a estrutura, mas não tenho idéia de como o lançaria para o tipo correto. O código acima falha miseravelmente. Então, como exatamente eu permitiria ao usuário passar por mim esse tipo de dados do Python?

Agora, algumas coisas a considerar: primeiro é que eu já tenho um pouco da minha camada Python/C ++ escrita e estou perfeitamente feliz com ela (me afastei do SWIG para ter mais flexibilidade). Eu não quero recodificá-lo novamente, então preferiria uma solução que funcione com o C Api nativamente. Segundo, pretendo que a estrutura dos vértices seja predefinida no meu código C ++, então eu preferiria não ter o usuário precisar redefini-lo no python (reduz os erros dessa maneira), mas estou Não tenho certeza de como expor uma estrutura contígua como essa. Terceiro, não tenho motivos para experimentar a estrutura do CTYPES, além de não saber outra maneira de fazê -lo. Quaisquer sugestões são bem -vindas. Finalmente, como isso é (como você deve ter adivinhado) para um aplicativo gráfico, eu preferiria um método mais rápido a um conveniente, mesmo que o método mais rápido demore um pouco mais.

Obrigado por qualquer ajuda! Ainda estou me sentindo em torno de extensões de Python, por isso é uma grande ajuda para obter informações da comunidade em algumas das partes mais pegajosas.

SOLUÇÃO

Então, primeiro, obrigado a todos que lançaram suas idéias. Foram muitos pequenos petiscos que aumentaram a resposta eventual. No final, aqui está o que eu encontrei: a sugestão de Sam de usar o Struct.pack acabou sendo certa com o dinheiro. Vendo que estou usando o Python 3, tive que ajustá -lo um pouco, mas quando tudo foi dito e isso realmente conseguiu um triângulo aparecendo na minha tela:

verts = bytes()
verts += struct.pack("fffffI", 0.0, 0.5, 0.0, 0.0, 0.5, 0xFF0000FF)
verts += struct.pack("fffffI", 0.5, -0.5, 0.0, 0.5, -0.5, 0x00FF00FF)
verts += struct.pack("fffffI", -0.5, -0.5, 0.0, -0.5, -0.5, 0x0000FFFF)

device.ReadVertices(verts, 3)

Com minha tupla analisando agora parecendo:

static PyObject* Device_ReadVertices(Py_Device* self, PyObject* args)
{
    void* py_verts;
    int len, count;

    if(!PyArg_ParseTuple(args, "y#i", &py_verts, &len, &count)) 
        return NULL;

    // Works now!
    Vertex* verts = static_cast<Vertex*>(py_verts);

    self->device->ReadVertices(verts, count);

    Py_RETURN_NONE;
}

Observe que, embora eu não use o len variável neste exemplo (embora eu faça no produto final), preciso analisar a tupla usando 'y#' em vez de apenas 'y', ou então ela parará no primeiro nulo (de acordo com a documentação). Também para ser considerado: void* moldes como esse são bastante perigosos, então, por favor, carregue mais verificação de erros do que eu mostro aqui!

Então, trabalho bem feito, feliz dia, faça as malas e vá para casa, sim?

Espere! Não tão rápido! Tem mais!

Sentindo -me bem como tudo isso deu certo, decidi, por um capricho, para ver se minha tentativa anterior ainda explodiu em mim e voltou ao primeiro trecho de Python neste post. (Usando o novo código C, é claro) e ... funcionou! Os resultados foram idênticos à versão Struct.pack! Uau!

Portanto, isso significa que seus usuários têm uma escolha de como fornecerá esse tipo de dados, e seu código pode lidar sem alterações. Pessoalmente, vou incentivar o método Ctype.Structure, pois acho que facilita a legibilidade, mas realmente é o que o usuário se sentir confortável. (Heck, eles poderiam digitar manualmente uma série de bytes em hexadecimal, se quisessem. Funciona. Eu tentei.)

Honestamente, acho que esse é o melhor resultado possível, por isso estou em êxtase. Obrigado a todos novamente, e boa sorte a qualquer outra pessoa que encontre esse problema!

Foi útil?

Solução

Não testado, mas você deve tentar e informe -nos se é rápido o suficiente para suas necessidades.

No lado do Python, empacote os vértices em uma corda em vez de um objeto.

str = "" # byte stream for encoding data
str += struct.pack("5f i", vert1.x, vert1.y, vert1.z, vert1.u, vert1.v, vert1.color) # 5 floats and an int
# same for other vertices

device. ReadVertices( verts, 3) # send vertices to C library

Na biblioteca C/invólucro python, modifique seu pyargs_parsetuple para usar a string do formato "si". Isso converterá sua sequência python em uma string c (char*), que você pode digitar como um ponteiro para o seu vetor. Neste ponto, a sequência C é um fluxo de bytes/palavras/flutuadores e deve ser o que você está procurando.

Boa sorte!

Outras dicas

A coisa mais fácil que posso ver seria apenas evitar o problema e expor um dispositivo de dispositivo_readvertex que absorva x, y, z, u, v e cor como argumentos. Isso tem desvantagens óbvias, como fazer com que os programadores do Python o alimentem um por um por um.

Se isso não for bom o suficiente (parece provável que não seja), você pode tentar definir um novo tipo Python, conforme descrito aqui. É um pouco mais de código, mas acho que esse é o método "mais arquitetonicamente sólido", porque você garante que seus desenvolvedores do Python estejam usando a mesma definição de tipo que você está no código C. Ele também permite um pouco mais de flexibilidade do que uma estrutura simples (é realmente uma classe, com potencial para adicionar métodos etc.), que não tenho certeza de que você realmente precisa, mas pode ser útil mais tarde.

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