Üdv!
Valószínűleg megfertőztek a "modern" programozási paradigmák, mert egy (szerintem) egész egyszerű problémát nem sikerül megoldanom a topicban jelzett tulajdonságú nyelvben (egész pontosan GLSL, de ez most irreleváns).
A probléma demonstrálására kitaláltam egy egyszerű feladatot: szimbolikus algebrát kell megvalósítani a négy alapművelettel, konstansokkal, változókkal, lehetőleg bővíthető módon. A változók behelyettesítési értéke a kifejezés létrehozása után kerül megadásra, vagyis a kifejezésnek többször felhasználhatónak kell lennie. A problémát mindjárt meg is oldottam OOP és funkcionális szemléletben is, mindkét esetben Scalaban. :) A bővíthetőséget az sqrt függvény demonstrálja, ami a SymbolicExpressions objektumon kívül került megvalósításra, vagyis az eredeti kódhoz nem kellett nyúlni.
Egyaránt várok ötleteket és konkrét megoldásokat. Utóbbi esetben ha a választott nyelv C, akkor nem ér makrókat, fordítóspecifikus feature-öket, függvény pointereket, uniont és idegen típusra castolást (lásd alább) alkalmazni. Más nyelv esetén előbbi szabályok értelem szerűen alkalmazandóak. A lényeg, hogy a megoldás tiszta, szép, a nyelvet nem megerőszakoló kód legyen.
Foo* foo = newFoo();
// nem ér, Bar != Foo
Bar* bar = (Bar*)foo;
// ér, minden pointer egyben void* is
void* baz = (void*)foo;
// ér, baz eredetileg Foo*
Foo* quz = (Foo*)baz;
Hozzászólások
Nem értem.
Objektum-orientált virtuális metódushívást C-ben nem tudsz csinálni függvénypointerek nélkül, csak ha felsorolod a lehetséges opciókat (pl. egy switchben elágazol a típustól függően, és az n különböző osztályhoz tartozó metódus-függvényeket belefordítod ebbe a switchbe) - így viszont a bővíthetőséget buktad el. Lehet random egyéb megoldásokkal trükközni (pl. makróbűvészkedés, scripttel generált forráskód, dinamikus linker, stb.), de ez a korrekt megoldás. A "szép" kitételt nem is értem ennek kontextusában: kevés "szebb" dolog van a C-ben a függvénypointereknél.
Ergó első ránézésre az, amit szeretnél, C-ben pl. lehetetlen. Nem azért, mert a nyelv nem tud eleget, hanem mert kellően sok constraintet raktál a saját nyakadba, így üres halmaz maradt...
+1
Szerintem se megoldható az eredeti kódmódosítás bővíthetősége vagy függvény pointerek nélkül. A kettő közül egy kell, hogy bővíthető megoldást kapj.
BaT, azt megkérdezhetem, hogy szerinted a statikusan típusos nyelvet vagy az proceduralitást a függvény pointer hol töri meg?
Üdv,
LuiseX
Az egyszerű bővíthetőségről hajlandó vagyok lemondani, a követelmény úgy szól, hogy lehetőleg bővíthető módon.
A függvénypointerekkel csak az a baj, hogy nem minden nyelv támogatja, illetve azt szeretném megakadályozni a megszorítással, hogy OOP megoldás szülessen, OOP megközelítéssel én is le tudom a feladatot implementálni, nem az a cél. :)
Szia,
Hát, igazából a függvénypointert minden C alapú nyelv támogatja :)
De ilyen alapon Object-ekkel sem szabad gondolkoznod, illetve Arrayekkel sem... Ha egy nyelvet már kiválasztasz a feladatra, szerintem érdemes annak az eszközeit használnod, mintsem túlzott megkötésekkel élned...
De szerény véleményem szerint, ha az OOP-t teljesen kiakarod zárni, akkor már érdemesebb egy jól felépített állapotgépet implementálni a feladatra ( ld. https://en.wikipedia.org/wiki/Finite-state_machine, de C-ben ebből kb. egy switch case-t fogsz látni ideális esetben, mint például itt is : http://www.javakode.com/c-programming/c-code-for-calculator-application/ ).
Remélem, sikerült részben segítenem,
Üdv,
LuiseX
Lehet nem voltam elég világos, nem C-ben próbálom megoldani a feladatot, csak szerintem GLSL fejlesztőből valamivel kevesebb van a HUP-on. :) Ezért írtam a megszorításokat, ezért van általánosan megfogalmazva a kérdés, és nem C-re specifikusan.
Ha már OpenGL shadert írsz, akkor egyszerűbb ha leírod hogy pontosan mit szeretnél és nem virágnyelven járod körben. Ha a fenti példád akarod megvalósítani, annak semmi értelme nincs GLSL -ben
// Happy debugging, suckers
#define true (rand() > 10)
Rendben. Fragment shadert szeretnék írni, Shadertoy-ban, a magam szórakoztatására. A shader egy egyszerű raytracer lenne, amihez kitaláltam egy egyszerű 3D objektum reprezentációt, aminek annyi a lényege, hogy féltereket definiálok, amik tulajdonképpen vec4-ek. Ezekből a félterekből szeretnék ezután unió és metszet segítségével komplex objektumokat definiálni. Például egy kockát 6 féltér metszete határoz meg. Egy ilyen objektumot függvényként nagyon egyszerű leírni, de én szeretném szétválasztani és objektumként reprezentálni őket, és ezekre az objektumokra megvalósítani a megjelenítéshez, transzformáláshoz, stb. szükséges függvényeket.
Értelek. Akkor szembesültél azzal a problémával, amiért nem igazán használnak hasonszőrű megoldásokat modern engine-ekben:)
Egyrészt azért, mert a GLSL kódod közvetlen GPU kódra fordul, így igazából teljesen mindegy hogy mivel próbálkozol, a vége az lesz hogy elágazások lesznek belőle.
A másik probléma az, hogy egy fragment shadernek nehéz áttolni az adatokat. Az uniform-ok száma véges (GL_MAX_UNIFORM_LOCATIONS), illetve lehet arrayként is használni uniform-okat, viszont az array elemei és végesek (GL_MAX_VERTEX_UNIFORM_VECTORS).
A shadertoy pedig egyébként sem olyan rugalmas mintha közvetlen az opengl-t hajtanád.
A shaderekre microcode-ként szabad csak tekinteni, vagyis amit szeretnél megvalósítani az ebben a formában nem csak hogy nem lehetséges, de nem is ajánlott. Ha mindenféle módon szeretnéd ezt megvalósítani, akkor úgy jársz a legjobban ha csinálsz egy preprocessort ami egy kiterjesztett nyelvből csinál neked GLSL kódot, amiből végül a GPU kód áll elő.
Vannak hasonlók (de nem ezt valósítják meg), pl nvidia CG.
Szóval nem azt mondom hogy nem lehet, de nem ez a jó megoldás hogy megpróbálod "elrejteni" a dolgok valódi működését.
// Happy debugging, suckers
#define true (rand() > 10)
Ez rendben van, nem is az a cél, hogy egy profi engine-t írjak, csak ki szeretnék próbálni egy koncepciót. Vagyis nem baj, ha lassú lesz, vagy ha belefutok olyan akadályokba, amik megoldása nem kifizetődő. Viszont feltételeztem, hogy a topicban vázolt probléma egyrészt megfogalmazható kellően általánosan ahhoz, hogy a megoldás azután átfordítható legyen GLSL-re, másrészt ez már egy megoldott probléma, hiszen ez a programozási paradigma évtizedekig uralta a szoftverfejlesztés világát és még ma is sok helyen aktívan használatban van.
Nem is arról beszéltem hogy írj profi engine-t, csak az általad vázolt igényeket szegény GLSL-be nem lehet beleszuszakolni.
Amit tehetsz, használsz struct-ot és inline metódusokat (hogy legalább gyors legyen) és maszkolsz (pl írsz rá egy preprocesszort)
// Happy debugging, suckers
#define true (rand() > 10)
Szia,
Amit linkeltem megoldás, az sem teljesen C specifikus (mármint nem épít pointerekre/makrókra/bármire amit csak C-ben lehetne csinálni, csak c függvényeket és keywordoket kell átírni, hogy tetszőleges nyelvre váltsd), bár tény a másik általam vélt kívánalmadat, az operátorok adatjellegű kezelését nem valósítja meg ( Bár tény, hogy abban a kódban a bal és jobb értékek az adatok, az operátorok pedig művelet jellegűek ) - igaz, lehet egy lépésre van attól még, amit pontosan szeretnél: ez stdio-ra van kihegyezve, neked pedig string alapú feldolgozás kellene, de szerintem ebből a mintából nem teljesen lehetetlen tovább lépni arrafelé...
Sajnos, ez utóbbiból kész példát nem nagyon találtam, ami nem tartalmaz objektumokat ( holott, nem lenne szükséges, sőt, egyszerűen áthidalható lenne azok jelenléte is ).
Ellenben GLSL-hez nem nagyon értek, így kétlem hogy abban a részben érdembeli segítséget tudnék adni :)
Üdv,
LuiseX
Valóban, a hozzászólásod második fele felett elsiklottam, bocsánat.
A FSM mint ötlet jó, bár egyelőre nem látom, hogy hogyan fog segíteni.
Ez nem teljesen igaz, lehet vtable-t implementalni C-ben is, makrokat hasznalva pedig viszonylag olvashato C++-szeru kodot is lehet irni, oroklodessel meg mindennel. Itt egy pelda ahol nincsenek makrok a topicszerzo kedveert :) Az egyetlen "csunya" dolog, hogy ki van hasznalva hogy a struct elso memberje ugyanaz, de C-ben ez nem ordogtol valo.
?
Ez nem teljesen igaz, lehet vtable-t implementalni C-ben is
struct Vtable {
void (*func)(void*, int);
};
vs
nem tudsz csinálni függvénypointerek nélkül
Jabocs, nemtudok olvasni :)
Mi lenne, ha nem OO-ban gondolkozva próbálnád megoldani. Mondjuk egy csúnya ötlet: csinálsz egy fát, ahol egy node a művelet, és amikor bejárod a fát, azzal oldod fel. A leaf a bemeneti paraméter. A kód pedig bővíthető lesz, csak nem örökléssel, hanem egy helyen történő bővítéssel. :)
http://www.geeksforgeeks.org/expression-tree/
Na valami ilyesmire gondoltam én is, hogy külön van választva az adat és a viselkedés. Csak annyi a gondom vele, hogy így is különféle adattípusokra van szükségem, mert például a négy operátoron kívül a többinek nincs két gyereke, a konstans egy lebegőpontos értéket tárol, míg a változó semmit (önmagát definiálja). Erre első körben egy ilyen megoldás jutott eszembe, de elég bénának érzem. A Type enumok számomra azt jelzik, hogy még mindig OOP megoldást próbálok megvalósítani, az egész topic tulajdonképpen arról szól, hogy milyen más megoldás lehetséges.
Kitaláltam egy megoldást a castolás elkerülésére, igaz ettől még merevebb lett a kód. Ne tévesszen meg senkit, hogy a nyelv Scala, direkt nem használtam a legtöbb nyelvi és standard library szolgáltatást. Egy mini DSL segít, hogy az adatstruktúra könnyen felépíthető legyen, igaz egy rakás kódduplikáció árán. Az lenne a jó, ha a store-ok kezelhetőek lennének egységesen, de mivel mindegyik store tömbje más típusú, generikusok/templatek/makrók/castolás nélkül nem látom, hogyan lehetne megoldani.