kategorie: ANSI C, C++
[#1] Początki programowania w C
Jakiś czas temu postanowiłem popróbować swoich sił w programowaniu w C i do tego jeszcze na Amidze. W związku z różnymi problemami z moim programem, których nie potrafiłem zdiagnozować lub rozwiązać oraz brakiem czasu temat zarzuciłem ale chcę do niego wrócić, więc mam trochę pytań.

Na początek:
1) Oprogramowując port szeregowy w Amidze można zmusić wiele programów (tasków) do równoczesnego/równoległego korzystania z portu. Służy do tego flaga SERF_SHARED. Udało mi się doprowadzić do tego, że mój program uruchomiony jednocześnie w wielu kopiach uzyskuje bez problemu dostęp do portu i każda jego kopia może wysyłać dane przez port. Problem jest jednak przy odbieraniu danych przychodzących z zewnątrz gdyż docierają one tylko do jednej z tych uruchomionych kopii mojego programu (chyba tej, która była uruchomiona jako pierwsza). Czy można jakoś zmusić system, żeby przekazywał dane odebrane na porcie szeregowym jednocześnie do wszystkich programów, które mają otwarty ten port?

2) Jeżeli tworzę taką strukturę:
struct MyMessage
{
  struct Message Message;
  UWORD Data1;
  UWORD Data2;
  ULONG Data3;
  UBYTE Data4[12];
}

i na jej podstawie deklaruję zmienną MojKomunikat tak:
struct MyMessage MojKomunikat;

to czy muszę jeszcze w moim programie alokować i zwalniać pamięć dla MojKomunikat z pomocą AllocMem i FreeMem, czy będzie to odbywało się automatycznie jak dla zwykłych zmiennych np. typu char czy int.

3) Jak dla takiej zmiennej MojKomunikat (zmodyfikowanej struktury Message), zadeklarowanej w powyższy sposób wywoływać takie funkcje jak:
void PutMsg(struct MsgPort *, struct Message *);
struct Message *GetMsg(struct MsgPort *);
void ReplyMsg(struct Message *);

jaki powinien być poprawny zapis, bo w tej formie:
PutMsg(MojPort, &MojKomunikat);
&MojKomunikat=GetMsg(MojPort);
ReplyMsg(&MojKomunikat);

kompilator tego nie przyjmie ze względu na niezgodność typów danych (Message != MyMessage).

4) Bardziej z podstaw C, jak się dobierać do poszczególnych pól w zmiennej/strukturze MojKomunikat zadeklarowanej w powyższy sposób, za pomocą ".", czy "->"?
Który sposób będzie poprawny:
MojKomunikat.mn_Node.ln_Type=NT_MESSAGE
MojKomunikat->mn_Node->ln_Type=NT_MESSAGE
MojKomunikat->mn_Node.ln_Type=NT_MESSAGE

W jakich przypadkach używa się jednego operatora a w jakich drugiego?

5) Czy funkcja ReplyMsg sama zmienia typ komunikatu z NT_MESSAGE na NT_REPLYMSG, czy ja muszę sam to zrobić przed jej użyciem.

6) Czy deklarację MojKomunikat z pkt.2 mogę umieścić jeszcze przed funkcją main, żeby mieć do niej bezpośredni dostęp z każdego miejsca mojego programu, żeby nie trzeba było przekazywać wskaźników do niej między różnymi funkcjami.

Na pierwszy raz wystarczy, będę wdzięczny za rozjaśnienie mi powyższych kwestii, w szczególności pkt.1.
[#2] Re: Początki programowania w C

@dolek, post #1

ad. 2) Alokacja będzie automatyczna – na stosie programu, jeżeli będzie to zmienna lokalna, albo w obszarze zmiennych globalnych.

ad. 3) Można to zrobić na dwa sposoby. Pierwszy, to zrzutowanie typu MyMessage na Message. Możemy tak zrobić, jeżeli typ do którego rzutujemy jest pierwszym elementem typu rzutowanego.

ReplyMsg((struct Message*)&MojKomunikat);


Drugi, bardziej elegancki, ale czasem dłuższy to wyłuskanie składowej typu docelowego:

ReplyMsg(&MojKomunikat.Message);


ad. 4) W Twoim przypadku trzecia wersja (same kropki). Kropka to operator wyjęcia elementu ze struktury, strzałka to jakby połączenie operatora * (wyłuskanie wskaźnika) z kropką. Jeżeli więc mamy strukturę, to element wyciągamy kropką, jeżeli wskaźnik na strukturę – strzałką.

ad. 5) Zmienia. Natomiast PutMsg() nie ustawia automatycznie NT_MESSAGE, to trzeba zrobić samemu.

ad. 6) Możesz tak zrobić, pamiętając jednak, że nadmiar zmiennych globalnych utrudnia zarządzanie kodem i utrzymanie projektu. A już zupełna maniana może nastąpić, gdy uczynisz swój program wielowątkowym.

Przy okazji, nie nazywaj składowych struktur nazwami typów, bo to może prowadzić do irytujących pomyłek.

Ostatnia aktualizacja: 03.01.2017 22:42:24 przez Krashan
[#3] Re: Początki programowania w C

@dolek, post #1

Co do 1) to teoretyzuję, ale nadchodzące do Amigi dane będą pewnie wypełniać kolejne requesty z kolejki, bez wnikania który task wstawił który request. Bo pomyśl, załóżmy że na każdy wysłany pakiet urządzenie na drugim końcu coś odpowiada. To skąd serial.device ma wiedzieć która odpowiedź jest na który pakiet? A jak obsłużyć sytuację, gdy któryś task coś wyśle, ale nie wystawi requesta na odczyt? Mój wniosek jest taki, że kilka niezależnych aplikacji nie jest w stanie współdzielić portu szeregowego do odczytu. Wątki jednej aplikacji mogłyby, ale prościej jest czytać port w jednym wątku i ewentualnie potem rozdzielać dane.
[#4] Re: Początki programowania w C

@Krashan, post #3

Akurat w przypadku mojego programu nie przeszkadzało by mi jak by system zwracał informacje przychodzące na port szeregowy do wszystkich jego uruchomionych kopii jednocześnie. Mój program i tak wysyłając pakiet przez port szeregowy oznacza go identyfikatorem a i odpowiedzi posiadają też taki adres, więc program sam sobie analizuje, który z otrzymanych pakietów jest dla niego i musi go przetworzyć a który ma odrzucić.
Jeśli nie da się tego zrobić to wymyśliłem też drugi sposób. Niestety jest bardziej skomplikowany, wymaga więcej roboty, więcej kodu, wykonania dodatkowego programu pośredniczącego w komunikacji i wymaga właśnie opanowania systemu komunikatów z którym na razie mam problem.
Miałoby to działać tak, że byłby dodatkowy program serwer z jednej strony obsługujący dwukierunkową komunikację przez port szeregowy a z drugiej strony obsługujący dwukierunkową komunikację z podłączonymi do niego programami klienckimi. To serwer byłby odpowiedzialny za rozesłanie tego co otrzymał z portu szeregowego do wszystkich podłączonych do niego klientów i w drugą stronę. Każdy klient dostawałby ten sam pakiet danych od serwera i na podstawie zawartego w nim adresu podejmowałby działanie albo nie.
[#5] Re: Początki programowania w C

@Krashan, post #2

ad. 3) Czy oprócz "eleganckości" jest jeszcze coś co wyróżnia te dwa sposoby, czy są całkowicie równoważne. Rozumiem, że tego rzutowania mogę użyć dlatego, że w mojej strukturze MyMessage, strukutra Message jest na pierwszym miejscu. Czy jak by nie była na pierwszym miejscu to musiałbym użyć drugiego sposobu?

ad. 4) To znaczy, że jak deklaruję zmienną w ten sposób:
struct MyMessage MojKomunikat;

to stosuję:
MojKomunikat.mn_Node.ln_Type=NT_MESSAGE;

a jak bym zrobił to w ten sposób:
struct MyMessage *MojKomunikat;

to musiałbym się odwoływać w taki sposób:
MojKomunikat->mn_Node->ln_Type=NT_MESSAGE;

Dobrze zrozumiałem?
[#6] Re: Początki programowania w C

@dolek, post #4

A nie możesz podejść do tego na zasadzie klient-serwer? Jeden proces do komunikacji przez porty na węzeł i twoje aplikacje wysyłają komunikaty do niego i od niego odbierają. Takim czymś najłatwiej zarządzać.
[#7] Re: Początki programowania w C

@perinoid, post #6

Wydaje mi się, że to o czym piszesz właśnie opisałem w poście 4.
Przepływ danych wyglądał by tak w moim przypadku:
Klienci->Serwer->Port szeregowy
Port szeregowy->Serwer->Klienci
Serwer byłby tym węzłem.
[#8] Re: Początki programowania w C

@dolek, post #5

ad. 4) To znaczy, że jak deklaruję zmienną w ten sposób:
struct MyMessage MojKomunikat;

to stosuję:
MojKomunikat.mn_Node.ln_Type=NT_MESSAGE;

a jak bym zrobił to w ten sposób:
struct MyMessage *MojKomunikat;

to musiałbym się odwoływać w taki sposób:
MojKomunikat->mn_Node->ln_Type=NT_MESSAGE;

Dobrze zrozumiałem?


Nie. Musialbys uzyc

MojKomunikat->mn_Node.ln_Type = NT_MESSAGE;


Najpierw -> bo MojKomunikat jest typu struct MyMessage *. Potem kropka, bo struct Message zaczyna sie nastepujaco:

struct Message {
    struct Node mn_Node;
    ....
};


Innymi slowy, to czy uzywasz wskaznika na MyMessage czy tez samego MyMessage nie wplywa na to, jak zbudowana jest struktura Message :)
[#9] Re: Początki programowania w C

@dolek, post #5

Czy oprócz "eleganckości" jest jeszcze coś co wyróżnia te dwa sposoby
Przy rzutowaniu element typu docelowego musi być na początku typu rzutowanego. Przy wyłuskaniu elementu – niekoniecznie.

Na resztę odpowiedział już mschulz.
[#10] Re: Początki programowania w C

@dolek, post #4

Miałoby to działać tak, że byłby dodatkowy program serwer z jednej strony obsługujący dwukierunkową komunikację przez port szeregowy a z drugiej strony obsługujący dwukierunkową komunikację
Sprawa by się rypła w momencie gdyby ktoś uruchomił inną (niezależną od Twojej) aplikację również używającą portu szeregowego.
[#11] Re: Początki programowania w C

@Krashan, post #10

Ale w takim przypadku serwer i tak miałby ustawiony dostęp do portu szeregowego na wyłączność, więc żadna inna aplikacja wymagająca dostępu do tego portu by się nie uruchomiła. Czyli mielibyśmy do czynienia z sytuacją standardową/zwykłą: jeden port szeregowy->jedna aplikacja korzystająca z niego na raz.
[#12] Re: Początki programowania w C

@dolek, post #11

No tak, ale wtedy nie korzystasz z trybu shared.
Na stronie www.PPA.pl, podobnie jak na wielu innych stronach internetowych, wykorzystywane są tzw. cookies (ciasteczka). Służą ona m.in. do tego, aby zalogować się na swoje konto, czy brać udział w ankietach. Ze względu na nowe regulacje prawne jesteśmy zobowiązani do poinformowania Cię o tym w wyraźniejszy niż dotychczas sposób. Dalsze korzystanie z naszej strony bez zmiany ustawień przeglądarki internetowej będzie oznaczać, że zgadzasz się na ich wykorzystywanie.
OK, rozumiem