több ötlet
This commit is contained in:
parent
f56a322328
commit
81890e9aa3
187
BASED.md
187
BASED.md
@ -66,6 +66,27 @@ Lesz továbbá tagged union is - lásd lejjebb - és interfészek (ami tagged un
|
||||
|
||||
Szerintem itt a 'T' helyett talán érdemes csak egy interfész-nevet megengedni???
|
||||
|
||||
# Placement new
|
||||
|
||||
Igazából szükség van valamire, amivel `emplace_back` jellegű dolgokat írhatunk le, tehát a konstruktor kódját egy adott
|
||||
másik memória címen futtatjuk, ahol elég hely van az adatnak. A placement new C++ alakja nekem nem túl szimpi:
|
||||
|
||||
// placement new in buf1-2
|
||||
int *pInt = new (buf1) int(3);
|
||||
DataSource *pds = new (buf2) DataSource("localhost:1212");
|
||||
|
||||
A C++20-as megoldás valamivel jobb, de az sem teljesen kényelmes szerintem...
|
||||
|
||||
https://en.cppreference.com/w/cpp/memory/construct_at
|
||||
|
||||
Főleg, hogy a C++ esetén ugye ahhoz, hogy adott memóriacímen lévő dolgot destruáljak, külön kell mókolni kézi
|
||||
destruktor hívással. De annál a const adattagok értékét a fordító optimalizálásból trükkösen értheti, majd
|
||||
persze emiatt ilyen hack-elések jelentek meg, mint az std::launder ami ezt megakadályozza...
|
||||
|
||||
Szóval erre szerintem jobb syntax kell - esetleg úgy is dönthetünk, hogy egyáltalán ne legyen ilyen (optimizer megoldja?)
|
||||
|
||||
Kérdés: Ha csinálok ilyet, akkor kell-e "placement delete" is?
|
||||
|
||||
# Rekord VS handler: copy és move konstruktorok / assignment
|
||||
|
||||
Mindenképp jobbnak látom a C++ féle move szemantikát a rust-hoz képest: tehát ha én mondom meg mi történjen!
|
||||
@ -275,16 +296,124 @@ Ez szerintem low-level helyekre jó lehet... Ha jól definiálom mi történik,
|
||||
|
||||
## Jobb pointerek
|
||||
|
||||
Valahogy jó lenne elérni, hogy a default a "restrict" kulcsszóhoz legyen közelebb. Szerintem tömbre a default a restrict, de
|
||||
a pointerre ki kell írni?
|
||||
Valahogy jó lenne elérni, hogy a default a "restrict" kulcsszóhoz legyen közelebb.
|
||||
Egy lehetőség, hogy tömbre (tehát ptr+méret-re)a default a restrict, de pointerre ki kell írni.
|
||||
|
||||
* Talán a tömbök külön típussá emelése ebben már eleve segít - igen... legyenek továbbá mérettel is ellátva stb. lásd ott
|
||||
* Mivel az ownerek kezelnek csak ptr-eket, ők adhatnk ki referenciákat és array-eket, de azok ugye nem alias-olnak be könnyen
|
||||
* A restrict kulcsszó támogatása a minimum - de talán kéne gondolkozni mit lehetne még kihozni, hogy jobb legyen!
|
||||
* Ugye mivel van külön "tömb" típusunk, ezért arra is lehetővé kéne tenni a restrict-et pl. paramétereknél, különben bakis lesz.
|
||||
|
||||
Egy (talán elvetendő) ötlet: A pointer típusa lehessen csoportosítható:
|
||||
|
||||
handle A {
|
||||
int n;
|
||||
};
|
||||
|
||||
A@aliasinggroup1 variable = { 1 };
|
||||
A@aligrp2 array[5] = { 1, 2, 3, 4, 5 };
|
||||
|
||||
inline int magic(A *ptr1, A *ptr2) {
|
||||
ptr2->n = 42;
|
||||
return ptr1->n; // return 1 when inlined
|
||||
}
|
||||
|
||||
magic(&variable, &array[3]); // see that it cannot alias
|
||||
|
||||
Esetleg szükséges legyen ilyenkor a használati helyen is kiírni a típust?
|
||||
|
||||
inline int magic(A@aliasinggroup1 *ptr1, A@aligrp2 *ptr2);
|
||||
|
||||
Az a bajom ezzel... hogy "gyalázatosan ocsmány" sajnos ez mind...
|
||||
|
||||
Alternatíva:
|
||||
|
||||
* Tömbökről feltételezzük, hogy sosem alias-olnak be (hacsak nem "unrestrict" kulcsszavas).
|
||||
* Referenciák sem alias-olódnak
|
||||
|
||||
Alternatíva (all-in):
|
||||
|
||||
* Mindent restrict-nek veszünk by default és az unrestrict-et be kell írni...
|
||||
* Kicsit unsafe-nek hangzik és sok benne a meglepetés, de a perf jó.
|
||||
|
||||
Lásd FORTRAN: https://flang.llvm.org/docs/Aliasing.html
|
||||
|
||||
Alternatíva (typedef-szerű):
|
||||
|
||||
restrictptr PooledPtr int*;
|
||||
restrictptr RawPtr int*;
|
||||
|
||||
inline int magic(PooledPtr ptr1, RawPtr ptr2) {
|
||||
*ptr2 = 42;
|
||||
return *ptr1; // Should optimize as: return 2
|
||||
}
|
||||
|
||||
int i1 = 1;
|
||||
int i2 = 2;
|
||||
magic(&i1, &i2); // can optimize
|
||||
|
||||
Jelenleg ez az alternatíva tetszik a legjobban + persze a restrict kulcsszó támogatása emellett még pluszban!
|
||||
+ Talán lehetne restrictptr PooledArr int[]; jellegű típus definíció is (tömbök alias kerülésére)
|
||||
|
||||
Szemantika:
|
||||
|
||||
* Ha csak különböző ilyeneket látok egy paraméterlistán, akkor restrict-et kódgenerálunk mindre.
|
||||
* Ha csak különböző ilyeneket ÉS más típusú dolgokat... akkor is...
|
||||
* Egy ugyan ilyen típusú és egy másik ugyan ilyen típusú restricptr között viszont kell aliasing!
|
||||
* Egy (fenti példával élve) PooledPtr-es int* és egy sima int* között viszont nem kell!
|
||||
* Van továbbra is restrict kulcsszó (C-s szemantikával)
|
||||
|
||||
Ezekkel a szabályokkal egész jó dolgot csináltunk szerintem, mert ha valaki kiad egy pointert, akkor kiadhat
|
||||
hozzá ilyen plusz infókat, ami jelzi a fordítónak, hogy mik alias-olhatnak és mik nem. Egy jó példa, ha egy
|
||||
játék mondjuk ilyen memory arénákban tárol dolgokat és az "engine" tudhatja, hogy melyik arénából adjuk ki
|
||||
éppen a mutatót. Gyakorlatilag ez majdnem olyan, mintha valamit restrictptr-el deklarálunk, akkor annak
|
||||
az összes előfordulásánál beíródik a generált pointer elé a "restrict" - ez csak annyiban téves, hogy
|
||||
ha több ilyen is van egy függvényben / scope-ban azonos típusnéven, akkor azok egymással nem restrict-elnek!
|
||||
Ez utóbbit első körben implementálhatjuk úgy, hogy ilyenkor lekerül a restrict, de úgy is, hogy kódgenerálással
|
||||
megoldjuk csak a kettő közötti restrict-álást és fenn hagyjuk a kulcsszót. Ez egy kicsit bonyolítja a dolgokat...
|
||||
|
||||
Az ilyen pointer cast-olható az alaptípusára - de ezzel elveszítjük a speed boost lehetőségét olyankor!
|
||||
Megj.: Szerintem ezzel generálható C-kód -fno-strict-aliasing mellett is akár, ha típus alapút nem akarok!
|
||||
|
||||
## Jobb referenciák
|
||||
|
||||
* A referenciának lehetne "értéket adni" - nem mint C++ban...
|
||||
* A referencia "lényegében egy pointer, de nincs rajta aritmetika"...
|
||||
* Kérdőjel hozzáadásával jelölhető, hogy null lehet-e (default: nem).
|
||||
|
||||
## Jobb tömbök
|
||||
|
||||
Néhány dologra van szerintem szükségünk:
|
||||
|
||||
* (Pointer + méret / range) jellegű tömb történet, ahol a méret / range lekérhető a típusból ADA-szerűen (.len) és az elemtípus is!
|
||||
* Range subtype (ada szerűen) NEM kell külön a típusrendszerben, mert hajlok arra, hogy 0..méret lehessen csak (praktikus: dyn)
|
||||
* Dinamikus esetben, hívhasd az ilyen függvény paramétert sort(array(ptr, 42)) történettel (ahol ptr egy pointer, 42 méret).
|
||||
|
||||
A fordító két függvényt is generál ilyen függvényekből. A típusrendszer szerint azt a függvényt hívjuk meg, amelyik gyorsabb!
|
||||
Igazából ha fordítási időben ismert méretű tömbbel hívjuk meg, akkor a "sima" függvény hívódik, kivéve, ha ez valami linkelt lib,
|
||||
mert abban az esetben természetesen a "valódi" függvény fog hívódni - ezt C-re fordításnál még nem látom át hogyan kéne, de majd
|
||||
meg kellene oldani...
|
||||
|
||||
A lényeg, hogy a "tömb" típus az pointer + hossz minden esetben, de ahol tudja, a fordító kioptimalizálja a történetet!
|
||||
Ha valami mást akar az ember, például performancia okokból, akkor simán adjon át egy pointert (és mondjuk null termináns).
|
||||
|
||||
Ha az "array" második paramétere nem fordítási idejű konstans szám, akkor ott is a dinamikus függvényt hívjuk majd meg.
|
||||
|
||||
A range, mint altípus megadás lehetővé tenné, hogy dolgok "osztozzanak" a méreten - pl. ECS egy játékmotorban, stb.
|
||||
De mivel nem tól-ig, vagyis ADA-szerű range-jeink vannak, simán csak méret, ezért ha ilyet akarsz konstans számként tárolod.
|
||||
|
||||
Emellett természetesen a standard library-ban kell "vector" típus és az tudjon array-t is adni neked.
|
||||
|
||||
Szerintem az így kapott kód "már kellően biztonságos" általában. A vektorra debug build esetén legyen range check kötelezően!
|
||||
|
||||
A tömböknél a restrict lehet ESETLEG a default - de ennek fényében kellene unrestrict kulcsszó rájuk!
|
||||
|
||||
Lásd még:
|
||||
|
||||
https://youtu.be/MUISz2qA640?t=1980
|
||||
|
||||
ui.: Ha nem lesz operátor overload, akkor szerintem legyen több dimenziós tömb (nem a tömbök tömbje, hanem: matrix[15, 10, 42] = 5;
|
||||
|
||||
## Típusos indexek
|
||||
|
||||
Egy pointer a modern gépen 64 bit, sokszor tömörebben tudsz tárolni "referencia-szerűséget" ha indexet tárolsz rá!
|
||||
@ -301,6 +430,18 @@ Viszont! A pointerek - mint itt is látszik - típusosak, de az indexek NEM! Vis
|
||||
|
||||
A syntax-on lehetne még variálni...
|
||||
|
||||
De az a helyzet, hogy erre már van megoldás jelenleg is (sőt C-ben is, struct-al):
|
||||
|
||||
record ChildIndex {
|
||||
uint32_t index;
|
||||
};
|
||||
|
||||
esetleg még jobb, ha "like-olást" is használunk közben:
|
||||
|
||||
record child_index {
|
||||
like uint32_t index;
|
||||
};
|
||||
|
||||
### NonOwningPointer - smartpointer
|
||||
|
||||
Ezt szerintem érdemes C++ template-el előre megcsinálni! A lényeg az, hogy kreálni lehet a dolgot - tehát van egy factory-szerű,
|
||||
@ -352,37 +493,6 @@ invalidálódik, de nem is egyesével, hanem blokkosával láncolt lista!
|
||||
|
||||
Ezt esetleg nem is nyelvi elemként tennénk be, hanem library-ként a BASED-be is, az talán jobb?
|
||||
|
||||
## Jobb tömbök
|
||||
|
||||
Néhány dologra van szerintem szükségünk:
|
||||
|
||||
* (Pointer + méret / range) jellegű tömb történet, ahol a méret / range lekérhető a típusból ADA-szerűen (.len) és az elemtípus is!
|
||||
* Range subtype (ada szerűen) NEM kell külön a típusrendszerben, mert hajlok arra, hogy 0..méret lehessen csak (praktikus: dyn)
|
||||
* Dinamikus esetben, hívhasd az ilyen függvény paramétert sort(array(ptr, 42)) történettel (ahol ptr egy pointer, 42 méret).
|
||||
|
||||
A fordító két függvényt is generál ilyen függvényekből. A típusrendszer szerint azt a függvényt hívjuk meg, amelyik gyorsabb!
|
||||
Igazából ha fordítási időben ismert méretű tömbbel hívjuk meg, akkor a "sima" függvény hívódik, kivéve, ha ez valami linkelt lib,
|
||||
mert abban az esetben természetesen a "valódi" függvény fog hívódni - ezt C-re fordításnál még nem látom át hogyan kéne, de majd
|
||||
meg kellene oldani...
|
||||
|
||||
A lényeg, hogy a "tömb" típus az pointer + hossz minden esetben, de ahol tudja, a fordító kioptimalizálja a történetet!
|
||||
Ha valami mást akar az ember, például performancia okokból, akkor simán adjon át egy pointert (és mondjuk null termináns).
|
||||
|
||||
Ha az "array" második paramétere nem fordítási idejű konstans szám, akkor ott is a dinamikus függvényt hívjuk majd meg.
|
||||
|
||||
A range, mint altípus megadás lehetővé tenné, hogy dolgok "osztozzanak" a méreten - pl. ECS egy játékmotorban, stb.
|
||||
De mivel nem tól-ig, vagyis ADA-szerű range-jeink vannak, simán csak méret, ezért ha ilyet akarsz konstans számként tárolod.
|
||||
|
||||
Emellett természetesen a standard library-ban kell "vector" típus és az tudjon array-t is adni neked.
|
||||
|
||||
Szerintem az így kapott kód "már kellően biztonságos" általában. A vektorra debug build esetén legyen range check kötelezően!
|
||||
|
||||
Lásd még:
|
||||
|
||||
https://youtu.be/MUISz2qA640?t=1980
|
||||
|
||||
ui.: Ha nem lesz operátor overload, akkor szerintem legyen több dimenziós tömb (nem a tömbök tömbje, hanem: matrix[15, 10, 42] = 5;
|
||||
|
||||
## Holy-C féle kiegészítések a switch statement-hez
|
||||
|
||||
Erről még nem vagyok meggyőzve, de néha hasznosnak tűnnek:
|
||||
@ -1260,7 +1370,7 @@ Természetesen itt kellhet típust adni:
|
||||
|
||||
'some text word' 21 #inserts (word(W) float(F)) { .... }
|
||||
|
||||
Az inserts továbbá rendelkezhez output átirányítós paraméterrel:
|
||||
Az inserts továbbá rendelkezhet output átirányítós paraméterrel:
|
||||
|
||||
#inserts [0] { ... }
|
||||
|
||||
@ -1703,12 +1813,17 @@ Szerintem ezeket inline-olható, function pointerré kéne "elkódolnom", akár
|
||||
|
||||
* Engine kódja
|
||||
* Callstack - előre adott méret (pl. parancssori paraméter, vagy embeddednél beállított)
|
||||
* Insert-stack - az input "nyelének" kiegészítésére egy char elemekből álló stack
|
||||
* Adatstack, a műveleteivel - előre adott méret (pl. parancssori paraméter, vagy embeddednél beállított)
|
||||
* Szimbólumtáblára MAP adatstruktúra (pl. kismap - név alapú lookupra + változó tároláshoz is [key alapján tudható melyik!])
|
||||
* Complexword-ök "vektora" (mindig indexelés alapú! Nem pointerezős!
|
||||
* Session storage - egy charakter vektorhoz hasonló, de csak állandóan növő storage + session reset rá.
|
||||
* IO accessor (fájlműveletek, pipe-ok stb.)
|
||||
|
||||
Megj.:
|
||||
|
||||
* A különdféle "stack"-ek igazából használhatják ugyan azt az API-t.
|
||||
* A session storage viszont nem lehet stack, sőt "vektor" sem, mert nem pointer invalidálódhat ki! Pl. egy realloc-os tömb se jó!
|
||||
|
||||
^^Ezzel a memória kialakítással a memory layout:
|
||||
|
||||
| enginecode | callstack | datastack | symbolmap -> ... <-malloc_store|insert_stack-> ... <- session_storage | src |
|
||||
@ -1716,7 +1831,7 @@ Szerintem ezeket inline-olható, function pointerré kéne "elkódolnom", akár
|
||||
Igazából embedded környezetben, ilyen kismap-szerű (vagy még egyszerűbb) szimbólumtáblával tehát a legtöbb dolog
|
||||
vagy konstans, vagy csak két ponton van balról-jobbra és jobbról-balra folyton akár végtelenig (memhatár) növő rész!
|
||||
|
||||
A `malloc_store` egy embedded megoldásban "mozoghat", tehát mivel csak indexálással érjük el és NEM pointeresen, ezért
|
||||
A `malloc_store` egy embedded megoldásban "mozoghat", tehát mivel csak indexálással érjük el és NEM pointeresen, ezért
|
||||
ott memcpy-vel mozgatható, ha kezd elfogyni vagy a szimbólumtábla, vagy a session storage.
|
||||
|
||||
A jobbra-balra mozgó indexes elérésű malloc-ozgatón túl még egy ilyen memóriánk van, tehát egy cövek két oldalát is használva!
|
||||
|
Loading…
x
Reference in New Issue
Block a user