Pytanie Łączenie dużych programów C i C ++


Czytałem o kilku metodach łączenia kodów C i C ++, jednak nadal jestem zdezorientowany, jak postępować w moim przypadku. Oto mój problem:

Mam stosunkowo dużą ilość kodu C (składającą się z różnych .c i .h pliki), które są używane do modelowania brył w elementach skończonych i dyskretnych. Ten kod ma stosunkowo krótką i prostą funkcję główną z for Pętla, w której różne inne funkcje (z innych plików) są nazywane sekwencyjnie. Ten kod działa poprawnie, gdy jest skompilowany zarówno w systemie Unix (kompilator icc), jak i Visual Studio.

Mam inny kod w C ++, który rozwiązuje interakcje dynamiki molekularnej. Ten kod składa się również z różnych plików i działa dobrze zarówno w systemie Unix (kompilator icpc), jak i VS. Oba programy są samodzielnymi programami z własnym zestawem plików wejściowych i wyjściowych.

Co muszę zrobić jest uruchomienie obu programów w taki sposób, że mój program C "wywoła" kod C ++ w swojej głównej pętli. Niektóre informacje muszą być przekazane w obie strony między dwoma kodami, które mogą mieć postać tablic (lub wskaźników).

Jaki jest najprostszy sposób to zrobić?

W szczególności mam wiele pytań opartych na zaleceniach, które przeczytałem:

  1. Czy powinienem zawijać moje pliki nagłówkowe C? extern "C" {}?
  2. Powinienem użyć extern "C" w moich funkcjach C?
  3. A może powinienem użyć extern "C" w moich plikach C ++? (nagłówki? funkcje? wszystkie z nich? lub tylko te, które muszę wywołać z programu C?)
  4. W rozumieniu nie mogę mieć dwóch main Funkcje. Czy mogę po prostu zmienić nazwę mojego C ++ main funkcjonować?
  5. Czy podczas kompilacji w systemie UNIX należy używać kompilatorów C (icc) i C ++ (icpc) do różnych plików? lub po prostu kompilator C ++?
  6. Czy może to być opcja (aby uprościć rzeczy) do konwersji? main funkcja od C do C ++?
  7. Jeśli nie potrzebuję przekazywać informacji o zajęciach między tymi dwoma programami, czy muszę coś z nimi zrobić?
  8. W jakiej kolejności sugerujesz rozwiązanie tego problemu? (najpierw skompiluj mój program C przez kompilator C ++, po drugie skompiluj oba kody razem bez linków, trzeci, połącz kody, czwarty, zmień nazwę main w C ++ i niech będzie "wywoływany" przez mój kod C; po piąte, wdrożenie transferu informacji?)
  9. Wreszcie, w każdym programie są pewne makra, które się powtarzają (ta sama nazwa, ta sama implementacja). Czy istnieje konflikt z tym? Czy powinienem przechowywać tylko jeden zestaw makr?

Przepraszamy za długi tekst i wiele pytań. Jestem stosunkowo nowy w C, a jeszcze nowszy w C ++, więc nawet moje słownictwo dotyczące tych programów jest ograniczone.

Dzięki za pomoc. Wszelkie wskazówki zostaną docenione. Jeśli potrzebujesz dodatkowych informacji, daj mi znać.

Oto "główna" funkcja mojego kodu C:

#include "Yproto.h"
void show_time_info(YDC ydc,CHR Ystage[3]);

main(argc, argv)
  INT argc; char **argv;
{ CHR c1name[300];         /* name of the problem i.e. input file */
  struct YD_struct yd;     /* Y database                          */
  YDC ydc=&(yd.ydc);       /* Y control database                  */
  YDE yde=&(yd.yde);       /* Y element database                  */
  YDI ydi=&(yd.ydi);       /* Y interaction database              */
  YDN ydn=&(yd.ydn);       /* Y node database                     */
  YDB ydb=&(yd.ydb);       /* Y borehole database                 */
  YDS yds=&(yd.yds);       /* Y source (inter. fluid) database    */
  YDO ydo=&(yd.ydo);       /* Y output database                   */
  YDPE ydpe=&(yd.ydpe);    /* Y property database  for elements   */
  YDPN ydpn=&(yd.ydpn);    /* Y property database  for nodes (BC) */
  YDPJ ydpj=&(yd.ydpj);    /* Y property database  for joints     */
  YDPM ydpm=&(yd.ydpm);    /* Y property database  for meshing    */
  INT Tctrlc, itimes=0;
  CHR *p=NULL;

  /* get name of the problem */
  if(argv[1]!=NULL)
  { CHRcpy(c1name,argv[1]);
  }
  else
  { CHRwcr(stdout);
    CHRw(stdout,"  please define input file names: "); CHRwcr(stdout);
    CHRw(stdout," >");
    fgets(c1name,sizeof(c1name),stdin);
    if((p=strrchr(c1name,'\n'))!=NULL) *p = '\0';
  }
  strcpy(ydc->cfiname, c1name);   ydc->cfiname[255]='\0';
  ydc->finp=FILENULL; ydc->fcheck=FILENULL;

  /* Process while any input */
  while(Yrd(c1name,&yd)>0)
  { itimes=itimes+1;
    CHRw(stdout,"NEW INPUT: "); CHRw(stdout, c1name); CHRwcr(stdout);
    if(Ycheck(&yd)<0) break; date_and_time(ydc->cruntime); timestamp();
    CHRw(stdout, "Start calculating ...\n");
    omp_set_num_threads(8);
    for(ydc->ncstep=ydc->ncstep;ydc->ncstep<ydc->mcstep;ydc->ncstep++)
    { show_time_info(ydc,"Ymd");                      /* show time information    */
      Ymd(ydc,yde,ydi,ydn,ydpe,ydpn,ydpm);            /* mesh elements            */

      /********** HERE IS WHERE I WOULD LIKE TO CALL MY C++ PROGRAM ***************/

      Yfd(ydc,yde,ydn,ydi,ydo,ydpe,ydpn,ydpj);        /* nodal forces             */
      Ybor(ydc,yde,ydn,ydb,yds,ydpe,ydpj,ydpn);       /* borholes, inter. fluid   */
      Ycd(ydc,yde,ydi,ydn,ydpe,ydpn);                 /* contact detection        */
      Yid(ydc,yde,ydi,ydn,ydo,ydpe,ydpn, ydpj,ydpm);  /* interaction              */
      Yod(c1name,&yd);                                /* output results           */
      Ysd(ydc,yde,ydn,ydo,ydpe,ydpn );                /* solve equations          */
      Yfrd(ydc,yde,ydi,ydn,ydpe,ydpn,ydpj,ydpm);      /* fracture                 */
      ydc->dctime=ydc->dctime+ydc->dcstec;            /* update time              */
      /* CTRL-C Interruption */
      Tctrlc = enablc(ydc->dctime, ydc->ncstep, ydc->mcstep);
      if(Tctrlc!=1) break;
    }
  }

  /* Termination */
  CHRw(stderr,"   ***** Y HAS ORDERLY FINISHED *****");  CHRwcr(stderr);
  CHRw(stderr,"Press a key to continue");  CHRwcr(stderr);
  getchar();
}

AKTUALIZUJ 24 GODZINY PO ODPOWIEDZI

Postępowałem zgodnie z zaleceniami zgodnie z dostarczonymi odpowiedziami, a rozwiązanie mojego problemu okazało się znacznie prostsze niż pierwotnie sądzono (chociaż musiałem zbadać kilka opcji, zanim zacząłem działać). Najlepsze jest to, że działa zarówno w systemie Unix, jak i Visual Studio. Oto podsumowanie podjętych kroków:

  1. Konwertuj mój główny plik C na C ++. W tym celu zmień nazwę pliku zawierającego main funkcja mojego kodu C z rozszerzeniem .cpp (zmieniono z Y.c na Y.cpp) i zmieniam początek main funkcja od:

    main(argc, argv)
      INT argc; char **argv;
    

    do

    int main(int argc,char **argv)
    

    w celu uczynienia go przyjaznym w C ++. (Uwaga: rozumiem, że zmiana nazwy pliku na .cpp nie jest niezbędna, ale myślę, że lepiej jest zrobić to dla jasności).

  2. Zawiń wszystkie moje pliki nagłówkowe C za pomocą

    #ifdef __cplusplus
    extern "C" {
    #endif
    

    na początku, i

    #ifdef __cplusplus
    }
    #endif
    

    na końcu.

  3. Zmień nazwę mojego main Funkcja C ++ i (tymczasowo) nie używają żadnych argumentów. Nazwałem to int Ynano().

  4. Utwórz nowy plik nagłówkowy o nazwie Y_NANO.h (Y_NANO.cpp to nazwa pliku zawierającego pierwotnie główną funkcję C ++) z linią:

    int Ynano();
    
  5. Dołącz nowy nagłówek zarówno do Y.cpp, jak i Y_NANO.cpp:

    #include "Y_NANO.h"
    
  6. Zadzwoń do funkcji Ynano() z main funkcja w Y.cpp.

  7. Aby skompilować w Visual Studio, po prostu umieść wszystkie pliki źródłowe w tym samym folderze i utwórz nowy projekt. W systemie Unix postępowałem zgodnie z podanymi krokami tutaj.

Te kroki sprawią, że programy będą działać razem, bez przesyłania informacji między nimi. Aby przesyłać informacje między programami, konieczne jest uwzględnienie niektórych parametrów jako argumentów Ynano(), ale to już inna historia.

Kilka uwag końcowych:

  • Problem powtarzania makr w różnych plikach nagłówkowych nie wydaje się być poważnym problemem, o ile żaden plik nie zawiera obu nagłówków (nie musiałem nic z tym robić).
  • Dziękuję wszystkim, którzy udzielili odpowiedzi. Byli naprawdę pomocni. Wybrana odpowiedź została wybrana na podstawie kompletności, ale inne były równie dobre. Mam nadzieję, że wątek pomaga innym wykonywać swoją pracę, ponieważ wiele innych wątków pomogło mi zrobić to samo.

11
2018-06-25 15:42


pochodzenie


Uwaga do stylu: Backticks nie są przeznaczone do podkreślenia. : P Służą do wyróżniania kodu i innego dosłownego tekstu wyprowadzanego przez komputer lub wprowadzanego do niego. - cHao
@ cHao, dzięki za oczyszczenie tego. Jestem stosunkowo nowy tutaj, więc nie byłem pewien co do formatowania :-P - Leonardo Trivino


Odpowiedzi:


1) Powinienem zawinąć moje pliki nagłówkowe C. extern "C" {}?

2) Powinienem użyć extern "C" w moich funkcjach C?

Tylko jeśli planujesz #include nagłówki C z niektórych plików źródłowych C ++, np. jeśli chcesz wywołać jedną z funkcji C z twojego kodu C ++. Typowy sposób na użycie pliku nagłówkowego C w C ++ jest następujący:

#ifndef MY_C_HEADER_H
#define MY_C_HEADER_H

#ifdef __cplusplus
extern "C" {
#endif

/* All the original content of the C header */

#ifdef __cplusplus
}
#endif

#endif

Jeśli nie chcesz modyfikować nagłówka, możesz również zastosować go extern "C" z zewnątrz nagłówka, gdy jest włączony do pliku źródłowego C ++:

// in my_source.cpp (or some C++ header file):

extern "C" {

#include "my_c_header.h"

}

UWAGA: Takie rozwiązanie nie jest zalecane ani nie jest rozwiązaniem długotrwałym / łatwym w utrzymaniu, jest to po prostu szybkie i "brudne" rozwiązanie, które często zawodzi, ale czasami działa, w zależności od sposobu C wyglądają jak nagłówki (nagłówki C nie muszą zawierać wielu innych nagłówków i nie powinny generalnie, ale niektórzy autorzy nie mają takiego rozsądku).

Powód dla extern "C" jest wyłączenie wymazywania nazw w C ++, tj. informowanie kompilatora, że ​​funkcje powinny być kompilowane w taki sposób, aby odpowiadały symbolom nierozwiniętym i / lub powinny być odczytywane w tablicy symboli w niezmienionej postaci (podczas łączenia z nimi). Tak więc reguła jest prosta, wszystkie funkcje C ++, które chcesz skompilować do biblioteki, która mogłaby być wywoływana z kodu C (lub jakiegokolwiek innego języka) muszą być zadeklarowane jako extern "C". I wszelkie deklaracje funkcji, które wywołasz w kodzie C ++, ale linki do biblioteki skompilowanej z C (lub dowolnego innego języka) muszą być extern "C" także.

3) Czy powinienem użyć zewnętrznego "C" w moich plikach C ++? (nagłówki? funkcje? wszystkie z nich? lub tylko te, które muszę wywołać z programu C?)

Jeśli chcesz wywołać niektóre funkcje C ++ z twojego kodu C, to te specyficzne funkcje będą musiały być zadeklarowane jako extern "C" podczas kompilowania tego kodu C ++. W pliku nagłówkowym C, który deklaruje te funkcje (w celu wywołania ich z kodu C), nie ma takiej potrzeby extern "C" (zawsze jest implikowane w C).

4) Rozumiem, że nie mogę mieć dwóch "głównych" funkcji. Czy mogę po prostu zmienić nazwę mojej głównej funkcji C ++?

Jaki byłby cel dwóch głównych funkcji? To niedozwolone i nieprzydatne. Wciąż możesz mieć tylko jeden "program" z jednym początkiem i jednym końcem, tj. Jedną główną funkcją. Musisz wybrać jedną z głównych funkcji i dodać do niej dowolne dodatkowe kroki (wywołanie drugiej biblioteki). Innymi słowy, musisz "scalić" główne funkcje.

5) Czy podczas kompilacji w systemie UNIX należy używać kompilatorów C (icc) i C ++ (icpc) do różnych plików? lub po prostu kompilator C ++?

Kompilator C służy do kompilowania kodu C i kompilatora C ++ do kompilowania kodu C ++. Większość systemów kompilacji (cmake, make, itp.) Automatycznie to zrobi. Z technicznego punktu widzenia można spróbować skompilować kod C przy użyciu kompilatora C ++, ale nie oczekuj, że zadziała od razu, a nawet będzie łatwy w obsłudze, a nie wart wysiłku IMHO.

6) Czy może to być opcja (aby uprościć rzeczy) do konwersji mojej głównej funkcji z C na C ++?

To jest opcja. Plik źródłowy zawierający funkcję główną C wydaje się stosunkowo prosty, zawiera jeden plik nagłówkowy C i ma dość prostą funkcję główną. Jeśli tak, nie będzie to trudne do skompilowania na kompilatorze C ++ (chyba że nagłówek C, który zawiera, zawiera wiele innych nagłówków C, co jest złą praktyką, ale prawdopodobnie). Będziesz musiał zawinąć włączenie pliku nagłówkowego C przez extern "C" { } jak pokazane powyżej. Następnie możesz po prostu spróbować skompilować (tylko plik źródłowy zawierający główną funkcję) w kompilatorze C ++, a resztę kodu C kompilatorem C, a następnie połączyć całość razem. Jeśli to działa od razu, to świetnie, możesz zacząć łączyć tę główną funkcję C z główną funkcją C ++ z drugiej biblioteki, a będziesz gotowy do pracy.

W przeciwnym razie, zwykłą opcją jest ustalenie, do czego potrzebny jest kod C ++. Następnie utwórz przyjazną dla C funkcja (bez klas itp.) W C ++, która robi te rzeczy, używając biblioteki C ++. Następnie utwórz plik nagłówkowy, który deklaruje tę funkcję, z extern "C" specifier (podczas kompilowania tylko w C ++ (__cplusplus)) i upewnij się, że ten nagłówek nie zawiera żadnego innego nagłówka C ++ (nie standardowe nagłówki, a nie inne nagłówki z biblioteki C ++). I na koniec, w swoim kodzie źródłowym C, w którym masz główną funkcję, dołącz ten nagłówek i wywołaj funkcję (funkcje) tam, gdzie jest to potrzebne w funkcji głównej. Połącz wszystko razem i powinno działać.

7) Jeśli nie potrzebuję przekazywać informacji o zajęciach między dwoma programami, czy muszę coś z nimi zrobić?

Nie. Dopóki nie dodasz żadnego nagłówka C ++ z kodu C (który kompilator nie zaakceptuje w żaden sposób), kod C nie jest świadomy, że klasy istnieją. Więc nie ma tutaj żadnego niebezpieczeństwa.

8) W jakiej kolejności sugerujesz rozwiązanie tego problemu? (najpierw skompiluj mój program C przez kompilator C ++, po drugie skompiluj oba kody razem bez linków, trzeci, połącz kody, czwarty, zmień nazwę na główny w C ++ i każ mu "wywołać" mój kod C; piąty, wykonaj transfer informacji?)

Pierwszym krokiem jest oczywiście upewnienie się, że możesz skompilować oba osobno. Drugi krok polega na sprawdzeniu, czy możliwe jest skompilowanie głównej funkcji programu C (tylko głównej funkcji) z kompilatorem C ++ (jak wyjaśniono powyżej). Jeśli się powiedzie, zacznij wprowadzać elementy z głównej funkcji C ++ do nowej "połączonej" funkcji głównej. Jeśli się nie powiedzie, wykonaj kroki, o których wspomniałem.

9) Wreszcie, w każdym programie są pewne makra, które się powtarzają (ta sama nazwa, ta sama implementacja). Czy istnieje konflikt z tym? Czy powinienem przechowywać tylko jeden zestaw makr?

MAKRO ... trudno to powiedzieć. Jeśli postępujesz zgodnie z procedurą tworzenia funkcji C ++, która może być wywołana z głównej funkcji C, to zasadniczo masz doskonałą izolację obu bibliotek, tj. Są one kompilowane oddzielnie i łączone razem po. W takim przypadku nie będzie problemu ze sprzecznymi MACRO (ale mogą istnieć funkcje o tej samej nazwie, jeśli niektóre są extern "C" w bibliotece C ++). Jeśli spróbujesz połączyć główne funkcje w jedną główną funkcję C ++, możesz mieć problemy ze sprzecznymi MACRO pomiędzy nagłówkami C i nagłówkami C ++, które będą dołączone razem.


7
2018-06-25 16:27



extrern "C" { #include <my.h> } jest dużym nie-nie. nagłówki zwykle zawierają inne nagłówki, w tym bibliotekę, standard, umieszczanie ich w tym bloku jest złe. - Balog Pal
@BalogPal: +1, masz rację, to ostateczność bardziej niż cokolwiek innego. Kiedy chcesz, żeby to zadziałało, warto spróbować. Ale na pewno nie jest to rozwiązanie długoterminowe / możliwe do utrzymania. - Mikael Persson
@MikaelPersson dzięki za odpowiedź. Tylko komentarz: 4. Być może nie było jasne w tej kwestii. Obecnie zarówno programy C, jak i C ++ są samodzielne, więc każdy ma swoją własną główną funkcję. Chciałem zapytać, czy powinienem zmienić moją obecną funkcję główną C ++ jako coś innego, aby móc wywołać ją z mojego kodu C. - Leonardo Trivino
@LeonardoTrivino Tak, możesz oczywiście zmienić nazwę głównej funkcji w swoim kodzie C ++ na coś innego, oznaczyć ją jako extern "C" i wywołaj go z kodu C. Po prostu nie widzę zbyt wiele do tego, poza praktyką. Bardziej praktyczne będzie tworzenie innej funkcji z bezpośrednimi parametrami w pamięci (jak wskaźniki tablicowe itp.) Zamiast parametrów (argc, argv). W przeciwnym razie nie różni się to od zwykłego wykonywania programu w C ++ system("/path/to/the/cpp/project/my_cpp_program"); z kodu C. - Mikael Persson


  1. tak, ale owinięte jak wyjaśniono tutaj: Łączenie C ++ i C - jak działa #ifdef __cplusplus?
  2. nie są potrzebne, jeśli znajdują się w pliku nagłówkowym. Jeśli nie, to potrzebujesz deklaracji extern w swoich plikach C ++, jeśli jest ona potrzebna i tak z zewnętrznym "C"
  3. Nie zawsze jest to możliwe, ponieważ klasy i niektóre typowe dla C ++ rzeczy po prostu nie działają w C. Ale jeśli kod C ++ jest naprawdę tylko C, to też będzie działać. Używanie C w C ++ jest dużo łatwiejsze niż odwrotnie.
  4. Po co zmieniać nazwę drugiego głównego fnction? Nie zostanie wywołane, możesz mieć tylko jedną główną funkcję
  5. możesz zmienić nazwy plików c na C ++ i zacząć kompilować wszystko za pomocą C ++. To rozwiązałoby twoje problemy z powiązaniem z zewnętrznymi "C" i jest to, co robiłbym w pierwszej kolejności. W przeciwnym razie C jest kompilowany z kompilatorem C i C ++ z kompilatorem C ++. Te kompilatory zachowują się oczywiście inaczej.
  6. tak oczywiście, twój kod C może wymagać trochę przeróbek. Oto co bym zrobił
  7. nie sądzę. Z drugiej strony wydaje się, że nie ma żadnych zależności? Jak to może być ?
  8. nie ma znaczenia, rozpocznij kompilację, a następnie napraw problemy z linkerem
  9. mogą występować konflikty, jeśli uwzględnisz 2 pliki nagłówkowe zawierające to samo makro. Kompilator narzeka z ponownym zdefiniowaniem.

2
2018-06-25 16:01



zwykle dałeś kompilatorowi lub makefile magic, aby wymusić kompilację C lub C ++, zmiana nazw plików nie jest absolutnie konieczna. - Balog Pal
@philip dzięki za odpowiedź. Kilka uwag: 4. Być może nie było jasne w tej kwestii. Obecnie zarówno programy C, jak i C ++ są samodzielne, więc każdy ma swoją własną główną funkcję. Chciałem zapytać, czy powinienem zmienić moją obecną funkcję główną C ++ jako coś innego, aby móc wywołać ją z mojego kodu C. 7. Nie byłem dokładny w tym punkcie. Istnieje kilka tablic, które obecnie istnieją w programie C i muszą zostać przekazane i zmodyfikowane przez program C ++. Chodzi mi o to, że nie ma klas ani obiektów z C ++, które będą musiały zostać przekazane do programu C. - Leonardo Trivino
@Balog Pal: Wiem, ale kompilowanie plików C ++, które mają rozszerzenie c, nie jest dobrym pomysłem. To jest mylące. - Philip Stuyck
@LeonardoTrivino: Możesz po prostu wywołać nazwę głównej nazwy z drugiej strony głównej. - Philip Stuyck
@PhilipStuyck: OK, dzięki - Leonardo Trivino


Podsumowanie:

Myślę, że możesz po prostu skompilować swoje C main() w oddzielnej jednostce kompilacji CPP, a następnie "extern C" wszystkie twoje definicje funkcji C. Dzwonienie z CPP do C jest łatwe. Na odwrót jest trochę bardziej kłopotliwa, ponieważ będziesz musiał stworzyć "... C API do ujawniania funkcjonalności twojego kodu C ++ ..." - zobacz Jak wywołać funkcję C ++ z C?

EDYCJA: Powyżej edytowane dzięki opiniom Mikaela (patrz komentarze). Patrząc na to, myślę, że C ++ do C jest ogólnie łatwiejsze, jeśli kod C ++ wykorzystuje specyficzne funkcje C ++, takie jak przeciążanie obiektów, itp., Ponieważ wtedy może potrzebować wapperów C API (patrz powyższy link). W tym przypadku, jak zauważa Mikael, tak nie jest, więc tak czy inaczej jest tak łatwo / trudno ...

Uwaga: połącz oba main()do jednej funkcji CPP.

Szczegół:

uruchom oba programy w taki sposób, że mój program C wywoła kod C ++

Zwykle jest to trochę trudne, obawiam się. C ++ robi coś, co nazywa się wymieszać nazwę, więc wywołanie funkcji C ++ z C jest trudne do zrobienia w przenośny sposób, chyba że stworzyłeś opakowanie C (zobacz powyższy link). Powodem jest to, że kompilator CPP (wewnętrznie bez tego widziany) ponownie zapisuje nazwy funkcji i zawiera takie rzeczy, jak typy parametrów, zwykle jako przyrostek nazwy, aby mógł wykonywać operacje takie jak przeciążanie funkcji. Kompilator języka C tego nie robi, ponieważ przeciążanie funkcji nie jest możliwe w C.

Powiedziałbym, że lepiej byłoby uruchomić swoją główną z modułu C ++ i wywołać stamtąd funkcje C ... w ten sposób omijasz problem z manglingiem nazwy.

Czy powinienem zawijać pliki nagłówkowe C z zewnątrz "C" {}

Tak, ważne jest, aby w plikach nagłówkowych C zawijać wszystkie definicje funkcji. Normalnie zobaczysz coś takiego

#ifndef HEADER_FILE_NAME
#define HEADER_FILE_NAME
#ifdef __cplusplus
   extern "C" {
#endif
/// FILE CONTENTS
#ifdef _cplusplus
   }
#endif
#endif // HEADER_FILE_NAME

To, co to robi, mówi kompilatorowi CPP, że te nazwy funkcji powinny nie być zmasakrowanym. W ten sposób podczas łączenia z funkcjami C będą używane prawidłowe nazwy symboli.

Podczas kompilowania modułu CPP __cplusplus należy określić, ale przy kompilacji modułu C nie powinien. Oznacza to, że gdy moduł CPP zawiera plik nagłówkowy C, nie będzie on modyfikował nazw funkcji i dlatego może poprawnie wywoływać funkcje C.

Czy powinienem używać zewnętrznego "C" w moich plikach C ++?

Zewnętrzna "C" po prostu informuje kompilator, że funkcja ma powiązanie w języku C, więc wygenerowany symbol nie zostanie zniekształcony. Tak więc myślę, że jeśli jest to funkcja, która nie została przeciążona, to wykonanie tej czynności (w pliku H dla definicji funkcji) uniemożliwi nazwanie nazwy funkcji, dzięki czemu będziesz mógł wywołać ją z C. Jednakże, jeśli extern "C", na przykład klasa, która nadal będzie miała połączenie C ++, to samo dla funkcji członka klasy itd. ... zależy, czy użyjesz ich, czy nie ... nie wygląda jak z próbki kodu.

W rozumieniu nie mogę mieć dwóch głównych funkcji. Czy mogę po prostu zmienić nazwę mojej głównej funkcji C ++?     Czy może to być opcja (aby uprościć rzeczy) do konwersji mojej głównej funkcji z C na C ++?

Tak, myślę, że to najlepsza opcja. Jeśli tylko main() funkcja musi wywoływać oba typy kodu, a wtedy wszystko jest w porządku. Jeden main() funkcja zapisana w jednostce kompilacji CPP.

Jeśli jednak istnieje moduł C, który musi wywołać moduł C ++, należy rozważyć skompilowanie go jako pliku CPP lub upewnienie się, że funkcja CPP jest extern "C" i nie jest przeciążony.

Wreszcie, w każdym programie są pewne makra, które się powtarzają (ta sama nazwa, ta sama implementacja). Czy istnieje konflikt z tym? Czy powinienem przechowywać tylko jeden zestaw makr?

Jeśli makra są zdefiniowane w plikach C / CPP, to jesteś w porządku. Jeśli znajdują się w plikach nagłówkowych, może wystąpić konflikt, jeśli jeden plik zawiera dwa pliki nagłówkowe, które zawierają to samo makro. W obu przypadkach zaleciłbym usunięcie wszystkich popularnych makr do wspólnych plików nagłówkowych, tak aby istniało tylko jedno wystąpienie makra ... o wiele łatwiejsze do utrzymania ... Przejdź z mantrą "nie powtarzaj się" :)

Nie odniosłem się do wszystkich twoich punktów, ale mam nadzieję, że to wystarczy, abyś zaczął :)


1
2018-06-25 16:03



Nie zgadzam się, że wywoływanie C ++ z C jest trudne. Oba są równie łatwe / trudne. W obu przypadkach potrzebny jest interfejs C z nagłówkiem gotowym do dołączenia z innego języka. Wybór, które połączenia są głównie kwestią związku kodu (kto potrzebuje kto, kto kontroluje proces, a kto dostarcza usługę). W tym przypadku wydaje się, że biblioteka C ++ jest tą, która pomaga bibliotece C. - Mikael Persson
@ Jimbo Dzięki za odpowiedź. O punkcie makr, są one w dwóch plikach nagłówkowych, ale nie sądzę, że będą jakieś pliki C / CPP zawierające oba te pliki nagłówkowe (chyba że moja główna funkcja potrzebuje wywoływać je oba, co nie jestem pewien ale jeśli to wymaganie). - Leonardo Trivino
@Mikael: Miałem trochę wykopaliska w odpowiedzi na twój komentarz i zgodziłem się na jakieś wyjście ... Myślę, że C ++ do C jest ogólnie łatwiejsze, bo inaczej kod C ++ korzysta z funkcji specyficznych dla C ++, takich jak przeciążanie itp. W tym przypadku, jak zauważyłeś, nie jest tak naprawdę tak, więc tak czy inaczej jest tak łatwo / trudno. Dzięki za opinie :) - Jimbo
@Leonardo: Niektóre aktualizacje oparte na komentarzach Mikaels. W odpowiedzi na Twój komentarz, jeśli makra są identyczne, to z punktu widzenia konserwacji nadal uważam, że jeden plik jest lepszy. Jeśli zmienisz makro A w pliku X, aby np. Rozwiązać błąd, musisz go również zmienić w pliku Y ... o wiele łatwiej mieć to wszystko w jednym miejscu :) - Jimbo


Cóż, najlepiej byłoby skompilować źródła C jako C ++ bez zmiany semantyki, a następnie po prostu cieszyć się jednolitym systemem. W zależności od wielkości Twojego kodu i jego kształtu, który warto rozważyć jako opcję. W rzeczywistości może to być mniej pracy niż błahostka z tymi wszystkimi zewnętrznymi "C" -s i fallout.

Kolejną opcją jest współpraca. C ++ jest zaprojektowany do grania zgodnego z C, drugi kierunek nie jest tak teoretycznie, ale jest w praktyce - oczekuję, że kompilatory tego samego dostawcy będą wspierać cross-talk we wszystkich kierunkach.

Najważniejszą rzeczą, o której należy pamiętać, jest to, że jeśli dodajesz C ++, cały system jest uważany za C ++, więc musisz posłuchać zasady jednej definicji, mieć swoją główną z C ++, itd. Kompilujesz C z kompilatorem C i traktujesz je jak gość ... Musisz dostroić opcje kompilatora, aby były kompatybilne i insturmentuj wszystkie nagłówki współdzielone między systemami. Zwykle oznacza to użycie tych warunkowych, zewnętrznych "C", wpisanie struktur do ich własnych nazw i tak dalej.

W przypadku połączeń krzyżowych należy uwzględnić wyjątki. W niektórych systemach nie mogą przekraczać granicy C / C ++. W innych mogą, ale musisz zmienić opcje, aby działało dobrze.

W pierwszym przebiegu musisz tylko sprawić, by rzeczy były wykonane tak jak poprzednio. Sugestie dotyczące późniejszych refaktur są dobre pytania na SO i w innych miejscach.


0
2018-06-25 16:26





Powiem ci poprawną odpowiedź i prawdopodobnie ci się to nie spodoba. Nie znając wszystkich szczegółów kodu, brzmi to tak, jakbyś musiał zrobić ciężki refaktoryzacji.

W idealnym świecie, kiedy piszesz swoją aplikację, powinna być napisana w taki sposób, że implementacja jest wykonywana jako formalny interfejs API, a main () tłumaczy / parsuje argumenty wiersza poleceń w odpowiednie wywołania interfejsu API. Jeśli zostanie to zrobione poprawnie, plik zawierający procedurę main () jest potrzebny tylko do zbudowania pliku wykonywalnego dla jego zadania.

Co więcej, implementacja zostanie zbudowana jako biblioteka i zestaw nagłówków do korzystania z tej biblioteki.

Wtedy twoim zadaniem nie będzie blenderowanie dwóch plików wykonywalnych, ale tworzenie nowej aplikacji, która wywoła dwie różne biblioteki.

Powodem, dla którego ci się nie spodoba, jest to, że jest to czasochłonne. Czasochłonne jest wykonanie poprawnego projektu, aby utworzyć bibliotekę zamiast łączenia kilku klas / funkcji, aby wykonać zadanie de jour, ale jeśli twój obecny kod jest bardzo dobrze zorganizowany, jest to przeznaczone do oszczędzania czasu. z problemami z kodem, które ostatecznie pojawią się.

To, co zrobiłbym, gdybym był tobą, to najpierw dowiedzieć się dokładnie, jak działa każda aplikacja. Przejdź przez nie. Zobacz, gdzie idzie kod i wprowadź swoją teorię operacji do twojej głowy. Pozwoli to na spakowanie lub przepakowanie w odpowiedni sposób.


-1
2018-06-25 15:51



Istnieje wiele przykładów kodu, które używają C i C ++ razem i nie korzystają w ogóle z bibliotek. Spożywanie C z C ++ jest w rzeczywistości dość łatwe, a odwrócenie jest bardziej problematyczne. Jego pytanie o główny temat jest jednak zagadkowe. Możesz mieć tylko jeden główny i tylko ten zostanie wykonany. Więc jeśli obie rzeczy muszą być uruchomione, jeden zadzwoni do drugiego. W takim przypadku wywołaj C ++ główną funkcję o zmienionej nazwie C na końcu na początku w zależności od sytuacji. - Philip Stuyck
Nie mówię, że to niemożliwe, mówię, że to nie najlepsza praktyka. - plinth
@pple Cóż, najpierw dzięki za odpowiedź. Po drugie, masz rację, że nie lubię tego. Jeśli dobrze rozumiem, sugerujesz, aby całkowicie przeprojektować oba kody ... Cóż, to są duże kody i mimo że rozumiem większość algorytmów i przepływ informacji, nie wierzę, że zmiana projektu jest właściwa iść. To są kody inżynieryjne i faktycznie jest wiele rzeczy, które muszę przetestować zanim stwierdzę, że ich scalenie jest dobrym pomysłem (i do tego faktycznie muszę je scalić w pierwszej kolejności!). Przeprojektowanie można rozważyć na późniejszym etapie. - Leonardo Trivino
To też nie jest zła praktyka. Jest to powszechna praktyka. Ale gdyby było to zależało ode mnie, skompilowałbym wszystkie starsze moduły C z kompilatorem C ++, aby uniknąć wszystkich hastles ze specyfikacjami połączeń i tworzenia dodatkowych plików nagłówkowych dla interfejsu C interfejsu C ++, aby mógł on być użyty w module C. Niemniej jednak istnieje wiele przykładów, w których w osadzonym w czasie rzeczywistym sw moduły związane ze sprzętem znajdują się w C, podczas gdy aplikacje na nich umieszczone są w C ++. Jako takie zależności są takie, że moduły C ++ używają C. Nic wielkiego. - Philip Stuyck
"To także nie jest zła praktyka, tak naprawdę jest to powszechna praktyka." Oczywiście - ponieważ jest to celowe, ale stwarza wszelkiego rodzaju problemy, gdy kod zawiera, powiedzmy, zmienne globalne, niewłaściwe zależności modułów, niewłaściwe (lub nie) abstrakcje itp. - wszystkie powszechne praktyki w monolitycznej aplikacji, ale stają się żywym piekłem po raz pierwszy zostanie przepakowany w innej aplikacji, która wymaga wielu oddzielnych kontekstów lub wielowątkowość, lub ... - plinth