Új hozzászólás Aktív témák
-
daninet
veterán
válasz
jattila48 #4353 üzenetére
igen
dabadab: igenmindenkinek köszi, működik.
Egyébként ha valaki érdekel egy ilyen homokba rajzasztal playlistjét indítja majd a kód, a 9 fájl darabja 30-60perc egymás után rajzolódik a homokba. -
dabadab
titán
-
jattila48
aktív tag
válasz
jattila48 #4149 üzenetére
Elnézést, a const int valóban olyan "változó", aminek nem lehet értéket adni. A static const int-re gondoltam, erre már áll az amit eddig írtam. Template argumentumként és tömb méretként is static const int használható, const int nem. Amiket a tömb vs. pointer témában írtam, azt fenntartom.
-
mgoogyi
senior tag
válasz
jattila48 #4146 üzenetére
Ezzel nem mondtál semmi újat. A gyakorlati jelentőség = hogyan használod a kódban.
Évekig dolgoztam c++-ban, csak a saját gyakorlati nevezéktanomban változónak hívom a konstansokat is (legalábbis a const int-eket pl). Viszont a #define-olt cuccokat viszont konstansnak hívom.
A const int nekem egy olyan változó, amin nem tehetek meg tetszőleges műveletet.És szerintem a kódodtól függ, hogy minden tekintetben konstans lesz vagy sem.
Ugyanis a kódodhoz hozzácsapva azt, hogy&harom
simán megy, kiírathatod, és így már igazi változód lesz.A const-ot egy optimalizációs tippnek tekintem a fordító számára.
Ez nyilván péklapáttal tarkónbaszós kód, de példának jó.
-
mgoogyi
senior tag
válasz
jattila48 #4143 üzenetére
Ok, főleg nevezéktanról beszélgetünk gyakorlatilag.
Én abból indulok ki, ahogy angolul hívni szoktunk dolgokat. Magyar szakirodalmat csak egyetemen olvastam utoljára kb.[https://msdn.microsoft.com/hu-hu/library/07x6b05d.aspx?f=255&MSPPError=-2147217396]
Pl:
"The const keyword specifies that a variable's value is constant and tells the compiler to prevent the programmer from modifying it."" you must declare your const variable as"
const int harom=3;
Szóval ez itt nekem egy konstants változó. Hülyén hangzik valóban.
-
mgoogyi
senior tag
válasz
jattila48 #4141 üzenetére
"Nem, nem lehet a heapen tömb. Az nem tömb lesz, hanem dinamikusan foglalt memória"
Lehet én vagyok a retardált, de nekem a tömb = összefüggő memóriaterület egy adott típusra.
Szóval nekem van olyan, hogy statikusan allokált array és dinamikusan allokált array.https://msdn.microsoft.com/hu-hu/library/kewsb8ba.aspx?f=255&MSPPError=-2147217396
Az msdn meg le mer írni ilyet, hogy "When allocating an array using the new operator"?
"Ahogy írtam, a t NEM változó, hanem konstans és ez a lényeg."
int * a = new int[10];
int b[10];Pusztán ránézve a kódra én "a"-ra és "b"-re is változóként(előbbire pointer típusú változóként, utóbbira tömb típusúként) fogok hivatkozni. Utóbbi konstans és nincs mögötte valódi változó? Pont nem érdekel, nem látom a gyakorlati jelentőségét. Lehet ez rossz berögzültség, soha egyszer nem volt még hátrányom belőle a munkám során.
-
mgoogyi
senior tag
válasz
jattila48 #4139 üzenetére
Ja értem, hogy mire gondolsz, én gyakorlatiasabban közelítem a kérdést.
int t[10] esetén a t a gyakorlatban úgy viselkedik, mint egy konstans pointer. Amikor a tömbről beszélek a kódban, akkor én a t-re gondolok, mint változóra, márpedig ez pontosan a 0. elem címe.
Az a konstans cím, amiről beszélsz, az maga a t változó tartalma. Annyit tud egy pointerhez képest, hogy a típusából kifolyólag tud arról, hogy hány elemű, azaz nem int * hanem int[10] típusú. De ettől még értékként a 0. elem címe van benne. És igen, valóban nem pointer, de mégis pontosan ugyanúgy viselkedik.Másrészt nem csak a stacken lehet a tömbünk, hanem a heapen is new-val foglalva, akkor meg direktbe egy int *-ba toljuk be a 0. elem címét és azzal a pointerrel is pont azt fogod csinálni, mint a stack-en foglalt t-vel.
Az assemblys részt elolvasom megint, amit írtál, abban nem vagyok otthon. Egyetemen meglehetősen félvállról vettem, így ennyi is maradt meg belőle. Egyébként aki most ismerkedik a C++-szal, annak lehet inkább riasztó, mint segítség.
-
dobragab
addikt
válasz
jattila48 #4081 üzenetére
Ha capture list van a lambdadban, akkor nem tudod belole siman kikerni a fuggvenypointert. Szerencsere van ra trukk
Ez a standard
signal
-t hivja meg, ami valoban egy pattintott fuggvenypointert var.std::function<void(int)> sh = [capture](int sig) {
std::cout << "Signal " << sig << " caught, captured " << capture << std::endl;
};
signal(SIGINT, tmp_lib::to_fptr(sh, []{}));(#4086) PandaMonium
target()
sem jo, ha objektumot capture-oltel, akkor a target nem lesz soha fuggvenypointer. -
PandaMonium
őstag
válasz
jattila48 #4083 üzenetére
Ha kinyitod a cppreference.com-ot akkor látod, hogy az std::function
T* target() noexcept;
metódusával visszakapod a függvény pointert amit úgy hívsz meg ahogy akarsz. Ha nem észt osztani járnál ide (főleg miután te kérsz segítséget), hanem utána olvasnál a dolgoknak előrébb lennél. -
PandaMonium
őstag
-
jattila48
aktív tag
válasz
jattila48 #4073 üzenetére
Egy szimuláció a következő lehet:
class Akarmi{
public:
Akarmi(int x):a(x){}
int addto(int x){
return a+x;
}
private:
int a;
};
Akarmi *pa;
int closure_func(int x){
return pa->addto(x);
}
void main(int argc,char *argv[]){
pa=new Akarmi(5);
std::cout << closure_func(6);
}A fenti példában closure_func 5-öt ad hozzá az argumentumához. és nem tfv., címe pedig használható lenne callback fv. pointerként. A gond csak az, hogy ha szükségem lenne egy 11-et hozzáadó closure-re is, akkor létre kéne hozni egy globális Akarmi(11) objektumot is (pl. pa11 nevu pointerrel), és definiálnom kéne egy closure_func11-et, ami a pa11-gyel hívja az addto tfv.-t. Ez nem túl jó megoldás. Amikor előző hozzászólásomban azt írtam, hogy futás közben kódot generálnak, akkor arra gondoltam, hogy kb. ezt a konstrukciót hozzák létre. Amikor létrejön az Akarmi(5) objektum, annak címével (mint konstanssal, vagyis a generált kódban ezt a címet "bedrórozva" konstansként használják) legenerálják a closure_func kódot, és closure fv. pointerként pedig visszaadják a röptében generált kód kezdőcímét. Így talán világosabb mire is gondoltam.
Várom a hozzászólásokat! -
jattila48
aktív tag
válasz
jattila48 #4044 üzenetére
Ha egyáltalán nem definiálsz copy ctor-t, akkor a fordító generál. Ez valószínűleg jó is lesz neked, mivel nem írtál destruktort. Az értékadó operátor hasonlóan. Egyébként ezt a három fv.-t (copy ctor, értékadó operátor, destruktor) általában vagy mindet definiálja a programozó, vagy egyiket sem.
-
kemkriszt98
tag
-
kemkriszt98
tag
válasz
jattila48 #4040 üzenetére
Én ezt tényleg értem.. csak azt nem értem, hogy ha tettem, egy breakpointot a cpy. konstruktroba, és az előző kommentemben írt sornál nem állt meg, tehát nem hívódik copy konstruktor, hanem feltételezem az = operátor, akkor miért hisztizik a fordító, hogy nincs megfelelő konstruktor?
-
dobragab
addikt
válasz
jattila48 #3857 üzenetére
Végül nem is használtam többszörös öröklést emiatt, megoldottam kompzícióval. Így azonban az ősosztály pointert kell static_cast-olnom egy típusmezőtől függően egyik illetve másik leszármazott osztály pointerré, és ezzel elérni a kompozícióval mindkét osztályban létrehozott tagobjektumot, holott ez lehetett volna ős is.
Jol ertem, hogy a dynamic_cast runtime overhead-je helyett felvettel egy tipusmezot?
Mintha annak nem lenne overheadje, vagy negativ hatasa a karbantarthatosagra.
Ha az interface konstruktora trivialis, akkor a leszarmazott konstruktoraval nem kell foglalkozni, meg virtualis oroklesnel sem. Megoldja a fordito.
a kompozícióval mindkét osztályban létrehozott tagobjektumot
Ezzel ujrakrealtad a diamond-problema alapproblemajat, hogy ketszer szerepel a diamond "csucsa" a memoriakepben (nalad is ketto van), amit a virtual inheritance old meg. Szerintem sokkal egyszerubb lenne inkabb a diamondbol kihagyni a virtual kulcsszavakat, azt' jonapot. Igen, az interface ketszer lesz benne, de abban csak 1-1 vptr van. [link]
Szerintem sokkal kifizetodobb helyenkent leirni azt a dupla static_cast-ot (ahol mindegy, hogy A-ra vagy B-re cast-olod kozepen), mint tipusmezot hasznalni.
Egyebkent javaban sem fenekig tejfel az interface. Ott kotelezo az implementalt fuggvenyeket mind kiirni, akkor is, ha absztrakt. Azaz a linkelt peldaban A-ban es B-ben is kb. meg kellene ismetelni f deklaraciojat. Ja, es a javas interfesznek is van tisztesseges overheadje.
-
dobragab
addikt
válasz
jattila48 #3618 üzenetére
A keresést úgy értettem, hogy a
bool symbol_exists(Symbol)
van szétosztva, és nem a find_symbol. Szarul fogalmaztam.Szóval valami ilyen type switch van neked:
Symbol * s = find_symbol("int");
switch(s.type)
{
case KEYWORD:
Keyword * k = static_cast<Keyword*>(s);
// blablabla
break;
case TYPE:
Type * t = static_cast<Type*>(s);
// blablabla
break;
}Ennél mennyivel rosszabb ez?
std::string symbol = "int";
auto it_keyword = keywords.find(symbol);
if(it_keyword != keywords.end())
{
// use *it_keyword
return;
}
auto it_type = types.find(symbol);
if(it_type != types.end())
{
// use *it_type
return;
}A scope kezelést még mindig nem tudom, hogy kéne működnie.
-
dobragab
addikt
válasz
jattila48 #3599 üzenetére
Futásidejű költsége nem a static_cast-nak van, hanem a type switch-nek. De ha az enum értékei 0-n-ig folytonosak, a fordító O(1) jump table-t tud belőle generálni. Pont pár hete néztem meg.
Szerintem az a legtisztább megoldás, ha külön tárolod. Egyedül a keresést kell az összes tárolóra kiterjeszteni, és a scope keresésben azt hívni. A scope kezelése nem tudom, hogy megy, főleg, hogy nem tudom, milyen nyelvről van szó
A hozzáadás tuti nem macerásabb, hiszen hozzáadáskor tudod, milyen típust adsz hozzá, csak a megfelelő kollekcióba kell belerakni.
Aztán ja, nem hiszem, hogy ezeket
std::vector
-ban kéne tárolni, ha a tárolási sorrend nem életbe vágó, és csak egy lehet mindenből. Errestd::set
vagystd::unordered_set
jobb, szerintem az utóbbi. -
mgoogyi
senior tag
válasz
jattila48 #3610 üzenetére
Lehetséges, hogy nem tudom..
Nem az, amit leírtál? Egy tábla, ami tárolja a lefordítandó kód szimbólumait, mint változó, függvény, stb. és arra használod pl., hogy ellenőrizd, hogy az adott objektumra az adott scopeban lehet e egyáltalán hivatkozni.
A process kapcsán arra gondoltam, hogy valami gépi vagy köztes kódot generálsz, mikor mész a következő sorra a kódban és találkozol pl. egy szimbólumon végzett művelettel.
És elnézést, hogy beledumáltam (inkább ne tettem volna), nem vágom, hogy működnek a fordítóprogramok. -
mgoogyi
senior tag
válasz
jattila48 #3602 üzenetére
Tudom mi a szimbólumtábla, viszont nem tudom, hogy mi történik pontosan a "tárolt attribútumai szerint folytatni a fordítást" során. Különböző külső függőségei vannak a különböző objektumoknak a további feldolgozás szempontjából? Még mindig az van a fejemben, hogy minden objektumra lehetne egy Process-t hívni, ami kezelné az adott objektumot a típusának megfelelően, ami kaphat egy olyan külső függőséget, amin keresztül mindent megkaphat, amire esetleg szüksége van, vagy esetleg vmi callback-et. Ha viszont teljesen heterogén és abszolút összegyezhetetlen dolgok vannak, akkor nincs ötletem.
-
mgoogyi
senior tag
válasz
jattila48 #3599 üzenetére
Vektorban ismétléseket keresni nem annyira hatékony. Vagy arra alapozol, hogy úgyis alacsony lesz az elemszám egy adott scope-ra?
Alapból nem tűnik jó ötletnek vektorba berakni egy csomó castolni való cuccot.Valszeg többet kellene tudnunk, hogy érdemben hozzá tudjunk szólni a témához.
Jó lenne tudni, hogy mikor mire használod a vektort, mikor van beszúrás, keresés, törlés, stb.
Az objektumok feldolgozása soros módon történik? Teszem azt be lehet rakni mindezt egy process nevű virtuális függvénybe, ami specifikus minden altípusra? -
-
dobragab
addikt
válasz
jattila48 #3582 üzenetére
Igen, az
static int E
akart lenni.Ha masra nem, arra mindenkepp jo, hogy a
typename
kulcsszot megtanulod becsulni, sot orulsz neki, hogy csak ennyi helyen kell kirakni, mert legtobbszor ki tudja talalni a fordito.(#3583) kispx
Az elsot tenyleg elcsesztem.
Masodik esetben vigyazz, a
::E
global scope. -
dobragab
addikt
válasz
jattila48 #3579 üzenetére
Akkor a tobbi megoldas, termeszetesen nem csak a definialt tipusban ternek el, hanem elvi kulonbseg van.
2.
int A, B;
A * B; // eldobjuk a visszateresi erteket
typedef int D;
template<typename T>
struct C
{
int E;
};
int X;
C<D>::E * X;3. Talan ez faj legjobban. Szintaktikailag ez is helyes.
int C, D, E, X;
C<D>::E * X; -
dobragab
addikt
válasz
jattila48 #3470 üzenetére
Légy szíves írj már rá egy jó kódot, ha már az enyém annyira szar! De legyen aztán boost, regexp, STL, meg minden nyalánkság, ami egy igazán szép C++ kódba kell!
unsigned long long getFibonacciNumber(unsigned i)
{
static std::vector<unsigned long long> cache = {0, 1};
if(cache.size() > i)
return cache[i];
unsigned long long new_val = getFibonacciNumber(i-1) + getFibonacciNumber(i-2);
cache.push_back(new_val);
return new_val;
} -
dobragab
addikt
válasz
jattila48 #3456 üzenetére
Tessék! 9GAG-en sokadszor botlottam bele ebbe a stílusgyakorlatba reggel.
Annyit módosítottam a fejlécen, hogy
unsigned long long getFibonacciNumber(unsigned i)És írtam hozzá egy tesztprogramot:
int main()
{
unsigned long long result[1000];
clock_t start = clock();
for(int i = 0; i < 1000; ++i)
result[i] = getFibonacciNumber(1370 * i);
clock_t finish = clock();
for(auto n : result)
std::cout << n << ' ';
std::cout << ((finish - start) / double(CLOCKS_PER_SEC)) * 1000 << " ms" << std::endl;
return 0;
}Az én megoldásom a nemigazán bivalyerős laptopomon (i5-4200U) átlagosan 80 ms körül végez, és szépen tagolva 11 sor. Egyelőre nem lövöm le a kódot, hátha szeretnél és tudsz jobbat írni STL nélkül. Beégetett méretű tömb lehetőleg ne legyen benne, de amúgy bármi (szálkezelés is).
Összehasonlítási alap: egy egyszerű iteratív megoldás nálam 3800 ms körül teljesít.
-
dabadab
titán
válasz
jattila48 #3446 üzenetére
"A kódom nem rossz (legalábbis remélem)"
Hát ez
char trimmed_ipv6_addr[40];
meg ez
for(int i=0;i<strlen(ipv6_addr);++i){
kiad egy potenciális buffer overflowt.Egyébként kb. ugyanez az algoritmus úgy nézne ki C++-ban, hogy (most nincs nálam fordító, így csak IQ-ból):
string src="2001:0e00:41a0:006b:00de:03c0:0e00:60bc";
string dst;
char prev=0;
bool trimming=true;
for ( char c in src )
{
if ( trimming && c == '0' && prev == '0' )
{
// skip char
continue;
}
if ( c == ':' )
{
trimming = true;
}
else if ( c != '0' )
{
trimming = false
}
dst +=c;
prev=c;
} -
dobragab
addikt
válasz
jattila48 #3435 üzenetére
Csak az algoritmust akartam bemutatni, hogy mennyire egyszerű, és nem kellett hozzá sem boost, sem regexp, sem vector<string>, amiknél feltehetőleg jóval hatékonyabb így (futásidőben, és memória használatban is (főleg ha helyben történik a feldolgozás)).
Van még egy dolog, amit nem vettél számításba, vagy csak tojtál rá. A kolléga, aki segítséget kért, tanul. Ez fontos következményekkel jár.
1. Nem kéne mindent a hatékonyságra fogni. Az lenne a lényeg, hogy egyszerű, világos megoldást mutass neki, amit könnyen felfog, megért, és reprodukálni is tudna.
2. Neked sok-sok év tapasztalatod van abban, hogy hogyan hatékonyabb, és mindent ennek alávetve írsz meg. Ő most ismerkedik a nyelv lehetőségeivel, és pont lesz*rja, mennyire hatékony, ha érti. Gőze sincs arról, hogy hogyan kéne hatékony kódot írni, és ne is próbáld erre nevelni, mert ahhoz tárgyi ismeretek hiányoznak. Ráadásul ha megpróbálja, annál rosszabb. Nem fog neki menni, de legalább érthetetlen kódot ír.
3. A kolléga tanul, és te példakóddal segítesz. Ilyenkor te - akarva, akaratlanul - oktatóként lépsz fel. Mivel hozzánk képest meglehetősen kevés kódot látott, zömmel a sajátját, baromi nagy hatással van rá, milyen kódot lát követendő mintaként. Olyan kódot, ami saját bevallásod szerint is több ponton rossz, nem nagyon kéne neki mutatni.
4. A kolléga további életében valószínűleg az ilyen szintű optimalizálás soha nem lesz fontos. Manapság sem illik C kódot írni az STL használata helyett, mert feleslegesen bonyolítja a kódot. Optimalizálni ráadásul csak akkor kéne, ha lassú a kód (a nyilvánvaló f*szságokat kivéve). Inkább arra kéne nevelni, hogy használjon STL tárolókat, azért vannak.
Így elsőre ennyi, ami az érdemi segítséget illeti.
-
dobragab
addikt
válasz
jattila48 #3441 üzenetére
Feltéve, hogy függvénybe rakod, csak akkor, ha így deklarálod:
restrict const char *ipv6_addr
És azt sem egyszerű megoldani, hogy juttatod ki az eredmény-tömböt a függvényből. Out paraméterrel persze lehet, de valahogy szebbnek tűnik rendes visszatérési értékként, std::string-ben.
-
dobragab
addikt
válasz
jattila48 #3325 üzenetére
Vagyis nem feltétlenül a taginicializáló listában írt sorrendben (néhány fordító warning-ot generál, ha a taginicializáló lista sorrendje eltér a deklaráció sorrendjétől). Ezek után hajtódik végre a szóban forgó objektum ctorának törzse.
Pontosan. Mivel az inicializáló listát már ismeri, feltételeztem, ezzel is tisztában van.
Normális fordítóktól kapsz ilyenkor warning-ot, különben _nagyon_ könnyű hibás kódot írni.
class String
{
char * data;
size_t size;
public:
String(char* d, size_t s) :
size(s),
data(new char[size+1])
{
// ...
}
// ...
};Első ránézésre nem is látszik, miért hibás a kód, ha a fordítótól nem kapsz warningot, órákig keresheted.
Ha egyáltalán generálható implicit move operáció, akkor az kb. ugyanaz mint a copy operáció, ezért fölösleges a move.
Háát... nem. Ha az Example osztálynak adattagja egy std::vector, marhára nem mindegy, hogy az Example-nek generál-e move ctort, vagy nem. Ha generál, a vector is move-olódni fog, míg ha nem, másolódik (ahol beleszólhat a copy elision, de ezt most hagyjuk).
-
dobragab
addikt
válasz
jattila48 #3307 üzenetére
Kezdőknek pedig nem OOP szemléletet és STL-t kell tanítani, hanem írja meg C-ben az alap algoritmusokat (keresés, rendezés, stb), értse meg a paraméterátadás rejtelmeit (érték, cím,...), írjon rekurzív fv. hívást, stb. Ez később mind hasznára fog válni, amikor már a C++ esetleg magasabb szintű absztrakcióit használja. Pontosabban enélkül, soha nem lesz belőle jó C++ programozó.
Tökéletesen egyetértek.
Igazából lényegtelen, hogy C vagy C++ a nyelv, de C++-ban egy csomó olyan dolog van, aminek dependency-je, hogy értse, hogyan van C-ben.
Például amíg nem tudja _tökéletesen_, mi az a pointer, nem kéne sem referenciát, sem okos pointert tanulni. Sőt, okos pointereket csak azután kéne, hogy megvolt az OOP, a RAII, a template és esetleg a jobbérték referencia.
Amíg nem érti tökéletesen, mi az a tömb, hogy a méretét nem tudja lekérdezni függvényből, hogy mi köze a pointerhez, stb. addig nem szabad neki std::vectort tanítani, mert utána már sosem tanulja meg, mi is az a tömb.
std::string-et is csak azután kéne tanítani, hogy érti a tömböket, használta a karaktertömböket, stb.
Fvptr -> öröklés ugyanez.
Szóval sorolhatnám még a dependency-ket, de nem sok értelme van. Az a lényeg, hogy ezeket be kell tartani, és bizony C-ben nem lehet ezt a szabályt megszegni, mert nincsenek ilyen cuccok. C++-ban sokkal könnyebb "rossz útra tévedni", míg C-ben nem lehet. Ha betartja ezeket a játékszabályokat, akkor a C++ jobb lehet, mert egy csomó f*szságot meg tud úszni a scanf-től a qsort-ig, de jóval nehezebb ezeket betartani.
-
ToMmY_hun
senior tag
válasz
jattila48 #3263 üzenetére
Használtam VS-t, és tényleg nem rossz - már amennyit én láttam belőle. Sajna olyat tapasztaltam, hogy corrupted lett egy bizonyos fájl (valami adatbázis) és emiatt nem engedte elmenteni az általam frissen létrehozott fájlokat.
Persze ez nem olyan vészes, letöröltem a fájlt, újragenerálta és rendbe is jött. Az IntelliSense és a debugger/profiler frankó.
Java-hoz rengeteget használom a NetBeans-t és kipróbáltam C++-hoz is, azzal is ügyesen bánik. (Linuxon legalábbis, Win-en a compiler beállítással kínlódni kell). A fejlesztést sok eszközzel támogatja, amiket próbáltam és mennek: kódkiegészítés, debug, refaktorálás, library kezelés, CPP Unit test, Git addon (linuxon fontos). A profiler-re még kíváncsi vagyok, lesz olyan kódrész amit megnézek vele - feltéve ha van és működik.
MinGW gcc: thread-eket támogat? Ősszel próbálkoztam vele, és akkor azt olvastam hogy nem annyira, persze lehet hogy én voltam suta.
-
ToMmY_hun
senior tag
válasz
jattila48 #3259 üzenetére
Most hogy mondod jönnek vissza az emlékek. Anno Win 10-en kezdtem el a fejlesztést, aztán amikor eljutottam a Qt használatáig, rá kellett jönnöm hogy szívás van. Le kellett volna fordítani az MSVC2015-tel az egész Qt-t, hogy tudjam használni a saját statikus könyvtáramat, vagy a saját kódot kellett volna lefordítani olyan fordítóval, amiből már van Qt bináris. Egyik megoldás sem lett volna egyszerű. Qt-re majd kíváncsi leszek, de átpártoltam Debian-ra, és ezen neki mernék állni a fordításának is.
Egyébként minden egyszerűbb rajta, szóval C++ fejlesztéshez ideálisabb, mint a Microsoft kacatjai.
-
dobragab
addikt
válasz
jattila48 #3259 üzenetére
Ez nem a C++ libek telepítési nehézsége, hanem az összes Windows szegénységi bizonyítványa. Jól működő operációs rendszeren ennél kicsit egyszerűbb a dolog.
sudo apt-get install libboost-all-dev
A futtatáshoz szükséges binárisokat meg könnyebb beszerezni, akár Win, akár Linux, a Windows-ra fordításhoz ott a mingw-gcc cross.
-
ToMmY_hun
senior tag
válasz
jattila48 #3257 üzenetére
Részben igazad van. Az tényleg nagyon hasznos, ha megírja az ember saját maga tanulási céllal az ilyen, és ehhez hasonló problémákat megoldó programokat. Ezt majd én is szeretném megtenni, csak egy kicsit kevésbé túlhajtott időszakban. Abban is igazad van, hogy a könyvtár használat sem mindig jó. Pár hete jártam úgy, hogy egy céges kódban kiváltottam egy 3rd party library-vel egy nagyobb kódrészletet. Teljesen jól szuperált, örültünk is neki, aztán rá egy hétre hozzá akartunk adni egy új feature-t, amit viszont nem lehetett megoldani azzal a library-vel. A vége az lett, hogy vissza kellett hozni a kigyomlált saját kódot. Van ilyen is, főleg akkor, ha nincs fix specifikáció.
Azzal viszont nem értek egyet, hogy nehéz telepíteni/megtanulni őket. Az installálás nem volt vészes eddig egyik könyvtárnál sem. (Egyébként ez iszonyat jól meg van oldva Java-ban, ha érdekel nézz utána a Maven-nek). Általában annyiból áll, hogy le kell szedni a projektet, lefordítani és bemásolni a megfelelő mappákba. (ZMQ automatikusan installál is, szóval itt a kézzel másolgatás is megúszható). Ezután ugye linker-nek kell megadni a lib nevét, include-olni a header-t és készen is van. A használat már más kérdés. Sok esetben hiányos, vagy nem is létezik a dokumentáció. Azokkal nagyon nehéz bánni, de a komolyabbak - amelyek mögött nagyobb közösség áll - rendelkeznek megfelelő doksival. A használatának megtanulása pedig a doksin múlik. Egy lényegre törő, jól strukturált dokumentációból pillanatok alatt ki lehet szűrni a lényeget. A többi funkció meg nem is biztos hogy akkor, abban a pillanatban érdekli az embert.
-
ToMmY_hun
senior tag
válasz
jattila48 #3248 üzenetére
"ágyúval lövöldözés verébre"
Ezt jól látod, viszont egy sokszorosan tesztelt, sok ember által használt kódot felhasználni okosabb döntés, mint írni egy sajátot - az én tapasztalatommal legalábbis biztosan. Aztán az időnek is híján vagyok, van még egy hónapom befejezni a kódolást, szóval csak azt írom meg, amit muszáj.
(Ez amúgy sem rossz stratégia)
Amúgy anno azért választottam a ZMQ-t, mert van Python és C++ variánsa is, így elég könnyű dolgozni vele. Bemegy a sztring C++ oldalon és a Pythonban megjelenik ugyanaz. A többi érvet, hogy cross-platform, cross-language és több kommunikációs modellt is támogat nem is sorolnám.
-
dabadab
titán
válasz
jattila48 #3253 üzenetére
"Miért lenne kevés?"
Eleve implementálni kell valami protokollt, a socketek kezelése alapvetően nem thread-safe (meg ha az egyes műveletek azok is), hibák lekezelése, stb. Nézz meg akár egy szimple ftp szervert v klienst, hogy abban mennyi hálózati kód van, pedig csak TCP csomagokat küldenek meg fogadnak
-
dabadab
titán
-
dabadab
titán
válasz
jattila48 #3248 üzenetére
"Nem kell megijedni a TCP socket-ektől, semmilyen külön library nem kell hozzá, simán a BSD (POSIX) socket API-t kell használni, nem bonyolult."
Az API nem bonyolult.
Amíg megcsinálod azt, hogy azt használva legyen egy megbízható, normálisan és jól működő kommunikációs layered, az viszont igen. -
ToMmY_hun
senior tag
válasz
jattila48 #3234 üzenetére
Jól érted, és valóban az általad írt is jó megoldás. Amennyiben sok elemem lenne a map-ekben, akkor az általad javasoltat alkalmaznám, mert kisebb az erőforrásigénye a beillesztésnek. Az enyémben ugye ellenőrizni kell, hogy rendelkezésre áll-e az ID, és ez a lista egy sima vector-ban van tárolva, amiben iterálva keresek. Mivel az a helyzet, hogy a program indításakor nagyjából 20 elem kerül tárolásra a különböző konténerekben, itt nem igazán nyernék sokat az általad javasolt implementációval, legalábbis amennyire én meg tudom ítélni. Nagy elemszám vagy gyakori beillesztés esetén egészen más a helyzet.
Köszi a választ! Amúgy örülök neki hogy belekérdezel/kérdeztek, gondolom ilyesmire kell majd számítani a szakdoga bemutatáson is.
-
dobragab
addikt
válasz
jattila48 #3226 üzenetére
Valóban.
template<typename T>
void print(std::vector<T> const& vec)
{
for(int i = 0; i < vec.size(); ++i)
std::cout << vec[i] << ' ';
}És igen, minden saját osztályra, amit ki szeretnél írni, érdemes overload-olni az operator<<-t.
std::ostream& operator<<(std::ostream& os, Ratio r) // tört osztályra
{
os << r.get_num() << '/' << r.get_den();
return os;
} -
ToMmY_hun
senior tag
válasz
jattila48 #3227 üzenetére
Megint nem írtam le a teljes problémakört.
Szóval ez az ID azonosítani fogja egyértelműen az objektumot ebben a programban, és egy socketen keresztül csatlakozott programban a hozzá tartozó, ID alapján meghívandó szkripteket. Ahhoz, hogy ne legyen kavarodás abból, hogy egy ID többször szerepel, ezt már itt lekezelem, így könnyebb lesz a dolgom a későbbiekben. Logikailag is inkább ide tartozik, mint egy felsőbb rétegbe.
"Nem overloadolni, hanem megvalósítani."Ezt miért nem overloadnak nevezik?Ismereteim szerint egy már létező operátor új típusokkal való műveletvégézésnek definiálása az overload, ezek szerint rosszul tudom?
-
ToMmY_hun
senior tag
válasz
jattila48 #3222 üzenetére
Először is köszi a választ!
Konkretizálom a kérdést. Konténereket szeretnék csinálni, de mindegyik konténerben különböző típusú objektumot akarok tárolni. Szeretnék egy ősosztályt ezeknek a konténereknek, amelyben megadom a kötelezően megvalósítandó tagfüggvényeket, egyelőre két darabot. Egyik új elemet helyez bele, a másik pedig elkéri az elemet azonosító alapján. Az ötletem az lenne, hogy készítek egy template osztályt, amiben definiálom a put és get tagfüggvények működését, mivel a konténereknél csak típusbeli különbség van, ezen felül működésbeli nincs. Azért szeretném így megoldani, mert így viszonylag jól rétegezett és utólag könnyen bővíthető lenne a kód - legalábbis szerintem.
Tehát ezek tükrében jó ötlet a template, vagy más felé tapogatózzak?
-
dobragab
addikt
válasz
jattila48 #3208 üzenetére
Fordítsd normális fordítóval, ami támogatja a C++11-et, a zárójelek is rendben vannak. Pl. MinGW-GCC 4.8+
Létrehoz egy int-ekből álló tömböt, amit nullákkal tölt fel, és feltöltés közben egyesével kiírja az argumentumokat. Majd ezt a tömböt eldobja, mert nekünk csak a kiírás kellett, a (void) a compiler warning miatt kell.
Tudtommal ez a legegyszerűbb implementációja a variadic template-es mindent kiíró függvénynek, általában két függvény kell hozzá.
-
jattila48
aktív tag
válasz
jattila48 #3189 üzenetére
Na megint tanultam valamit. Ezt írtam:
"Jó lenne, ha a forward deklarációban meg lehetne mondani, hogy a BodyClass teljesen közönséges osztály, nem örökölt senkitől (főleg nem többektől) és nincs virtuális tfv.-e (még emiatt is lehet ez az igazítás). Akkor talán nem generálná ezt az ilyen osztályokra amúgy tényleg fölösleges igazító kódot. Ilyet sajnos tudtommal nem lehet a C++-ban."És VS-ben lehet! handle_class.h-ban:
class __single_inheritance BodyClass;__single_inheritance kulcsszó a forward deklarációban a megoldás! Így már igazító kód nélkül fordít, és főleg jól. Ha még azt is meg lehetne mondani, hogy az mfp nem virtuális fv.-re mutató member function pointer, akkor az indirekt címzésű call helyett is lehetne direkt címzésűt fordítani (bár ez nem nagy veszteség).
-
jattila48
aktív tag
válasz
jattila48 #3191 üzenetére
Debuggolással sikerült kideríteni, hogy az igazító kód valóban a pimpl pointert igazítja. Akkor van rá szükség, ha pl. a BodyClass osztály két ösosztálytól örököl publikusan, méghozzá az f tfv.-ét a másodiktól örökli. Mivel a forward deklaráció miatt a fordító nem látja a teljes BodyClass osztály definíciójat, így erre az esetre mindenképp fel kell készülnie. A pimpl az egész objektumra mutat, de ez az első ősosztály részét jelenti az objektumnak. Ezért, ha a második ősosztályból örökölt tfv,-re mutat az mfp, akkor a pimpl-nek is a BodyClass objektum második ősosztály részére kell mutatnia. Ezt végzi ez a bizonyos igazító thunk kód. Ha a teljes BodyClass definíciót látja a fordító, akkor el tudja dönteni, hogy a BodyClass nem örököl többszörösen, ezért nincs szükség a thunk kód generálására. Az első
004116D6 cmp dword ptr ds:[4168B4h],0
sort nem értem, mert az eredméntyét nem befolyásolja, hogy a tfv. virtuális-e vagy sem, az sem hogy örökölt-e vagy sem. De a hamis kimenetelkor lefutó ágból nekem mégis úgy tűnik, hogy valami virtuális dologgal lehet összfüggésben. Ha a tfv. virtuális, akkor a kód végén az indirect címzésű call virtuális hívást megvalósító kódra ugrik. Tehát ezért kellet a call-nak indirekt címzésűnek lenni. Ha látná, hogy a tfv. nem virtuális, akkor elvileg direkt címzéssel is meghívhatná, azonban ekkor sem teszi. -
jattila48
aktív tag
válasz
jattila48 #3190 üzenetére
Közben kicsit jobban belegondoltam, és az igazító kód valószínűleg a többszörös öröklődésből adódó this pointer igazítást is, és az esetleges virtuális fv. híváshoz szükséges vftbl pointer igazítást is elvégzi. Az incomplete BodyClass forward deklaráció miatt a fordító nem látja, hogy a BodyClass *pimpl pointer valójában egy BodyClass-ból publikusan leszármazott osztályra is mutathat, sőt az is lehet, hogy az osztálynak egy másik publikus ősosztálya is van, amitől előbb örököl, mint a BodyClass-tól:
class DescClass : public UnknownClass, public BodyClass{...}
A handle_class.cpp-ben pedig a HandleClass ctor.-a:
HandleClass::HandleClass():pimpl(new DescClass ()){}Ebben az esetben a pimpl valójában az UnknownClass részre mutat (mivel az van előbb az ősök között), és nem a BodyClass részre, ahogy kéne. Ezért ha a DescClass * típusú pointert a pimpl értékül kapja, azt igazítani kell a BodyClass részre (ezt végzi egyébként a thunk kód többszörös öröklődés esetén).
Virtuális tfv. esetén a member function pointer szintén fordítás idejű konstans, azonban nem a tfv. közvetlen címe, hanem a virtuális fv. táblában a rá vonatkozó bejegyzés indexe (ez nem biztos, ezt csak így gondolom). Ezért az esetleges öröklődésből adódó vftbl címet is igazítani kell, mivel a fordító ugye nem látja, hogy a BodyClass valójában egy ebből leszármazott osztállyal példányosult. Az igazító kódban szerintem az első sor
004116D6 cmp dword ptr ds:[4168B4h],0
annak vizsgálata lehet, hogy tfv. virtuális-e. Ha nem, akkor egyszerűen a thunk kód kerül végrehajtásra a linkelési időben kitöltött információk alapján. Ha virtuális, akkor a másik ágban a vftbl. megfelelő beállítása, és a thunk végrehajtása történik. Mivel rossz működés esetén ebben az első utasításban rossz a "globális" cím, ezért gondolom, hogy a linker (vagy a betöltő) ront el valamit. Az, hogy a call ezután indirekt címzéssel történik, arra utal, hogy futási időben dől el, hogy milyen kódrészletet kell végrehajtani (talán maga az esetleges virtuális hívás?). Remélem érthető volt, és elnézést ha kicsit hosszúra nyúlt. -
jattila48
aktív tag
válasz
jattila48 #3189 üzenetére
"Úgy tűnik, hogy a forward deklarációval nincs elég információja a BodyClass osztályról, hogy a pimpl és az mfp alapján meghatározza a BodyClass f tfv.-ének valódi címét."
Ez butaság, a BodyClass f tfv.-ének címe konstans, és jól van eltárolva. Az aktuális BodyClass objektumra mutató pipl-et akarja hozzá igazítani. Talán mégis inkább virtuális tfv.-re mutató member function pointer esetén lehet erre szükség. Ha tudná, hogy az f nem virtuális, akkor erre nem is lenne szükség. Ez csak tipp.
-
jattila48
aktív tag
válasz
jattila48 #3184 üzenetére
A "fölösleges" kód akkor keletkezik, ha handle_class.h-ban a BodyClass incomplete type-ként forward deklarálva van. Ha beinkludolom a teljes body_class.h-t, akkor "szép" kódot generál. Ekkor persze nincs értelme a pimpl-nek. Úgy tűnik, hogy a forward deklarációval nincs elég információja a BodyClass osztályról, hogy a pimpl és az mfp alapján meghatározza a BodyClass f tfv.-ének valódi címét. Mintha a pimpl-et akarná különböző információk alapján igazítani, ahogy a this-t szokta igazítani a thunk kód többszörösen örökölt osztályok esetén. Jó lenne, ha a forward deklarációban meg lehetne mondani, hogy a BodyClass teljesen közönséges osztály, nem örökölt senkitől (főleg nem többektől) és nincs virtuális tfv.-e (még emiatt is lehet ez az igazítás). Akkor talán nem generálná ezt az ilyen osztályokra amúgy tényleg fölösleges igazító kódot. Ilyet sajnos tudtommal nem lehet a C++-ban. Egyébként a kódban valószínűleg csak cím hibás, mégpedig a
004116D6 cmp dword ptr ds:[4168B4h],0
sorban a 4168B4. Amikor rosszul fut, akkor ez a cím unicode sztringekre mutat, vagyis nyilvánvalóan rossz. Amikor jól fut, akkor különböző globális konstansok lehetnek itt és környékén (ha jól fut, akkor 0). Mégsem a const deklarációtól függ, hogy jó-e vagy nem, mert újra buildeléskor const-tal is elromlott. Talán a linker lesz a hibás. Egyébként az LLVM erre a kódra assertion failed-del elszáll, és kéri, hogy a hibajelentést küldjem be. Csak a gcc tudta minden gond nélkül buildelni. -
jattila48
aktív tag
válasz
jattila48 #3186 üzenetére
A másik programomban teljesen hasonló kódra a VS ezt fordította:
auto pimpl=hc.pimpl;
00426AAF mov eax,dword ptr [ebp-1Ch]
00426AB2 mov ecx,dword ptr [eax]
00426AB4 mov dword ptr [ebp-28h],ecx
.
.
.
auto sc=hc.f(1);
00426ABF push 1
00426AC1 lea eax,[ebp-74h]
00426AC4 push eax
00426AC5 mov ecx,dword ptr [ebp-1Ch]
00426AC8 call HandleClass::f (04113CFh)
.
.
.
auto sc2=(pimpl->*hc.mfp)(2);
00426AD7 mov esi,esp
00426AD9 push 2
00426ADB lea eax,[ebp-1D4h]
00426AE1 push eax
00426AE2 mov ecx,dword ptr [ebp-28h]
00426AE5 call dword ptr ds:[42B8A0h]Látható, hogy a mfp-én keresztüli hívás előtt nincs befordítva a "fölösleges" kód. Egyébként a kétfajta hívás kódja teljesen hasonló, csak az első call direkt címzéssel, a második pedig indirekt címzéssel történik. Viszont a második call már közvetlenül a BodyClass f tfv.-ét hívja, vagyis valóban nincs forwarding call overhead (ami egyébként elég jelentős). Fórumokon (pl. StackOverflow) azt olvastam, hogy a pimpl-et nem lehet enélkül megcsinálni. Ezek szerint mégis. Ez azért működik, mert az incomplete type-ok member pointerei is forward deklarálhatók.
-
EQMontoya
veterán
válasz
jattila48 #3186 üzenetére
Simán elképzelhetőnek tartom, hogy compiler bugot fogtál.
Először azt hittem, ott lehet benne buktató, hogy egy static ptr úgy kap értéket, hogy akitől kapja, az member volt, ezért mondjuk move-olódhatna máshová, de ez már utána nem befolyásolja a staticot, mert közvetlenül mutat, nem ezen keresztül, tehát nem okoz gondot.
-
MageRG
addikt
válasz
jattila48 #3184 üzenetére
Nem értek annyira a C++ programozáshoz, de nekem fura hogy egy static memberbe akarsz belepakolni egy másképp példányosodó valamit.
A HandleClass::mfp közös az összes HC-ben, ugyanazt a BodyClass példány memberét hívja.
De a HandleClass::f meg egy-egy külön BodyClass példány membert hív, ami a konstruktorban jön létre.
Vagyis a két hívás *vára nem ugyanazt csinálja.
Akkor lenne ugyanaz, ha a *pimpl member is statikus lenne.De ez csak az én "két centem".
-
jattila48
aktív tag
válasz
jattila48 #3182 üzenetére
Úgy néz ki, megoldódott a probléma. Ha az mfp member function pointert const-nak deklarálom, úgy jól működik. handle_class.h-ban:
int (BodyClass::* const mfp)(int);
handle_class.cpp-ben:
int (BodyClass::* const HandleClass::mfp)(int)=&BodyClass::f;
pimpl_proba.cpp-ben visszaírtam egy utasításba a debug miatt több sorra szedett hívást:
BodyClass *pimpl=hc.pimpl;
n=(pimpl->*hc.mfp)(2);Szerintem const nélkül is jól kéne működnie, úgyhogy a VS-ben lehet hiba. Továbbra sem értem azonban, hogy mi az a "fölöslegesnek" tűnő kód, amit a hívás előtt generál (ez volt hibás ha nem const volt az mfp):
004116D6 cmp dword ptr ds:[4168B4h],0
004116DD jne main+80h (04116F0h)
004116DF mov eax,dword ptr [pimpl]
004116E2 add eax,dword ptr ds:[4168ACh]
004116E8 mov dword ptr [ebp-100h],eax
004116EE jmp main+0A9h (0411719h)
004116F0 mov ecx,dword ptr [pimpl]
004116F3 add ecx,dword ptr ds:[4168B0h]
004116F9 mov edx,dword ptr [pimpl]
004116FC add edx,dword ptr ds:[4168B0h]
00411702 mov eax,dword ptr [edx]
00411704 mov edx,dword ptr ds:[4168B4h]
0041170A add ecx,dword ptr [eax+edx]
0041170D add ecx,dword ptr ds:[4168ACh]
00411713 mov dword ptr [ebp-100h],ecx
00411719 mov esi,esp
0041171B push 2
0041171D mov ecx,dword ptr [ebp-100h]
00411723 call dword ptr ds:[4168A8h]A pimpl-et igazgatja, de nem tudom miért. Másik programomban ez nincs. Valami hasonlót a gcc is csinál. Szerintetek mi lehet ez?
-
jattila48
aktív tag
válasz
jattila48 #3181 üzenetére
Közben annyit sikerült kideríteni, hogy ha a HandleClass::mfp BodyClass tfv.-re mutató member function pointer nem statikus, hanem sima tag, akkor jól működik. Azzal, hogy az mfp statikus, azt akartam elkerülni, hogy a hc-én keresztül "meg kelljen hivatkozni", hiszen ez is overhead lenne. Nem értem miért nem működik, hiszen a a member pointerek fordítás idejű konstansok, ezért lehet az mfp statikus. Ugyanakkor lefordul, tehát a VS szerint is lehet.
-
jattila48
aktív tag
válasz
jattila48 #3176 üzenetére
Tehát így már jó:
struct tempfv{
int operator()(){return 42;}
};
template<typename R,typename F> struct Temp
{
int f(){
return F()();
}
};
template <template<typename R,typename F> class T,typename R,typename F> struct TempTemp
{
T<R,F> t;
int f(){return t.f();}
};
TempTemp<Temp,int,tempfv> t; -
-
EQMontoya
veterán
válasz
jattila48 #3169 üzenetére
Csinálsz egy template classt, aminek van két int paramétere.
Az első hívásnál az első értelemszerűen 0, a második N.Ezzel tudod mindet példányosítani, és meghívni rajtuk valamit. Az más kérdés, hogy utána
Valahogy így:
template<int from, int to>
struct static_for
{
void operator()()
{
P<from> p;
p.doSomething();
static_for<from+1, to>()();
}
};
template<int to>
struct static_for<to, to>
{
void operator()()
{}
};
Új hozzászólás Aktív témák
Hirdetés
● ha kódot szúrsz be, használd a PROGRAMKÓD formázási funkciót!
- BESZÁMÍTÁS! MSI SUPRIM X RTX 4080 16GB videokártya garanciával hibátlan működéssel
- BESZÁMÍTÁS! 6TB Seagate SkyHawk SATA HDD meghajtó garanciával hibátlan működéssel
- BESZÁMÍTÁS! 4TB Samsung 870 EVO SATA SSD meghajtó garanciával hibátlan működéssel
- BESZÁMÍTÁS! Intel Core i7 8700K 6 mag 12 szál processzor garanciával hibátlan működéssel
- BESZÁMÍTÁS! Intel Core i7 4790 4 mag 8 szál processzor garanciával hibátlan működéssel
- Bomba ár! Lenovo ThinkPad T480s - i7-8GEN I 16GB I 256GB I 14" WQHD I HDMI I Cam I W11 I Gari!
- LG 27GR93U-B - 27" IPS - UHD 4K - 144Hz 1ms - NVIDIA G-Sync - FreeSync Premium - HDR 400
- Bomba ár! HP ProBook 450 G7 - i5-10GEN I 16GB I 256SSD I HDMI I 15,6" FHD I Cam I W11 I Gar
- Bomba ár! Dell Latitude 3590 - i5-8GEN I 8GB I 256SSD I HDMI I 15,6" FHD I Cam I W11 I Garancia!
- DUPLA XEON GOLD 6134!!! HP Z8 G4 LEGNAGYOBB WORKSTATION 64GB 2x8 mag 2x16 szál gamer, szerver, munka
Állásajánlatok
Cég: PC Trade Systems Kft.
Város: Szeged
Cég: Liszt Ferenc Zeneművészeti Egyetem
Város: Budapest