Pergunta

Eu tenho um programa C++ que compilo com mingw (gcc para Windows).Usando a versão TDM do mingw que inclui o gcc 4.4.1.O executável está vinculado a dois arquivos de biblioteca estática (.a):Uma delas é uma biblioteca de terceiros escrita em C;a outra é uma biblioteca C++, escrita por mim, que usa a biblioteca C e fornece minha própria API C++.

Uma parte (na minha opinião, excessiva) da funcionalidade da biblioteca C é implementada em funções inline.Você não pode evitar a inclusão das funções embutidas ao usar a API da biblioteca C, mas quando tento vincular tudo, recebo erros de link dizendo que há uma definição múltipla de todas as funções embutidas - ambas eu tenho chamado em minha biblioteca wrapper C++ e aqueles que eu não tenho, basicamente qualquer coisa definida in-line nos cabeçalhos obteve uma função criada para ele na biblioteca C e na biblioteca C++.

Isso não causa vários erros de definição quando os arquivos de inclusão são usados ​​várias vezes em diferentes arquivos .c ou .cpp no ​​mesmo projeto;o problema é que ele gera uma definição por biblioteca.

Como/por que o compilador está gerando funções e símbolos para essas funções embutidas em ambas as bibliotecas?Como posso forçá-lo a parar de gerá-los no meu código?Existe uma ferramenta que eu possa executar para remover as funções duplicadas do arquivo .a ou uma maneira de fazer o vinculador ignorar várias definições?

(Para sua informação, a biblioteca de terceiros inclui #ifdef __cplusplus e protetores extern "C" em todos os seus cabeçalhos;de qualquer forma, se esse fosse o problema, não causaria uma definição múltipla de símbolo, causaria o problema oposto porque o símbolo seria indefinido ou pelo menos diferente.)

Notavelmente, os erros de link NÃO ocorrem se eu vincular à DLL da biblioteca C de terceiros;no entanto, recebo estranhas falhas de tempo de execução que parecem ter a ver com o fato de meu código ter sua própria versão de funções que deveria estar chamando da DLL.(Como se o compilador estivesse criando versões locais de funções que não solicitei.)

Versões semelhantes desta pergunta já foram feitas antes, no entanto, não encontrei a resposta para minha situação em nenhuma destas:

A resposta a esta pergunta foi que o cartaz estava definindo múltiplas variáveis, meu problema é a definição múltipla de funções embutidas: Erros repetidos de múltiplas definições ao incluir o mesmo cabeçalho em vários cpps

Este era um programa MSVC, mas estou usando o mingw;Além disso, o problema do postador nesta questão foi a definição de um construtor de classe C++ fora do corpo da classe em um cabeçalho, enquanto meu problema é com funções C que estão embutidas: Problema de definição múltipla de biblioteca estática

Esse idiota renomeou todo o seu código C como arquivos C++ e seu código C não era seguro para C++: Definição múltipla de muitos std::funções ao vincular

Este estava apenas querendo saber por que violar a regra de uma definição não era um erro: comportamento imprevisível de funções Inline com diferentes definições

Foi útil?

Solução

Primeiro você precisa entender o modelo inline C99 - talvez haja algo errado com seus cabeçalhos.Existem dois tipos de definições para funções inline com ligação externa (não estática)

  • Definição externa
    Esta definição de função só pode aparecer uma vez em todo o programa, numa TU designada.Ele fornece uma função exportada que pode ser usada em outras TUs.

  • Definição embutida
    Estes aparecem em todas as TU onde são declaradas como uma definição separada.As definições fazem não precisam ser idênticos entre si ou com a definição externa.Se usados ​​internamente em uma biblioteca, eles podem omitir a verificação dos argumentos da função que, de outra forma, seriam feitos na definição externa.

Cada definição da função possui suas próprias variáveis ​​estáticas locais, porque suas declarações locais não têm ligação (elas não são compartilhadas como em C++).Uma definição de uma função inline não estática será uma definição inline se

  • Cada declaração de função numa TU inclui o especificador inline, e
  • Nenhuma declaração de função em uma TU inclui o especificador extern.

Caso contrário, a definição que deve aparecer nessa TU (porque as funções inline devem ser definidas na mesma TU onde foram declaradas) é uma definição externa.Em uma chamada para uma função embutida não é especificado se a definição externa ou in-line é usada.No entanto, como a função definida em todos os casos ainda é a mesma (porque possui ligação externa), o endereço dela é igual em todos os casos, não importa quantas definições embutidas apareçam.Portanto, se você usar o endereço da função, é provável que o compilador resolva a definição externa (especialmente se as otimizações estiverem desativadas).

Um exemplo que demonstra um uso errado de inline, porque inclui uma definição externa de uma função duas vezes em duas TUs, causando um erro de definição múltipla

// included into two TUs
void f(void); // no inline specifier
inline void f(void) { }

O programa a seguir é perigoso porque o compilador é livre para usar a definição externa, mas o programa não fornece uma

// main.c, only TU of the program
inline void g(void) {
  printf("inline definition\n");
}

int main(void) {
  g(); // could use external definition!
}

Fiz alguns casos de teste usando GCC que demonstram ainda mais o mecanismo:

principal.c

#include <stdio.h>

inline void f(void);

// inline definition of 'f'
inline void f(void) {
  printf("inline def main.c\n");
}

// defined in TU of second inline definition
void g(void);

// defined in TU of external definition
void h(void);

int main(void) {
  // unspecified whether external definition is used!
  f();
  g();
  h();

  // will probably use external definition. But since we won't compare
  // the address taken, the compiler can still use the inline definition.
  // To prevent it, i tried and succeeded using "volatile". 
  void (*volatile fp)() = &f;
  fp();
  return 0;
}

principal1.c

#include <stdio.h>

inline void f(void);

// inline definition of 'f'
inline void f(void) {
  printf("inline def main1.c\n");
}

void g(void) {
  f();
}

main2.c

#include <stdio.h>

// external definition!
extern inline void f(void);

inline void f(void) {
  printf("external def\n");
}


void h(void) {
  f(); // calls external def
}

Agora, o programa produz o que esperávamos!

$ gcc -std=c99 -O2 main.c main1.c main2.c
inline def main.c
inline def main1.c
external def
external def

Olhando para a tabela de símbolos, veremos que o símbolo de uma definição inline não é exportado (de main1.o), enquanto uma definição externa é exportada (de main2.o).


Agora, se cada uma de suas bibliotecas estáticas tiver uma definição externa de suas funções embutidas (como deveriam), elas naturalmente entrarão em conflito umas com as outras.A solução é tornar as funções inline estáticas ou apenas renomeá-las.Estes sempre fornecerão definições externas (portanto, são definições completas), mas não são exportados porque têm ligação interna, portanto não são conflitantes

static inline void f(void) {
  printf("i'm unique in every TU\n");
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top