[#61] Re: Wyzwania programistyczne

@jubi, post #60

można zastosować regułę "return early":


Ta regula nie zawsze ma sens i w przypadku tego kodu wbrew twoim oczekiwaniom skomplikowala by czytelnosc:

int main(void)
{
    struct IntuitionBase * IntuitionBase = NULL;
    struct GfxBase * GfxBase = NULL;
    struct Window * myWindow = NULL;
    BYTE running = TRUE;
    struct RastPort *rastPort = NULL;

    IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library", 0);   
    if (IntuitionBase == NULL)
    {
        return 0;
    }

    GfxBase = (struct GfxBase *)OpenLibrary("graphics.library", 0);
    if (GfxBase == NULL)
    {
        CloseLibrary((struct Library *)IntuitionBase);
        return 0;
    }

    myWindow = OpenWindowTagList(0, myWindowTags);
    if (myWindow == NULL)
    {
        CloseLibrary((struct Library *)GfxBase);
        CloseLibrary((struct Library *)IntuitionBase);
        return 0;
    }

    rastPort = myWindow->RPort;
    SetAPen(rastPort, 1);

    /* Wylosuj pierwszy punkt */
    ULONG x = (rand() * WINDOW_WIDTH) >> 16;
    ULONG y = (rand() * WINDOW_HEIGHT) >> 16;

    Move(rastPort, points[0].x, points[0].y);
    Draw(rastPort, points[1].x, points[1].y);
    Draw(rastPort, points[2].x, points[2].y);
    Draw(rastPort, points[0].x, points[0].y);

    do {
        struct IntuiMessage *msg;

        while ((msg = (struct IntuiMessage *)GetMsg(myWindow->UserPort)) != 0)
        {
            if (msg->Class == IDCMP_CLOSEWINDOW)
            {
                running = FALSE;
            }

            ReplyMsg((struct Message *)msg);
        }

        /* Wylosuj punkt trojkata */
        UWORD p = (rand() * 3) >> 16;

        x = (x + points[p].x) / 2;
        y = (y + points[p].y) / 2;

        WritePixel(rastPort, x, y);

    } while(running == TRUE);

    CloseWindow(myWindow);
    CloseLibrary((struct Library *)GfxBase);
    CloseLibrary((struct Library *)IntuitionBase);

    return 0;
}


Masz teraz co prawda mniej poziomow niz wczesniej ale, poniewaz otwierasz 3 zasoby z czego 2 sa od siebie zalezne (zeby otworzyc okno musisz najpierw otworzyc intuition.library) nie mozesz jakos ekstremalnie uproscic tych poczatkowch if-ow. Mozna wyciagnac to poza funkcje main() z mojego kodu i czasami tak sie robi, ale w tej pchelce chodzilo o to zeby kod byl jak najmniejszy. Dodatkowo, niedoswiadczony programista moze sie pogubic w tym co bylo zaalokowane, co nie bylo, a to juz najszybsza droga do wyciekow pamieci. A kompilator z kodu czy to w jednej postaci czy drugiej i tak postara sie zrobic cos w miare optymalnego.

Owszem, return early czasami ma sens (tym wiekszy w przypadku jezykow ktore pomagaja programiscie zarzadzac zasobami) i sam ja czesto stosuje w ten czy inny sposob (nie wazne czy przez return przy przez throw w C++/C#). Ale, poniewaz mnie czytelnosc kodu z paroma poziomami zaglebien nie przeraza, stosuje jedno albo drugie rozwiazanie w zaleznosci od potrzeb. Nie jestem zagorzalym fanatykiem ani "return once" nawet kosztem tak zagniezdzonego kodu ze pierwsze 80 kolumn jest pustych, ani "return early" gdzie stosuje return co 5 linijek.

Wszystko wedle potrzeb :P

Ostatnia aktualizacja: 27.09.2018 10:32:19 przez mschulz
[#62] Re: Wyzwania programistyczne

@jubi, post #60

Przykład podany w artykule niestety zupełnie pomija kwestię zwolnienia zasobów. W AmigaOS typowe jest, że każdy zasób należy jawnie zwolnić. Spróbuj to zapisać z regułą "return early" i powiedz gdzie się podziała czytelność.
[#63] Re: Wyzwania programistyczne

@Krashan, post #62

Już nie mówiąć o sugerowania programowaniu w stylu rzucania wyjątkami. brrr....

Odpowiednio sformatowany kod i dobre rozbicie na pliki/funkcje/metody daje o wiele lepszą czytelność z returnami na końcu. Wyłażenie z funkcji w środku jako sposób na programowanie to dla mnie sieczka do analizy.
[#64] Re: Wyzwania programistyczne

@mschulz, post #61

Dzięki za wyjaśnienie, w takim razie całe zwalnianie zasobów (i parę innych rzeczy) kwalifikuje się do wyodrębnienia do osobnych funkcji, w tej chwili ta jedna funkcja robi wszystko, ale rozumiem, że to miała być pchełka jak najkrótsza. Sama reguła return early jest tu nadal valid i poprawia czytelność, to że musisz zwalniać zasoby to jest kwestia osobna.
[#65] Re: Wyzwania programistyczne

@jubi, post #64

Jakbyś nie kombinował to trzymanie się tej reguły komplikuje kod w przypadku, gdy mamy do czynienia z kolejnym zdobyciem kilku zasobów i ich późniejszym zwolnieniem. Ona polepsza czytelność tylko i wyłącznie wtedy, gdy masz gwarancję, że system sam zwolni wszystkie zasoby przy wyjściu z programu i zrobi to we właściwej kolejności. W przypadku AmigaOS i pochodnych takiej gwarancji nie ma.
[#66] Re: Wyzwania programistyczne

@Krashan, post #65

kernelowcy liuksowi radzą sobie z tym tak, że robią "goto fail" i za labelem fail masz zwalnianie wszystkiego jak leci, z ifologią czy jest zaalokowane. Ja osobiście wolę takie podejście niż wielokrotne klamrowanie, ale trzeba się pilnować żeby zwolnić wszystko.
[#67] Re: Wyzwania programistyczne

@jubi, post #64

Oj Jubi, czytelnosc chciales poprawic, mimo ze jak juz podalem w przykladzie kod czytelniejszy sie nie zrobil. Wywalenie otwierania bibliotek poza funkcje main() skutkowalo by niepotrzebnym w tym przykladzie tworzeniem zmiennych globalnych. A takich brzydkich bledow jak te dwa w OpenLibrary nie zauwazyles ;) Pierwszy argument powinien byc typu CONST_STRPTR a w AmigaOS STRPTR jest "unsigned char". A ja flagami kompilatora tego nie ustawilem (-funsigned-char). Czyli powinno byc np. (CONST_STRPTR)"intuition.library".

Co gorsza, w _startup() zmienna ret nie jest zainicjalizowana. Nie, zeby mialo to znaczenie, bo jedyna mozliwa sciezka programu inicjalizuje ret po chwili, ale purysci jezykowi by sie przyczepili
[#68] Re: Wyzwania programistyczne

@teh_KaiN, post #66

kernelowcy liuksowi radzą sobie z tym tak, że robią "goto fail" i za labelem fail masz zwalnianie wszystkiego jak leci, z ifologią czy jest zaalokowane.


Mozna tez bez ifologii:
... OpenLibrary("intuition...
if (IntuitionBase == NULL)
    goto intuition_fail;
... OpenLibrary("graphics...
if (GfxBase == NULL)
    goto gfx_fail;


    CloseLibrary(GfxBase);
gfx_fail:
    CloseLibrary(IntuitionBase);
intuition_fail:
    return 0


Tyle ze takie rozwiazanie to sztuczna emulacja klamerek i wciec w kodzie wiec jak dla mnie nie ma racji bytu ;)

Ja osobiście wolę takie podejście niż wielokrotne klamrowanie, ale trzeba się pilnować żeby zwolnić wszystko.


A ja nie trawie stosowania goto w C bez wyraznego powodu. A w tym wypadku powod nie jest az tak wyrazny (kwestia oszczedzenia paru klamerek w kodzie ). A w takich wypadkach poza swiatkiem C i tak lepiej rzucic wyjatkiem :)

Zreszta, to i tak glownie kwestia smaku :) A ze o gustach sie nie dyskutuje....

Ostatnia aktualizacja: 27.09.2018 11:17:05 przez mschulz
[#69] Re: Wyzwania programistyczne

@mschulz, post #67

Po prostu mniej poziomów zagłębień - łatwiej się czyta kod, łatwiej też się skupić na mięsie kiedy wszystkie wstępy masz już odsiane. Kwestia zwalania zasobów to już jest szczegół implementacji i da się go zrobić tak, żeby nie psuł czytelności kodu. Ta konkretna kwestia nie wymusza wielokrotnych zagłębień, to był po prostu Twój wybór aby zaoszczędzić sobie pracy kosztem czytelności, być może w wypadku takiej pchełki całkowicie uzasadniony ekonomicznie.
[#70] Re: Wyzwania programistyczne

@teh_KaiN, post #66

Ostatnio zacząłem stosować styl, który eliminuje problem kaskady klamer, a jednocześnie praktycznie uniemożliwia zapomnienie o zwolnieniu czegoś. Załóżmy, że program alokuje jakąś pamięć, otwiera plik i otwiera okienko:

static void GetMemory(...)
{
    if (memory = AllocMem(...))
    {
        GetFile(...);
        FreeMem(memory, ...);
    }
}

static void GetFile(...)
{
    if (file = Open(...))
    {
        GetWindow(...);
        Close(file);
    }
}

static void GetWindow(...)
{
    if (window = OpenWindowTags(...))
    {
        ProceedToMainLoop(...);
        CloseWindow(window);
    }
}


Można temu kodowi zarzucić pewną rozwlekłość, ale jest bardzo prosty do analizy. Jeżeli chodzi o rozmiar i narzut wykonania, przyzwoity kompilator widząc atrybut static zwinie sobie te wywołania w sposób jaki uzna za optymalny. Bardzo łatwo bez utraty czytelności można go wzbogacić czy to o wsteczną propagację kodu błędu, czy to o raportowanie błędów użytkownikowi.
[#71] Re: Wyzwania programistyczne

@teh_KaiN, post #66

Ja preferuję schodkową alokację. Jednak unikanie "goto" ma znaczenie.

Poza tym tworzy to kontekst. W przypadku "goto fail" nie zawsze wiesz co zostało zaalokowane. Przykładem jest funkcja OpenDevice().

Ostatnia aktualizacja: 27.09.2018 11:29:08 przez Hexmage960
[#72] Re: Wyzwania programistyczne

@jubi, post #69

Jubi, jak juz powiedzialem. Nie popadam w skrajnosci, nie jestem ani zagorzalym fanem jednej ani drugiej strony. Stosuje i jedna i druga w zaleznosci od potrzeb i poziomu skomplikowania kodu.

Gdyby w tej funkcji bylo jeszcze wiecej zagniezdzen to bym sie ich pozbyl, jednak dla mnie 3 zagniezdzenia to nie jest nieczytelny kod. Gdyby byla taka potrzeba zastosowal bym ewentualnie rozwiazanie mieszane. Sugerowanie mi oszczedzania sobie pracy kosztem czytelnosci kodu uwazam za dosc nieuczciwe.

Prace oszczedzilem sobie w inny sposob - prawie nie ma komentarzy w kodzie :P
[#73] Re: Wyzwania programistyczne

@mschulz, post #72

To była tylko sugestia, jak widać są argumenty przeciw i zgadzam się. Twój przykład był świetny i sporo się nauczyłem z samej dyskusji :) Dawaj więcej przykładów i argumentów, tak się to powinno robić.
[#74] Re: Wyzwania programistyczne

@Krashan, post #65

Jakbyś nie kombinował to trzymanie się tej reguły komplikuje kod w przypadku, gdy mamy do czynienia z kolejnym zdobyciem kilku zasobów i ich późniejszym zwolnieniem. Ona polepsza czytelność tylko i wyłącznie wtedy, gdy masz gwarancję, że system sam zwolni wszystkie zasoby przy wyjściu z programu i zrobi to we właściwej kolejności. W przypadku AmigaOS i pochodnych takiej gwarancji nie ma.


Podobny cytat znalazłem w komentarzach do tego artykułu:

It is also interesting to point out that the current understanding of single exit is probably a remainder of manual resource managing in C or other languages. If you have to manually release resources before exiting a function it is very error-prone to have multiple exits. RAII solves this problem in a very nice way, but there are still people to write manual new/deletes.


https://en.wikipedia.org/wiki/Resource_acquisition_is_initialization

I teraz pytanie - czy RAII da się zastosować w programach amigowych w C? Konkretnie akapit "Clang and GCC "cleanup" extension for C" z powyższego linka.


Ostatnia aktualizacja: 27.09.2018 12:02:18 przez jubi
[#75] Re: Wyzwania programistyczne

@jubi, post #74


https://en.wikipedia.org/wiki/Resource_acquisition_is_initialization

I teraz pytanie - czy RAII da się zastosować w programach amigowych w C? Konkretnie akapit "Clang and GCC "cleanup" extension for C" z powyższego linka.


Da sie:

static inline __attribute__((always_inline)) void closelib(void *l) { if (l) CloseLibrary((struct Library *)l); }
#define cleanup_lib __attribute__((cleanup(closelib)))

int main(void)
{
    struct Window *myWindow = NULL;
    cleanup_lib struct IntuitionBase *IntuitionBase = NULL;
    cleanup_lib struct GfxBase * GfxBase = NULL;

    IntuitionBase = (struct IntuitionBase *)OpenLibrary((CONST_STRPTR)"intuition.library", 0);
    GfxBase = (struct GfxBase *)OpenLibrary((CONST_STRPTR) "graphics.library", 0);

    if (IntuitionBase == NULL || GfxBase == NULL)
        return 0;

    ....

    return 0;
}


Zauwaz tylko ze closelib bierze jako parametr typ void *. Typu struct Library * nie moze bo byly by bledy przy podawaniu np. struct GfxBase *. To czyni taka wersie RAII bardo podatna na bledy, bo wystarczy sie w kodzie "machnac" na przyklad tak:

int main(void)
{
    cleanup_lib struct Window *myWindow = NULL;
    cleanup_lib struct IntuitionBase *IntuitionBase = NULL;
    cleanup_lib struct GfxBase * GfxBase = NULL;


Zeby potem w kodzie mozolnie szukac bledu. Totaj to jest jeszcze widoczne, ale w jakims duzym projekcie juz niekoniecznie.
[#76] Re: Wyzwania programistyczne

@jubi, post #74

W morphosowym i arosowym gcc pewnie tak. Nawet całkiem sensowne, z tym że nie mozna mieszać stylów, bo np. po fclose nieraz dla pewności nulluje sie zmienną plikową, a dla RAII juz tak elegancko nie zrobisz tego.
[#77] Re: Wyzwania programistyczne

@michal_zukowski, post #76

W morphosowym i arosowym gcc pewnie tak.


gcc od bebbo tez daje rade. wiec pod klasyka tez to mozna stosowac.
[#78] Re: Wyzwania programistyczne

@mschulz, post #77

Wersja dla jubiego z atrybutem cleanup :P

#include <exec/types.h>
#include <exec/execbase.h>
#include <intuition/intuition.h>
#include <intuition/screens.h>
#include <workbench/startup.h>
#include <graphics/gfxbase.h>
#include <graphics/gfx.h>
#include <dos/dos.h>
#include <dos/dosextens.h>
#include <proto/exec.h>
#include <proto/graphics.h>
#include <proto/intuition.h>
#include <utility/tagitem.h>

#undef SysBase
#define SysBase (*(struct ExecBase **)4)

int main(void);

/* Minimalny kod startowy */
int _start(void)
{
    struct Process *p = NULL;
    struct WBStartup *wbmsg = NULL;
    int ret = 0;

    p = (struct Process *)SysBase->ThisTask;

    if (p->pr_CLI == 0)
    {
        WaitPort(&p->pr_MsgPort);
        wbmsg = (struct WBStartup *)GetMsg(&p->pr_MsgPort);
    }

    ret = main();

    if (wbmsg)
    {
        Forbid();
        ReplyMsg((struct Message *)wbmsg);
    }

    return ret;
}

#define WINDOW_WIDTH    400
#define WINDOW_HEIGHT   400
#define TRIANGLE_H      (WINDOW_HEIGHT * 4 / 5)
#define TRIANGLE_A      (TRIANGLE_H * 2 * 1000 / 1732)

ULONG seed = 1;

UWORD rand(void)
{
    seed = ((seed * 1103515245) + 12345) & 0x7fffffff;
    return seed;
}

struct Point {
    LONG x;
    LONG y;
};

struct Point points[3] = {
    { WINDOW_WIDTH / 2, WINDOW_HEIGHT / 2 - TRIANGLE_H / 2 },
    { WINDOW_WIDTH / 2 - TRIANGLE_A / 2, WINDOW_HEIGHT / 2 + TRIANGLE_H / 2 },
    { WINDOW_WIDTH / 2 + TRIANGLE_A / 2, WINDOW_HEIGHT / 2 + TRIANGLE_H / 2 }
};

struct TagItem myWindowTags[] = {
    { WA_InnerWidth, WINDOW_WIDTH },
    { WA_InnerHeight, WINDOW_HEIGHT },
    { WA_DepthGadget, TRUE },
    { WA_CloseGadget, TRUE },
    { WA_DragBar, TRUE },
    { WA_Title, (Tag) "Dywan" },
    { WA_Activate, TRUE },
    { WA_GimmeZeroZero, TRUE },
    { WA_IDCMP, IDCMP_MOUSEBUTTONS | IDCMP_CLOSEWINDOW },
    { TAG_DONE, 0UL }
};

static inline __attribute__((always_inline)) void __closelib(APTR ptr) { struct Library **l = ptr; if (*l) CloseLibrary((struct Library *)*l); }
#define cleanup_lib __attribute__((cleanup(__closelib)))

int main(void)
{
    struct Window *myWindow = NULL;
    struct RastPort *rastPort = NULL;
    BYTE running = TRUE;
    cleanup_lib struct IntuitionBase *IntuitionBase = NULL;
    cleanup_lib struct GfxBase * GfxBase = NULL;

    IntuitionBase = (struct IntuitionBase *)OpenLibrary((CONST_STRPTR)"intuition.library", 0);
    GfxBase = (struct GfxBase *)OpenLibrary((CONST_STRPTR) "graphics.library", 0);

    if (IntuitionBase == NULL || GfxBase == NULL)
        return 0;

    myWindow = OpenWindowTagList(NULL, myWindowTags);

    if (myWindow == NULL)
        return 0;

    rastPort = myWindow->RPort;

    SetAPen(rastPort, 1);

    /* Wylosuj pierwszy punkt */
    ULONG x = (rand() * WINDOW_WIDTH) >> 16;
    ULONG y = (rand() * WINDOW_HEIGHT) >> 16;

    Move(rastPort, points[0].x, points[0].y);
    Draw(rastPort, points[1].x, points[1].y);
    Draw(rastPort, points[2].x, points[2].y);
    Draw(rastPort, points[0].x, points[0].y);

    do {
        struct IntuiMessage *msg;

        while ((msg = (struct IntuiMessage *)GetMsg(myWindow->UserPort)) != 0)
        {
            if (msg->Class == IDCMP_CLOSEWINDOW)
            {
                running = FALSE;
            }

            ReplyMsg((struct Message *)msg);
        }

        /* Wylosuj punkt trojkata */
        UWORD p = (rand() * 3) >> 16;

        x = (x + points[p].x) / 2;
        y = (y + points[p].y) / 2;

        WritePixel(rastPort, x, y);

    } while(running == TRUE);

    CloseWindow(myWindow);

    return 0;
}


Po kompilacji 748 bajtow. Funkcje __closelib mozna uproscic wywalajac "if (*l)". Zadziala to od kickstartu V36 w gore, bo tam CloseLibrary akceptuje podanie NULL-a. Taka wersja ma 732 bajty. To nadal o 4 bajty wiecej od mojej wersji z klamerkami ;)
[#79] Re: Wyzwania programistyczne

@mschulz, post #78

To jeszcze do window mógłbyś to dorobić. Z tym, że jak dla mnie logika jest zaciemniona bo przy analizie nie wiadomo, które zmienne automatycznie coś wywołują, a które nie. Więc albo robić na wszystko albo dodać jakies pre/postfixy do nazw zmiennych.
[#80] Re: Wyzwania programistyczne

@mschulz, post #61

Masz teraz co prawda mniej poziomow niz wczesniej ale, poniewaz otwierasz 3 zasoby z czego 2 sa od siebie zalezne (zeby otworzyc okno musisz najpierw otworzyc intuition.library) nie mozesz jakos ekstremalnie uproscic tych poczatkowch if-ow. Mozna wyciagnac to poza funkcje main() z mojego kodu i czasami tak sie robi, ale w tej pchelce chodzilo o to zeby kod byl jak najmniejszy. Dodatkowo, niedoswiadczony programista moze sie pogubic w tym co bylo zaalokowane, co nie bylo, a to juz najszybsza droga do wyciekow pamieci. A kompilator z kodu czy to w jednej postaci czy drugiej i tak postara sie zrobic cos w miare optymalnego.


Jeszcze jedno pytanie apropos takiego programowania. Załóżmy, że nie zwolnię zasobu np. nie zrobię CloseLibrary((struct Library *)GfxBase); i wyjdę z programu. Czy dobrze myślę, że po jednokrotnej takiej akcji nic zauważalnego się nie stanie? Ale jeśli uruchomię taki program milion razy to w końcu zabraknie pamięci? Czyli system nie ma żadnej swojej procedury "sprzątania" po takich programach i zamykania tego, czego zapomniały zamknąć? Czy współczesne systemy coś takiego mają? Np. czy programując w C pod Linuksem też trzeba o tym myśleć, czy to jest tylko archaizm systemów z epoki Amigi?
[#81] Re: Wyzwania programistyczne

@jubi, post #80

Jak nie zwolnisz biblioteki to będzie otworzona i się jej nie będzie dało usunąć. Inne efekty są zależne od tego co siedzi w bibliotece w funkcji obslugującej CloseLibrary.

W MorphOsie otwieranie i zamykanie bibliotek jest robione przez kod startowy więc w sumie nie robimy już OpenLibrary/CloseLibrary w większości przypadków w kodzie. Chyba także zamykanie plików FILE* jest robione automatycznie przy wychodzeniu z programu, BPTRy trzeba zwalniac recznie.

Ostatnia aktualizacja: 27.09.2018 14:05:57 przez michal_zukowski
[#82] Re: Wyzwania programistyczne

@jubi, post #80

Jeszcze jedno pytanie apropos takiego programowania. Załóżmy, że nie zwolnię zasobu np. nie zrobię CloseLibrary((struct Library *)GfxBase); i wyjdę z programu. Czy dobrze myślę, że po jednokrotnej takiej akcji nic zauważalnego się nie stanie? Ale jeśli uruchomię taki program milion razy to w końcu zabraknie pamięci?


to zalezy od biblioteki. Graphics library udostepnia jedna i ta sama GfxBase kazdemu programowi ktory otwiera biblioteke. Inne biblioteki (chyba wszystkie mathieee* tak robia) alokuja nowa baze biblioteki przy kazdym otwarciu i w takim wypadku pamiec bedzie ciekla bez konca.

Czyli system nie ma żadnej swojej procedury "sprzątania" po takich programach i zamykania tego, czego zapomniały zamknąć?


Nie ma w AmigaOS takich mechanizmow.

Czy współczesne systemy coś takiego mają?


Tak, wspolczesne systemy zwalniaja pamiec i np. zamykaja otwarte pliki kiedy proces konczy prace czy to w sposob zaplanowany czy tez przypadkowy.

Np. czy programując w C pod Linuksem też trzeba o tym myśleć, czy to jest tylko archaizm systemów z epoki Amigi?


Zawsze trzeba o tym myslec i to nie jest archaizm! Zostawianie po sobie zaalokowanej pamieci i otwartych plikow swiadczy o zlej kulturze programowania. Fajnie ze system to robi ale jest to raczej rozwiazanie awaryjne. To troche tak jak bys przyszedl do przyjaciela w odwiedziny i musial pojsc do toalety. Raczej spuscisz po sobie wode, opuscisz deske klozetowa i zamkniesz drzwi, prwada? ;) Chociaz z drugiej strony, jezeli w lazience stracisz przytomnosc i przyjedzie po ciebie karetka to nieposprzatanie po sobie bedzie ci wybaczone ;)

Sorki za oblesny przyklad :)
[#83] Re: Wyzwania programistyczne

@jubi, post #80

Czy dobrze myślę, że po jednokrotnej takiej akcji nic zauważalnego się nie stanie?

Rezultatem będzie to, że biblioteka nie będzie zwalniać pamięci, gdy licznik otwarć osiągnie liczbę 0. Więc należy zamykać biblioteki, jest to w dobrym stylu.

Generalnie są funkcje do sprzątania. Każda funkcja Open() ma Close(). Kwestia czy chcesz je wywoływać jawnie czy nie.

W języku Java nie ma w ogóle zwalniania pamięci, ani wskaźników. Jest zamiast tego "garbage collector", który zwalnia nieużywane obiekty oraz są referencje.

Chyba także zamykanie plików FILE* jest robione automatycznie przy wychodzeniu z programu.

Tak, biblioteka standardowa instaluje funkcje które podczas zamykania zwolnią pamięć zaalokowaną przez malloc() oraz zamkną pliki otwarte przez fopen().

Tak samo program może zarejestrować swoją funkcję zamykającą za pomocą atexit().

Tylko, że należy zachować umiar przy stosowaniu zmiennych globalnych. Lepiej korzystać ze zmiennych lokalnych i parametrów, bo czyni to nasze funkcje wszechstronniejszymi.

Ostatnia aktualizacja: 27.09.2018 14:15:03 przez Hexmage960
[#84] Re: Wyzwania programistyczne

@michal_zukowski, post #81

W MorphOsie otwieranie i zamykanie bibliotek jest robione przez kod startowy więc w sumie nie robimy już OpenLibrary/CloseLibrary w większości przypadków w kodzie.


Ale to chyba dotyczy libnixa, bo ixemul chyba takich rzeczy nie mial, a było to juz z tego co pamiętam i w gcc dla 68k (przynajmniej w którymś porcie którego używałem w zamierzchłych czasach)

Co do early return, to jeśli nie jest na początku funkcji, to ja akceptuje już jedyne jego wystąpienie jedynie na końcu funkcji? Inaczej może być źródłem problemów.
I to nie tylko chodzi o zwalnianie zasobów, ale np zwalnianie semaforów itp... A konieczność dodatnia chronienia jakiegoś fragmentu kodu może przyjść później po napisaniu programu i to przez innego programistę,więc niespodzianki w stylu goto tudzież returny ze środka funkcji sa wtedy bardzo niepożądane.

Styl przedstawiony przez Krashana właśnie robi to co trzeba. Dodatkowo static nam ładnie ukrywa funkcje przed wyciekiem ich na zewnątrz jako robocze...
[#85] Re: Wyzwania programistyczne

@mschulz, post #82

Dzięki za wyjaśnienie. Chodziło mi głównie o to, czy system w Amidze sam posprząta po niechlujnym programiście. Wygląda na to, że nie, i że system jest narażony nie tylko na strzały w pamięć podczas działania programu ale też na to, że jakiś program zapomni czegoś zwolnić po wyjściu i po którymś tam wyjściu w końcu zabraknie jakiegoś zasobu. Czy są jeszcze jakieś rzeczy tego typu na które trzeba uważać w Amidze a które współczesne systemy robią same, nawet awaryjnie?
[#86] Re: Wyzwania programistyczne

@jubi, post #85

Czy są jeszcze jakieś rzeczy tego typu na które trzeba uważać w Amidze a które współczesne systemy robią same, nawet awaryjnie?


chociazby wychodzenie poza przydzielona ci przez system pamiec. I pewnie jeszcze cala inna masa ciekawostek.

PS. wbrew pozorom to ze amigaos jest tak archaiczny ma swoje dobre strony. czlowiek sie uczy nie tylko pisac na rympal jak jakis podrzedny klepacz kodu. tutaj trzeba myslec i pamietac co sie robi. :) Doswiadczenia z amigi cholernie mocno przydaja sie potem przy programowaniu systemow embedded - malo pamieci, mocno ograniczony OS albo jego brak, pomylki sa karane zwiecha sprzetu czesto bez szans na debugowanie :) cud miod i orzeszki.
[#87] Re: Wyzwania programistyczne

@jubi, post #60

Bardzo fajna lekcja. Mam jeszcze sugestię aby polepszyć czytelność kodu:


No i w ten sposób wątek z "wyzwań" zrobiła się typowa dyskusja o wszystkim i niczym.
Nota bene, pierwotne "wyzwanie" jest klasy tego, co robiło się przysłowiowy hektar czasu temu na C64 w Warsaw lub Simons Basicu albo u kolegów na A500 w Amosie.

Marek (S.) jak czytasz - sypnij jakimiś zadankami z globalnego testu firmowego ;)

A w międzyczasie:
https://app.codility.com/programmers/lessons/
[#88] Re: Wyzwania programistyczne

@michal_zukowski, post #81

Nie, nie jest to robione przez kod startowy. A przynajmniej nie tylko (nie pamiętam ale obsługa konstruktorów/destruktorów jest chyba dostarczana przez kompilator). Jest to robione przez libauto + linker. libauto zawiera funkcje które otwierają i zamykają poszczególne biblioteki (oznaczone jako konstruktor i destruktor). Dodatkowo moduł który zawiera te funkcje zawiera też deklarację bazy biblioteki (globalną). Teraz jeżeli nasz kod nie zadeklaruje jakiejś bazy to linker wciągnie moduł z libauto który tą bazę zawiera (aby zaspokoić zależność w trakcie linkowania. W przeciwnym przypadku moduł nie jest potrzebny) a wraz z tą bazą odpowiednie funkcje do jej inicjalizacji.
[#89] Re: Wyzwania programistyczne

@jubi, post #74

Mechanizm __attribute__((cleanup(func))) nie współpracuje z setjmp/longjmp co zmniejsza jego uniwersalność :-/
[#90] Re: Wyzwania programistyczne

@recedent, post #51

Ta wersja zadziałała:


Sprawdziłem pod Blenderem 2.48 bo taka jest najnowsza wersja pod OS4. 10 lat to w tej dziedzinie wieczność - nie działa. Brak modułu random, znalazłem jakiś kod implementujący linear congruential generator na stack overflow i dla potrzeb tej zabawy wystarczy. Do tego jest tam Python 2.x który inaczej reaguje na operator dzielenia niż 3.x. API Blendera też się zmieniło, dokumentacja do API 2.48 dawno zniknęła, na szczęscię jest archive.org:

https://web.archive.org/web/20090129121018/http://blender.org:80/documentation/248PythonDoc

Jak ktoś ma dostęp do Blendera pod OS4 to można spróbować z ciekawości czy to zadziała. W sumie dobre ćwiczenie z softwareowej archeologii.



#Blender 2.48, Python 2.52
#https://web.archive.org/web/20090129121018/http://blender.org:80/documentation/248PythonDoc
#Linear congruential generator
import bpy
import math

def create_mesh(verts):
    mesh = bpy.data.meshes.new("mesh")
    mesh.verts.extend(verts)
    return mesh

def create_object(object_name, verts):
    scene = bpy.data.scenes.active
    object = scene.objects.new(create_mesh(verts), object_name)
    scene.objects.active = object

#https://stackoverflow.com/a/39714436/4940395
def r(seed=[0], m=2**32, a=1664525, c=1013904223):
	seed[0] = (a*seed[0] + c) % m
	return seed[0]

def randint(a, b):
    return int(a + (1 + b - a) * r() / float(2**32))

verts = []
scale_factor = float(100)
float_two = float(2)

# Rozmiar planszy
(width, height) = (800, 800)

# Wysokosc trojkata
triangle_h = 600
triangle_a = int(triangle_h * 2 / float(math.sqrt(3)))

# Koordynaty trojkata - zawsze na srodku okna
triangle = ((width/float_two, height/float_two-triangle_h/float_two), 
            (width/float_two - triangle_a / float_two, height /float_two+triangle_h/float_two), 
            (width/float_two + triangle_a / float_two, height/float_two+triangle_h/float_two))

# Wylosuj pierwszy punkt
x = (randint(0, width-1), randint(0, height-1))

cnt = 0
running = True

# Glowna petla
while running:
  cnt = cnt + 1
  
  # Rysuj punkt
  verts.append([x[0]/scale_factor, x[1]/scale_factor, 0])
  
  # Wylosuj jeden z punktow trojkata
  tx = triangle[randint(0, 2)]

  # Oblicz nowe koordynaty
  x = ((x[0] + tx[0]) / float_two, (x[1] + tx[1]) / float_two)

  if cnt % 100000 == 0:
    running = False

create_object("Trojkat Sierpinskiego", verts)


Ostatnia aktualizacja: 27.09.2018 17:40:02 przez jubi
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