Newsgroup: it.comp.lang.c
Titolo: Problemi di linking

Salve a tutti.
Ho un piccolo problema e chiedo la vostra collaborazione per risolverlo.
Sto scrivendo un programma in C+-+- sotto Linux, formato da diversi file
ognuno dei quali include un header file contenente le dichiarazioni delle
classi e delle funzioni condivise. Avendo bisogno di utilizzare un programma
di ottimizzazione gia' fatto (min-cost flow), ho messo, in una delle mie
funzioni, delle chiamate a funzioni appartenenti a quel programma.  Di quel
programma ho tutti i sorgenti nonche' il Makefile. Quindi ho aggiunto, nel
mio header file, le dichiarazioni di quelle funzioni chiamate (extern)+ADs-
inoltre, ho aggiunto, nel MIO Makefile, le istruzioni relative alla
compilazione dei file del programma di min-cost flow (prese dal SUO
Makefile), dopo di che ho linkato gli obj del min-cost flow ai miei obj.
Purtroppo non funziona, perche' in fase di compilazione vengono visualizzati
messaggi tipo +ACI-la funzione XXX (una di quelle che chiamo dal mio file) non
puo' essere utilizzata come funzione+ACI-.
Ho sbagliato qualcosa? Capisco che non e' possibile rendersi conto bene
della situazione da queste poche spiegazioni, ma gradirei moltissimo la
vostra collaborazione.
Grazie,
Paolo Alessandrini
aaeal+AEA-tin.it
http://space.tin.it/scienza/aaale

A me e' successa una cosa stranissima ieri sera...
Considera questo makefile:
eseg:	venere.o marte.o
	gcc -o eseg venere.o marte.o
venere.o:	venere.c marte.h
	gcc -c venere.c
marte.o:	marte.cpp marte.h
	gcc -c marte.cpp
Linkando, ricevevo il tipico errore di simbolo non risolto (o
disallineato???).
Per caso, il file marte.cpp era un file di C++ cui avevo tolto un bel po'
di roba, ed era ridiventato C normale (cosa vorra' dire poi normale
riferito al C???). Allora l'ho rinominato e tutto funziona!!!
Mi pare strano, eppure e' l'unica cosa che ho modificato.
Qualcuno so darmi una spiegazione?
Rony Cesana
ces...@cli.di.unipi.it
---___---___---___---
due persone su una 
sono schizofreniche;
non ch'io sia una di
quelle, ma io si'.

Se indichi l'estensione .cpp il compilatore usa la convenzione dei nomi del
c++ e cioe' aggiunge al nome la "decorazione" con i tipi dei parametri. Se
richiama la funzione dal C questa non viene trovata dal linker.
Ciao.
Antonio Donatiello
Rony Cesana ha scritto nel messaggio ...
...


Mi pare strano, eppure e' l'unica cosa che ho modificato.
Presumibilmente, il file "marte.h" dichiara una funzione in
una forma simile a:
 
    int guerreggia();
e marte.cpp, o marte.c che dir si voglia, la implementa, tipo:
    int guerreggia() {
        return 943;
    }
Ora, se si parla di marte.cpp, questo _non puo`_ esportare
una funzione chiamabile da c e con nome-per-linker pari al
semplice "guerreggia".  Infatti, il C++ garantisce la correttezza
a tempo di link dei tipi di argomenti delle funzioni, e ne permette 
l'overload, per cui il "nome" (per-linker) della funzione deve 
contenere qualche indicazione dei tipi degli argomenti che
riceve, ad esempio:
        guerreggia__void
(normalmente, i vari compilatori C++ usano schemi piu`
compatti e meno leggibili).  Sono i nomi che quasi tutti
chiamano "mangled" ("massacrati"), eccetto Microsoft
che li chiama "decorati":-).
E dunque, se una funzione scritta in C cerca di chiamare
"guerreggia" (richiedendo al linker appunto questo nome,
senza decorazioni), il linker non potra` trovarla nel marte.o
prodotto da questo marte.cpp.
Quando rinomini marte.cpp a marte.c, e, naturalmente, lo
ricompili, il "problema" sparisce.
C'e` naturalmente modo di risolvere il problema anche
restando in C++.  Il linguaggio C++, infatti, prevede la
interoperabilita` con altri linguaggi, come il C, e per
questo scopo introduce il costrutto:
    extern "nome dell'altro linguaggio"
da premettere al resto della dichiarazione, ovvero ad
un paio di parentesi graffe { } fra le quali puoi porre
tutte le dichiarazioni che desideri.  Quali linguaggi
siano cosi` supportati dipende dal compilatore (non
tutti i compilatori C++ supportano interoperabilita`
con Cobol, Fortran, Ada, Pascal, ecc -- solo alcuni);
l'interoperabilita` col linguaggio C, invece, e` garantita
da tutti i compilatori C++ (lo richiede lo Standard).
Dunque, se tu dichiari:
    extern "C" int guerreggia();
il nome esportato al linker sara` quello "pulito" (e si
perdono le garanzie di correttezza e le possibilita`
di overload); inoltre, a seconda della macchina, si
saranno eventualmente cambiate convenzioni di
chiamata, eccetera, sempre per far si` che questa
funzione sembri a tutti gli effetti implementata in C.
Resta un piccolo problema: se vogliamo tenere
questa dichiarazione in marte.h, e rendere questo
header file indifferentemente includibile, sia da C,
sia da C++, come facciamo?  Perche` il compilatore
C non manderebbe giu` quella stringa fra extern e
il tipo e nome della funzione...
A questo scopo, lo Standard C++ impone che il
compilatore C++ definisca al preprocessore il
simbolo _cplusplus (che, si puo` sperarlo, nessun
compilatore C definisce!).  Bisogna dunque rendere
le "extern qualificate" condizionali su questo simbolo.
La forma canonica di un header file, come sarebbe
il tuo marte.h, fatto per essere incluso sia da C, sia
da C++, e` dunque qualcosa del tipo:
#ifndef _marte_h    /* guardia contro doppio include */
#define _marte_h
#ifdef _cplusplus
extern "C" {
#endif
int guerreggia();
/* tutte le altre dichiarazioni da condividere */
#ifdef _cplusplus
}    /* terminiamo la extern "C" */
/* eventuali dichiarazioni che debba vedere solo il C++ */
#else
/* eventuali dichiarazioni che debba vedere solo il C */
#endif
#endif    /* fine della guardia contro doppio include */
Alex