ubuntuusers.de

C/C++ Variable Parameterliste an Funktion übergeben

Status: Gelöst | Ubuntu-Version: Lubuntu 14.04 (Trusty Tahr)
Antworten |

eagle87

Anmeldungsdatum:
29. Dezember 2010

Beiträge: 85

Hallo zusammen,

ich will in C/C++ eine Funktion schreiben, die eine variable Parameterliste an eine weitere Funktion übergibt. Mein bisheriger Versuch sieht wie folgt aus:

 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
#include <stdio.h>
#include <stdarg.h>

void func(const char *msg, ...)
{
	va_list params;
	
	FILE *f = fopen("log", "w");
	va_start(params, msg);
	fprintf(f, msg, params);
	va_end(params);
	fputc('\n', f);
	fclose(f);
	
	va_start(params, msg);
	printf(msg, params);
	va_end(params);
	printf("\n");
}

int main(void)
{
	func("#%s#", "test");
	return 0;
}

Wenn ich das Programm aufrufe entstehen folgende Ausgaben. Über Stdout:

#

In der Datei "log":

#^H#

Was ich eigentlich will ist folgende Ausgabe:

#test#

Könnt ihr mir sagen wo der Fehler liegt / wie es richtig gemacht wird?

Viele Grüße
Michael

Dakuan

Avatar von Dakuan

Anmeldungsdatum:
2. November 2004

Beiträge: 6479

Wohnort: Hamburg

Ich habe so etwas schon lange nicht mehr verwendet. Aber schon die mehrfache Verwendung von va_start() erscheint mir suspekt. Ich kopiere hier mal den Anfang einer Funktion aus einem funktionierenden Programm:

// ---------------------------------------------------------------------------
//  n channel scope (max 8)
//  pass values as int
//
int
Scope::shown( int count, ... ) {
    unsigned char * p = bufpt;
    unsigned char * s;
    int             shift;
    int             move_len;
    int             chs[8];     // array of channel values
    int             val;
    int i;
    int n;
    int k;
    int hlen;
    int r, g, b;
    int l1, l2; // levels

    va_list list;
    va_start( list, count );
 
    for( i = 0; i < count  &&  i < 8; i++ ) {
        chs[i] = va_arg( list, int );
    }
    va_end( list );
    ...
}

Da wird ein mehrspuriges Impulsdiagramm gezeichnet. Du brauchst auf jeden Fall einen Mechanismus, mit dem deine Funktion die Anzahl der übergebenen Parameter bestimmen kann. Hier ist es ein einfacher Zähler, bei der bekannten printf() Funktion und in deinem Fall, ist es der Format String. Diese Arbeit nehmen Dir va_start() - va_end() nicht ab.

eagle87

(Themenstarter)

Anmeldungsdatum:
29. Dezember 2010

Beiträge: 85

Hallo Dakuan,
danke für die Antwort. Die mehrfache Verwendung von va_start() ist nicht das Problem. Beim Aufruf von va_end() wird die Variable wieder zurückgesetzt. Bei einfacher Verwendung von va_start() und va_end() ändert sich nichts an der Ausgabe.

Du brauchst auf jeden Fall einen Mechanismus, mit dem deine Funktion die Anzahl der übergebenen Parameter bestimmen kann. Hier ist es ein einfacher Zähler, bei der bekannten printf() Funktion und in deinem Fall, ist es der Format String. Diese Arbeit nehmen Dir va_start() - va_end() nicht ab.

Anhand des Format-Strings die Anzahl der Elemente zu bestimmen und die einzelnen Elemente mit va_arg() abzurufen bringt mir leider nichts dann habe ich zwar die einzelnen Werte, kann sie aber trotzdem noch nicht an printf() oder fprintf() übergeben.

Eben bin ich noch auf die Funktion vfprintf() gestoßen. Damit lässt sich die gewünschte Ausgabe erzeugen. Hier der Code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>
#include <stdarg.h>

void func(const char *msg, ...)
{
	va_list params;
	
	FILE *f = fopen("log", "w");
	va_start(params, msg);
	vfprintf(f, msg, params);
	va_end(params);
	fputc('\n', f);
	fclose(f);
	
	va_start(params, msg);
	vfprintf(stdout, msg, params);
	va_end(params);
	printf("\n");
}
int main(void)
{
	func("#%s#", "test");
	return 0;
}

Allerdings würde mich noch interessieren, ob es auch eine Möglichkeit für Funktionen gibt die wie printf(const char *format, ...) aufgebaut sind. Für den Fall dass es mal keine Ersatzfunktion gibt die eine va_list als Parameter direkt akzeptiert...

Dakuan

Avatar von Dakuan

Anmeldungsdatum:
2. November 2004

Beiträge: 6479

Wohnort: Hamburg

Für den Fall dass es mal keine Ersatzfunktion gibt die eine va_list als Parameter direkt akzeptiert...

Bisher ist mir diese alternative Parameterübergabe nur bei den *print*() Funktionen aufgefallen und selber habe ich das noch nicht gebraucht. Meist bin ich den Weg über sprintf() oder snprintf() und einem eigenen Datenpuffer gegangen, aber das geht natürlich nur, wenn man die Datenmenge abschätzen oder begrenzen kann.

Aber wenn du damit rechnest, so etwas zu brauchen, lohnt sich bestimmt ein Blick in den Quelltext der verschiedenen v*print*() Funktionen oder zumindest auf die va_list Struktur.

eagle87

(Themenstarter)

Anmeldungsdatum:
29. Dezember 2010

Beiträge: 85

Bisher ist mir diese alternative Parameterübergabe nur bei den *print*() Funktionen aufgefallen ...

Da hast du wohl recht. Eine andere Funktion habe ich auch noch nicht gesehen.

Meist bin ich den Weg über sprintf() oder snprintf() und einem eigenen Datenpuffer gegangen, aber das geht natürlich nur, wenn man die Datenmenge abschätzen oder begrenzen kann.

Wenn man die Datenmenge nicht abschätzen kann müsste es auch nen Weg geben (Code aber nicht getestet).

1
2
3
4
5
char *s;
int n;
n = snprintf(s, 0, "#%s#", "test");
s = (char*) malloc(n);
snprintf(s, n, "#%s#", "test");

Aber wenn du damit rechnest, so etwas zu brauchen, lohnt sich bestimmt ein Blick in den Quelltext der verschiedenen v*print*() Funktionen oder zumindest auf die va_list Struktur.

Der Quelltext der v*print*() Funktionen hat mich nicht weiter gebracht. Aber bevor ich mich da jetzt ewig aufhänge benutze ich einfach die bestehenden Funktionen. Zur Not auch über einen Buffer. Nochmals Danke für die Antworten.

Dakuan

Avatar von Dakuan

Anmeldungsdatum:
2. November 2004

Beiträge: 6479

Wohnort: Hamburg

Ich war durch eigene Probleme abgelenkt und hatte mir Deinen Code erst jetzt richtig angesehen (dafür muss man den Kopf ja auch frei haben).

Also wenn Deine Idee wirklich funktioniert, währe das genial. Ich werde das mal austesten (aber nicht heute, ist schon zu spät). Jedenfalls habe ich mal in der Doku nachgeschaut:

If the resulting string would be longer than n-1 characters, the remaining characters are discarded and not stored, but counted for the value returned by the function.

In meinen Büchern fehlt dieser Satz. Das würde mir neue Optionen für meine "dynamische" Pufferung eröffnen. Mir ist bisher entgangen, das die Anzahl der benötigten Bytes zurückgegeben wird. In meinen Büchern wird da immer nur auf sprintf() verwiesen, was aber die Anzahl der tatsächlich geschriebenen Bytes zurück liefert.

Um es frei nach Rüdiger Hoffmann zu sagen: "... ich bin Ihnen für diese Frage direkt dankbar ..."

BTW. er antwortete dies auf die Frage, was seine hervorstechendste Eigenschaft sei, und ergänzte nach einigen Minuten Bedenkzeit, "Meine Spontanität". ...

eagle87

(Themenstarter)

Anmeldungsdatum:
29. Dezember 2010

Beiträge: 85

Hab den Code für dich noch vervollständigt, getestet und ein paar Fehler beseitigt 😉

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
#include <stdlib.h>
#include <stdio.h>

int main(int argc, char **argv)
{
	char *in, *s;
	int n;
	
	in = argc>1?argv[1]:(char*) "";
	
	n = snprintf(NULL, 0, "#%s#\n", in);
	s = (char*) malloc(n+1);
	snprintf(s, n+1, "#%s#\n", in);
	
	puts(s);
	free(s);
	return 0;
}

Dakuan

Avatar von Dakuan

Anmeldungsdatum:
2. November 2004

Beiträge: 6479

Wohnort: Hamburg

Danke, schön zu wissen, das so etwas funktioniert. Ich währe nicht auf die Idee mit dem NULL-Pointer gekommen. Ich dachte da aber auch mehr an so etwas wie eine Fehlerbehandlung, falls ein vorhandener Puffer zu klein ist.

Also ein Objekt startet z.B. mit einem kleinen Puffer, der bei Bedarf schrittweise vergrößert werden kann.

Antworten |