@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 0Ja 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)