[C++] Optymalizacja szybkości działania klas

0

Witam, mam dwa proste pytania związane z optymalizacją szybkości działania klas:

  1. Jak wiadomo zwykłe metody klasy wymagają wskaźnika na obiekt (zwykle trzymanego w rejestrze EBP).

Aby pozbyć się tej wady można zadeklarować wszystkie zmienne/metody w postaci static.

Wówczas taka klasa będzie działać szybciej, aczkolwiek będę mógł naraz używać tylko jednego egzemplarza takiej klasy.

No i właśnie tu jest pytanie - jak można by to było rozwiązać, bym mógł zrobić więcej egzemplarzy (ok. 2-4, nie więcej). Jakie widzę tutaj rozwiązanie problemu? Np. powielenie klasy przez kompilator, tak żeby były metody działające dla pierwszej klasy i takie same metody dla drugiej klasy. Da się to jakoś elegancko rozwiązać? Może szablonowo?

  1. Drugie pytanie w sumie pokrywa się z pierwszym. Mam np. taką klasę:
class Klasa {
  static const int x = 7;
  /*
  A tu jakieś zmienne i metody
  */
}

Teraz chciałbym, żeby powstały dwa egzemplarze tej klasy, ale żeby w jednym z nich stała x wynosiła 7, w innym np. 9. Dlaczego nie zrobię stałej x w postaci zmiennej? Stała x występuje bardzo często w metodach, na które są położone wysokie wymagania czasowe. Jeśli x nie będzie stałą, to wówczas kompilator nie uprości większości wyrażeń i nie zastosuje dodatkowych optymalizacji.

Da się to jakoś elegancko rozwiązać, bym nie musiał robić dwóch wersji klas?

*)
Właśnie sobie pomyślałem o sztuczce z #include. Można by umieścić ciało klasy z pominięciem linijki z jej nazwą, a x zrobić pod postacią #define. Np:
KlasaPart.cpp:

static const int x = XXX;
/* zmienne i metody - wszystkie STATYCZNE! */
}

Main.cpp

class Pierwsza {
#define XXX 7
#include "KlasaPart.cpp"

class Druga {
#define XXX 9
#include "KlasaPart.cpp"

int main(){return 0;};

Tylko, że to rozwiązanie nie jest za bardzo eleganckie :D Może da się to zrobić z użyciem szablonów? Albo jeszcze jakoś inaczej?

0

To pewnie nie pomoże, ale przyszedł mi do głowy taki pomysł, by z każdego elementu klasy zrobić tablicę tylu elementową, ile chcesz mieć obiektów.
tzn. zamiast

int a;
int b;

miałbyś

int a[3];
int b[3];

Wówczas byłby jeden obiekt rzeczywisty, ale mógłbyś go traktować jak kilka obiektów odwołując się do indeksów w tablicach. Ale to takie rozważania śpiącego newbie ;)

0

Powiem tak. Nie rozumiem cię. Po pierwsze, po co ci optymalizować klasy? Jeżeli ci przeszkadza coś w nich - przenieś się na C. poza tym po co korzystać z szablonów? Chcesz poprawić prędkość działania programu, czy ją pogorszyć? Człowieku, klasy nie powstały po to, abyś ty je "poprawiał". One mają zastosowanie w organizowaniu danych i pracy w teamie. A jeżeli ci tak zależy, to możesz korzystać z metod statycznych:
klasa1 {...};
klasa1 bla;
klasa1::metodaStatyczna(bla);
Chyba wiesz o co mi chodzi. A jeżeli tak ci przeszkadza C++ - jeden z najszybszych wysokopoziomowych języków programowania to zacznij się uczyć ASM'a.

0

No da się szablonowo, bo dlaczego nie:

template<int Numer>
class Klasa {
   public:
   static const int stala = Numer;

   static void blabla() { ... }
   static int bla2bla() { ... }
   ...
   };

// ...

Klasa<77> jeden;
Klasa<78> dwa;

Tylko... jak klasa ma wszystko statyczne, to co to za klasa? To dupa, a nie klasa - nawet singletonu za bardzo nie ma co robić z tego... Namespace'a sobie zrób i do niego wepchnij wszystkie funkcje, które u Ciebie nie są w żadnym wypadku obiektowe, tylko w stylu proceduralnym napisane, skoro thisa nie potrzebują.

A, no i tak poza tym... jeszcze tego consta mogę zrozumieć... Ale żeby szukać zysku w wydajności usuwając this, to jeszcze nie słyszałem... też mi zysk - jedno odwołanie via wskaźnik per wywołanie funkcji. To sam call/return chyba więcej zajmuje...

0

dokladnie, dokladnie.. zwlaszcza ze this jest praktycznie zawsze przez rejestr przekazywany nawet nie przez stos.. to juz predzej by sie przyspieszylo jakby .... powycinac wszystkie parametry i wartosci zwracane z metod hahaha!

deus /ponizej/ - i stad moje malo merytoryczne rozbawienie

0

Zależy... jeżeli nie poda się konwencji to dla metod domyślnie przyjmowana jest thiscall, ta zaś działa jak stdcall dla argumentów jawnych, w bodaj *cx zaś idzie this. Jeżeli programista sprecyzuje jakiej konwencji funkcja używa /no, może pomijając podanie thiscall/ to this jest przekazywany jak każdy inny argument, jako pierwszy, za nim pozostałe - tak jakby wszystkie argumenty były jawne. Tzn. jak?

  • cdecl: wszystkie argumenty idą poprzez stos, wywołujący dokonuje po wywołaniu korekcji stosu, usuwa wrzucone argumenty.
  • stdcall: wszystkie argumenty idą poprzez stos, argumenty usuwa wywoływana funkcja, w x86 jest instrukcja retn rozmiar_argumentów usuwająca podczas wracania n bajtów ze stosu.
  • fastcall: dwa pierwsze argumenty są przekazywane w rejestrach *cx i *dx, pozostałe poprzez stos tak jak w stdcall.

Usunąć this? A po kiego grzyba? I tak będziesz jawnie przekazywał wtedy pointer do struktury, tablicy czy cośtam... na jedno wychodzi, przy małych funkcjach wręcz this szybsze będzie.

peter_k napisał(a)
  1. Jak wiadomo zwykłe metody klasy wymagają wskaźnika na obiekt (zwykle trzymanego w rejestrze EBP).
    Oj, stary, nie prowokuj mnie... *bp to podstawa ramki stosu, w *bp trzyma się wartość *sp przed utworzeniem obszaru na zmienne lokalne. Gdybyś miał podstawy assemblera to wiedziałbyś jaką głupotę palnąłeś - przy wywołaniu funkcji rejestry *bx, *si, *di i *bp mają pozostać niezmienione, tak zakładają konwencje wywołań... i Intel. Przecież to jest sprzeczne - rejestr ma pozostać niezmieniony a w nim przekazywać argument!? I nie 'trzyma się', przekazuje.
0

Ok, zostałem zaatakowany, już spieszę z odpowiedzią.

deus napisał(a)

I tak będziesz jawnie przekazywał wtedy pointer do struktury, tablicy czy cośtam... na jedno wychodzi, przy małych funkcjach wręcz this szybsze będzie.

Nie zrozumiał Pan tego co napisałem: proszę mi powiedzieć, gdzie w mojej wizji pseudo-klasy, gdzie wszystkie zmienne i metody są statyczne, występuje jakiś this czy wskaźnik do struktury? Ano, nie ma, wszystko jest globalne i kompilator zna wszystkie adresy już w czasie kompilacji. Po co mi to? Proszę czytać dalej.

Ranides napisał(a)

Ale żeby szukać zysku w wydajności usuwając this, to jeszcze nie słyszałem... też mi zysk - jedno odwołanie via wskaźnik per wywołanie funkcji. To sam call/return chyba więcej zajmuje...

Zgadza się, jeśli chodzi o call i return zysk jest znikomy... Aczkolwiek może nie powiedziałem jak moja klasa będzie działać. Otóż metody będą często korzystały z małych tablic "Look-up" (tak małych, że będą się mieściły w L1). Tzn, często do wyliczenia różnych danych będę adresował te tablice kilkoma (najczęściej dwoma zmiennymi). W architekturze IA-32 mogę odwoływać się do danych używając stałej i dwóch modyfikatorów adresowych. Dzięki temu, że nie mam żadnego wskaźnika na obiekt dostaję jeden rejestr do wskazywania więcej, i szybkość działania programu jest już znacząco wyższa (rzędu kilkudziesięciu procent - w moim przypadku).

deus napisał(a)
peter_k napisał(a)
  1. Jak wiadomo zwykłe metody klasy wymagają wskaźnika na obiekt (zwykle trzymanego w rejestrze EBP).
    Oj, stary, nie prowokuj mnie... *bp to podstawa ramki stosu, w *bp trzyma się wartość *sp przed utworzeniem obszaru na zmienne lokalne. Gdybyś miał podstawy assemblera to wiedziałbyś jaką głupotę palnąłeś - przy wywołaniu funkcji rejestry *bx, *si, *di i *bp mają pozostać niezmienione, tak zakładają konwencje wywołań... i Intel. Przecież to jest sprzeczne - rejestr ma pozostać niezmieniony a w nim przekazywać argument!? I nie 'trzyma się', przekazuje.

Proszę pamiętać, że mi zależy na optymalizacji kodu. Zapewne słyszał Pan o trybie kompilacji "-fomit-frame-pointer". Wówczas zmienne na stosie są wskazywane przez rejestr ESP, a rejestr EBP zwalnia się dla naszych potrzeb! Napisałem, że wówczas wskaźnik this obiektu jest przekazywany w rejestrze EBP. Przyznaję się do błędu - w innym rejestrze.

Ranides napisał(a)

No da się szablonowo, bo dlaczego nie:

template<int Numer>
class Klasa {
   public:
   static const int stala = Numer;

   static void blabla() { ... }
   static int bla2bla() { ... }
   ...
   };

// ...

Klasa<77> jeden;
Klasa<78> dwa;

Dziękuję Panu bardzo za odpowiedź! Właśnie o coś takiego mi chodziło. A czy jeśli zrobię:

Klasa<33> jeden;
Klasa<33> dwa;

to czy wtedy kompilator utworzy dwie osobne klasy (zwracam uwagę na słowo: klasy, a nie obiekty, czyli realizacje tych klas)? Jeśli tak to dobrze, bo o coś takiego mi chodziło.

0

nie, nie utworzy dwoch. pamietaj ze wszystko w klasie co jest oznaczone jako STATIC jest zwiazane z danym TYPEM a nie OBIEKTEM. w ostatnim przykladzie jaki podales, obie zmienne maja ten sam typ (klasa-33), a wiec odwolanie jeden.blabla() oraz dwa.blabla() beda wywolaniem tej samej metody z tego samego typu/klasy a wiec wywola sie ta sama fizyczna funkcja..

0

ja tylko dodam jeden komentarz: uwazaj na 'static' gdy piszesz wielowatkowe aplikacje.

0

Jedno pytanie, moze glupie. Po co pisac w c++ skoro uzywane sa metody i wlasciwosci statyczne? Moze ja glupi jestem ale static kojarzy mi sie z ostrym lamerstwem.

// right, strerror() i strtok() az smierdza lamerstwem [mf] [rotfl]

0

EgonOlsen - nie przeginaj :D Źle ci się kojarzy ;) Może klasa mająca same statyki jest dziwaczna, ale coś-tam gdzieś-tam lamerstwem żadnym nie jest.

peter_k: jak masz zysk, to ok - nie będę się już czepiał - też kilka razy dziwną akrobatykę robiłem... Na pytanie quetzalcoatl odpowiedział - jedna będzie klasa, obiekty dwa.

Stogu: co ty koleś tworzysz:

poza tym po co korzystać z szablonów? Chcesz poprawić prędkość działania programu, czy ją pogorszyć?

wiesz, co to szablon? bez zbędnego rozpisywania się - w ogólnym przypadku szablony spowalniają kompilację, zwiększają wydajność aplikacji. Więc w ogólnym przypadku, pieprzysz waść coś :]

0

Pytanie z innej beczki. Czy nie lepiej zamiast kombinowac z static przyjrzec sie czasowo krytycznym miejscom kodu? Powolanie obiektu, wywolanie metody, odczyt wlasciwosci nie wydaja mi sie miejscami w ktorych mozna zaoszczedzic na szybkosci. Ostatnio udalo mi sie przyspieszyc algortym kompilacji pliku projektu w pewnym programie prawie dziesieciokrotnie dzieki zamianie miejsca przechowywania danych z pliku na pamiec. Szukalbym raczej optymalizacji gdzie indziej.

Ranides napisał(a)

Może klasa mająca same statyki jest dziwaczna, ale coś-tam gdzieś-tam lamerstwem żadnym nie jest.

Uzycie static to mi tak bardziej to singletona pasuje albo czegos w tym stylu. Static w "zwyklej" klasie? Brrrrrrr.....

0

Nie no, ja się tam nie znam, ale to z tymi tablicami lookupowymi + zyskiem z rejestru mnie przekonuje. A ponieważ się nie znam na tak niskim poziomie, to nie będę cwaniaka udawał. Faktycznie mi to grzebaniem w kodzie asemblerowym śmierdzi mocno i jeśli to ma być w c++ i jednocześnie brać takie rzeczy pod uwagę, to może to i tak jakoś dziwnie wyglądać... Oczywiście takiego stylu nie lubię, kiedy w HLLu coś się pisze myśląc o cache'u procesora, ale wielu rzeczy nie lubie, a je robię.

Jakby coś z szablonami było potrzebne, to pisać, postaram się rozwiązać.

EgonOlsen:
A weź z tym brrrr odejdź szatanie :] Nie widzę żadnego argumentu na nie. Widzę za to argumenty na tak: współdzielenie zasobu (pliku/urządzenia/pamięci), zliczanie referencji, ilości obiektów. Dana klasa obiektów ma właściwości charakterystyczne dla obiektu, ma charakterystyczne dla klasy. Normalne. Zgodne z to z paradygmatami OO, zgodne ze zdrowym rozsądkiem.

Masz do wyboru albo statyk (i zmienna powiązana jest przynajmniej z obiektem zeń korzystającym) albo zasięg globalny (i wisi w powietrzu) albo singleton (de facto równoznaczny z globalnym). Jest jeszcze zasięg pliku - chyba największe paskudztwo, ale też przydatne, i też go używam.

Mam np klasę do obsługi portu USB - w statycznych siedzą wyniki zwrócone przez find_busses i find_devices. Bo co - mam co chwilę enumerować urządzenia? Bo co - mam tworzyć zmienną globalną z tymi wynikami? Albo tworzyć singleton na raptem dwie tablice dynamiczne, z których korzystają tylko obiekty typu USBDevice?

Wielowątkowość - no tak, dostęp statycznych tylko z sekcji krytycznych, ale statyki i tak wskazują mi na współdzielone rzeczy, więc bez różnicy, gdzie to dam, jakiś mutex czy inny semafor i tak muszę wsadzić, żeby kontrolować dostęp.

O co kaman z tym całkowitym wyklęciem s. składowych?

0
Ranides napisał(a)

Mam np klasę do obsługi portu USB - w statycznych siedzą wyniki zwrócone przez find_busses i find_devices. Bo co - mam co chwilę enumerować urządzenia? Bo co - mam tworzyć zmienną globalną z tymi wynikami? Albo tworzyć singleton na raptem dwie tablice dynamiczne, z których korzystają tylko obiekty typu USBDevice?

A po co w kazdy obiekt ma "wiedziec" ze obok niego istnieja inne obiekty? A gdyby to zrobic w architekturze client-serwer, gdzie sterownik danego urzadzenia jest klientem, a klasa obslugujaca USB serwerem?

Ranides napisał(a)

Wielowątkowość - no tak, dostęp statycznych tylko z sekcji krytycznych, ale statyki i tak wskazują mi na współdzielone rzeczy, więc bez różnicy, gdzie to dam, jakiś mutex czy inny semafor i tak muszę wsadzić, żeby kontrolować dostęp.

To akurat jest najmniejszy problem. Wystarczy klase zdziedziczyc z obiektu typy TCriticalSection lub TMultiReadExclusiveWriteSynchronizer i Od razu klasa ma metody do zabiezpieczenia sie przed wielodostepem. Trzeba pamietac tylko o tym zeby przy kazdej metodzie typy Get/Set uzyc odpowiednio BeginRead/BeginWrite na poczatu i EndRead/EndWrite na koncu. Jesli metoda ma przeprowadzic jakies operacje na wlasciwosciach tak zabezpieczonych to wystarczy ze na wejsciu zrobi sobie kopie tych wlasciwosci i moze szalec. Inny watek nie ma w tym czasie problemow z odczytem/zapisem tych wlasciwosci. Program nie "kleszczy" sie. Jest to proste i eleganckie.

Ranides napisał(a)

O co kaman z tym całkowitym wyklęciem s. składowych?

Powiem tak: globalne zmienne byly dobre w BASIC'u, uzywanie globalnych zmiennych, metod w OOP zakrawa mi na herezje, swietokradztwo itd. Jesli ktos uzywa globalnych rzeczy w OOP to moze to wynikac z tego ze:

  1. architektura softu jest do bani
  2. czlowiek ktory tak czyni nie bardzo kuma OOP

Zalozmy taka sytuacje: jest globalna funkcja ktora cos tam sobie robi na obiektach (np. wypisz()), wszystkie te obiekty dziedzicza z jakiejs tam abstrakcyjnej klasy. W pewnym momencie chcemy zeby zachowanie tej funkcji dla jednych obiektow bylo takie a dla innych inne. W tym momencie za kazdym razem gdy dochodzi obiekt nowej klasy musimy na nowo grzebac w tej funkcji globalej a nie o to w OOP chodzi. Klasy maja byc zrobione z maksymalnym wykorzystaniem dobrodziejstw polimorfizmu itd. Zaimplementowac i zapomniec, jesli bede chcial cos zmienic to zmieniam tylko w jednej klasie i nie musze sie zastanawiac czy i jaki to ma wplyw na zachowanie innych obiektow.

Sa oczywiscie sytuacje gdzie bez zmiennych statycznych nie obedzie sie (np. ten singleton) ale o zmiennych/metodach globalnych radze zapomniec.

0

Ech, chcesz przyspieszyc wywolania małych funkcji to zrób je inline.
Odpada wtedy w ogóle cały call, problem przekazywania parametrów i zwracania wyników, a na dodatek można zrobić czesto wiele innych optymalizacji. Niektóre kompilatory robią inline takich funkcji z automatu, bez konieczności podawania słowa kluczowego. Sprawdziłeś, że rzeczywście ten Twój this Ci spowalnia program? Może Twój kompilator wyrzucił go już dawno za Ciebie, skoro w tych funkcjach nie odwołujesz się do zmiennych klasy.

Poza tym kierowanie się mikrooptymalizacją przy projektowaniu kodu jest [glowa].

0

A po co w kazdy obiekt ma "wiedziec" ze obok niego istnieja inne obiekty?

Żaden obiekt nie wie, że istnieją obok niego inne obiekty w tym konkretnym przypadku. Po prostu każdy wie, że istnieje coś takiego, jak jeden jedyny kontroler usb. Taaak - mogę zrobić klient-serwer i do "serwera" wstawić dwie tablice na krzyż, jak post wyżej sam napisałem - pytanie po co? Żeby rozdąć projekt jeszcze bardziej, skoro można minimalistycznie wstawić po prostu dane w taki zakres leksykalny, w jakim mają być? I związać z klasą obiektów, które zeń korzystają?

Panie zielonka, jakbym wszędzie wszystko rozbijał w taki ultra OO sposób, to bym miał projekt, o którego strukturze bym książkę mógł napisać. Książkę nikomu niepotrzebną swoją drogą, bo łatwiej, chociaż mniej trendy, jest napisać ten jeden moduł od nowa, niż ślęczeć x dni nad dokumentacją projektu i szukać, co po czym ma dziedziczyć, co przeładować, i dlaczego do cholery obsługa urządzenia możliwa do napisania w 150 liniach zajmuje 10KB i wprowadza 10 klas abstrakcyjnych albo pośredniczących!?

Po co inny obiekt ma wiedzieć, że istnieją inne? A boże mój, czasem po prostu ma wiedzieć, bo w modelowanym problemie obiekty wiedzą o sobie nawzajem. I nie będę zawsze tworzył dodatkowej klasy-pojemnika, skoro obiekty same mogą się orientować w tym, że są w stadzie.

Zresztą ja się pytam, co to niby za lepsiejsze rozwiązanie, że mamy klasę "grupa" i wszystkie obiekty wsadzone doń od rozwiązania, że mamy po prostu obiekty, samodzielnie wiedzące o tym, że są jednego typu, więc są razem? Pojęcie klasy samo naturalnie tworzy grupę, nie muszę wprowadzać jakieś dodatkowego gościa, co mi grupuje - bez łaski, bez komplikacji, bez narzutów.

No i na OO świat się nie kończy, a C++ nie jest tylko OO, jest wieloparadygmatowy. Ja mam przedstawić problem w elastycznym języku tak, jak on wygląda, a nie okrajać zagadnienie tak, żeby wpasowało się w język.

uzywanie globalnych zmiennych, metod w OOP zakrawa mi na herezje, swietokradztwo itd.

Aaaa! Ekskomuniką pewnie objęty zostanę - zwłaszcza, że i proceduralne programy mi się pisać zdarzało... oj :| Ale cóź, agnostyk jestem - heretykiem nie raz okrzyknięty zostałem, świetokradztwo niejedno w życiu popełniłem.

Faktycznie, słowa herezja i świętokradztwo dobrze dobrałeś. Z reguły słowa te padają z ust ludzi z ciasnym horyzontem, powiedziałbym fanatyków. A trzymanie się jednego paradygmatu to właśnie ciasnota umysłowa - "poza OO nie zbawienia"? Toż to używanie STLa to herezja - tam wszystko jedzie na szablonach, nigdzie polimorfizmu albo OO nie uświadczysz. Siła biblioteki standardowej C++ i Boosta tkwi w konkretyzacji wzorców, w metaprogramowaniu, a nie OO.

Sa oczywiscie sytuacje gdzie bez zmiennych statycznych nie obedzie sie (np. ten singleton) ale o zmiennych/metodach globalnych radze zapomniec.

Zapomnieć o bibliotece iostream - używa cin, cout, clog, cerr:]
A dodam, że takich globalnych obiektów może być całkiem sporo, od reprezentacji urządzeń właśnie poczynając.

A, to jeszcze jest:

jest globalna funkcja ktora cos tam sobie robi na obiektach (np. wypisz()), wszystkie te obiekty dziedzicza z jakiejs tam abstrakcyjnej klasy. W pewnym momencie chcemy zeby zachowanie tej funkcji dla jednych obiektow bylo takie a dla innych inne. W tym momencie za kazdym razem gdy dochodzi obiekt nowej klasy musimy na nowo grzebac w tej funkcji globalej

A o przeładowaniu funkcji globalnej, albo o szablonach i ich specjalizacjach pan nie słyszał oczywiście. Powiem więcej, właśnie w standardowej bibliotece funkcja "wypisz" jest globalna i wprowadzając własny obiekt wprowadza się przeładowany, globalny operator<<(ostream&, const MojaKlasa&) (najczęściej zaprzyjaźniony; o zgrozo! kolejne łamanie świętych praw OO, tym razem hermetyzacji!!!) Przypowieść twa więc ewidentnie do bani jest :]

Złota myśl na dziś: "nigdy nie przeginać pały" ;P
Złota myśl na jutro: "mikrooptymalizacja w HLLu to zło, ale czasem jest to mniejsze zło"

0
Ranides napisał(a)

Żaden obiekt nie wie, że istnieją obok niego inne obiekty w tym konkretnym przypadku. Po prostu każdy wie, że istnieje coś takiego, jak jeden jedyny kontroler usb.

A co będzie jeśli 2 urządzonka będą chciały rozmawiać na tym samym porcie RS485 na ten przykład? ACCESS DENIED?

Ranides napisał(a)

Taaak - mogę zrobić klient-serwer i do "serwera" wstawić dwie tablice na krzyż, jak post wyżej sam napisałem - pytanie po co?

Ja zrobiłbym w tym serwerze liste dostepnych portów, następnie obiekt portu abstrakcyjnego i konkretne obiekty dla obsługi USB,RS,Ethernetu,LPT i czego tam jeszcze dusza zapragnie.

Ranides napisał(a)

Żeby rozdąć projekt jeszcze bardziej, skoro można minimalistycznie wstawić po prostu dane w taki zakres leksykalny, w jakim mają być? I związać z klasą obiektów, które zeń korzystają?

... a potem poprawiać jednego buga w 20tu miejcach ...

Inny problem: Co w przypadku jeśli dane urządzenie zapragnie pogadać innym protokołem, też zbudujesz nową klasę? A jeśli kilka urządzeń gada tym samym protokołem to do każdego metodą kopiuj-wklej dołożysz ten sam kod?

Ranides napisał(a)

ślęczeć x dni nad dokumentacją projektu i szukać, co po czym ma dziedziczyć, co przeładować, i dlaczego do cholery obsługa urządzenia możliwa do napisania w 150 liniach zajmuje 10KB i wprowadza 10 klas abstrakcyjnych albo pośredniczących!?

Wystarczy rzut oka na diagram UML.

Ranides napisał(a)

No i na OO świat się nie kończy, a C++ nie jest tylko OO, jest wieloparadygmatowy. Ja mam przedstawić problem w elastycznym języku tak, jak on wygląda, a nie okrajać zagadnienie tak, żeby wpasowało się w język.

Okrajanie zagadnienia mi sie podoba, takiego argumentu jeszcze nie słyszałem w dysputach o OOP.

Ranides napisał(a)

Panie zielonka Aaaa! Ekskomuniką pewnie objęty zostanę - zwłaszcza, że i proceduralne programy mi się pisać zdarzało... oj :| Ale cóź, agnostyk jestem - heretykiem nie raz okrzyknięty zostałem, świetokradztwo niejedno w życiu popełniłem.

Faktycznie, słowa herezja i świętokradztwo dobrze dobrałeś. Z reguły słowa te padają z ust ludzi z ciasnym horyzontem, powiedziałbym fanatyków. A trzymanie się jednego paradygmatu to właśnie ciasnota umysłowa - "poza OO nie zbawienia"?

Kolega podchodzi do tego zbyt emocjonalnie, kolega uważa bo jeszcze kiedyś koledze pompka wysiądzie od tego stresu.

Ranides napisał(a)

Złota myśl na dziś: "nigdy nie przeginać pały" ;P
Złota myśl na jutro: "mikrooptymalizacja w HLLu to zło, ale czasem jest to mniejsze zło"

Złota myśl na zawsze: "program zrozumiały dla maszyny może napisać każdy głupiec, sztuką jest napisać program zrozumiały dla innego człowieka". ;)

0

A co będzie jeśli 2 urządzonka będą chciały rozmawiać na tym samym porcie RS485 na ten przykład? ACCESS DENIED?

a co to ma wspólnego z USB?

Ja zrobiłbym w tym serwerze liste dostepnych portów, następnie obiekt portu abstrakcyjnego i konkretne obiekty dla obsługi USB,RS,Ethernetu,LPT i czego tam jeszcze dusza zapragnie.

a mi płacą na komunikację z urządzeniem, które ma tylko USB, i nic więcej mieć nie będzie - decyzja projektowa. Za pierdoły nie płacą - szkoda.

Co w przypadku jeśli dane urządzenie zapragnie pogadać innym protokołem, też zbudujesz nową klasę? A jeśli kilka urządzeń gada tym samym protokołem to do każdego metodą kopiuj-wklej dołożysz ten sam kod?

A tak w ogóle, to się zgubiłem, sugerujesz, że code reusability jest niemożliwe w przypadku klas, które mają składniki statyczne? Że nie mogą dziedziczyć i po nich dziedziczyć nie można? Że nie mogą być klasami uogólnionymi, zapisanymi szablonowo? Krótko: mylisz się pan.

... a potem poprawiać jednego buga w 20tu miejcach ...

Nie wiem o czym mówisz - na pewno nie o konsekwencji składników statycznych, które konsolidują i skracają kod właśnie, nie powodując rozbicia na x klas, x miejsc. Mam w jednym pliku - ba! - w jednym zasięgu leksykalnym (jednej klasy) i dane i ich obsługę.

Wystarczy rzut oka na diagram UML.

Tak, jeśli te 120 lini zapiszesz tak, że będzie trzeba oczami rzucać, to mi oczu zabraknie, jak przyjdzie resztę analizować. Ogólnie - trochę się znowu zagubiony czuję i porządek robię: co to ma wspólnego z pytaniem początkowym? Jak nic, to wycinamy.

Okrajanie zagadnienia mi sie podoba, takiego argumentu jeszcze nie słyszałem w dysputach o OOP.

Może po prostu gadasz z ludźmi, co przesiadają się z proceduralnego na OO. Dla nich to faktycznie OO jest szczytem abstrakcji, nawet nadmiarowym pewnie. Ale to nie jest szczyt. Chociażby te elementy metaprogramowania co znam, pokazują, że OO to Mount Blanc nie jest; najwyżej Giewont. Nie zawsze starcza. Ale znowu - porządki, wycinamy, bo to nie prowadzi to odpowiedzi na pytanie.

Kolega podchodzi do tego zbyt emocjonalnie,

Podchodzę, bo ani jednego argumentu nie dostałem. Sugerujesz, że łamiąc jeden paradygmat automatycznie rezygnuję z całego OO - bzdura. Nota bene nie wiem, dlaczego statyki mają być łamaniem OO. Nie wiem, bo dużo piszesz, ale nie na temat, a ja się głupi nabieram i polemizuję z przekory nie z tym, co trzeba.

program zrozumiały dla maszyny może napisać każdy głupiec, sztuką jest napisać program zrozumiały dla innego człowieka

No i super. Dlaczego statyk jest niezrozumiały ja się pytam? Mówisz, że emocjonalnie podchodzę? Facet, ja powinienem na wzmiankę o herezji zwyczajnie rzucić paroma epitetami i zakończyć. Na pytanie "dlaczego złe" mam odpowiedź "bo złe", a następnie wywody o dziedziczeniu/code reusability które nie mają z niczym związku, a na pewno nie z tą rozmową.

Robię więc reload, bo głębia dygresji mnie przeraziła:
dlaczego składnik statyczny łamie OO? Dlaczego go unikać? Dlaczego jest lamerstwem? Jakie mogą być negatywne następstwa jego użycia? Poza tym, że tobie się to użycie nie podoba (zapewne nie jest trendy).

Dopisane:
i ustosunkuj się do wzmianki o iostream - powiedz proszę jasno, że jest lamerska, bo używa globali.
ustosunkuj się do <algorithms> <map> <vector> itd - powiedz, że są lamerskie, bo nie używają polimorfizmu.
same herezje w stdlibie są. jak to uzasadnisz?

0

Doprawdy nie chce mi sie przekrzykiwac z kims kto "zawsze ma racje". Wspolczuje twoim klientom. Z mojej strony EOT.

// Hasłami z podręczników OOP każdy rzucać potrafi, przedstawić konkrety, jak widać, już nie. (dop. deus)

Pracuje juz kilka lat jak programista-analityk i architekt software'u. To nie sa hasla z podrecznikow, to sa doswiadczenia z zycia. Mialem kiedys w teamie takiego mlodego-gniewnego jak przedmowca. Caly kod ktory napisal dla nas wyladowal dawno w koszu.

Kilka postow wyzej przedstawilem pewna koncepcje i zostalem zakrzyczany. Well...

Doprawdy EOT.

// oj stary, ty nie widziales moich kodow, deus to sie za glowe lapie i mowi ze na to trzeba armii deszyfrantow
// poza tym rzeczywiscie eot, bo sie wątek mocno zboczyl. [mf]

1 użytkowników online, w tym zalogowanych: 0, gości: 1