logo elektroda
logo elektroda
X
logo elektroda
REKLAMA
REKLAMA
Adblock/uBlockOrigin/AdGuard mogą powodować znikanie niektórych postów z powodu nowej reguły.

C++ - Jak podzielić program do transmisji danych przez RS232 na pliki?

daniel93 07 Paź 2012 23:51 3282 40
Najlepsze odpowiedzi

Jak poprawnie współdzielić zmienne między kilkoma plikami .cpp w C++, żeby uniknąć błędów zduplikowanej definicji?

Zmienne globalne trzeba zdefiniować tylko raz w jednym pliku .cpp, a w pliku nagłówkowym zostawić jedynie deklaracje z `extern`; nie wolno definiować ich w `.hpp`, bo każdy `#include` tworzy kolejną kopię i linker zgłasza błąd [#11411244][#11410919] Jeśli dana zmienna jest używana wyłącznie w module `UART`, można zrobić ją `static` w tym `.cpp`, żeby nie była widoczna poza tym plikiem [#11411108] W nagłówku powinny więc być deklaracje, a właściwe definicje i inicjalizacja w jednym miejscu w `.cpp` [#11411244] Autorzy zwracają też uwagę, że lepiej byłoby to opakować w klasę zamiast operować na globalach [#11411108][#11391545]
Wygenerowane przez model językowy.
REKLAMA
  • #1 11388847
    daniel93
    Poziom 29  
    Posty: 1673
    Pomógł: 30
    Ocena: 94
    Napisałem program do transmisji danych przez RS232, który działa gdy jest w jednym pliku. Schody zaczęły się gdy chciałem go podzielić:

    main.cpp:
    Kod: text
    Zaloguj się, aby zobaczyć kod


    UART.cpp:
    Kod: text
    Zaloguj się, aby zobaczyć kod


    UART.hpp:
    Kod: text
    Zaloguj się, aby zobaczyć kod


    Kompilator wywala takie błędy:
    Kod: Text
    Zaloguj się, aby zobaczyć kod


    Czego zupełnie nie rozumiem, bo wywala mi zduplikowane definicję, a przeciez są zdefiniowane tylko w UART.hpp :|
  • REKLAMA
  • #2 11389905
    szefkozak
    Poziom 11  
    Posty: 68
    Nie możesz w pliku *.hpp definiować zmiennych, poza zmiennymi const.
  • #3 11391470
    daniel93
    Poziom 29  
    Posty: 1673
    Pomógł: 30
    Ocena: 94
    Tylko gdzie? Przeniosłem je na początek głównego pliku i jest jeszcze więcej błędów:
    Kod: text
    Zaloguj się, aby zobaczyć kod


    [EDIT]
    Jak wrzuciłem je do pliku UART.cpp to problem jest ze zmiennymi w pliku main.cpp:
    Kod: text
    Zaloguj się, aby zobaczyć kod
  • #4 11391482
    gaskoin
    Poziom 38  
    Posty: 4159
    Pomógł: 436
    Ocena: 102
    Mają być zadeklarowane w cpp. Z resztą jezyk, w którym tworzysz jest bardzo daleki od C++.
  • REKLAMA
  • #5 11391511
    daniel93
    Poziom 29  
    Posty: 1673
    Pomógł: 30
    Ocena: 94
    Ale jak jakaś zmienna jest w dwóch plikach cpp to ma byc zadeklarowana w obu?

    Dlaczego daleki?
  • #6 11391540
    stanleysts
    Poziom 27  
    Posty: 838
    Pomógł: 115
    Ocena: 2
    Poprzez dodanie słowa kluczowego extern, gaskoin ma zapewne na myśli to, że to powinno być w jakąś klasę opakowane z wewnętrznymi metodami i odpowiednimi polami.
  • #7 11391545
    gaskoin
    Poziom 38  
    Posty: 4159
    Pomógł: 436
    Ocena: 102
    Bo C++ jest językiem obiektowym a nie funkcyjnym. Powinieneś to jakoś opakować rozsądnie w klasy. To co napisałeś to takie C rozszerzone o biblioteki C++.

    Rozwiązaniem typu walnięcie młotem jest deklaracja tych zmiennych w *.cpp a w hpp dodanie deklaracji tych zmiennych poprzedzając je słowem kluczowym extern. Walnięcie młotem dlatego, ponieważ współdzieląc zmienne pomiędzy kilkoma plikami (Ty masz 3 więc przeżyjesz :) ) zaczyna się robić bałagan. Wyobraź sobie co się będzie działo jak będziesz potrzebował wysyłać coś przez RS z 10 różnych plików.

    W C robi się to tak, że tworzy się odpowiednie funkcje do ustawiania i zwracania zmiennych z danego pliku. W C++ jest w zasadzie podobnie. Jakbyś opakował to w klasy, to Twój kod w mainie wyglądałby tak:

    Kod: text
    Zaloguj się, aby zobaczyć kod


    I nie potrzebowałbyś wcale tych zmiennych mieć jako globalne.
  • #8 11410882
    daniel93
    Poziom 29  
    Posty: 1673
    Pomógł: 30
    Ocena: 94
    Pomińmy na razie opakowanie tego w klasy.

    Ale jak w ogóle współdzielić zmienną w kilku plikach?

    Próbowałem zrobić tak jak pisał gaskoin. Zdefiniowałem zmienne w UART.cpp. A w UART.hpp dodałem definicje tych 2 zmiennych które chce współdzielić ze słówkiem extern i kompilator wywala blędy które nie mają zwiazku ze zmianą jaką wprowadziłem:
    Kod: text
    Zaloguj się, aby zobaczyć kod
  • #9 11410888
    stanleysts
    Poziom 27  
    Posty: 838
    Pomógł: 115
    Ocena: 2
    Pokaż kod jak możesz.
  • REKLAMA
  • #10 11410897
    daniel93
    Poziom 29  
    Posty: 1673
    Pomógł: 30
    Ocena: 94
    main.cpp:
    Kod: text
    Zaloguj się, aby zobaczyć kod

    UART.cpp
    Kod: text
    Zaloguj się, aby zobaczyć kod

    UART.hpp
    Kod: text
    Zaloguj się, aby zobaczyć kod


    Zanim dopisałem te 2 linijki były tylko 2 błędy, które wcześniej wklejałem( te o bufn i bufn_i)
  • #11 11410919
    stanleysts
    Poziom 27  
    Posty: 838
    Pomógł: 115
    Ocena: 2
    W pliku nagłówkowym nie możesz mieć tych zmiennych co o nie się pluje, zrób to w pliku .cpp a w .hpp tylko deklaracje z extern. Tak to miałbyś 2 kopie tej samej zmiennej.
  • #12 11411010
    daniel93
    Poziom 29  
    Posty: 1673
    Pomógł: 30
    Ocena: 94
    Ale o które dokładnie zmienne Ci chodzi?

    Bo on sie pluje o CommTimmeouts itp., a to przeciez nawet nie jest zmienna
  • #13 11411014
    stanleysts
    Poziom 27  
    Posty: 838
    Pomógł: 115
    Ocena: 2
    A to w takim razie to co to jest? Przecież to są obiekty albo zmienne.
  • #14 11411082
    daniel93
    Poziom 29  
    Posty: 1673
    Pomógł: 30
    Ocena: 94
    Mógłbyś mi pokazać kolego który fragment hpp powinien być tez w cpp, a w hpp z dodatkiem extern?
  • Pomocny post
    #15 11411108
    stanleysts
    Poziom 27  
    Posty: 838
    Pomógł: 115
    Ocena: 2
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    Jeśli np. z niektórych zmiennych korzystasz tylko w module usart a nie w main to możesz je zrobić jako global static (chyba te zmienne const się do tego nadają) i wtedy nie trzeba ich też extern robić bo main z nich nie korzysta
  • Pomocny post
    #16 11411244
    gaskoin
    Poziom 38  
    Posty: 4159
    Pomógł: 436
    Ocena: 102
    stanleysts napisał:
    Jeśli np. z niektórych zmiennych korzystasz tylko w module usart a nie w main to możesz je zrobić jako global static (chyba te zmienne const się do tego nadają) i wtedy nie trzeba ich też extern robić bo main z nich nie korzysta


    const musi być znany podczas kompilacji a nie jest. Przerzuć te zmienne do pliku cpp i będzie ok.

    Ja może wyjaśnię w czym problem. Masz sobie plik hpp, w którym deklarujesz zmienne (nigdy nie wolno tego robić):

    Kod: text
    Zaloguj się, aby zobaczyć kod


    Masz też dwa pliki cpp, w których includujesz sobie ten plik hpp:

    Kod: text
    Zaloguj się, aby zobaczyć kod


    Kod: text
    Zaloguj się, aby zobaczyć kod


    Co widzi kompilator (a w zasadzie linker)?
    Kompilator w uproszczeniu widzi coś takiego:

    Kod: text
    Zaloguj się, aby zobaczyć kod


    Jak w jednym pliku zadeklarujesz takie same zmienne, to otrzymasz taki sam błąd jak Ci wyskakuje. Może nie jest to jasne i oczywiste, ale zmienne zadeklarowane jako globalne, są dostępne nie tylko w obrębie całego pliku, ale całego programu. O fakcie, że zmienne zostały zadeklarowane gdzieś indziej informuje się linker poprzez dodanie słowa kluczowego extern (nie alokuje to pamięci dla zmiennych, jest to jedynie info dla linkera, że zmienna jest w innym pliku). Deklarując zmienną w w pliku nagłówkowym, deklarujesz ją w każdym pliku, w którym ten nagłówek includujesz. Stąd te błędy.
  • #17 11411613
    daniel93
    Poziom 29  
    Posty: 1673
    Pomógł: 30
    Ocena: 94
    Ok udało się :)

    A jak zrobić ten global static? Robi sie go w cpp czy hpp?

    Dodano po 24 [minuty]:

    Dopiero teraz zauważyłem posta gaskoin - dzięki, teraz rozumiem o co w tym chodzi. Zastanawiam sie czy nie lepiej było by zrobić oddzielny plik nagłówkowy dla każdego pliku + jeden z globalnymi zmiennymi?

    Dodano po 17 [minuty]:

    Mam jeszcze pytanie, bo klasy pozwoliłyby mi robić tak:
    Kod: text
    Zaloguj się, aby zobaczyć kod


    Jednak mi zalezy, żeby wysyłać wartości liczbowe czyli po prostu pojedyncze bajty. Jak zrobić funkcję żebym mógł robić tak?:
    Kod: text
    Zaloguj się, aby zobaczyć kod


    Gdzie każda liczba to osobny bajt, ale problem w tym, żeby ilość bajtów mogła być różna. Tzn w tym przykladzie wysyłam 4 bajty, ale chciałbym w taki sposób wysyłać też 3 albo 10, a funkcja ta pakowała by to do tablicy i informowała ile jest bajtów do wysłania.

    Dodano po 41 [minuty]:

    Przyszło mi do głowy tylko coś takiego:
    Kod: text
    Zaloguj się, aby zobaczyć kod


    Krótsze dane można kończyc zerami, które funkcja zinterpretuje jako koniec
  • #18 11411894
    gaskoin
    Poziom 38  
    Posty: 4159
    Pomógł: 436
    Ocena: 102
    Lepszym pomysłem jest użycie jako parametrów wskaźnika na tablicę z danymi oraz jej rozmiar.
  • #19 11412026
    daniel93
    Poziom 29  
    Posty: 1673
    Pomógł: 30
    Ocena: 94
    Wiem, że można tak zrobić, ale właśnie chodzi mi o prostu sposób na wypełniamie tej tablicy

    Będe używać tego wielokrotnie, dlatego chciałem to uprosić
  • #20 11412098
    stanleysts
    Poziom 27  
    Posty: 838
    Pomógł: 115
    Ocena: 2
    Parametr wskaźnik na tablicę i jej rozmiar to jest prosty wygodny i porzadny sposób.
  • #21 11412107
    daniel93
    Poziom 29  
    Posty: 1673
    Pomógł: 30
    Ocena: 94
    Tak, ale nie pozwoli mi to na wpisywanie wartości po przecinku i musze z góry wiedzieć ile tych wartości jest
  • #22 11412327
    stanleysts
    Poziom 27  
    Posty: 838
    Pomógł: 115
    Ocena: 2
    A po co masz po przecinku wpisywać? wpisujesz do tablicy, inrementujesz licznik za każdym razem i potem wskaźnik na tablicę i licznik jako parametry funkcji. To jest imho
    dużo lepsze rozwiązanie.
  • #23 11412414
    daniel93
    Poziom 29  
    Posty: 1673
    Pomógł: 30
    Ocena: 94
    Bo to będzie ciąg komend wysyłanych do różnych urządzeń:
    134 - adres urządzenia
    12,34,78,21 - komendy
    145 - CRC8
    255 - sygnał końca ramki

    Takie coś dużo wygodniej było by napisać w postaci:
    134,12,34,78,21,145,255 niż jak miałbym wypisywać jeden pod drugim
  • REKLAMA
  • #24 11412590
    stanleysts
    Poziom 27  
    Posty: 838
    Pomógł: 115
    Ocena: 2
    No i ja tak dalej nie wiem, dlaczego nie możesz użyć metody ze wskaźnikiem i rozmiarem...
  • #25 11412612
    gaskoin
    Poziom 38  
    Posty: 4159
    Pomógł: 436
    Ocena: 102
    Jest na to wygodny sposób, ale programować obiektowo Ci się nie chce więc nie pomogę :)
  • #26 11412621
    stanleysts
    Poziom 27  
    Posty: 838
    Pomógł: 115
    Ocena: 2
    Chodzi Ci gaskoin o skorzystanie z szablonów, przeładowania czy może jszcze czegoś innego?
  • #27 11412634
    daniel93
    Poziom 29  
    Posty: 1673
    Pomógł: 30
    Ocena: 94
    stanleysts,
    Ale ze wskaźnikiem wyglądało by to tak:
    Write(char *buff, dlugosc), a wczesniej by trzeba było wpisywać:
    buff[0] = 134
    buff[1] = 12
    itd

    gaskoin, chce mi się tylko jeszcze nie umiem :p
  • #28 11412727
    stanleysts
    Poziom 27  
    Posty: 838
    Pomógł: 115
    Ocena: 2
    No ale podawać bezsensownie niepotrzebne parametry albo wysyłać resztę danych jako zera to lepszy pomysł? Poza tym zrobiłbyś to jeśli nie na klasach to na jakichś strukturach, żeby ładniej wyglądało i było czytelne.
  • #29 11412747
    daniel93
    Poziom 29  
    Posty: 1673
    Pomógł: 30
    Ocena: 94
    stanleysts: własnie ze funkcja mogła by wykryć zera i zakończyć transmisje - masz lepszy pomysł żebym mógł polecenie napisać w jednej linijce a nie w 10?
  • #30 11412756
    stanleysts
    Poziom 27  
    Posty: 838
    Pomógł: 115
    Ocena: 2
    Przeładowanie funkcji?
    A jak będziesz chciał wysłać akurat zero a funkcja Ci obcina zera to co?

Podsumowanie tematu

✨ Użytkownik napotkał problemy podczas dzielenia programu do transmisji danych przez RS232 na kilka plików. W szczególności, wystąpiły błędy związane z deklaracją zmiennych w plikach nagłówkowych (*.hpp) oraz ich użyciem w plikach źródłowych (*.cpp). Uczestnicy dyskusji zasugerowali, aby zmienne globalne były zadeklarowane w plikach *.cpp, a w plikach nagłówkowych używać słowa kluczowego extern. Podkreślono również, że C++ jest językiem obiektowym, co sugeruje, że lepszym rozwiązaniem byłoby opakowanie kodu w klasy. Użytkownik zadał pytanie o sposób współdzielenia zmiennych oraz o to, jak zrealizować funkcję do wysyłania zmiennych liczbowych jako bajtów. W odpowiedziach zaproponowano użycie wskaźników oraz dynamicznej alokacji pamięci, a także omówiono problemy związane z deklarowaniem zmiennych o tej samej nazwie w różnych kontekstach.
Wygenerowane przez model językowy.
REKLAMA