Sziasztok,
Egy beagyazott linuxos board-ra kell fejlesztenem, ezert csak az STL-t szeretnem hasznalni. Egy listenert kell megvalositanom (Sajnos ennel jobban nem tudtam kodkent megjeleniteni).
class A;
typedef std::function<void(const A *, char)> Callback;
class A {
public:
A(){}
~A(){}
void addListener(int, Callback callback);
void removeListener(int, Callback callback);
private:
std::map<int, std::vector<Callback>> mCallbacks;
}
void A::notify(char ch) {
auto it = mCallbacks.find(mState);
if(it==mCallbacks.end())
return;
vector<Callback> scb = it->second;
if (scb.empty())
return;
for(unsigned int i=0;i<scb.size();i++)
scb[i](this,ch);
}
void A::addListener(int state, Callback callback)
{
auto it = mCallbacks.find(state);
if (it == mCallbacks.end()) {
vector<Callback> cb=it->second;
cb.push_back(callback);
mCallbacks.insert(map<int,vector<Callback>>::value_type(state, cb));
return;
}
vector<Callback> scb = it->second;
scb.push_back(callback);
}
void A::removeListener(int state, Callback callback)
{
auto it = mCallbacks.find(state);
if (it == mCallbacks.end())
return;
vector<Callback> scb = it->second;
if (scb.empty())
return;
// scb.erase(callback); <--------- Nem talal illeszkedo fuggvenyt
}
Az scb.erase-re uzengeti, hogy
no matching function for call to ‘std::vector<std::function<void(const A*, char)>>::erase(Callback&)’
Kellene irnom egy template fggv.-t, vagy belekeveredtem?
koszi,
Zamek
- 9272 megtekintés
Hozzászólások
A vector.erase() egy iteratort vár paraméterként, nem elemet.
- A hozzászóláshoz be kell jelentkezni
Milyen platform?
Vigyazz, mert ha nincs virtualis memoria a cuccon, akkor a memoriad toredezni fog, es le fog lassulni a program egy ido utan.
Beagyazott rendszereken csak ovatosan hasznalunk dinamikus memoriat.
- A hozzászóláshoz be kell jelentkezni
"Linuxos", valszeg a kollega emlitett wandboard lehet az. Szoval szerintem nem ez a szuk keresztmetszet. Ettol fuggetlenul (pont a beagyazott jelleg miatt) inkabb egyszerubben oldanam meg, mint hardcore c++. De lehet hogy annyira komplex a problema hogy igy lesz a legeffektivebb...
- A hozzászóláshoz be kell jelentkezni
azert a vectort helyett valami listat hasznalnek inkabb, ha random ki kell belole venni dolgokat
--
NetBSD - Simplicity is prerequisite for reliability
- A hozzászóláshoz be kell jelentkezni
Ez egy listener lesz soros port-hoz, vagyis amikor erkezik egy keret, akkor a kulonbozo allapotokhoz kivulrol lehet listener-eket rendelni. Tehat neha beszurnak egyet, de legtobbszor olvasni kell.
- A hozzászóláshoz be kell jelentkezni
Igen wandboard-ra keszul amin a 3.1x kernel fut, ugyhogy viszonylag kenyelmes, csak azt nem tudom, hogy merjek-e boost-ot hasznalni
- A hozzászóláshoz be kell jelentkezni
Igen, tudtommal Cortex A9-ben van virtualis memoria. Szoval lehet allokalgatni. :)
- A hozzászóláshoz be kell jelentkezni
azert egy jol megirt stl allokator okosabb, mint amit a legtobb ember viszonylag rovid ido alatt kepes megirni
--
NetBSD - Simplicity is prerequisite for reliability
- A hozzászóláshoz be kell jelentkezni
Pontosan.
Megirtam C-ben is, de ki szeretnem probalni C++-ban is, mivel sokkal egyszerubb! Marmint nem a nyelv, hanem az STL szolgaltatasai miatt. A nyelv inkabb sokkal bonyolultabb:)
- A hozzászóláshoz be kell jelentkezni
Jol megirt stl allokatort ertek "ovatos dinamikus memoria hasznalat" alatt.
A preallokacio mellett, persze. De ez kenyes topic, a legtobb arcbol heves vitat valt ki, tapasztalat es C++ szakertelem fuggvenyeben.
- A hozzászóláshoz be kell jelentkezni
std::remove ?
szerk:
pontositva: scb.remove(cb)
--
NetBSD - Simplicity is prerequisite for reliability
- A hozzászóláshoz be kell jelentkezni
Mindig az a baja, hogy nem talal == operatort.
A scb egy vector, amelyben callback fuggvenyek cimei vannak.
no matching function for call to ‘std::vector >::erase(Callback&)’
Ha jol ertelmezem, az a baja, hogy a vectorban std::function-ok vannak, es a Callback-re referenciat akarok kerestetni vele.
- A hozzászóláshoz be kell jelentkezni
Udv,
en egy ilyen kategoriaju vason (ha valoban a wandboardrol van szo) nem felnek Qt-t hasznalni (persze lehetnek mas elvarasok ami ezt itt megneheziti). A libQtCore.so.4 ARM-re forditva, strippelve 2.7M, a QextSerialPort statikus libje 61K, ebben vegulis benne van minden ami ide kell (signal-slot mechanizmus, filekezeles, adatszerkezetek, sorosport kezeles) ennyit szerintem ki lehet birni, es meguszod az STL-es mokolast.
Disclaimer:
Magaban a problemaban nem merultem el, csak a partszelrol okoskodok.
- A hozzászóláshoz be kell jelentkezni
Na ennek nekifutok...
- A hozzászóláshoz be kell jelentkezni
callback-nek kell legyen == operatora, kulonben nem tudja megallapitani melyiket kell kitorolni a containerbol
--
NetBSD - Simplicity is prerequisite for reliability
- A hozzászóláshoz be kell jelentkezni
En a fuggvenypointereket messzirol kerulnem.
Felejtsd el, ha van ra lehetoseg.
Inkabb csinalj egy ososztalyt, ami egy abstract class, es van egy pure virtual callback() methodusa. Az abstract class-ok id-it, vagy pointereit rakd a map-ba.
A fuggvenypointer helyett talaltak ki a virtualis fuggvenyt.
- A hozzászóláshoz be kell jelentkezni
pont igy csinalta!
--
NetBSD - Simplicity is prerequisite for reliability
- A hozzászóláshoz be kell jelentkezni
vector scb = it->second;
if (scb.empty())
return;
Na, ez a kod igen verzik. Tudod mit csinal, ha pl. scb 10000 elemu? :)
const vector &scb = it->second;
if (scb.empty())
return;
Ez igy mar jo.
- A hozzászóláshoz be kell jelentkezni
Igaz, ezt elfigyelmetlenkedtem...
- A hozzászóláshoz be kell jelentkezni
Ugyanigy itt:
vector cb=it->second;
cb.push_back(callback);
Hazi feladat: megszamolni hanyszor masolodik a vector.
Ez nem Java, hanem C++, itt tudni kell, hogy mikor csinal az ember deep copy-t es mikor shallow copy-t.
- A hozzászóláshoz be kell jelentkezni
Szamolom... :)
- A hozzászóláshoz be kell jelentkezni
1. Az addlistener-ben it->second -ot írsz,pedig elotte kiderült, hogy
it == mCallbacks.end()
ez crash, azonnal javítani kell.
2. mCallbacks.insert(map<int,vector<Callback>>::value_type(state, cb));
helyett
mCallbacks[state] = cb;
Hát nem sokkal egyszerubb?
3. Nagyon eroforráspazarló, hogy vector<Callback> másolódik ide-oda.
Használj referenciát! Pl az addlistener-ben
vector<Callback> scb = it->second;
itt másolatot készítesz az egész tömbrol, ehelyett
vector<Callback>& scb = it->second;
ez csak 1 pointert vesz ki, teljesítményben ég és föld különbség
Emiatt az addlistener ráadásul teljesen hibás is, feltétlen javítandó:
a lemásolt vector-ba beleteszel egy elemet, aztán az egész vektort eldobod, nem kerül vissza a map-be!
Ez a válasz a kérdésedre is. Nincs illeszkedo függvény, mert soha nem is regisztráltál be egyet sem.
4. removelistener-ben is lemásolod az egész tömböt. Ehelyett:
vector<Callback>& scb = it->second;
5. A függvényeidban érték szerint adod át a Callback-et.
Ez mindig egy objektum létrehozást, lemásolást jelent, akár költséges is lehet.
Ehelyett referenciaként add át, az garantáltan olcsó. Tehát ehelyett:
void addListener(int, Callback callback);
használd ezt:
void addListener(int, Callback& callback);
6. removelistener-ben:
scb.erase(callback);
Ez így vektorra soha nem muködhet, az csak iterátort átvéve képes törölni a mutatott elemet.
(Ahogy mások is mondták, az azonban vektornál költséges muvelet.)
Itt vektort használva nincs más lehetoséged, mint végigmenni a vektoron, megkeresni a callback-et és ha megvan, az erase-zel kitörölni:
for(vector<Callback>::iterator it=scb.begin(); it!=scb.end(); ++it) { const Callback& cb = *it; if( cb == callback ) { scb.erase(it); break; } }
7. Használj multimap-et a map helyett! Pont a te esetedre van kitalálva. Vektoros szenvedés megspórolva.
- A hozzászóláshoz be kell jelentkezni
Koszi a mely elemzest, ezt elfigyelmetlenkedtem...
- A hozzászóláshoz be kell jelentkezni
A tobbiek ramutattak 1-2 hibara (pl. sulyos baki hozzaferes soran kimasolni a callback tombot), de szerintem pont a lenyeg maradt ki:
Sajnos itt egy kicsit zsakutcaban vagy, az std::function<> template-nek nincs ==/!= operatora. Illetve van neki, de csak nullptr_t-hez lehet hasonlitani (ami azt jelenti, hogy meg tudod nezni, hogy az illeto peldany meghivhato-e vagy nem). Ha jobban belegondolsz, akkor ez igy is van rendjen, mert eleg nehez elkepzelni olyan muveletet, ami ket std::function objektumot hasonlitana ossze (az std::function-ban tarolt callable object lehet lambda, functor, fgv pointer, member fgv pointer ....).
Azokon a bakikon tulmenoen, amit a tobbiek feszegettek, szerintem ez a huzosabb koncepcionalis hiba. Te azzal a feltetelezessel elsz, hogy a fuggveny objektum azonosithato (azaz megallapithato, hogy azonos-e egy masik objektummal), igy jo lehet handle-nek, sajnos nem ez a helyzet a fenti okok miatt.
Csak hogy ne csak a pofamat jarassam, hanem valami javaslattal is eljek:
1: a Callback-eket tarolhatod igy:
private:
typedef std::multimap<int, Callback> callbacks_type;
public:
typedef callbacks_type::iterator callback_handle;
2: modositani kellene az addListener es removeListener fgv-t:
callback_handle A::addListener(int state, Callback callback);
void A::removeListener(callback_handle handle);
Magyarazat:
- az iterator amit a Callback reprezentalasara hasznalunk az mar osszehasonlithato/keresheto/eltavolithato. Mindezek tetejebe meg olcso is masolni, mig a Callback objektum masolasa pokolian koltseges is lehet (fuggoen attol, hogy tartalmaz-e mar kotott argumentumokat). Marpedig egy handle-t altalaban ide-oda passzolgat az ember, szoval jo ezt a koltseget alacsonyan tartani.
- szukseges, hogy az iterator amit visszaadsz, ne valjon invalidda Callback-ek beszurasa es a kivetele soran. Ezt a multimap teljesiti.
+1: Mivel a Callback-et ertek szerint passzolod be (ami itt a helyes megkozelites!!!), ezert erdemes move-ot hasznalni a megfelelo helyen...
tehat felteve, hogy a fenti typedef-et hasznalod, igy nezne ki az addListener fgv:
callback_handle A::addListener(int state, Callback callback) {
return mCallbacks.insert(std::make_pair(state, std::move(callback));
}
akkor mar raadaskent a ket masik fuggveny is:
void A::removeListener(callback_handle handle) {
mCallbacks.erase(handle);
}
void A::notify(char ch) {
auto r = mCallbacks.equal_range(mState);
for(auto it = r.first; it != r.second; ++it) {
it->second(this, ch);
}
}
Remelem nem vetettem hibat, probald ki! Ja es persze vegig feltettem, hogy c++11-et hasznalsz (ez mondjuk valszeg igaz az std::function miatt). Jo hir, hogy a fuggvenyek eleg egyszeruek, es a removeListener-nek mar nem kell state parameter.
- A hozzászóláshoz be kell jelentkezni
hello,
Koszi, ez nagyon elegans! :)
csak ide kellett meg egy zarojel:
return mCallbacks.insert(std::make_pair(state, std::move(callback)));
Megyek tesztet irni:)
koszi +1*
- A hozzászóláshoz be kell jelentkezni
Szerintem a fv pointerket hanyagolni kene, erre vannak a virtualis fv-ek.
- A hozzászóláshoz be kell jelentkezni
Nincsen benne fv pointer, std::function-t használ.
- A hozzászóláshoz be kell jelentkezni
Erre vannak a virtualis fuggvenyek a Java-ban. Konkretan c++-ban az std::function egy igen jo megoldas ezekben az esetekben.
- Mi van ha van egy legacy osztalyod, amit nem modosithatsz, de van olyan fuggvenye tudna fogadni a megfelelo callback-et.
pl.
struct foo {
void feed(A*, char);
};
Ne lehessen listener, csak mert nem ugy hivjak a fuggvenyt? Az std::function<>-ra epulo megoldassal ennyit kell tenni:
A a;
foo bar;
a.addListener(some_state, std::mem_fn(&foo::feed, bar));
Persze mondhatod, hogy ezen lehetne segiteni:
struct foo_adapter : public foo, public ilistener {
void fire(A* a, char ch) {
feed(a, ch);
}
/// es meg itt lesz kod boven
};
ott kezdodik a dolog, hogy ezt az adaptert jol megirni elegge hosszadalmas is lehet. A c++11-ben vannak feature-ok, amik segitenek (pl. konstruktor delegalas, de mi van, ha gcc-4.7 elotti forditod van?), de azert meg lehet porgetni a szopogepet, ha nekiallsz... Meg oszinten, egy egesz osztaly csak azert mert mashogy hivnak egy fuggvenyt.
de mi van, ha:
- foo-nak privat konstruktorai vannak (mert o egy singleton)?, akkor ez nem is mukodik
- foo destruktora nemvirtualis: ugy kell hasznalni az adaptert, mint a himes tojast, mert eleg csak egyszer foo-ra mutato pointeren keresztul torolni es maris csunya dolgok jonnek.
Masik pelda:
A a;
a->addListener(some_state, [](A* a, ch) { std::clog << a << " incoming char: " << ch << std::endl; });
es maris generaltunk egy aranyos egysoros tracert a-hoz, ami minden inputot kuld a logba...
Egyebkent kellene egy uj osztalyt szarmaztatnod az ilistener-bol, azt peldanyositani (a konstruktorban regisztralni, a destruktorban torolni a regisztraciot), a peldanyt valahol tarolni....
2. Tegyuk fel, hogy:
struct foo : public ilistener {
};
struct bar : public ilistener {
};
struct baz : public foo, public bar {
};
Na maris osszehoztunk egy jo kis "dreaded-diamond"-ot. Lehet ezt mukodokepesse tenni, de nem konnyu jol csinalni, vannak spec esetei... Mondjuk ugy, hogy en sok mindennel megprobalkoznek, csak hogy NE kelljen igy csinalni... ;)
3. virtualis fgv eseten legalabb protected-nek kell lennie callback fuggvenyeknek, az std::function<>-alapu megoldasnal siman lehet privat is fuggveny, ami fogadja a callback-et.
4. Ebben a konkret esetben hogy csinalod meg virtualis fuggvenyekkel, hogy egy objektumbol tobb regisztraciot is vegzel mas es mas allapothoz)? Leginkabb sehogy...
Es hidd el vannak meg tovabbi aprosagok is, amik az std::function<>-fele megoldast eleg vonzova teszik az ilyen esetekre.... A virtualis fuggveny koncepciojaval az a gond, hogy nem csak a szignaturat koti meg, hanem a nevet, a lathatosagi osztalyt es az ososztalyt is (amikre pedig semmi szukseg). Komoly lyukakat losz a az informaciorejtesbe es a kod ujrafelhasznalhatosagba, azaz a c++ legfontosabb alapelvei kozul kettobe is.
Ha csak annyi az elvaras, hogy ez-meg-az az erkezo parameterlista, akkor miert akarjuk megmondani, hogy mi legyen a fuggveny neve, hogy taggfuggveny legyen egy osztalybol(es hogy szarmazzon egy masik osztalybol) es legalabb protected legyen? (mert ezt mindet kikotod a virtualis fuggvennyel!!!).
Javaslat az implementaciohoz:
Erdemes lehet torolni a callback szignaturabol az A* argumentumot, mert ugy sokkal flexibilisebb lesz a design. Ha a listenernekszuksege van ra, akkor bind-dal hozzapakolja a Callback objektumhoz a hivas helyen.
class A {
// ez az egyszerubb szignatura
typedef std::function Callback;
}
struct listeners {
listeners(bool trace = false) :
h1(a.registerCallback(some_state1, std::bind(&listeners::handler1, this, std::placeholders::_1)), // ez a handler nem kapja meg az A* ptr
h2(a.registerCallback(some_state2, std::bind(&listeners::handler2, this, &a, std::placeholders::_1)), // ez a handler megkapja az A*-ot
h3(trace ? a.registerCallback(some_state2, std::bind(&std::ostream::put, std::clog, std::placeholders::_1)) : A::callback_handle()) // ez meg egy alap opcionalis logger ;)
{
}
private:
void handler1(char ch) {}
void handler2(A*, char ch) {}
A a;
A::callback_handle h1;
A::callback_handle h2;
A::callback_handle h3;
}
No ez volt vagy 15 sor, ezt valaki megcsinalja virtualis fuggvenyekkel, az eleg ugyes...
- A hozzászóláshoz be kell jelentkezni
like
--
NetBSD - Simplicity is prerequisite for reliability
- A hozzászóláshoz be kell jelentkezni
+1
"If you must mount the gallows, give a jest to the crowd, a coin to the hangman, and make the drop with a smile on your lips" The Wheel of Time series
- A hozzászóláshoz be kell jelentkezni
Sziasztok!
Bocs, hogy ide rakom! Lenne egy láma kérdésem.
http://www.cplusplus.com/reference/functional/mem_fun/?kw=mem_fun
Ezen az oldalon a dinamikusan kreált string-eket nem kellene felszabadítani?
Vagy csak helytakarékosság? :)
p00t
- A hozzászóláshoz be kell jelentkezni
A cplusplus.com-on fellelheto peldak altalaban nem nyujtanak komplett megoldast valos problemakra, hanem segitseget nyujtanak az "elmelet" megertesehez. Ennek megfeleloen kell oket kezelni. Egyebkent a kerdesedre a valgrind is megadta volna a trivialis valaszt ;)
- A hozzászóláshoz be kell jelentkezni
Köszi!
A valgrindet sajna nem tudom használni. A DUMA-t akartam alkalmazni, de nem sikerült feléleszteni. :)
szerk.: Király ez a valgrind!
p00t
- A hozzászóláshoz be kell jelentkezni
Elég gáz, hogy memleak-es példa van egy oldalon, amit kezdők olvasnak elsősorban...
A valgrind meg nem ad minden kérdésre választ, és ha ad sem biztos, hogy jót.
Az ember előbb gondolkodik, és csak aztán használ valgrindet bugkeresésre. Nem fordítva.
"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee." -- Ted Ts'o
- A hozzászóláshoz be kell jelentkezni
Szerintem nem az. A mem_fun hasznalatara mutat peldat, es nem azt taglalja, hogyan irj tokeletes programot. Hibakezeles sincs benne, feltunt? ;) Egy jo pelda tomor es lenyegretoro.
Mondtam, hogy a valasz trivialis. De ha megsem az, akkor eloszor jobb ha az ember "megkerdez" egy tool-t, mielott egy forumon erdeklodik. Az elsobol ugyanis tobbet lehet tanulni. Es ez lenne a cel, gondolom.
- A hozzászóláshoz be kell jelentkezni
Az oldal írói nem értenek egyet. Írtam nekik tegnap, hogy hiányzik a felszabadítás, és mára ott van.
Abban igazad van, hogy nincs minden hibalehetőség lekezelve (mondjuk a potenciális bad_alloc-on kívül nem látok mást), és valahol meg kell húzni a határt, de ebben az esetben 2 plusz sorról beszélünk, ami nem rontja az olvashatóságot, sőt.
Egyébként én azt a határt a mindig rossz, és a hiba esetén rossz között húznám meg ha már nem lehet tökéletes a példa, nem pedig a mindig rossz fölött...
"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee." -- Ted Ts'o
- A hozzászóláshoz be kell jelentkezni
En ertekelem a szorgalmadat, tenyleg. De irhatnal nekik megint, hogy mivel az oldalt alapvetoen kezdok latogatjak, talan jobb lenne, ha nem mutatnak be egy 15 soros peldan belul, hogyan kell ketfelekeppen egy vector-on vegignyargalni, mikozben semmi mast nem akarnak, mint a mem_fun hasznalatat szemleltetni ;)
- A hozzászóláshoz be kell jelentkezni
írj nekik ha zavar. Ott a link az oldalon, még az email kliensedig sem kell elfáradnod...
"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee." -- Ted Ts'o
- A hozzászóláshoz be kell jelentkezni
Engem az elozo verzio sem zavart ;)
- A hozzászóláshoz be kell jelentkezni
"mondjuk a potenciális bad_alloc-on kívül nem látok mást"
amire imho szinten leakelne :)
--
NetBSD - Simplicity is prerequisite for reliability
- A hozzászóláshoz be kell jelentkezni
De, fel kéne. Vagy a vektorba std::unique_ptr<std::string> std::string* helyett...
(Mondjuk ha van unique_ptr akkor az c++11, tehát mem_fn kell (a mem_fun deprecated).)
"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee." -- Ted Ts'o
- A hozzászóláshoz be kell jelentkezni
Ez jó kérdés. Azt gondolnám, mivel amúgy is befejeződik a program futása, az erőforrások automatikusan felszabadulnak.
Hogy ez kinek bántja a szemét, az más kérdés. Az enyémet nem annyira, mert nem okoz valós problémát, legfeljebb akkor, ha valaki ész nélkül nekiáll bemásolni valahova ezt a kódot. De pl Singleton-t sem szokás felszabadítani.
- A hozzászóláshoz be kell jelentkezni
szerintem igenis azkell, hogy minden lefoglalt byte fel van szabadítva:
- holnap átírják a Singletont. Ha nincs ott explicite kiírva egy kód, akkor azt nem refaktorálják: memóriaszivárgás lesz.
- az automatikus memória felszabadítás ellenőrzők működése így lehetetlenné válik, ráadásul egy singleton miatt esetleg egy csomó más member objektum sem lesz megfelelően elpusztítva.
- A hozzászóláshoz be kell jelentkezni
Ha a GoF fele singleton-t hasznalod, akkor valoban felszabaditatlan lesz kilepesnel a memoria. Ezen javithatsz annyit, hogy a letrehozott objektumot auto_ptr-ben vagy unique_ptr-ben tarolod, attol fuggoen, hogy mi erheto el. Mondjuk szalbiztos ettol meg nem lesz.
Amennyiben a Meyers-fele singletont hasznalod es a letrehozott objektum egy fuggvenyre lokalis statikus valtozoban tarolodik, akkor nem kell smart pointer. Ha a regi szabvany szerint megy a forditod, akkor ugyan szalbiztos nem leszel, de az uj szabvany mar kotelezoen eloirja, hogy a statikusok inicializalas szalbiztosan kell, hogy tortenjen.
Szerintem erdemes arra menni, hogy leak-mentes legyen a programod. Egyfelol, mert egy objektum nem csak memoriat (azaz process kilepessel visszakapott) eroforrast kezelhet. Masfelol meg, ha a valgrind-ban van 346 leak reportod, akkor a 347. valoban problemas bug mar nem fog szemet szurni....
Tehat jelen esetben, ha csak annyi a teendo, hogy a Meyers fele singleton-t hasznalod (ami szerintem meg elegansabb is), es megsporolsz egy leak-et magadnak, akkor egyertelmuen azt kell valasztani.
- A hozzászóláshoz be kell jelentkezni