@jubi, post #60
można zastosować regułę "return early":
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; }
@Krashan, post #62
@mschulz, post #61
@jubi, post #64
@jubi, post #64
@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.
... 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
Ja osobiście wolę takie podejście niż wielokrotne klamrowanie, ale trzeba się pilnować żeby zwolnić wszystko.
@mschulz, post #67
@teh_KaiN, post #66
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); } }
@jubi, post #69
@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.
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.
@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.
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; }
int main(void) { cleanup_lib struct Window *myWindow = NULL; cleanup_lib struct IntuitionBase *IntuitionBase = NULL; cleanup_lib struct GfxBase * GfxBase = NULL;
@mschulz, post #77
#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; }
@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.
@jubi, post #80
@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?
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?
@jubi, post #80
Czy dobrze myślę, że po jednokrotnej takiej akcji nic zauważalnego się nie stanie?
Chyba także zamykanie plików FILE* jest robione automatycznie przy wychodzeniu z programu.
@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.
@mschulz, post #82
@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?
@jubi, post #60
Bardzo fajna lekcja. Mam jeszcze sugestię aby polepszyć czytelność kodu:
@michal_zukowski, post #81
@recedent, post #51
Ta wersja zadziałała:
#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)