Pergunta

Estou trabalhando em uma biblioteca que permite que seus usuários (outras bibliotecas que residem no mesmo processo) troquem buffers e fluxos de dados.A biblioteca deve ser utilizável tanto no código MSVC quanto no mingw (mais compatibilidade não faz mal, mas não é estritamente necessária).Para conseguir isso, a funcionalidade principal deve ser fornecida a partir de uma interface pequena e compatível com o compilador que pode ser ocultada posteriormente por uma camada de conveniência que é compilada com o código do cliente.

Um aspecto desafiador da biblioteca é que ela precisa ser extensível, para que os clientes possam fornecer suas próprias implementações de buffer e fluxo, mas a interface principal da biblioteca deve permanecer estável depois de lançada.Se você estiver interessado em mais informações, você pode ler sobre isso no discussão do tópico do fórum.

Tentei aprender sobre problemas de compatibilidade binária entre compiladores, mas como sou novo neste tópico, estaria interessado em comentários sobre meu resultado.Não estou interessado no comportamento definido por padrões aqui (as estruturas provavelmente falham nesse teste), apenas na compatibilidade entre mingw e MSVC e talvez outros compiladores, se for convenientemente possível.

Em particular, as estruturas serão compatíveis?Eles consistem uniformemente em ponteiros de função, então não acho que o preenchimento seja um problema.Além disso, a convenção stdcall é necessária aqui ou cdecl funcionaria da mesma forma?Posso deixar isso sem especificação, já que ambos os compiladores terão como padrão o cdecl?Eu devo?Aqui está o que tenho agora:

#include <stdint.h>

typedef struct {
        uint32_t (__stdcall *read)(void*, uint8_t*, uint32_t);
        void (__stdcall *write)(void*, const uint8_t*, uint32_t);
        uint32_t (__stdcall *getBytesLeft)(void*);
        uint8_t (__stdcall *destroy)(void*);
} SharedStreamInterface;

typedef struct {
        uint32_t (__stdcall *read)(void*, uint8_t*, uint32_t);
        void (__stdcall *write)(void*, const uint8_t*, uint32_t);
        uint32_t (__stdcall *getBytesLeft)(void*);
        uint8_t (__stdcall *destroy)(void*);

        uint32_t (__stdcall *getreadpos)(void*);
        uint32_t (__stdcall *getwritepos)(void*);
        uint32_t (__stdcall *getlength)(void*);
        void (__stdcall *setreadpos)(void*, uint32_t);
        void (__stdcall *setwritepos)(void*, uint32_t);
        void (__stdcall *setlength)(void*, uint32_t);
} SharedBufferInterface;

extern "C" {
        // Functions applicable for both buffers and streams
        __stdcall uint32_t readData(uint32_t id, uint8_t* data, uint32_t size);
        __stdcall void writeData(uint32_t id, const uint8_t* data, uint32_t size);
        __stdcall uint32_t getBytesLeft(uint32_t id);
        __stdcall void destroyStreamOrBuffer(uint32_t id);
        __stdcall uint8_t streamOrBufferExists(uint32_t id);

        // Functions only applicable for buffers
        __stdcall uint32_t getReadPos(uint32_t id);
        __stdcall uint32_t getWritePos(uint32_t id);
        __stdcall uint32_t getLength(uint32_t id);
        __stdcall void setReadPos(uint32_t id, uint32_t pos);
        __stdcall void setWritePos(uint32_t id, uint32_t pos);
        __stdcall void setLength(uint32_t id, uint32_t length);
        __stdcall uint8_t bufferExists(uint32_t id);

        // Adding new buffers/Streams
        __stdcall uint32_t addStream(SharedStreamInterface *interface, void *stream);
        __stdcall uint32_t addBuffer(SharedBufferInterface *interface, void *buffer);
}

Editar:O projeto a que se destinava está parado há algum tempo e provavelmente precisará de muito repensar se for arquivado novamente.Estou deixando a pergunta em aberto, porque ainda estou interessado na resposta.

Foi útil?

Solução

Sim, eles serão compatíveis.Essa é a beleza com structS.Contanto que você não introduza problemas de preenchimento (o que de fato não seria o caso aqui, como você apontou corretamente) ou em C++ adicione funcionalidade ao structs que resultará em layouts vtable - específicos do compilador, isso será sempre compatível.

Você também notará que nos cabeçalhos do Windows as declarações C das interfaces COM usam structé da mesma maneira que você.

Nota:o SharedStreamInterface::destroy membro levanta a questão se existe também alguém para "criar" tal fluxo.Você poderia quero compartilhar isso também.Mas sua milhagem pode variar ...

Quanto à questão da convenção de chamada, tanto __cdecl e __stdcall deve trabalhar com binários, mas eu sempre preferiria __stdcall por outro motivo:é compatível com mais "idiomas" (ou seja,ferramentas) do que __cdecl.

Para estilo:use um #define declarar a convenção de chamada explicitamente (como você faz) semelhante a WINAPI nos cabeçalhos do Windows.

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