Forum: PC-Programmierung C++ generische Parameterliste


von Philip K. (philip_k)


Lesenswert?

Hallo,

ich habe da eine Problemstellung, bei der ich für ein paar Anregungen 
dankbar wäre.

Ich habe mehrere eigenständige SW-Module, die dafür gedacht sind, sowohl 
von der Komandozeile, als auch mit einem GUI-Frontend aufgerufen zu 
werden. Als Schnittstelle dient dabei eine Funktion, der ich die 
Paramter (entweder die kopierten CL-Parameter, oder eben von der GUI 
zusammengestellt) als vector<string> übergebe.

Jetzt hat zwar jedes Modul diese einheitlich definierte Schnittstelle, 
aber in der Regel unterschiedliche Parameter.
Damit ich bei Hinzufügen/Ändern eines Moduls nicht jedes Mal die 
Aufrufumgebung anpassen muss, ist mein Plan, dass jedes Modul über eine 
weitere Funktion seine Parameter bekannt machen kann. Diese Funktion 
soll eine Liste mit Descriptor-Objekten der möglichen Parameter liefen.
Ein solches Objekt z.B. folgende Informationen beinhalten:
- CL-Bezeichnung (z.B. -f für einen Dateinamen)
- Eine Kurzbeschreibung
- Ggf. eine ausführliche Beschreibung
- Den Typ (String, Int, Bool, ...)
- Default-Werte
- Für z.B. integer: Bereichs-Einschränkungen oder einzelne vorgegebene 
Werte
- Eine Liste auswählbarer Werte, die z.B. in einer GUI in einem 
Pull-Down menü erscheinen)
- Informationen welche Optionen sich gegenseitig ausschließen (Stichwort 
Radio-button). Dafür könnte man ja z.B. Objekte zu einer Gruppe 
zusammenfassen.
- ...

Ich suche jetzt nach eleganten Wegen eine solche Descriptor-Klasse 
aufzubauen. Eventuell wäre es auch sinnvoll für jeden Parameter-Typ eine 
eigene Klasse zu definieren und der gemeinsamen Basisklasse eine Funtion 
zu verpassen, mit der sich der Typ abfragen lässt (Ich denke da an sowas 
wie Qts QVariant).

Für kreative Vorschläge bin ich dankbar und offen.

Gruß

von Εrnst B. (ernst)


Lesenswert?

Wenns erstmal nur um "Variant" geht, evtl:
http://www.boost.org/doc/libs/1_55_0/doc/html/variant.html

von Philip K. (philip_k)


Lesenswert?

Kannte ich noch nicht. Sieht aber sehr brauchbar aus - danke!

von Karl Käfer (Gast)


Lesenswert?

Hallo Philip,

Philip K. schrieb:
> Ich habe mehrere eigenständige SW-Module, die dafür gedacht sind, sowohl
> von der Komandozeile, als auch mit einem GUI-Frontend aufgerufen zu
> werden. Als Schnittstelle dient dabei eine Funktion, der ich die
> Paramter (entweder die kopierten CL-Parameter, oder eben von der GUI
> zusammengestellt) als vector<string> übergebe.
>
> Jetzt hat zwar jedes Modul diese einheitlich definierte Schnittstelle,
> aber in der Regel unterschiedliche Parameter.
> Damit ich bei Hinzufügen/Ändern eines Moduls nicht jedes Mal die
> Aufrufumgebung anpassen muss, ist mein Plan, dass jedes Modul über eine
> weitere Funktion seine Parameter bekannt machen kann.
> [...]
> Für kreative Vorschläge bin ich dankbar und offen.

Da gibt es auch Boost.Program_options oder auch sowas:
1
// compile:  LANG=C g++ -Wall -Wextra -ggdb -std=c++11 main.cpp
2
3
#include <iostream>
4
#include <string>
5
#include <vector>
6
#include <cstdlib>
7
8
using namespace std;   // pfui!1!
9
10
11
class ModuleBase { public: virtual void print(void) = 0; };
12
class ModuleFactory {};
13
14
15
class ModuleA: public ModuleBase {
16
private: int a;
17
public:
18
    ModuleA(int a): a(a) {}
19
    void print(void) { cout << "ModuleA: " << a << endl; }
20
};
21
class ModuleAFactory: public ModuleFactory {
22
public:
23
    static ModuleA* makeModule(vector<string> params) {
24
        for(unsigned int e = 0; e < params.size(); ++e) {
25
            if( params[e].compare( "-a" ) == 0 ) {
26
                return new ModuleA( atoi(params[e+1].c_str()) );
27
            }
28
        }
29
        return NULL;
30
    }
31
};
32
33
34
class ModuleB: public ModuleBase {
35
private: double b;
36
public:
37
    ModuleB(double b): b(b) {}
38
    void print(void) { cout << "ModuleB: " << b << endl; }
39
};
40
class ModuleBFactory: public ModuleFactory {
41
public:
42
    static ModuleB* makeModule(vector<string> params) {
43
        for(unsigned int e = 0; e < params.size(); ++e) {
44
            if( params[e].compare( "-b" ) == 0 ) {
45
                return new ModuleB( atof(params[e+1].c_str()) );
46
            }
47
        }
48
        return NULL;
49
    }
50
};
51
52
53
int main(int argc, char* argv[]) {
54
    vector<string> vec;
55
    vector<ModuleBase> modules;
56
    for(int i = 0; i < argc; i++) {
57
        vec.push_back( string(argv[i]) );
58
    }
59
60
    ModuleA* a = (ModuleA*)ModuleAFactory::makeModule(vec);
61
    ModuleB* b = (ModuleB*)ModuleBFactory::makeModule(vec);
62
    if(a != NULL) { a->print(); }
63
    if(b != NULL) { b->print(); }
64
}

Das ist jetzt auf die Schnelle gehackt und kann auch viel schöner 
gemacht werden. Natürlich kann man da statische help()-Methoden 
hinzufügen und die Module seriell in einen Array-Container packen, mit 
Templates arbyten oder ... Aber der eigentliche Trick ist, daß jedes 
Modul sich die für das Modul nötigen und nützlichen Parameter 
herauspickt und den Rest ignoriert.

HTH,
Karl

von Philip K. (philip_k)


Lesenswert?

Hallo Karl,

Boost sollte ich mir wohl insgesammt mal näher ansehen, da scheints 
viele nützliche Dinge zu geben.

Zu Deinem Beispiel - Danke für die Mühe, aber das ist nicht ganz das was 
ich meinte.
Ich versuchs nochmal anders:

- Ein Programm (GUI, da wirds anschaulicher) wird mit einer 
erweiterbaren Anzahl von Modulen kompiliert. Diese Module haben eine 
einheitliche Aufrufschnittstelle aber unterschiedliche Parameter.
- Beim Programmstart wird von jedem Modul eine Instanz erstellt und ein 
paar Basisinformationen (Modulname etc.) abgeholt und in eine Liste 
(Combobox), dem Anwender zur Auswahl gestellt.
- Der Anwender wählt ein Modul aus und die Basisapplikation holt sich 
daraufhin von diesem Modul die verfügbaren Parameter und trägt sie in 
ein Config-Fenster ein.
- Der Anwender ändert ggf. Optionen und klickt auf einen Start-Button.
- Die Basisapplikation überreicht dem Modul die aktuellen Parameter als 
Strings über die einheitliche Aufrufschnittstelle und startet damit 
einen Ablauf.

Gruß
Philip

Karl Käfer schrieb:
> HTH,
??? :-)

von Klaus W. (mfgkw)


Lesenswert?

Philip K. schrieb:
> - Der Anwender wählt ein Modul aus und die Basisapplikation holt sich
> daraufhin von diesem Modul die verfügbaren Parameter und trägt sie in
> ein Config-Fenster ein.

Sind die auswählbaren Parameter immer endlich (und ggf. auf eine mäßige 
Größe beschränkt), oder kommen auch welche vor in der Art "ganze Zahl" 
oder Gleitkommazahl zwischen -4.21 und +10.987"?
Beliebige Dateinamen?
Dann wäre "holt sich daraufhin von diesem Modul die verfügbaren 
Parameter" natürlich ganz anders zu interpretieren.

von Klaus W. (mfgkw)


Lesenswert?

Philip K. schrieb:
>> HTH,
> ??? :-)

"Hope this helps"

von Sabine W. (sabine_w)


Lesenswert?

Ich habe in einem C++-Kurs mal für ein Art modularen Synthesizer mit 
flexiblen Modulen gearbeitet, die sich beim Pogramm registrieren 
konnten, damit die GUI bzw. das Konsolenprogramm was damit anfangen 
konnten.
Als Beispiel der Anfang (die Includes mal weggelassen) eines solchen 
Moduls zur Hallerzeugung, jedes Modul registrierte seine Ein- und 
Ausgänge, damit die Module in der GUI miteinander verdrahtet und damit 
dann ein Ton erzeugt werden konnte:
1
//Statisches Objekt von der eigenen Klasse wird initialisiert, dient der Modul-Registrierung
2
CS_Gen_Hall& CS_Gen_Hall::own=*new CS_Gen_Hall((t_init)S_HALL);
3
4
//Initialisierungskonstruktor zur Modul-Registrierung 
5
CS_Gen_Hall::CS_Gen_Hall(t_init id) 
6
{
7
  CList_Module::Add_Modul(this,this->info(),id);
8
}
9
10
//normaler Konstruktor
11
CS_Gen_Hall::CS_Gen_Hall(void) 
12
{
13
  //Aus- und Eingänge des Hall-Moduls werden angelegt
14
  //hall ist Ausgang
15
  hall=CSig_Val::Output("Hall",this);
16
17
  //signal ist Eingang und liefert das Signal, auf das der Hall angewendet wird
18
  CSig_Val::Input(&signal,"Signal",this);
19
20
  //faktor ist die Abschwächung
21
  CSig_Val::Input(&faktor,"Abschwaech-Faktor",this);
22
23
  //delay ist die Verzögerung
24
  CSig_Val::Input(&delay,"Verzoegerung",this);
25
}
26
27
CS_Gen_Hall::~CS_Gen_Hall(void)
28
{
29
}
30
31
//Info zurückgeben
32
char * CS_Gen_Hall::info(void)
33
{
34
  return "Hallmodul";
35
}
36
37
//Verweis auf neue Instanz zurückgeben 
38
CS_Modul * CS_Gen_Hall::instanz(void) {
39
return new CS_Gen_Hall();
40
}

Sabine

von Philip K. (philip_k)


Lesenswert?

Klaus Wachtler schrieb:
> Sind die auswählbaren Parameter immer endlich (und ggf. auf eine mäßige
> Größe beschränkt), oder kommen auch welche vor in der Art "ganze Zahl"
> oder Gleitkommazahl zwischen -4.21 und +10.987"?
Das sollte schon möglich sein. Deshalb dachte ich mir ja, dass ich eine 
Deskriptorklasse für jeden möglichen Datentyp (string, int float, bool, 
...)implementiere, die u.a. Auskunft darüber gibt, welche Werte erlaubt 
sind.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.