refined pointer and reference semantics

This commit is contained in:
Richard Thier 2024-09-29 16:55:53 +02:00
parent 2e297f0769
commit b04bc8736e

436
BASED.md
View File

@ -132,13 +132,13 @@ A lényeg ez (kb. "kötelező" RAII + extra szabályok dangling ref / pointer el
- referenciákat lehet paraméterként megadni elvárt típusnak ÉS visszatérési értékként is metódusoknál
- De referenciát eltárolni handle mezőben nem lehet - sima változóban félreteheted magadnak a stack-en ha nem akarod mindig kiírni az expression-t.
- Pointerekre meg van egy ownership modell, hogy a handle-khez (class/ struct megfelelője) tartozik egy "resources" vagy "pointers" blokk... Valszeg csak külön syntax van erre, nem akarok ilyen hosszú szavakat kiírva látni, mert öli a produktivitást, hanem mint a C++ban pl van az inicializálós rész a konstruktornál, úgy a class-nál valami hasonló kis syntax erre...
- Ugye pointereket vissza tudnak adni és el tudnak fogadni paraméterként @resource-nak és @release-nek jelölt függvények (mint pl. a malloc meg a free).
- Pointerekre meg van egy ownership modell, hogy a handle-khez tartoznak a pointereik. Csak ők írhatják őket (más nem) - típus szerint ők írhatják, tehát más azonos típusú handle-nek is írhatom!
- Ugye pointereket vissza tudnak adni és el tudnak fogadni paraméterként @resource-nak és @release-nek jelölt függvények (mint pl. a malloc meg a free). Ez egyszerűen kell az alap működéshez.
- Az ilyen (így jelölt) függvényeket, csak konstruktorból és destruktorból tudod meghívni!!!
- De persze a konstruktor beállíthat a pointerednek valami értéket - nem kell malloc-ból inicializálja meg! és ugye nem kell free-szerű dolgokat hívnia rá...
- Meg persze a @resource nem kötelező, hogy pointer-t adjon vissza - mert adhat valami handle-t is mondjuk. Tehát ennek a "pointeres" blokknak vagy lehetnek nem pointeres változói, ami mondjuk egy integer csak, de úgy jobb, ha minden resource-t szerintem oda írsz - alternatíva, hogy tényleg csak a pointereket jelöljük külön is és akkor sima mezőnek ad értéket a konstruktorban a @resource-os függvény ami mondjuk egy adatbázis handle-t ad vissza valami hívásból és tárol el.... Lehet hogy tisztább ez így, csak így a resource handle-k nem egy helyen vannak, hanem félig a pointerek - félig a sima adatok közt. Valszeg érdemes pointer blokkot csinálni ami tényleg csak pointereket tartalmaz és ha nem használod - hát nem használod.... Egyre inkább hajlok az utóbbira, mert mégis tisztábbnak hangzik...
- Meg persze a @resource nem kötelező, hogy pointer-t adjon vissza - mert adhat valami handle-t is mondjuk.
- Nyilván ezek mind privátok: de ha valaki referenciát ad paraméternek a saját típusodból egy metódusodban, az abban lévőket eléred mert van rá ugye visibility-d úgy. Meg esetleg ugye csinálhatsz friend jellegű dolgot, vagy "owner" class-t aki eléri a pointereid. Esetleg ha egymásba ágyazott class-okat csinálhatsz, akkor ez default bekerül friend-ként, hogy a class törzsében definiált másik class alapból a kintit tekinti ownernek... Ez az "owner class" jó ötletnek tűnik - el is neveztem protectornak - és így lehetnek protected pointerek - na azokat éri el az a speciális class / handler!
- A pointereknek adhatsz értéket, növelheted, csökkentheted, szorozhatod meg a faszomsetudja még mit akarsz vele. De csak a saját pointereidre van normális esetben láthatóságod...
- A pointereknek adhatsz értéket, növelheted, csökkentheted, szorozhatod meg a faszomsetudja még mit akarsz vele. De csak a saját pointereidre van normális esetben láthatóságod... Tehát más pointerei amit kapsz azokat nem tudod változtatni se értékadás jobb oldalában szerepeltetve "lementeni" magadnak.
- Alapból a pointer lehet null - alapból a referencia nem lehet null! Külön kell jelezni, ha lehet null egy visszaadott referencia, vagy paraméterben leírt ref - tehát ez a típus része. Mondjuk egy &ref? jellegű syntax, vagy valami ilyesmi.
^^ez szerintem majdnem mindent kezel, de nyilvánvalóan nem mindent és pont az a célom, hogy a rust-al ellentétben NE akarjak mindent safe-en tartani, csak minél többet...
@ -163,6 +163,231 @@ Ez meg szerintem fasság - nem jó érvek:
https://dept-info.labri.fr/~strandh/Teaching/MTP/Common/Strandh-Tutorial/need-for-garbage-collection.html
## Jobb pointerek 1: safety
A pointerek ahogy fenn írtuk, alapvetően csak @resource és @release függvényekben menthetők és használhatók korlátlanul,
és persze a handle-k adattagjai is lehetnek pointerek amiket akkor kezelünk, mert saját pointer állhat értékadás bal oldalán!
VISZONT! Kiadhatunk pointereket a user kód / caller felé, de számukra nem "menthetők" le ezek. Tehát alapból egy pointer
nem lesz "menthető", de @resource-os és @release-es függvények esetén, @unsafe kódban és a saját ownership alatt IGEN!
Még egy fontos kivétel talán:
for (char* c = str; *c; ++c) {
...
}
while (char* c = file.nextline()) {
...
}
^^Ennél szerintem engedni kéne, hogy a for/while használhassa talán? De ez egy nagyon jó kérdés, mert speciális eset!
Szerintem valahogy ez jó volna, mert praktikus baj nincs vele és C-interop miatt kellhet (főleg az utóbbi pl.)
## Jobb pointerek 2: speed
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...
* De ahogy fentebb írtuk másolni csak lokális változóba lehet a stackre
* A referencia "lényegében egy pointer, de nincs rajta aritmetika"... szóval nem tömböt jelképez.
* 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;
### restrict-ált tömbök
A tömbökre is kell a pointerekhez hasonló restrict.
## 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á!
struct A { uint32_t a, b, c; }; // 3*4=12 bytes with 4 byte alignment
struct B { child *a, *b, *c; }; // 3*8=24 bytes with 8 byte alignment
struct C { A aa; uint32_t x; }; // 4*4=16 bytes with 4 byte alignment
struct D { B bb; uint32_t x; }; // 4*8=32 bytes with 8 byte alignment (4 wasted bytes)
Viszont! A pointerek - mint itt is látszik - típusosak, de az indexek NEM! Viszont miért ne lehetnének?
child@uint32_t index; // Például így
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ű,
class Owner {
private:
std::vector<int> vec = {1, 2, 3};
// Has a vector of pointers to children
// So when this go out of scope we can set child pointers backpointers to nulls (indicating non-living)
OwnPtrFactory<int> fac;
public:
// or access(..) etc.
// NownPtr has a pointer to the factory's own elem and on its move assign / move constructor calls updates facs child vec's
// pointer. Caller can ask the pointer if owner is already deleted or not. Maybe should be threadsafe?
NownPtr<int> getMid() {
return fac.create(&vec[1]);
}
};
Ezt követően a használati ponton olyasmi történik, hogy:
class User {
void f(..) {
...
// Safe használat
NownPtr mid = owner.getMid();
mid.if_exists([](int &i) {
... i is accessible ...
});
// Unsafe használat (esetleg nálam @unsafe és konstruktor/destruktor esetben?)
int j = *mid + 40;
// Unsage-nél is lehessen kérdezni
if(mid) {
int k = *mid;
}
...
}
}
Ez tehát úgy látom C++ nyelven is lehetséges, egyedül mozgatáskor tűnik kicsit is "lassabbnak" meg ugye ez egy "fat" pointer, ami
2x pointernyi méretet használ fel... az egyik maga a mutatott terület címe, a másik a "factory"-ban lévő vektor megfelelő elemére
mutat rá - itt vigyázni kell: a vektor resize esetén invalidálódik ez! Kéne valami "stabil-vektor" ami blokkos és a resize-nál NEM
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?
## Custom allokátorok / arénák
A go-hoz újabban ajánlott "arénás" modell-t is lehet hogy valahogy alkalmazhatnánk - tehát hogy azzal oldjuk meg a custom allokátor kérdést... De szerintem egyszerűen úgy kell kialakítani a standard libet / kódokat, hogy template paraméter szerűen átvegye milyen @resource és @release függvényeket használ - tehát nem konkrétan malloc-ot, meg free-t mondjuk hanem csak azoknak megfelelő deklarációs függvényeket... Ezt C-ben is szoktam csinálni (pl. a kismap-ban van ilyen), de ugye ott függvény pointerekkel, amiket vagy eltárolok (de akkor runtime költsége van), vagy ha a perf fontos, akkor pl. a kismap esetén azt szoktam, hogy ezeket nem csak konstruálásnál, hanem minden hívásnál át kell adni ami használja - azért mert látom, hogy a compiler be tudja inline-olni, ha egy static inline force-inline stb. függvényt adok át neki function pointernek... De ez ugye "trükközgetés" feleslegesen és jó lenne, ha nyelvi elemként tudnánk ezt. Esetleg érdemes a hagyományos generik / template témától ezt különszedni? Nem biztos, lehet hogy azzal kezelendő. Cpp esetén template-ekkel szoktam ezt csinálni...
@ -294,209 +519,6 @@ Lásd:
Ez szerintem low-level helyekre jó lehet... Ha jól definiálom mi történik, akkor padding / align helyett (mellett) is talán.
## Jobb pointerek
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;
### restrict-ált tömbök
A tömbökre is kell a pointerekhez hasonló restrict.
## 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á!
struct A { uint32_t a, b, c; }; // 3*4=12 bytes with 4 byte alignment
struct B { child *a, *b, *c; }; // 3*8=24 bytes with 8 byte alignment
struct C { A aa; uint32_t x; }; // 4*4=16 bytes with 4 byte alignment
struct D { B bb; uint32_t x; }; // 4*8=32 bytes with 8 byte alignment (4 wasted bytes)
Viszont! A pointerek - mint itt is látszik - típusosak, de az indexek NEM! Viszont miért ne lehetnének?
child@uint32_t index; // Például így
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ű,
class Owner {
private:
std::vector<int> vec = {1, 2, 3};
// Has a vector of pointers to children
// So when this go out of scope we can set child pointers backpointers to nulls (indicating non-living)
OwnPtrFactory<int> fac;
public:
// or access(..) etc.
// NownPtr has a pointer to the factory's own elem and on its move assign / move constructor calls updates facs child vec's
// pointer. Caller can ask the pointer if owner is already deleted or not. Maybe should be threadsafe?
NownPtr<int> getMid() {
return fac.create(&vec[1]);
}
};
Ezt követően a használati ponton olyasmi történik, hogy:
class User {
void f(..) {
...
// Safe használat
NownPtr mid = owner.getMid();
mid.if_exists([](int &i) {
... i is accessible ...
});
// Unsafe használat (esetleg nálam @unsafe és konstruktor/destruktor esetben?)
int j = *mid + 40;
// Unsage-nél is lehessen kérdezni
if(mid) {
int k = *mid;
}
...
}
}
Ez tehát úgy látom C++ nyelven is lehetséges, egyedül mozgatáskor tűnik kicsit is "lassabbnak" meg ugye ez egy "fat" pointer, ami
2x pointernyi méretet használ fel... az egyik maga a mutatott terület címe, a másik a "factory"-ban lévő vektor megfelelő elemére
mutat rá - itt vigyázni kell: a vektor resize esetén invalidálódik ez! Kéne valami "stabil-vektor" ami blokkos és a resize-nál NEM
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?
## 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: