dynamische Parameterliste



  • Guten Abdend

    ich habe derzeit das Problem, dass ich nicht verstehe wie genau ich eine Funktion schreibe der beliebig viel Parameter mit Typ std::string übergeben werden kann und diese dann aufgerufen werden.
    Kann mir dies bitte jemand erläutern?

    PS: Ein voll funktionsfähiger Quellcode würde mir wahrscheinlich auch schon reichen.

    Mfg
    Machtl



  • Guck mal hier... Das dürfte ungefähr sein, was du suchst http://www.c-plusplus.net/forum/viewtopic-var-t-is-39350.html

    Felix



  • Phoemuex schrieb:

    Guck mal hier... Das dürfte ungefähr sein, was du suchst http://www.c-plusplus.net/forum/viewtopic-var-t-is-39350.html

    Finger weg von Ellipsen unter C++ (Thema Fehleranfälligkeit, Überbordwerfen von Typsicherheit...).

    Besser, übergabe eines Stringvectors (und den dann durchiterieren):

    void foo(std::vector<std::string> const & stringliste)
    


  • meine Funktion sieht nun folgendermaßen aus:

    int makeOption(std::string s, ...)
    {
    	vector<option*> myvec;
    	int n=0;//ich habe s in n geändert
    	va_list arglist;
    	va_start(arglist, n);
    	for (int i=0; i<n; i++)
    	{
    		myvec.push_back(new option(va_arg(arglist, std::string)));//Konstruktor verlangt einen Parameter mit Typ std::string
    	}
    	va_end(arglist);
    	myvec[0]->setinactive();// setinactive() ist eine Methode von option
    	return 0;
    }
    

    ich bekomme nun eine Fehlermeldung:
    "vector subscript out of range"

    Ich denke die Fehlermeldung heisst soviel dass myvec[0] nie belegt wird.
    Wenn ich "myvec[0]-->setinactive();" auskommentiere funktioniert die Funktion prächtig.

    Ich bin völlig ratlos. Kann mir jemand bitte erklären was

    va_list arglist;
    va_start(arglist, n);
    va_end(arglist);
    

    ist bzw. macht?

    Mfg
    Machtl



  • asc schrieb:

    Phoemuex schrieb:

    Guck mal hier... Das dürfte ungefähr sein, was du suchst http://www.c-plusplus.net/forum/viewtopic-var-t-is-39350.html

    Finger weg von Ellipsen unter C++ (Thema Fehleranfälligkeit, Überbordwerfen von Typsicherheit...).

    Besser, übergabe eines Stringvectors (und den dann durchiterieren):

    void foo(std::vector<std::string> const & stringliste)
    

    Aber hier muss ich mir zuerst den Vector vollmachen und dannkann ich ihn erst der Funktion übergeben? Ich möchte lieber der Funktion die Strings direkt übergeben können.



  • Machtl schrieb:

    Aber hier muss ich mir zuerst den Vector vollmachen und dannkann ich ihn erst der Funktion übergeben? Ich möchte lieber der Funktion die Strings direkt übergeben können.

    Wenn du weder Fehlerquellen in dein Programm haben willst, noch die Typsicherheit über Bord werfen möchtest, wirst du nicht drum herum kommen. Ansonsten leb mit den Fehlerquellen.

    Eine Möglichkeit Vektoren "schöner" zu füllen würdest du in der assign-Bibliothek von Boost finden.

    Gekürztes Boostbeispiel schrieb:

    Komplett hier

    #include <boost/assign/std/vector.hpp>
    #include <vector> 
    
    using namespace boost::assign;
    
    int main()
    {
        vector<int> values;  
        values += 1,2,3,4,5,6,7,8,9;
    }
    


  • Folgende Möglichkeiten:

    #include <cstdarg>
    
    void printStr1(char *str, ...)
    {
    	va_list arglist;
    	va_start(arglist, str);
    	char *val = NULL;
    
    	while ((val = va_arg(arglist, char *)) != NULL)
    	{
    		printf(val);
    	}
    
    	va_end(arglist); 
    }
    
    void printStr2(int count, ...)
    {
    	va_list arglist;
    	va_start(arglist, count);
    
    	for (int i = 0; i < count; ++i)
    	{
    		std::cout << va_arg(arglist, char *);
    	}
    
    	va_end(arglist); 
    }
    
    int main()
    {
    	printStr1("lol", "lol", "loL", NULL);
    	printStr2(2, "hey", "test");
    }
    

    Bei der ersten Variante muss der letzte Parameter NULL sein. Bei der zweiten Variante muss der erste Parameter, die Anzahl an Parameter angeben. Ein std::string kannst du in beiden Fällen nicht übergeben. Keine Typ-Sicherheit, etc. Selbst Variante 2 ist noch sehr fehleranfällig...



  • Hi,

    eine Idee wäre, sich den operator<<() (wie bei ostream) zum Vorbild nehmen.

    struct Collector {
       vector<string> v;
    };
    
    struct foo {};
    
    Collector& operator<<(Collector& col, string const& s) {
       col.push_back(s);
       return col;
    }
    
    void operator<<(foo& foo, Collector& col) {
       // ... hier mit allen col.v[i] machen, was man machen will
    }
    
    foo() << s1 << s2 << s3;
    

    ... nur mal so als Skizze. Ausprobieren kann ich das gerade nicht.

    Gruß,

    Simon2.



  • Blaaaaa schrieb:

    ...

    ...
    

    ...

    Also welchen Vorteil soll die Konstruktion gegenüber dem normalen ostream (also cout, cerr, ...) haben ?

    Gruß,

    Simon2.


  • Administrator

    Die folgende Möglichkeit gibt es auch noch, wenn ich mich recht entsinne:

    class Test
    {
    private:
      struct Proxy
      {
        Test* test;
        std::vector<std::string> container;
    
        Proxy(Test* test)
          : test(test)
        { }
    
        ~Proxy()
        { test->foo(*this); }
    
        Proxy& operator ,(std::string const& name)
        {
          container.push_back(name);
          return *this;
        }
      };
    
    private:
      void foo(Proxy& proxy)
      {
        // Zugriff auf proxy.container
      }
    
    public:
      Proxy bar()
      {
        return Proxy(this);
      }
    };
    
    // Verwendung:
    Test test;
    
    test.bar(), "Hello", "World", "!", " How", "are", "you!";
    

    So ähnlich macht es glaub ich auch Boost.Assign, oder? Kann das jemand bestätigen, dass dies auch geht? Bin eigentlich der Meinung, dass es geht, bin mir aber nicht mehr ganz sicher, ob es wirklich so war.

    Grüssli



  • Simon2 schrieb:

    Blaaaaa schrieb:

    ...

    ...
    

    ...

    Also welchen Vorteil soll die Konstruktion gegenüber dem normalen ostream (also cout, cerr, ...) haben ?

    Gruß,

    Simon2.

    War nur ein Beispiel, wie man mit va_xyz umgeht. Ich rate jedoch von der Verwendung dieser ab, da sie einfach eine sehr große Fehlerquelle darstellen.



  • Blaaaaa schrieb:

    ...
    War nur ein Beispiel, wie man mit va_xyz umgeht. Ich rate jedoch von der Verwendung dieser ab, da sie einfach eine sehr große Fehlerquelle darstellen.

    👍 💡
    OK - sorry, hatte ich nicht begriffen.

    Danke,

    Simon2.



  • asc schrieb:

    Machtl schrieb:

    Aber hier muss ich mir zuerst den Vector vollmachen und dannkann ich ihn erst der Funktion übergeben? Ich möchte lieber der Funktion die Strings direkt übergeben können.

    Wenn du weder Fehlerquellen in dein Programm haben willst, noch die Typsicherheit über Bord werfen möchtest, wirst du nicht drum herum kommen. Ansonsten leb mit den Fehlerquellen.

    Eine Möglichkeit Vektoren "schöner" zu füllen würdest du in der assign-Bibliothek von Boost finden.

    Gekürztes Boostbeispiel schrieb:

    Komplett hier

    #include <boost/assign/std/vector.hpp>
    #include <vector> 
    
    using namespace boost::assign;
    
    int main()
    {
        vector<int> values;  
        values += 1,2,3,4,5,6,7,8,9;
    }
    

    funktionier wunderbar. danke



  • Blaaaaa schrieb:

    Folgende Möglichkeiten:

    Bei der ersten Variante muss der letzte Parameter NULL sein. Bei der zweiten Variante muss der erste Parameter, die Anzahl an Parameter angeben. Ein std::string kannst du in beiden Fällen nicht übergeben. Keine Typ-Sicherheit, etc. Selbst Variante 2 ist noch sehr fehleranfällig...

    dem sollte man hinzufügen, dass ellipsen in diesem fall nicht nur die typ-sicherheit untergraben; viel einfacher: es ist schlicht undefiniertes verhalten, ein nicht-POD als argument zu übergeben.


Anmelden zum Antworten