[ Zevs85 @ 22.08.2009. 14:04 ] @
Pozdrav,

interesuje me koji je najbolji nacin resiti sledeci problem.
Imam klasu sa npr 3 atributa, ime, prezime, jmbg. Imam XML datoteku ciji elementi se isto tako zovu.
<korisnik>
<ime> Korsink </ime>
<prezime> Prezime </prezime>
<jmbg> 1234434 </jmbg>
</korisnik>

Sad mi treba najlaksi nacin da inicijalizujem atribute klase...
Uvek mogu napraviti nesto tipa
if (element == "ime") objekat.ime = ime
else if....

Ali mislim da to nije pravo resenje.

Jel postoji u C++ mogucnost da kazem da atribut sa "imenom" dobije "vrednost"... Ili tako nekako.. ??
[ Goran Arandjelovic @ 22.08.2009. 16:12 ] @
Je l' ti bilo kakvo menjanje izgenerisanih klasa odgovara ili njih nebi hteo da diraš?
[ Mihajlo Cvetanović @ 23.08.2009. 18:46 ] @
Ne vredi razmišljati mnogo o takvim stvarima. Ako imaš tri članice klase, onda je if-else-if najefikasnije rešenje, pod uslovom da se to if-else-if ne ponavlja na deset mesta. Možeš eventualno umesto tri članice da koristiš map<string, string>, ali slutim da nema nikakve potrebe za tim. Kad bi bilo stotinak članica klase onda bi imalo smisla razmišljati o nekom elegantnijem rešenju.
[ Zevs85 @ 24.08.2009. 07:55 ] @
Citat:
Goran Arandjelovic: Je l' ti bilo kakvo menjanje izgenerisanih klasa odgovara ili njih nebi hteo da diraš?

Mogu da menjam klase.
Citat:
Mihajlo Cvetanović: ...Kad bi bilo stotinak članica klase onda bi imalo smisla razmišljati o nekom elegantnijem rešenju.

E bas o tome se radi :)

Znam da u C#-u i Javi postoji mogucnost dobijanja informacija o atributima, metodama i tako to, dalo bi se uraditi... Ali za C++ bas ne znam da li je to moguce i kako...
[ Mihajlo Cvetanović @ 24.08.2009. 10:29 ] @
Prvo što mi pada na pamet je da umesto 100 članica klase imaš jednu članicu koja je map<string, string>.

Druga opcija nije za ljude sa pejsmejkerom:

Code:

#include <map>
#include <string>
using namespace std;

class Klasa {
public:
  string jedan, dva, tri;
  static map<string, string Klasa::*> mapa;
}

map<string, string Klasa::*> Klasa::mapa;

main() {
  // mapa je staticka, ova inicijalizacija se radi samo jednom
  Klasa::mapa["jedan"] = &Klasa::jedan;
  Klasa::mapa["dva"]   = &Klasa::dva;
  Klasa::mapa["tri"]   = &Klasa::tri;

  Klasa objekat;
  objekat.*Klasa::mapa["jedan"] = "vrednost jedan";
  objekat.*Klasa::mapa["dva"] = "druga vrednost";
  objekat.*Klasa::mapa["tri"] = "nesto trece";
}
[ Goran Arandjelovic @ 24.08.2009. 17:27 ] @
@Mihajlo
S tim sto nigde nije pomenuo da su svi atributi istog tipa, vec verovatno i ne moraju da budu.

--

Evo jedne ideje:

Code:

#include <string>
#include <iostream>
using namespace std;

class izgenerisana_klasa
{
  public:
    /* konstruktori/destruktor i ostale metode */
    template<typename T>
    void set_attr(string attr_name, T value)
    {
      if(attr_name == "jedan"){
        jedan = value;
      }else if(attr_name == "dva"){
        dva = value;
      }else if(attr_name == "tri"){
        tri = value;
      }else if(attr_name == "cetiri"){
        cetiri = value;
      }
    }
  private:
    int jedan;
    string dva;
    float tri;
    int cetiri;
};

int main()
{
  izgenerisana_klasa Test; // ako postoji default konstruktor
  
  Test.set_attr("jedan", 5);
  Test.set_attr("dva", "neka vrednost");
  Test.set_attr("cetiri, 15);
  Test.set_attr("tri", 5.14); // mada bi mogao da prodje i int

  // ili... umesto if(element == "ime")...

  Test.set_attr(element, val); // gde je val vrednost koja odgovara tom elementu

  return(0);
}


Ovo gore nisam testirao, ali bi trebalo da je ok. Naravno, imam i neku ideju za mnogo krace resenje (koje cak verovatno nebi ni zahtevalo izmenu generisanih klasa, ali je malo bolesno...)

Edit: @Mihajlo, na koju si drugu opciju mislio ako nije tajna?

[Ovu poruku je menjao Goran Arandjelovic dana 24.08.2009. u 19:12 GMT+1]
[ Mihajlo Cvetanović @ 25.08.2009. 09:25 ] @
Ni ja nisam testirao, ali cenim da ovo ne može da se kompajlira. Ako instanciraš set_attr sa T kao int onda će ti kompajler javiti grešku u liniji "dva = value;" da nema operator= koji prima int. Ako instanciraš sa T kao string onda je greška u liniji "jedan = value;" jer kompajler ne zna kako da konvertuje string u int.
[ 1jedini @ 25.08.2009. 11:59 ] @
Kombinuj explicitnu specijalizaciju sa resenjem koje je dao Mihajlo Cvetanović.
[ Goran Arandjelovic @ 25.08.2009. 15:34 ] @
@Mihajlo
Da, dobro si primetio. Greška. Ali evo sada TESTIRANOG rešenja koje koristi nešto slično statičkom IF-u:

Code:

#include <string>
#include <iostream>
using namespace std;

template<typename T, typename U>
struct SET_F
{
  static void set(T &arg1, U &arg2)
  {
  }
};

template<typename T>
struct SET_F<T, T>
{
  static void set(T &arg1, T &arg2)
  {
    arg1 = arg2;
  }
};

struct SET
{
  template<typename T, typename U>
  SET(T &attr, U &value)
  {
       SET_F<T, U>::set(attr, value);
  }
};

class izgenerisana_klasa
{
  public:
    /* konstruktori/destruktor i ostale metode */
    template<typename T>
    void set_attr(string attr_name, T value)
    {
      if(attr_name == "jedan"){
        SET i(jedan, value);
      }else if(attr_name == "dva"){
        SET i(dva, value);
      }else if(attr_name == "tri"){
        SET i(tri, value);
      }else if(attr_name == "cetiri"){
        SET i(cetiri, value);
      }
    }
  
  private:
    int jedan;
    string dva;
    float tri;
    int cetiri;
};

int main()
{
  izgenerisana_klasa Test; // ako postoji default konstruktor
  
  Test.set_attr("jedan", 5);
  Test.set_attr("dva", string("neka vrednost"));
  Test.set_attr("cetiri", 15);
  Test.set_attr("tri", 5.14f);

  // ili... umesto if(element == "ime")...

  // Test.set_attr(element, val); // gde je val vrednost koja odgovara tom elementu

  return(0);
}


Naravno, moguće je dodati potrebne specijalizacije da se dopusti float -> double, const char* -> string i slične stvari. Ovo radi za identične tipove.
[ Mihajlo Cvetanović @ 25.08.2009. 15:55 ] @
Mislim da je kod malo neefikasan, jer imaš template funkciju set_attr koja će se instancirati onoliko puta koliko ima različitih tipova, ali u svakoj of tih funkcija ispitivaće se i oni attr_name koji nisu datog tipa. Drugi detalj je što mi se čini da su input vrednosti za naš algoritam uvek stringovi, a algoritam je taj koji treba da uradi konverziju u odgovarajući tip na osnovu imena atributa. A možda ne treba da komplikujemo, možda je string jedini tip. Početni poster treba da kaže šta mu treba.

Nego da se vratimo početnom problemu. Šta ako imaš stotinu (ili hiljadu) atributa, a ne samo četiri? Da li i onda da koristimo if-else-if kobasicu?
[ Goran Arandjelovic @ 25.08.2009. 17:41 ] @
To što ima nepotrebnih ispitivanja, to bi moglo da se reši bez problema. Ako su input-i isključivo stringovi, onda ja nisam dobro razumeo, mislio sam da je input uređeni par (string ime, T vrednost), gde je T baš odgovarajući tip, a ne string.

Ali u pravu si, neka čovek kaže tačno kakav mu je input, nije baš bio najprecizniji tako da sam ja obradio generalan slučaj.
[ Eurora3D Team @ 26.08.2009. 00:41 ] @
Mozes ovako nesto ...
Broj podataka nije ogranicen ali jeste vrsta ... ali to moze lako da se prevazidje dodavajem funkcija itd.
Podatci se inicijalizuju prvim upisom,
Nisam mnogo razradjivao ali bi trebalo da je to to sto si trazio.
Code:

//
#include <iostream>
#include <string>
#include <vector>
using namespace std;

//prouci i
//#include <typeinfo>
// cout << "a is: " << typeid(a).name() << '\n';

//prva klasa
class DataClass
{
public:
    string value_name;

    DataClass():type(0){}
    inline bool SetValue(string s_value_name, int value){if(!type) {type = 1; value_name = s_value_name;} if(type == 1) {Int = value;return true;} return false;}
    inline bool SetValue(string s_value_name, float value){if(!type) {type = 2; value_name = s_value_name;} if(type == 2) {Float = value; return true;} return false;}
    inline bool SetValue(string s_value_name, string value){if(!type){type = 3; value_name = s_value_name;} if(type == 3) {String = value;return true;} return false;}
    inline bool GetValue(string s_value_name,int& i){if(type == 1) {i = Int;return true;} return false;}
    inline bool GetValue(string s_value_name,float& f){if(type == 2) {f = Float;return true;} return false;}
    inline bool GetValue(string s_value_name,string& s){if(type == 3) {s = String;return true;} return false;}

private:
    int type;
    int Int;
    float Float;
    string String;
};

typedef vector<DataClass*> DC; // definisanje vektora tipa DataClass

//druga klasa
class DataContainer
{
public:
    //set
    void SetValue(string value_name, int value)
    {
        bool n = false;
        DataClass* dc = GetData(value_name);
        if(!dc)
        {
            dc = new DataClass();
            n = true;
        }
        if(!dc)
            return;
        if(n && dc->SetValue(value_name,value))
            data_list.push_back(dc);
    }
    void SetValue(string value_name, float value)
    {
        bool n = false;
        DataClass* dc = GetData(value_name);
        if(!dc)
        {
            dc = new DataClass();
            n = true;
        }
        if(!dc)
            return;
        if(n && dc->SetValue(value_name,value))
            data_list.push_back(dc);
    }
    void SetValue(string value_name, string value)
    {
        bool n = false;
        DataClass* dc = GetData(value_name);
        if(!dc)
        {
            dc = new DataClass();
            n = true;
        }
        if(!dc)
            return;
        if(n && dc->SetValue(value_name,value))
            data_list.push_back(dc);
    }
    //get
    bool GetValue(string value_name, int& value)
    {
        DataClass* dc = GetData(value_name);
        if(!dc)
            return false;
        return dc->GetValue(value_name,value);
    }
    bool GetValue(string value_name, float& value)
    {
        DataClass* dc = GetData(value_name);
        if(!dc)
            return false;
        return dc->GetValue(value_name,value);
    }
    bool GetValue(string value_name, string& value)
    {
        DataClass* dc = GetData(value_name);
        if(!dc)
            return false;
        return dc->GetValue(value_name,value);
    }
    
private:
    DataClass* GetData(string value_name)
    {
        
        if(!data_list.empty())
        {
            DataClass* dc = 0;
            DC::iterator Iterator;
            for (Iterator = data_list.begin(); Iterator != data_list.end();Iterator++)
            {
                dc = *Iterator;
                if(dc->value_name == value_name)
                    return dc;
            }
        }
        return (DataClass*) 0;
    }

    DC data_list; // vektor tipa DataClass
};

DataContainer Memory;

int main () 
{
    int i1,i2;
    Memory.SetValue("int_jedan", 1);
    Memory.SetValue("int_dva", 2);

    Memory.GetValue("int_dva", i2);
    Memory.GetValue("int_jedan", i1);

    float f1=1.0f, f2=2.0f;

    Memory.SetValue("float_jedan", f1);
    Memory.SetValue("float_dva", f2);

    //zamenili smo mesta
    Memory.GetValue("float_jedan", f2);
    Memory.GetValue("float_dva", f1);

    string s1, s2 = "s2 string";

    Memory.SetValue("string_jedan", s2);
    Memory.SetValue("string_dva", "s1 string");

    Memory.GetValue("string_jedan", s2);
    Memory.GetValue("string_dva", s1);

    cout << "int_jedan " << i1 << endl;
    cout << "int_dva " << i2 << endl;

    cout << "float_jedan " << f1 << endl;
    cout << "float_dva " << f2 << endl;

    cout << "string_jedan " << s1 << endl;
    cout << "string_dva " << s2 << endl;
    
    return 0;
}


[Ovu poruku je menjao Eurora3D Team dana 26.08.2009. u 01:52 GMT+1]
[ Mihajlo Cvetanović @ 26.08.2009. 09:53 ] @
Ovde bi mogao da se ubaci i jedan union, greota da se rasipa memorija. Uzgred, if-else-if (teoretsku) kobasicu i direktan pristup članicama klase si zamenio iteracijom liste u for-if petlji. Taj deo koda je sad manji, ali je neefikasniji i što se tiče upisa i što se tiče čitanja. Kod upisa moramo da prođemo kroz celu listu da bismo shvatili da dati atribut ne postoji. Kod čitanja nemamo više direktan pristup nego opet moramo da iteriramo po listi.
[ Eurora3D Team @ 26.08.2009. 12:43 ] @
Da greota :) (ti si iz c++ kao i ja, ovi iz NETa bacaju MB a ne bytove) , ali koliko sam shvatio najvise ce da koristi string tako da se rasipa ~ (sizeof(int) + sizeof(float)) * broj podataka ...
A i nije neki sampion u brzini ... to mi je prvo palo na pamet.
U svakom slucaju resenje je fleksibilno i moze lako da ga sredi i neko ko nema mnogo iskustva , tako da kome treba eto mu neki pocetak
...
Da najvaznije
U ovu klasu moze da se lako ubaci 1000 raznih podataka na jednostavan nacin ... normalno mogu i da se procitaju


[Ovu poruku je menjao Eurora3D Team dana 26.08.2009. u 14:24 GMT+1]
[ Eurora3D Team @ 26.08.2009. 17:48 ] @
Da dodam , ovim nacinima kako ste vi probali nije moguce u klasu dodati hiljede atibuta kojima unapred ne znamo imena ... tako da je ovo trece resenje jedino koje moze da resi ovaj problem. Ja sam ogranicio ono sto znamo , tipove podataka.
Ako neko ima bolju ideju neka napise ... poz
[ kiklop74 @ 27.08.2009. 13:48 ] @
Meni se cini da bi koriscenje boost::any sa std::map bilo adekvatno resenje.

http://www.boost.org/doc/libs/1_39_0/doc/html/any.html