[MEGOLDVA] Class-ok dinamikus tömbben

Fórumok

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ások

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.

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


#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

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

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

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.

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

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