Toon posts:

[C++/Linux] Networking, hoe structs verzenden?

Pagina: 1
Acties:
  • 145 views sinds 30-01-2008
  • Reageer

Verwijderd

Topicstarter
Hoi,

Na het lange tijd succesvol (en angstvallig :P ) vermeden te hebben moet ik er toch weer eens aan geloven: Networking in C++.
Nu is het vrij eenvoudig om simpele strings te versturen en een eenvoudig chatprogsel heb je dan ook zo voor elkaar. Maar het lukt me dus niet om een hele struct met data te verzenden.

Heeft iemand hier enig idee hoe je dit aanpakt? Ik kan structs namelijk ook eerst wel uit elkaar slopen, de hele inhoud converteren naar string en dan versturen en het dan vervolgens bij de ontvanger weer in mekaar puzzelen, maar dat wil niemand. 8)7

Mvg,

plors

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 02-05 01:32
Doordat C/C++ (uit zichzelf) geen garantie geeft over manier waarop structs in elkaar zitten (alignment van velden, maar in sommige gevallen ook de volgorde van velden), kun je niet zomaar een struct over het netwerk versturen. Als je binary data wilt versturen, zul je de inhoud van je struct dus moeten converteren naar iets wat portable is.

Denk er daarbij ook aan dat verschillende platformen verschillende manieren van data alignment kennen. Twee veelgebruikte trucs zijn het converteren naar network order (big-endian, dacht ik?), waarvoor al standaardfuncties als htons, ntohs, htonl en ntohl beschikbaar zijn, of het taggen van berichten met de gebruikte codering/ordening (maar dan moet de ontvanger nog steeds de bytes kunnen herordenen).

Al met al zitten er aan het versturen van binary data dus nogal wat haken en ogen, wat het heel aantrekkelijk maakt om dit soort fratsen uit te besteden aan libraries die daar voor gemaakt zijn (CORBA, XML-RPC, etc). Een wat minder ingrijpende manier van uitbesteden is het loslaten van het binaire formaat en "plain-text" strings construeren, waarbij je (bijvoorbeeld) velden scheid door tab characters en aparte berichten op aparte regels zet. Je moet getallen en dergelijke dan van en naar een stringrepresentatie converteren (bijvoorbeeld met een stringstream of fprintf/fscanf).

Overigens valt er wel een beetje vals te spelen: als je weet dat je alleen maar op/voor platform X compileert dan kun je misschien wegkomen met het oversturen van een ruwe struct (desnoods met de allignment ervan via een compiler-specifieke directive vastgesteld). Bedenk dan echter wel dat je eigenlijk geen nette protocolspecificatie hebt opgezet.

[ Voor 15% gewijzigd door Soultaker op 19-10-2003 16:03 ]


Verwijderd

code:
1
2
3
4
5
6
7
8
9
10
struct Foo
{
.
.
.
char *m_pBar;
.
.
.
}l


Als je dit soort dingen over wilt gaan sturen moet je toch inderdaad een manier vinden om de struct te serializen (wat jij uit elkaar halen en converteren naar string noemt) om hem aan de andere kant weer de de-serializen. Zoals soultaker al zegt zijn daar goed bruikbare standaard bibliotheken voor, maar zelf een protocolletje daarvoor in elkaar draaien is ook niet zo moeilijk, maarja, het moet wel gebeuren :+ .

Verwijderd

Je kan er waarschijnlijk een SOAP library voor gebruiken.
SOAP is (ondere andere iig) een standaard voor het serializeren/deserializeren van complexe objecten naar en van XML.
Je zal alleen zelf even moeten zoeken naar een C++ SOAP library :)

Verwijderd

Topicstarter
Ok, hier kan ik weer iets mee. Ik denk dat ik zelf maar ff lekker ga hobbyen, is ook wel weer leuk :)

bedankt iig!

Verwijderd

Aangezien in C++ een struct niets anders is als een klasse waarbij standaard je members public zijn kun je ook operator << en >> implementeren zodat je best wel simpel je struct naar en van je (socket-)stream kunt sturen/halen.

Suc6.

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 02-05 01:32
Verwijderd schreef op 20 oktober 2003 @ 11:31:
Aangezien in C++ een struct niets anders is als een klasse waarbij standaard je members public zijn kun je ook operator << en >> implementeren zodat je best wel simpel je struct naar en van je (socket-)stream kunt sturen/halen.
Dat is op zich geen verkeerde gedachte, maar ik vind het toch geen ideale oplossing. De genoemde operators zijn namelijk al overloaded voor operaties op iostream-objecten (behalve als shifting operators, natuurlijk). Het mooiste zou zijn om ook je sockets te wrappen in iostream-objecten (want dat zijn het conceptueel gewoon) en dan de bestaande implementaties te gebruiken.

Het probleem hierbij is echter dat de bestaande implementatie van de operators niet symmetrisch is en (dus) ook niet geschikt is voor serilization. Voor de hand liggende code als:
C++:
1
2
3
4
input << 1 << 2 << 3;
//
int a, b, c;
output >> a >> b >> c;
werkt niet, omdat de uitvoer ("123") niet weer ingevoerd kan worden als 3 integers. Met de in- en uitvoer van strings met whitespace erin heb je soortgelijke problemen.

Als je dus de operators >> en << voor serialization wil gebruiken, dan moet je ze overloaden voor iets anders dan iostream-objecten, maar wat dan precies? File descriptors kan praktisch niet, want dat zijn integers en die worden al gebruikt voor de echte toepassing van de operators: shifting. (Een operator>>(int, int) bestaat dus al!) Overloaden voor FILE* objecten zou kunnen, maar is nogal C-achtig en ook weer lastig met sockets. De beste keuze is dan misschien een eigen wrapper-klasse te verzinnen voor je file descriptors en daar dan alle functies op te definiëren. Blijft het bezwaar over dat je >> en << operators afhankelijk van de context een van de drie verschillende betekenissen kunnen hebben; vrij verwarrend, als je het mij vraagt!

Als je dus seriailization wilt implementeren, zou ik aanraden dit gewoon met nieuwe functies te doen, en niet door een al veel gebruikte operator te overloaden. Voor meer overwegingen en suggesties is de C++ FAQ over Serialization wel nuttig.

[ Voor 15% gewijzigd door Soultaker op 20-10-2003 13:33 ]


  • curry684
  • Registratie: Juni 2000
  • Laatst online: 06-05 14:03

curry684

left part of the evil twins

De hamvraag is vooral of je een open systeem schrijft of dat je zelf de ontvangende en zendende kant schrijft, op een identiek OS met een identieke compiler. Het 2e geval maakt je leven wel extreem makkelijk namelijk :)

Professionele website nodig?


Verwijderd

Topicstarter
serialization is niet zo'n probleem, waar ik nu tegen aan loop te hikken is het volgende.
Stel ik heb een vector met daarin 2500 strings. Ik wil deze "overpompen" naar een andere PC, ik gebruik daarvoor de volgende code:
Server gedeelte:
code:
1
2
3
for(long i=0;i<werknemers.size();i++) {
  send( klantSocket, werknemers[i],strlen(werknemers[i]), 0 );
}

En het Client gedeelte:
code:
1
2
3
4
while ( werknemers.size() < 2500 ) {
  recv( Serversocket, bericht, 51, 0 ); 
  werknemers.push_back( bericht );
}


Flink in geknipt, maar het idee is duidelijk denk ik. Dit gaat zo niet echt werken natuurlijk, omdat de server de data in principe sneller verstuurd dan de client het verwerken kan. Dit is vrij eenvoudig op te lossen door de server steeds als hij een bericht verzonden heeft ff te laten "slapen" ( usleep(100) ), maar dit vertraagt de zaak enorm.
Daarna kreeg ik de volgende "briljante" inval: Waarom laat ik de client niet een berichtje sturen zodra een message binnen en verwerkt is? Pas als dat bericht binnen is op de server mag hij de volgende string versturen.
Ff voor de duidelijkheid de code:
Server gedeelte:
code:
1
2
3
4
for(long i=0;i<werknemers.size();i++) {
 send( klantSocket, werknemers[i],strlen(werknemers[i]), 0 );
 recv( klantSocket, status, 1, 0 );
}

En het Client gedeelte:
code:
1
2
3
4
5
while ( werknemers.size() < 2500 ) {
  recv( Serversocket, bericht, 51, 0 ); 
  werknemers.push_back( bericht );
  send( Serversocket, "1",1, 0 );
}


Werkt bijna prima, op een "kleinigheidje" na, zo eens in de 15 keer gaat het mis met de volgende melding op de client "broken pipe", het gaat dan fout bij de "send" opdracht.
Verder valt het me op dat deze fout vooral optreed als ik de vector groter maak, denk dan aan 250000 ipv 2500 elementen.

Pff, heel verhaal. Iemand enig idee?

[ Voor 4% gewijzigd door Verwijderd op 20-10-2003 21:56 ]


  • Soultaker
  • Registratie: September 2000
  • Laatst online: 02-05 01:32
Gaat het om TCP? Zo ja, dan is het (in principe) geen probleem om een heleboel gegevens achter elkaar over te sturen; je besturingssysteem zorgt er dan wel voor dat die gegevens gebufferd worden en vroeger of later verstuurd worden. Gaat het echter om UDP, dan is het de vraag waarom je geen TCP gebruikt, als je blijkbaar transmission control (en reliability, neem ik aan) wilt hebben.

Verwijderd

C++ network hell... gebruik maar wat middle ware.... SOAP is al genoemd....
als je het echt op een laag niveau wilt oplossen kan TCP/IP gebruiken ->dit raad ik je zeker niet aan... het wiel nog een keer uitvinden is niet erg verstandig lijkt me!

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 10:04
Verwijderd schreef op 20 October 2003 @ 19:44:
C++ network hell... gebruik maar wat middle ware.... SOAP is al genoemd....
als je het echt op een laag niveau wilt oplossen kan TCP/IP gebruiken ->dit raad ik je zeker niet aan... het wiel nog een keer uitvinden is niet erg verstandig lijkt me!
Mwoch, dat valt ook allemaal wel mee. En protocolletje bouwen bovenop TCP is toch niet zo hi-tec ?

Belangrijkste is dat je een mechanisme inbouwt dat ervoor zorgt dat je app niet in een endless loop terecht komt zodra iets niet overkomt, of binnenkomt. ( Bv door timeouts )

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


Verwijderd

nou... als jij het wiel nog een keer wilt uitvinden ga je gang.... ik zeg niet dat het moeilijk of onmogelijk is.... het is zeker mogelijk! Maar wat ik zeg is dat er betere oplossingen zijn.... het nadeel om alles zelf te doen is dat de onderhoudbaarheid van je code achter uit zal gaan en dat er meer code wordt gecodeerd

het nadeel van meer code is dat er meer fouten worden gemaakt die dan weer moeten worden gedebugged... wat weer tijd kost en uiteindelijke geld!

Het beschreven probleem is niet uniek! er bestaat dus al veel voor... dus als zo een tool/lib of wat dan ook gebruikt heb je een bewezen module in je programma wat niet hoeft worden gedebugged. wat weer een enorm voordeel is! dit is namelijk een van de begin principes van OOP

Verwijderd

Topicstarter
idd zal er gerust wel middleware zijn dat dit kan ( heeft trouwes iemand url's? ), maar nu ik zo close ben wil ik eigelijk dit wel gebruiken. Onderhoudbaarheid van de code maakt niet uit, zoals te zien zijn het nauwelijks 10 regels.
Waar het mij om gaat, wat veroorzaakt die broken pipe?? Kan ik dit simpelweg opvangen met wat exception handling of is er een andere oplossing?

Bedankt voor de reacties trouwes! :*)

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 10:04
[b][message=19045810,noline]Marik2000 schreef op 20 October 2003 een verhaal
Tuurlijk, hergebruiken is een goed iets, maar van zelf iets doen is nog nooit iemand dommer geworden.

Als het voor een hobbyprojectje is, zou ik gewoon wat zelf in elkaar knutselen. In een professionele omgeving zijn er weer andere maatstaven.

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 13:45

.oisyn

Moderator Devschuur®

Demotivational Speaker

waarom dan uberhaupt nog een programma maken? Dat wat plors wilt bestaat vast ook al. Bovendien, dit is nog altijd PW, niet hotscripts. Wij maken hier zelf spul ;)

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • EnEmA
  • Registratie: April 2000
  • Laatst online: 03-04-2025

EnEmA

Worst

heb het geheel niet zo heel aandachtig doorgelezen, maar volgens mij kan het simpel door een pointer naar de struct te maken en als lengte sizeof(betreffende struct) te gebruiken

Een koe is geen kangaroe


  • Exirion
  • Registratie: Februari 2000
  • Laatst online: 09:39

Exirion

Gadgetfetisjist

Verwijderd schreef op 20 October 2003 @ 21:01:
nou... als jij het wiel nog een keer wilt uitvinden ga je gang....
Nou, het lijkt me toch dat je bij een practicum networking een beetje ervaring met sockets op moet doen, denk je niet? Nou denken ze daar op de TUe bijvoorbeeld weer anders over aangezien ze daar meer wakker liggen van correctheidsbewijzen en formele modellen, maar toch... TCP/IP en UDP zijn toch wel het minste waar je aan moet knutselen wil je een beeeetje networking ervaring op doen bij een practicum.
ik zeg niet dat het moeilijk of onmogelijk is.... het is zeker mogelijk!
Sterker nog, het is helemaal niet zo moeilijk. Het is vervelender dat teveel mensen gewend zijn geworden aan allerlei standaard componentjes en middelware, en de basics uit het oog zijn verloren.

Sorry voor deze offtopic onderbreking maar het moest even gezegd worden ;)

"Logica brengt je van A naar B, verbeelding brengt je overal." - Albert Einstein


  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 09-04 22:08
EnEmA schreef op 22 oktober 2003 @ 10:48:
heb het geheel niet zo heel aandachtig doorgelezen, maar volgens mij kan het simpel door een pointer naar de struct te maken en als lengte sizeof(betreffende struct) te gebruiken
Lees het dan ook eerst. Wat jij voorstelt werkt matig tot niet (probeer 't maar eens als je een pointer in je struct hebt, of een std::string, of een willekeurige Image class, of... )

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


Verwijderd

Topicstarter
Heb het "broken pipe probleem" al opgelost. Serverside gaat er soms iets mis met het creeeren van de serversocket. Vreemd genoeg kan de client dan wel een connectie maken ( connect statement geeft netjes succes terug :? ) maar als de client daarna iets naar de server wil sturen gaat dat natuurlijk mis. Is eenvoudig opgelost door wat exception handling aan de serverside. Ik pomp nu duizenden structs ( serialized ) per seconde over de lijn. :*)
EnEmA schreef op 22 oktober 2003 @ 10:48:
heb het geheel niet zo heel aandachtig doorgelezen, maar volgens mij kan het simpel door een pointer naar de struct te maken en als lengte sizeof(betreffende struct) te gebruiken
Dit kan iig niet op de manier waarop ik het send commando nu gebruik. Dan kun je wel een adres zenden, maar daar heeft de ontvanger niets aan, omdat dat adres refereert aan geheugen op de zender. Maar ik ben me bewust van mijn onkunde op dit gebied, dus het zou heel goed wel mogelijk kunnen zijn. :) Zou je even willen uitleggen hoe je dit zou aanpakken?

  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 09-04 22:08
Als je al zoiets wil - waarschijnlijk niet - dan moet je googlen op gather-scatter.

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


Verwijderd

denk eraan dat per compiler het alignment kan verschillen. zegmaar, de sizeof() van dezelfde struct kan verschillen. voor borland & visual c++ kan je met de #pragma pack n directive je alignment instellen.

  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 09-04 22:08
Niet alleen alignment kan verschillen, zelfs de volgorde van velden. Daar helpt geen #pragma pack je mee.

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


  • farlane
  • Registratie: Maart 2000
  • Laatst online: 10:04
MSalters schreef op 22 October 2003 @ 23:31:
Niet alleen alignment kan verschillen, zelfs de volgorde van velden. Daar helpt geen #pragma pack je mee.
Is dat echt zo? Dat zou toch problemen opleveren als je een struct in een union hebt gepropt ?
Of is dit alleen bij C++ ( en niet C ) het geval omdat de structs daar ook objecten zijn ?

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 13:45

.oisyn

Moderator Devschuur®

Demotivational Speaker

Waarom geeft dat problemen? De union wordt gewoon het maximum van de sizeof van z'n members. De volgorde in de structs maakt daarvoor weinig uit

(ik moet er overigens wel bijzeggen dat ik nog nooit met een compiler heb gewerkt die de volgorde van members aanpast. Correctie, dat was er 1, een brakke compiler voor de ARM, en die hergroeppeerde verschillende bitfields zodat het minder geheugen kostte :))

[ Voor 48% gewijzigd door .oisyn op 23-10-2003 10:56 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Verwijderd

Dit kan iig niet op de manier waarop ik het send commando nu gebruik. Dan kun je wel een adres zenden, maar daar heeft de ontvanger niets aan, omdat dat adres refereert aan geheugen op de zender. Maar ik ben me bewust van mijn onkunde op dit gebied, dus het zou heel goed wel mogelijk kunnen zijn. :) Zou je even willen uitleggen hoe je dit zou aanpakken?
C:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
struct abc {
int a;
char b;
char c;
};

int main()
{
struct abc x;
char *ptr;

  ptr=(char*)&x;
  send(ptr,sizeof(x),.....);
  return 0;
}

Zoiets :? Dit zou het idee moeten geven.

[ Voor 3% gewijzigd door Verwijderd op 23-10-2003 12:02 . Reden: typo ]


Verwijderd

Misschien kan onderstaand voorbeeld je helpen. In dit geval wordt gebruik gemaakt van de default iostream (console) maar het lijkt me een koud kunstje om dit om te vormen naar bijv. een socket-stream.
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#include<iostream>
#include<string>
#include<sstream>

struct Test_S
{
  std::string name;
  std::string street;
  unsigned int street_number;
  std::string zip;
  std::string city;
  operator std::string();
  operator =(std::string right);
};

Test_S::operator =(std::string right)
{
  unsigned int i = right.find_first_of('\n');
  name = right.substr(0, i);
  right.erase(0, i+1);
  i = right.find_first_of('\n');
  street = right.substr(0, i);
  right.erase(0, i+1);
  i = right.find_first_of('\n');
  street_number = std::atoi(right.substr(0, i).c_str());
  right.erase(0, i+1);
  i = right.find_first_of('\n');
  zip = right.substr(0, i);
  right.erase(0, i+1);
  i = right.find_first_of('\n');
  city = right.substr(0, i);
  return true;
}

Test_S::operator std::string()
{
  std::stringstream s;
  s << name << '\n' << street << '\n' << street_number << '\n' << zip << '\n' << city << '\n';
  return s.str();
}

int main(int argc, char* argv[])
{
  Test_S test;
  std::stringstream sstr;
  char* s=new char[255];
  std::cout << "Enter name: ";
  std::cin.getline(s, 255);
  sstr << s << '\n';
  s=new char[255];
  std::cout << "Enter your streetname: ";
  std::cin.getline(s, 255);
  sstr << s << '\n';
  s=new char[255];
  std::cout << "Enter your streetnumber: ";
  std::cin.getline(s, 255);
  sstr << s << '\n';
  s=new char[255];
  std::cout << "Enter your zipcode: ";
  std::cin.getline(s, 255);
  sstr << s << '\n';
  s=new char[255];
  std::cout << "Enter your city: ";
  std::cin.getline(s, 255);
  sstr << s << '\n';
  test = sstr.str();
  std::cout << (std::string)test;
  std::cin.get();

  return 0;
}

Dit is een oud stukje code wat ik even heb opgeduikeld dus er zullen gerust wel zaken zijn die beter/makkelijker kunnen (denk aan de getline's), maar ach, het gaat om 't idee.

[ Voor 14% gewijzigd door Verwijderd op 23-10-2003 12:46 . Reden: Taalfoutje ]


Verwijderd

Verwijderd schreef op 23 oktober 2003 @ 12:00:
C:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
struct abc {
int a;
char b;
char c;
};

int main()
{
struct abc x;
char *ptr;

  ptr=(char*)&x;
  send(ptr,sizeof(x),.....);
  return 0;
}

Zoiets :? Dit zou het idee moeten geven.
Kweet niet hoor, maar als er een send(char*, unsigned int) implementatie is voor die send, zou die 'm dan niet afkappen op \0? Lijkt me dus beter om te casten naar void*; maar aan de andere kant, wat heeft die size/length dan nog voor nut... Lijkt me i.i.g. wel handig om te weten of de zaak niet wordt afgekapt.

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 02-05 01:32
Verwijderd schreef op 23 oktober 2003 @ 12:31:
Misschien kan onderstaand voorbeeld je helpen. In dit geval wordt gebruik gemaakt van de default iostream (console) maar het lijkt me een koud kunstje om dit om te vormen naar bijv. een socket-stream.
Uhm, laat me je op het bestaan twee revolutionaire concepten wijzen:
• C++ kent een delete-operator (en niet voor niets!);
• de STL kent een getline-functie om string-objecten mee uit te lezen.

Verwijderd

Soultaker schreef op 23 October 2003 @ 14:05:
Uhm, laat me je op het bestaan twee revolutionaire concepten wijzen:
• C++ kent een delete-operator (en niet voor niets!);
• de STL kent een getline-functie om string-objecten mee uit te lezen.
Rustig maar, weet ik, vandaar dat ik ook had neergezet:
Dit is een oud stukje code wat ik even heb opgeduikeld dus er zullen gerust wel zaken zijn die beter/makkelijker kunnen (denk aan de getline's), maar ach, het gaat om 't idee.
En ik had geen zin om de code te gaan proberen/wijzigen.

En van die delete statement is inderdaad slordig maar goed, ik gebruik zelf ook altijd een wrapper template class voor m'n structs/classes zodat die hele delete overbodig wordt, maar dat terzijde. Toch wil ik je graag de methode, zoals ik die zelf vaak gebruik, niet onthouden (het is voor het voorbeeld wel zwaar vereenvoudigd want normaal zit er ook nog reference counting etc in):
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include<iostream>
#include<string>

template <class T> class ptr
{
private:
  T* m_T_p;
public:
  ptr(T* right)
  {
    m_T_p = right;
  }
  ~ptr()
  {
    if(m_T_p)
      delete m_T_p;
  }
  T* operator->() const
  {
    return m_T_p;
  }
};

struct Test
{
  std::string name;
};

int main(int argc, char* argv[])
{
  ptr<Test> test_p(new Test);
  test_p->name = "Een naam";
  std::cout << test_p->name;
  return 0;
}

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 13:45

.oisyn

Moderator Devschuur®

Demotivational Speaker

C++:
1
2
3
4
  T* operator->() const 
  { 
    return m_T_p; 
  }
dat mag niet eens compilen natuurlijk wel stomme idiote sukkel! (heb het even tegen mezelf)

Verwijderd schreef op 23 oktober 2003 @ 12:50:
[...]

Kweet niet hoor, maar als er een send(char*, unsigned int) implementatie is voor die send, zou die 'm dan niet afkappen op \0? Lijkt me dus beter om te casten naar void*


Waar is de unsigned int dan voor? std::ostream heeft ook een write (const char *, size_t), die kapt 'm toch ook niet af?

[ Voor 75% gewijzigd door .oisyn op 23-10-2003 16:25 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • Soultaker
  • Registratie: September 2000
  • Laatst online: 02-05 01:32
Verwijderd schreef op 23 October 2003 @ 14:38:
Rustig maar, weet ik, vandaar dat ik ook had neergezet:
Mja, ik wil niet al te negatief doen, hoor, maar als het van geen kanten klopt, wat hebben we er dan aan? Het gaat juist om de concrete aanpak; het globale idee is de topic starter inmiddels wel bekend. Als je dan een concreet voorbeeld geeft neem dan (ook) de moeite om het voorbeeld netjes te maken zodat het een zinnige suggestie is voor de topic starter. Op deze manier voegt je code (wat mij betreft) niets toe.
En van die delete statement is inderdaad slordig maar goed, ik gebruik zelf ook altijd een wrapper template class voor m'n structs/classes zodat die hele delete overbodig wordt, maar dat terzijde.
Dat is een zogenaamde smart pointer; een simpele versie daarvan zit ook in de standard library (std::auto_ptr). Erg nuttig en interessant, maar niet het onderwerp van deze thread, lijkt me. Ik ben geen moderator dus misschien is het niet aan mij om er wat van te zeggen, maar ik zou het op prijs stellen als de reacties een beetje toegespitst zouden kunnen worden op het onderwerp van de discussie.

Verwijderd

.oisyn schreef op 23 oktober 2003 @ 15:37:
dat mag niet eens compilen
Nou, 't compileert met BCB 6 wel degelijk, maar goed, zoals soultaker in zijn post al zei is dit idee gebaseerd op een smartpointer, en dat is het ook precies!
Waar is de unsigned int dan voor? std::ostream heeft ook een write (const char *, size_t), die kapt 'm toch ook niet af?
Dat vroeg ik me toch ook al af? Maar dit kan toch net zo goed een max zijn (dus tot '\0' of tot de size_t).

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 13:45

.oisyn

Moderator Devschuur®

Demotivational Speaker

Verwijderd schreef op 23 oktober 2003 @ 15:55:
[...]

Nou, 't compileert met BCB 6 wel degelijk, maar goed, zoals soultaker in zijn post al zei is dit idee gebaseerd op een smartpointer, en dat is het ook precies!
dan is het een brakke compiler
Je methode is namelijk const, waardoor alle members (behalve die gedefinieerd zijn als mutable) ook const zijn. Je m_T_p is dus van het type const T * const, en dus kun je nooit een T * retourneren zonder daar een const_cast op te doen


Nevermind, het klopt wel. Vreemd, ik was altijd onder de indruk dat de data waarnaar een const object wijst ook meteen const was. Nu blijkt dat niet zo te zijn :? Vreemd... Oh well, weer wat geleerd
Dat vroeg ik me toch ook al af? Maar dit kan toch net zo goed een max zijn (dus tot '\0' of tot de size_t).


Net zo goed... staat dat ergens? Heeft iemand gezegd dat dat zo is? Je kunt er dus ook geen aannames over doen imho :)

[ Voor 14% gewijzigd door .oisyn op 23-10-2003 16:16 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Verwijderd

Soultaker schreef op 23 October 2003 @ 15:40:
Mja, ik wil niet al te negatief doen, hoor, maar als het van geen kanten klopt, wat hebben we er dan aan?
Ik heb het stukje code dan maar even gecompileerd en gerunt en werkt gewoon hoor. Dat die oude code niet zo netjes is, wil nog niet zeggen dat het van geen kant klopt.
En daarbij ging het idee meer om het serializen middels de operators.
Het gaat juist om de concrete aanpak
Inderdaad, daarom deed ik de suggestie; ik ga ervan uit dat de topic starter zelf wel zo slim is om goede C++ code te schrijven.
het globale idee is de topic starter inmiddels wel bekend.
Oh, dus is het nutteloos om nog een suggestie aan te dragen!? Ik probeer alleen te helpen hoor!
Als je dan een concreet voorbeeld geeft neem dan (ook) de moeite om het voorbeeld netjes te maken zodat het een zinnige suggestie is voor de topic starter.
Ik heb simpelweg niet alle tijd van de wereld, maar ben best bereid om een suggestie aan te dragen. De TS lijkt me best in staat om de code te doorgronden, eventueel te gebruiken danwel naast zich neer te leggen.
Op deze manier voegt je code (wat mij betreft) niets toe.
Alhoewel jij er niets aan gehad hebt wil nog niet zeggen dat anderen er niets mee konden, toch?
Dat is een zogenaamde smart pointer; een simpele versie daarvan zit ook in de standard library (std::auto_ptr).
Inderdaad.
Erg nuttig en interessant, maar niet het onderwerp van deze thread, lijkt me.
Inderdaad offtopic, maar ik antwoorde op jou post hoor.
Ik ben geen moderator dus misschien is het niet aan mij om er wat van te zeggen, maar ik zou het op prijs stellen als de reacties een beetje toegespitst zouden kunnen worden op het onderwerp van de discussie.
Dit komt behoorlijk flamerig op mij over want de hele toon van je post lijkt me een tikje overdreven.
Ik probeer de topic starter inhoudelijk te helpen doormiddel van een stukje code, welke hij misschien kan gebruiken. Zelf gaf je in een post het volgende aan:
Een wat minder ingrijpende manier van uitbesteden is het loslaten van het binaire formaat en "plain-text" strings construeren, waarbij je (bijvoorbeeld) velden scheid door tab characters en aparte berichten op aparte regels zet.
Het stukje code wat ik gaf doet (bijna) precies wat jij hierboven beschrijft; alleen begin je te ziften over een delete waarvan de TS (hopelijk) allang het nut weet. Als dat jou concrete bijdrage is aan de TS vraag ik me af wie hier nou off-topic bezig is.

Verwijderd

.oisyn schreef op 23 October 2003 @ 15:59:
dan is het een brakke compiler
Ga ik niet tegen in, ik vind BCB sowieso een brakke omgeving, maar dat terzijde.
Je methode is namelijk const, waardoor alle members (behalve die gedefinieerd zijn als mutable) ook const zijn.
Dit vertelt dus dat ik geen mutaties mag doen op de members. Returnen is een ander verhaal.
Je m_T_p is dus van het type const T * const, en dus kun je nooit een T * retourneren zonder daar een const_cast op te doen
Waarschijnlijk voert de compiler een const_cast uit, hoewel ik geen enkele warning kreeg.
Net zo goed... staat dat ergens? Heeft iemand gezegd dat dat zo is? Je kunt er dus ook geen aannames over doen imho :)
Er wordt gecast naar een char*. Vaak wordt '\0' als einde-token beschouwd bij dit type. Daarom zei ik ook dat het misschien verstandiger is om naar void* te casten.

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 13:45

.oisyn

Moderator Devschuur®

Demotivational Speaker

Ik zat er compleet naast, ik had mijn post al geedit. Dus als je het niet erg vind ga ik me nu ergens heel diep zitten schamen, omdat ik dus van dit toch vrij belangrijke concept al die tijd heb gedacht dat het anders in elkaar zat |:(

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • Soultaker
  • Registratie: September 2000
  • Laatst online: 02-05 01:32
.oisyn schreef op 23 October 2003 @ 15:59:
Nevermind, het klopt wel. Vreemd, ik was altijd onder de indruk dat de data waarnaar een const object wijst ook meteen const was. Nu blijkt dat niet zo te zijn :? Vreemd... Oh well, weer wat geleerd
Dat klopt wel, maar m_T_p wordt gewoon by value geretourneerd. Natuurlijk mag je een const member wel gewoon kopiëren en een kopie retourneren! Vergelijk het met:
code:
1
2
3
4
5
6
7
8
struct Foo
{
    int member;
    int getMember() const
    {
         return member;
    }     
}

Als je het geheugen waar de member naar verwijst wil beschermen, had je 'm maar als "const T*" moeten declareren in plaats van "T*" en dan had je inderdaad een const-cast moeten doen (neem ik aan).

[ Voor 15% gewijzigd door Soultaker op 23-10-2003 16:27 ]


Verwijderd

.oisyn schreef op 23 October 2003 @ 16:22:
Ik zat er compleet naast, ik had mijn post al geedit. Dus als je het niet erg vind ga ik me nu ergens heel diep zitten schamen, omdat ik dus van dit toch vrij belangrijke concept al die tijd heb gedacht dat het anders in elkaar zat |:(
Ach, zoals je al zei, weer wat geleerd en zo houd je elkaar scherp.

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 10:04
Verwijderd schreef op 23 oktober 2003 @ 16:19:
Er wordt gecast naar een char*. Vaak wordt '\0' als einde-token beschouwd bij dit type. Daarom zei ik ook dat het misschien verstandiger is om naar void* te casten.
char * wordt evengoed gebruikt voor bytearray pointers.

Zoals je ongetwijfeld zult weten kan bijna elk datatype ( naast strings, maar ook BLOB'S ) naar een bytearray worden geconverteerd.

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 13:45

.oisyn

Moderator Devschuur®

Demotivational Speaker

Soultaker: ja dat snap ik wel, maar ik dacht dus dat de data waarnaar verwezen wordt ook const was. Als je van die member een pointer maakt, mag je dus wel *member=10; doen, en ik was altijd in de veronderstelling dat dat niet mocht. Oftewel, je int * wordt een int * const , en niet een const int * const

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 13:45

.oisyn

Moderator Devschuur®

Demotivational Speaker

wil ik wel nog even hierop reageren :)

[nohtml]
Verwijderd schreef op 23 October 2003 @ 16:19:
Waarschijnlijk voert de compiler een const_cast uit, hoewel ik geen enkele warning kreeg.
dat mag nooit impliciet, dus als je compiler het al zou doen zou ie iig een warning moeten geven, maar eigenlijk dus gewoon een error

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Verwijderd

farlane schreef op 23 oktober 2003 @ 16:27:
char * wordt evengoed gebruikt voor bytearray pointers.

Zoals je ongetwijfeld zult weten kan bijna elk datatype ( naast strings, maar ook BLOB'S ) naar een bytearray worden geconverteerd.
Jep, als ik er zo over nadenk maakt het ook geen ene moer uit want je geeft alleen het adres van het eerste char mee en de lengte, dus of je nu void* of char* doet zal weinig bommen.
Ik vroeg het me alleen af.

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 02-05 01:32
.oisyn schreef op 23 oktober 2003 @ 16:27:
Soultaker: ja dat snap ik wel, maar ik dacht dus dat de data waarnaar verwezen wordt ook const was. Als je van die member een pointer maakt, mag je dus wel *member=10; doen, en ik was altijd in de veronderstelling dat dat niet mocht. Oftewel, je int * wordt een int * const , en niet een const int * const
Hmm, dat is net zoiets als dat je een const pointer zou hebben en dat je dan niet naar z'n data zou mogen schrijven; dit kan ook gewoon:
C++:
1
2
3
typedef int *foo_t;
const foo_t foo = new int[1000];  // foo is const
foo[123] = 1; // legaal!

Maar goed, C++ is altijd lekker ingewikkeld dus dan krijg je dat soort vergissingen.

Verwijderd

.oisyn schreef op 23 oktober 2003 @ 16:30:
wil ik wel nog even hierop reageren :)
dat mag nooit impliciet, dus als je compiler het al zou doen zou ie iig een warning moeten geven, maar eigenlijk dus gewoon een error
Is al duidelijk, compiler voert geen const_cast uit, vandaar ook geen warning/error. Ik ging meer uit van jou gelijk t.o.v. const member functions.

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 13:45

.oisyn

Moderator Devschuur®

Demotivational Speaker

Soultaker schreef op 23 October 2003 @ 16:30:
[...]

Hmm, dat is net zoiets als dat je een const pointer zou hebben en dat je dan niet naar z'n data zou mogen schrijven; dit kan ook gewoon:
C++:
1
2
3
typedef int *foo_t;
const foo_t foo = new int[1000];  // foo is const
foo[123] = 1; // legaal!

Maar goed, C++ is altijd lekker ingewikkeld dus dan krijg je dat soort vergissingen.


ja maar dan is het een int * const, en geen const int * const ;)
Ja klopt, ik legde de klemtoon verkeerd in mijn vorige post. Ik bedoelde dus te zeggen dat je, naast dat je je eigen members niet mag wijzigen, ook de data waarnaar die members wijzen niet mag wijzigen.

Een voorbeeld: als je een linked list zou hebben, waarbij elke node naar de volgende wijst, en de eerste is const, dacht ik dus dat de rest van die lijst dus ook gelijk helemaal const was. Moet je je eens voorstellen dat ik dus al jaren dingen heb zitten maken met dit idee voor ogen |:(

[ Voor 6% gewijzigd door .oisyn op 23-10-2003 16:37 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • farlane
  • Registratie: Maart 2000
  • Laatst online: 10:04
Verwijderd schreef op 23 oktober 2003 @ 16:30:
[...]

Jep, als ik er zo over nadenk maakt het ook geen ene moer uit want je geeft alleen het adres van het eerste char mee en de lengte, dus of je nu void* of char* doet zal weinig bommen.
Ik vroeg het me alleen af.
Psies. :)

Het voordeel is alleen dat je niet terug hoeft te casten naar char *, aangezien je met void pointers niet mag rekenen.

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 13:45

.oisyn

Moderator Devschuur®

Demotivational Speaker

En het voordeel tov void * is weer dat je alle data mee kunt sturen, aangezien alles impliciet naar void * kan, maar dat is iets voor de ontwerper van de functie natuurlijk ;) Aan de andere kant is C++ er weer veel minder op gemaakt dat je gewoon byte copies kunt doen, dus dat is dan ook wel meer een C iets, alwaar PODs hoogtij vieren ;)

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • farlane
  • Registratie: Maart 2000
  • Laatst online: 10:04
.oisyn schreef op 23 oktober 2003 @ 16:42:
En het voordeel tov void * is weer dat je alle data mee kunt sturen, aangezien alles impliciet naar void * kan, maar dat is iets voor de ontwerper van de functie natuurlijk ;) Aan de andere kant is C++ er weer veel minder op gemaakt dat je gewoon byte copies kunt doen, dus dat is dan ook wel meer een C iets, alwaar PODs hoogtij vieren ;)
Mwach, uiteindelijk kom je bij dit soort dingen ( communicatie ) altijd uit op bytes die via een send / receive over een lijntje worden gestuurd.
Dus ook al heb je een ongelooflijk geweldige classesctructuur, als je iets over het lijntje wilt sturen zal het naar een bytearray moeten kunnen worden geconverteerd.

Ik kan me zo geen datatype voorstellen dat niet naar een char * kan worden geconverteerd, wat je wel over dat lijntje zou willen versturen.

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 13:45

.oisyn

Moderator Devschuur®

Demotivational Speaker

nou, bijvoorbeeld:

C++:
1
2
3
4
5
6
struct Node
{
    Node * next;
    int someData;
    double someMoreData;
};


;)

maar het ging me erom dat als je dit prototype hebt:
C++:
1
void send (const char * data, int len);


Dat dit niet werkt:
C++:
1
2
int i = 3;
send (&i, sizeof (i));


omdat hij een const char * verwacht, en dus moet je expliciet casten. Zou je van die send functie een const void * maken, dan hoef je dus zelf niet meer expliciet te casten, wat gewoon prettiger werkt :)

[ Voor 3% gewijzigd door .oisyn op 23-10-2003 16:54 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Verwijderd

.oisyn schreef op 23 October 2003 @ 16:54:
nou, bijvoorbeeld:
C++:
1
2
3
4
5
6
struct Node
{
    Node * next;
    int someData;
    double someMoreData;
};


;)
Zelf probeerde ik het net met:
C++:
1
2
3
4
struct Node
{
  std::string bla;
}

Gaat dus ook niet werken met versturen (ja, je kopieert naar alle waarschijnlijkheid een dword pointer).
Het rechtstreeks kopieeren van een struct gaat dus alleen goed als je gebruik maakt van base types a la bool, int, double, long, char, char[] etc.

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 02-05 01:32
.oisyn schreef op 23 October 2003 @ 16:42:
En het voordeel tov void * is weer dat je alle data mee kunt sturen, aangezien alles impliciet naar void * kan, maar dat is iets voor de ontwerper van de functie natuurlijk.
Anderzijds mis je dan weer het (twijfelachtige) voordeel dat je types met een impliciete conversie naar const char * (zoals bijvoorbeeld string classes - maar dan weer niet std::string - of exception classes) zonder casts kunt meegeven.

edit:
Oops! :o Voor conversie naar const void * blijkt een impliciete conversie naar const char * ook in aanmerking te komen... die ballon ging dus niet op!

edit2:
Verwijderd schreef op 23 October 2003 @ 17:12:
Zelf probeerde ik het net met:
C++:
1
2
3
4
struct Node
{
  std::string bla;
}
Volgens mij had .oisyn het nog/weer over constness van die members; maar wat betreft het oversturen van die structs heb je gelijk (maar dat was al gezegd).

[ Voor 40% gewijzigd door Soultaker op 23-10-2003 17:41 ]


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 13:45

.oisyn

Moderator Devschuur®

Demotivational Speaker

Nee het ging niet meer om de constness, farlane zei:
Ik kan me zo geen datatype voorstellen dat niet naar een char * kan worden geconverteerd, wat je wel over dat lijntje zou willen versturen.
dus ik gaf een voorbeeld met een pointer ;) Maar idd, hvdberg had al een dergelijk voorbeeld, niet met een pointer, maar wel met een datatype dat je gewoon niet byte voor byte kunt kopieren

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • farlane
  • Registratie: Maart 2000
  • Laatst online: 10:04
Dat je een datatype die pointers als members heeft niet _direkt_ kunt gebruiken aan de andere kant mag duidelijk zijn, maar daar doelde ik niet op.

Het ging meer over die const void * als parameter. Die zult je altijd terug moeten casten naar een char *, omdat je er anders niet mee mag rekenen.

.oisyn heeft natuurlijk wel gelijk dat het bij het aanroepen een stuk cleaner is, omdat je em daar niet hoeft te casten.

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 09-04 22:08
Verwijderd schreef op 23 October 2003 @ 14:38:
...
En van die delete statement is inderdaad slordig maar goed, ik gebruik zelf ook altijd een wrapper template class voor m'n structs/classes zodat die hele delete overbodig wordt, maar dat terzijde. Toch wil ik je graag de methode, zoals ik die zelf vaak gebruik, niet onthouden (het is voor het voorbeeld wel zwaar vereenvoudigd want normaal zit er ook nog reference counting etc in):
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include<iostream>
#include<string>

template <class T> class ptr
{
private:
  T* m_T_p;
public:
  ptr(T* right)
  {
    m_T_p = right;
  }
  ~ptr()
  {
    if(m_T_p)
      delete m_T_p;
  }
  T* operator->() const
  {
    return m_T_p;
  }
};
't Is wel versimpeld tot het punt waarop het niet fatsoenlijk meer werkt. Omdat je geen copy constructor declareert, krijg je er een. Die kopieert de pointer. Als de eerste kopie wordt gedelete, dan gaat alles goed. Bij de destructor van de tweede kopie is m_TP nog steeds != 0, dus wrodt er voor de tweede keer een delete gedaan ->Boem.

Ook de assignment operator klapt op zn'n manier, alleen verliest die z'n originele object ook nog eens.

De check if(p) voor delete p; is volledig overbodig, in alle situaties. delete 0 (en free(0) in C, sinds 1978 ) zijn no-ops.

Er zijn nog tig andere subtiliteiten, maar 99% daarvan zijn onbelangrijk als je een smart pointer van www.boost.org download. De laaste 1% staat in de manual.

[ Voor 3% gewijzigd door MSalters op 24-10-2003 00:34 ]

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


Verwijderd

MSalters schreef op 24 October 2003 @ 00:33:
't Is wel versimpeld tot het punt waarop het niet fatsoenlijk meer werkt. Omdat je geen copy constructor declareert, krijg je er een. Die kopieert de pointer. Als de eerste kopie wordt gedelete, dan gaat alles goed. Bij de destructor van de tweede kopie is m_TP nog steeds != 0, dus wrodt er voor de tweede keer een delete gedaan ->Boem.

Ook de assignment operator klapt op zn'n manier, alleen verliest die z'n originele object ook nog eens.
De hele smartpointer discussie was sowieso al offtopic en mijns inziens al afgerond. Wat heb je daadwerkelijk al voor de topicstarter uitgezocht zodat die verder kan?
De check if(p) voor delete p; is volledig overbodig, in alle situaties. delete 0 (en free(0) in C, sinds 1978 ) zijn no-ops.
Hoewel overbodig wel netter aangezien er al behoorlijk wat instructies (heb even geteld: 16) worden uitgevoerd alvorens de test en jz wordt uitgevoerd. De extra check vooraf is slechts 2 instructie's extra (test en jz) en zal bij bijv. een boomstructuur behoorlijk wat tijdswinst opleveren (mits deze natuurlijk NULL-pointers bevat).
Pagina: 1