[MEGOLDVA] Class-ok dinamikus tömbben

 ( arrabonus | 2008. március 4., kedd - 13:50 )

Sziasztok!

C++ class-okat szeretnék elhelyezni egy dinamikusan lefoglalt memóriaterületen tömbszerűen, egymás után. (Az eredeti környezetben shmget()-tel lefoglalt shared memory, de a példaprogramba malloc-ot írtam az egyszerűség kedvéért.)

A kérdés röviden: Hogyan kell a kostruktort meghívni?

sgyori@ubuntu:~/cppteszt$ cat teszt.cc
#include <malloc.h>

class A {
  public: A();
          ~A();
};

A::A() {
};

A::~A() {
}



class A *p, *p1;


main() {
 p=(A*)malloc(10*sizeof(A));
 p1=p;
 for (int i=0; i<10; i++) {
   p1->A();
   p1++;
 };
};
sgyori@ubuntu:~/cppteszt$ cc -o teszt teszt.cc
teszt.cc: In function ‘int main()’:
teszt.cc:23: error: invalid use of ‘class A’

Hozzászólás megjelenítési lehetőségek

A választott hozzászólás megjelenítési mód a „Beállítás” gombbal rögzíthető.

class A *p, *p1;

helyett

A *p, *p1;

Köszi, ettől még a probléma megmaradt.

A p1->A(); utasítást nem hajlandó lefordítani :-(

a ciklus belsejébe pedig:
pl=new A();

Természetesen a pl->A(); helyett

Épp a new hívást akarom elkerülni, mert az új memóriaterületet foglal a heapen, én pedig mindenképpen az előre lefoglalt "shared" területen akarom elhelyezni az objektumaimat, hogy más prcesszekből is elérhetők legyenek.

placement new:

for (int i=0; i<10; i++) {
new (p+i) A;
};

Ez a placement new ígéretesnek tűnik, de gnu c++ 4.1.3 nem hajlandó lefordítani.

A hibaüzenet:

teszt.cc: In function ‘int main()’:
teszt.cc:22: error: no matching function for call to ‘operator new(unsigned int, A*)’
<built-in>:0: note: candidates are: void* operator new(unsigned int)

Mindenesetre a tippet köszönöm, a new utasításnak ezt a formáját nem ismertem.

#include

azaz include memory

Köszi, közben megtaláltam én is.
Egy #include <new> megoldotta és jónak is tűnik.

Még egyszer köszönöm mindenkinek, aki segített!

Probald be#include-olni a <memory>-t :-)

Szerk.: ARGH.

Miért is ne csesztem volna el?
pl=new A(); már csak azért is szar volt, mert a pl mutató értéke változott.

(*p1)=A(); -ezt már remélem nem csesztem el

Ez lefordul ugyan, de mindig ugyanaz az objektumpéldány inicializálódik...

Azért köszi, de úgy tűnik a "placement new"-t kerestem.

/o\
Lefoglalsz egy memóriaterületet, az odáig oké, de mi van benne? És csak úgy meghívod a konstruktort direktben?

Úgy szeretnék objektumpéldányokat létrehozni, hogy ezek adatterületei az előzőekben lefoglalt memóriaterületre kerüljenek.

#include <new>
#include <iostream>

using namespace std;

class	TC
{
public:
	TC( int i );
	~TC();
private:
	int	m_i;
};

TC::TC( int i ) :
m_i( i )
{
	cout << "constructor called with " << i << endl;
}

TC::~TC()
{
	cout << "destructor called, m_i is " << m_i << endl;
}

int main( int argc, char **argv )
{
	void	*mem=malloc( sizeof( TC ) );

	::new( mem ) TC( 42 );

	TC	*typedptr( (TC*) mem );

	typedptr->~TC();

	free( mem );

	return 0;
}

egyeb kerdes?

Ha már placement new, akkor inkább:

int main() 
{
  A* p=(A*)malloc(10*sizeof(A));
  new(p) A[10];
...

Csak ne felejtsd el free előtt lefuttatni a destruktorokat...

...
  for (int j=0; j<10; ++j) 
    p[j]->~A();
	
  free (p);
}

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

A helyzet ennél bonyolultabb, mert több különböző (~70) osztály objektumait kellene egy közös területen tárolni, csak a példa kedvéért egyszerűsítettem a programot.

Amugy ez mind szep es jo, meg meg lehet eroszakolni a c++-t es ganyolni nagyokat (c alapu nyelvek nagy elonye) de meg lehetne ezt oldani szebben is: Pl irsz egy allocatort az std::vector-hoz. (amugy amikor kezdtem c++ -t hasznalni C utan, en is igy oldottam meg egy sajat container osztalyban a dolgokat, de akkor meg gozom nem volt rola mi is az az stl...)

Ezzel van egy kis gond. Mármint az allokátorral.
Gondolom azt látod magad előtt, hogy definiálsz egy allokátrot, majd egy ezt használó vektort, az egyik processz push_back-kel, és a cucc megjelenik a másik oldalon.
Ehhez viszont az kéne, hogy mikor processz_1 push_back-et nyom, elérje a processz_2 vektorjának size adattagját, tehát már a vektornak is shared memory-ban kéne lenni...
És ekkor már majdnem ott vagy ahol eddig.
Jobban jársz, ha saját osztályt írsz a dologra...

Arról nem beszélve, hogy ez osztályokra nem is működik rendesen, lásd lent:

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

És én még egy átlagos programozó szintjét sem érem el, csak egy egyszerű mérnök vagyok. Van valakinek egy negyvenötöse??? :-)

Upsz!

A "lásd lent" erre vonatkozott. Semmi beképzelt "hülyékvagytoktiehhez" sugallat nem volt tervbe véve. :)
Csak az a hozzászólás később készült mint a fenti...

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

De ez miert ganyolas? Egy kategoriaval altalanosabb megoldas, mint az allocator. Raadasul ha feluldefinialod a new operatort, akkor az stl (azon resze, ami nincs elore leforditva, tehat a templatek) is a te new-odat fogja hasznalni. Es ez jo. :-)

azert ganyolas mert ha elkerulheto hogy sajat kodot mirjunk meglevo, stabil, agyontesztelt konyvtari kodok (pl stl) felhasznalasa helyett, akkor el kell kerulni. jelen esetben ha stl-t hasznalsz es egy allocatort irsz hozza akkor egy viszonylag egyszeru allocatort kell hibatlanul megirnod. ha nem, akkor egy egesz containert.

hozzateszem nem tudom mi a tenyleges feladat, mert azt a kerdezo nem arulta el, de valoszinu valamit tervezesi szinten rontott el, ha komplett classokat akar processek kozott mozgatni. hogy ez miert nem mukodhet, elottem mar elmondtak paran. azt viszont tarom hogy amennyiben adatokat akar egy specialis memoriateruletre elhelyezni, arra sokkal biztonsagosabb megoldas valamelyik stl container hasznalata sajat allocatorral.

Ez egy sok év alatt többször továbbfejelszt(get)ett rendszer. Jelen állapotában egy szerver és több kliens processz fut, amelyek ugyanazokat az adatokat használják. Az érintett adatok lényegében statikusak, postgresből betöltött többszázezer rekordot tartalmaznak amelyeknek minden processzből elérhetőnek kell lenniük. Indulás után a szerver és a kliens processzek is betöltik ezeket az adatokat és a saját memóriaterületükön tárolják. (Egy jellemző adat: az összes objektum összes adatának elképzelés szerinti tárolására ~35MB osztott memória szükséges.) Arra gondoltam, hogy sok memóriát és a kliensek indulásakor kb 10 másodperc gépidőt spórolhatnék meg, ha a szerver processz betöltés után egy megosztott memóriaterületre töltené az adatokat és a kliens processzek adatbetöltés nélkül egyszerűen hozzákapcsolódnának a megosztott memóriához.

es ehez miert akarsz osztalyokat elhelyezni a shared memoryban? csinalsz valami fejlecet az adatok ele, ami megmondja mi mekkora offszetre van a memoria kezdetetol es feltoltod az adatokat.
nem vadaszunk termonuklearis ballisztikus raketaval nadi poszatara...

Van ~70 class, mindegyik tartalmaz néhány adatmezőt és néhány függvényt. Nem címezhetem át egyenként mindegyiket. Csinálhatnék mindegyikhez egy struktúrát, és az osztály maga csak egy pointert tartalmazna a struktúra kezdőcímére, de ehhez az összes függvényt módosítani kéne. Tovább tartana sörétes puskát reszelni, mint ballisztikus rakétát gyártani.

Amit felvetettél az nem fog működni!
Nevezetesen: tetszőleges osztály berakása a shared memory-ba.

Miért?
Mert ha az osztályod tartalmaz akár egyetlen virtuális függvényt is, akkor minden objektum tartalmaz egy mutatót a vtable-jére.
Ha két processzed van, akkor az elsőben létrehozott objektumok az első processz saját mem területén található vtable-re hivatkoznak, tehát amint használni akarod a másik proszesszből az objektumot SEGFAULT-ot kapsz.

http://gcc.gnu.org/bugzilla/show_bug.cgi?id=21251

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

Ez elgondolkodtatott... Arra felkészültem, hogy az osztályok nem tartalmazhatnak pointereket, mert azok az egyes processzek címterében mást jelentenének, de utána kell néznem, hogy vannak-e virtuális függvények az érintett osztályokban :-((

Jah, es ezen nem is igazan lehet segiteni

Szerencsére sikerült megoldanom, most van egy kis időm, leírom vázlatosan...

A linker -M kapcsolóját használva látható volt, hogy a "vtable" területeket a ".rodata" nevű szekcióba helyezi el, mégpedig a parancssorban felsorolt ".o" object file-ok sorrendjében, a szekció elejétől kezdve. Mivel ez a sorrend mindkét program linkelésekor egyforma volt, csak azt kellett biztosítani, hogy a ".rodata" mindkét programban azonos címen kezdődjön és máris mindkét programban érvényesek lettek az egyes objetumpéldányok "vtable" pointerei. A linker "--section-start" kapcsolójával megadtam a kívánt kezdőcímet és remekül futnak a programok. (Igaz a linker scriptet módosítani kellet, mert kevésnek találta program "header"-ek számát.)

Mindenkinek köszönöm a segítséget!

Jajj!!!

Isten irgalmazzon annak, aki ezt a forrást egyszer átveszi tőled...
Isten irgalmazzon annak, akinek ezt egyszer portolnia kell más oprendszerre, más fordítóra, vagy csak egyszerűen a fordítóprogram következő verziójára.
(Mindenképpen dokumentáld, akkor is, ha sose nyúl hozzá más rajtad kívül. 5 év múlva nem lesz ilyen egyértelmű. :) )

Legalább megérte? (Gyorsabb lett?)

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

Bocs, de nem követtem tovább a threadet. Természetesen gyorsabb lett, kb 3 nagyságrenddel :-)))

~15sec helyett ~50msec