Check alle échte Black Friday-deals Ook zo moe van nepaanbiedingen? Wij laten alleen échte deals zien

[C] va_list aanpassen

Pagina: 1
Acties:

  • Punkie
  • Registratie: Oktober 2005
  • Laatst online: 07-11 20:36
Bij functies met een variabel aantal argumenten is een va_list beschikbaar om de argumenten uit te lezen. In de libraries worden de macros va_start, va_arg en va_end gedefinieerd zoals reeds vastligt in ANSI C standaard. Deze specificatie valt op door de karigheid aan functionaliteit. GNU cc heeft nog een __va_copy functie maar daar blijft het dan ook bij. Handige functies die men nooit gemaakt heeft zijn vb. va_peek, va_setarg, va_prev, va_next ,....

Zijn er goede redenen waarom deze functies/macros ontbreken? Is er een goede reden waarom men er zelf niet aan zou beginnen?
Iets specifieker: Ik wil de argumenten van de functie manipuleren en zelfs van type veranderen. vb, argument 2 is een int en die wil ik aanpassen naar een float om vervolgens alle parameters door te geven aan een andere functie.
Is dit (g)een goed idee?

  • moto-moi
  • Registratie: Juli 2001
  • Laatst online: 09-06-2011

moto-moi

Ja, ik haat jou ook :w

Waarom zou je daarvoor de va_list willen veranderen :?
C:
1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>

main()
{
    float a = 23.34;
    int b;
    
    b = (float)a;
    
    printf("%d\n",b);

}

werkt tenslotte ook prima.

God, root, what is difference? | Talga Vassternich | IBM zuigt


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 20:54

.oisyn

Moderator Devschuur®

Demotivational Speaker

En dan nu in een variadic functie, waarbij je de tweede param wil veranderen in een float (of double eigenlijk), en alle params vervolgens wil doorgeven aan een andere variadic functie.

Je main mist trouwens het return-type.

Gelukkig krijgt C++0x variadic templates waarmee het prima kan. Vararg suckt :)

[ Voor 28% gewijzigd door .oisyn op 20-09-2007 16:49 ]

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.


  • Punkie
  • Registratie: Oktober 2005
  • Laatst online: 07-11 20:36
moto-moi schreef op donderdag 20 september 2007 @ 16:36:
Waarom zou je daarvoor de va_list willen veranderen :?
Omdat ik niet weet hoeveel argumenten er zijn. Dat is waarom de va_list nodig is.

OPGELET! We spreken hier over functie parameters en NIET de commandline arguments, waarbij men wel weet hoeveel er zijn.
C:
1
2
3
4
5
6
7
8
9
int my_func(int eins,...){
va_list ap;
va_start(ap,eins);
... /* change the args */

/* call another func*/
func2(eins,ap);
va_end(ap);
}

[ Voor 21% gewijzigd door Punkie op 20-09-2007 16:51 ]


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 20:54

.oisyn

Moderator Devschuur®

Demotivational Speaker

Punkie schreef op donderdag 20 september 2007 @ 16:49:
[...]

Omdat ik niet weet hoeveel argumenten er zijn. Dat is waarom de va_list nodig is.
Als dát je probleem is, dan gaat je va_list je sowieso niet helpen, omdat het aantal parameters nergens bekend is. Er valt dan ook weinig aan te passen, of je moet de compiler zelf aanpassen zodat hij die info meegeeft aan de aageroepen functie.

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.


  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
kan dat dan niet zo iets
C:
1
2
3
4
5
6
7
8
9
10
11
12
13
void foo(char * param1, ...)
{
    va_list pl;
    int param2;

    va_start( pl, param1 );

    param2 = va_arg( pl, int );

    va_end(pl);

    foo2( (float)param2 );
}

[disclaimer]
Nooit met varargs gewerkt in c dus het is maar een gokje
[/disclaimer]

Edit:
Ow je weet dus niet eens hoeveel parameters er zijn. Dan zul je toch minstens aan moeten geven hoeveel parameters er mee gegeven worden denk.

[ Voor 19% gewijzigd door Woy op 20-09-2007 16:56 ]

“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”


  • moto-moi
  • Registratie: Juli 2001
  • Laatst online: 09-06-2011

moto-moi

Ja, ik haat jou ook :w

.oisyn schreef op donderdag 20 september 2007 @ 16:45:
En dan nu in een variadic functie, waarbij je de tweede param wil veranderen in een float (of double eigenlijk), en alle params vervolgens wil doorgeven aan een andere variadic functie.
Ah, als je de lijst daarna direct wil doorgeven zonder hem opnieuw op te bouwen ben je fucked i.d.d., daar had ik inderdaad even niet bij stilgestaan :)
Je main mist trouwens het return-type.
mist..mist.., dat regelt gcc voor me in zo'n geval, bij dit soort voorbeeldjes ga ik niet m'n uiterste best doen om elke compilatiewarning te voorkomen ;)

God, root, what is difference? | Talga Vassternich | IBM zuigt


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 20:54

.oisyn

Moderator Devschuur®

Demotivational Speaker

rwb: maar nu geef je maar 1 parameter door aan foo2. Wat Punkie wil is de hele parameter-lijst meegeven, maar waarbij eentje is aangepast.

De reden dat dat niet kan is overigens heel simpel. C zegt niets over hoe de parameters doorgegeven moeten worden, en dat kan dus ook prima via registers (zoals ook gebeurt op x86-64). Je kunt dus niet zomaar even een type van een van die parameters wijzigen aangezien dat ervoor zorgt dat de volgorde wijzigt: als gedefinieerd is dat de eerste ints in eax, ebx, ecx staan, en de eerste floats in st0, st1, st2, en je geeft die functie de parameterlijst (1, 2, 3.0, 4), dan betekent dat eax=1, ebx=2, ecx=4 en st0=3.0. Maar nu wil je van de tweede parameter een double maken, waardoor st0=2.0, maar dan moet de 3.0 in st1, en bovendien moet de 4 uit ecx verhuizen naar de nu vrijgekomen ebx. Feest :).

Als je een float passed naar een variadic functie wordt hij overigens eerst geconverteerd naar double, dus als je een int converteert naar float moet je 'm dus eigenlijk converteren naar double.

[ Voor 10% gewijzigd door .oisyn op 20-09-2007 17:01 ]

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.


  • Punkie
  • Registratie: Oktober 2005
  • Laatst online: 07-11 20:36
.oisyn schreef op donderdag 20 september 2007 @ 16:52:
[...]

Als dát je probleem is, dan gaat je va_list je sowieso niet helpen, omdat het aantal parameters nergens bekend is. Er valt dan ook weinig aan te passen, of je moet de compiler zelf aanpassen zodat hij die info meegeeft aan de aageroepen functie.
Toch wel :) omdat de informatie over het aantal argumenten EN hun type wordt bepaald door de informatie in de argumenten. Think
C:
1
size_t vnsprintf(char*dest,size_t size,char* format,...)

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 20:54

.oisyn

Moderator Devschuur®

Demotivational Speaker

En dan weet je dus wél hoeveel parameters er zijn. Net zei je nog dat je dat niet wist :)

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.


  • Punkie
  • Registratie: Oktober 2005
  • Laatst online: 07-11 20:36
Even specifieren dan :) Ik weet NIET welke argumenten er gaan doorgegeven worden aan de functioncall, bij gevolg zijn *at compile time* de argumenten onbekend en gaan moto-moi en rwb's oplossingen de mist in.

Net zoals in printf kan aan de waarde van de argumenten afgeleid worden wat er veranderd moet worden en hoeveel argumenten er zijn van welke waarden. Dit alleen *at runtime* natuurlijk.

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 20:54

.oisyn

Moderator Devschuur®

Demotivational Speaker

Wat je dus moet doen is de calling convention van jouw platform uitzoeken, zelf de format string scannen op welke parameters je verwacht en de functie die je aan wilt roepen middels inline asm aanroepen.

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.


  • Punkie
  • Registratie: Oktober 2005
  • Laatst online: 07-11 20:36
.oisyn schreef op donderdag 20 september 2007 @ 21:39:
Wat je dus moet doen is de calling convention van jouw platform uitzoeken, zelf de format string scannen op welke parameters je verwacht en de functie die je aan wilt roepen middels inline asm aanroepen.
Dit soort antwoord had ik gehoopt niet te krijgen :)
Het is de bedoeling om alles portable te houden vermits het op veel platforms kan gebruikt worden en door heel wat compilers kan gedraaid worden. En assembler...

Maar laat ik een stuk illustreren. Wat is er tegen deze selfmade macros?
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
/* as defined in the libraries */
#define va_arg(ap,t)(*(t*)((ap +=sizeof(t*))-sizeof(t*)))

/* self-made */
#define va_peek(ap,t)(*(t*)ap)
#define va_set(ap,val,t)(memcpy(ap,val,sizeof(t*)))
...
void myPrint(char* format,...) {
int argval;
char *strargval_p;

va_list argptr;
va_start(argptr,format);
...

switch(format[i]);
case 'm':
/* change this parameter */
    argval = va_peek(argptr,int);
    myFormat(argval,strargval_p); /* create a string to pass as argument */ 
    va_set(argptr,&strargval_p,char*);
    format[i] = 's';
..

/* pass it to the library printf implementation */
vprintf(format,argptr);
va_end(argptr);

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 20:54

.oisyn

Moderator Devschuur®

Demotivational Speaker

Niets, het is alleen niet portable :) (bovendien moet je erop letten dat de grootte van het type dat je wilt veranderen gelijk blijft. Een conversie van int naar double waar je het in de topicstart over had kan bijvoorbeeld niet zomaar op de meeste platforms, dan moet je een buffer maken waar je alle parameters naartoe kopiëert).

Overigens dacht ik trouwens dat je een andere functie aan wilde roepen die ook een parameter-lijst verwacht ipv een va_list. Als de aan te roepen functie een va_list accepteert dan vergemakkelijkt dat de boel natuurlijk wel enigszins.

[ Voor 26% gewijzigd door .oisyn op 21-09-2007 12:24 ]

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.


  • Punkie
  • Registratie: Oktober 2005
  • Laatst online: 07-11 20:36
portable is relatief. Ik moet een va_set implementatie bedenken voor iedere mogelijke library, is het niet? Maar een portable stuk code heeft dit bijna altijd, vorral in ansi C. Denk maar aan inlining, aan string comparison op windows, locales, environment variables, ...

In bovenstaand geval werkt de va_list met pointers, welke dezelfde size_t zouden moeten hebben. Maar wat als men niet mer pointers maar met vb int en double en short werkt? De verschillende geheugen allocaties voor ieder type zorgen ervoor dat dit niet mooi erover van gekopieerd worden.

Ik kan, in C, eigenlijk geen manier bedenken om iets ander dan een va_list als argument te gebruiken om een variabel aantal argumenten door te geven. Bestaat er een alternatief? (naast assembler)

Verwijderd

Punkie schreef op vrijdag 21 september 2007 @ 12:16:
[...]
Wat is er tegen deze selfmade macros?
Om te beginnen het feit dat je macro's va_arg() en va_set() niet werken zoals je verwacht voor values met (grof gezegd) grootte != sizeof(*).

Ter verduidelijking: ap wijst naar een stuk geheugen waar de parameter waarden staan, en niet de adressen van die parameters. Dat is ook de reden dat voor het itereren naar de volgende parameter ap += sizeof(*) natuurlijk niet werkt.

Daarnaast zal je voor de hand liggende reparatie ook niet goed werken, omdat in C de op de stack gereserveerde ruimte voor een parameter niet altijd gelijk is aan de grootte van de parameter zelf. Voorbeeld: een char wordt gepushed als int, een float als double...

Tevens ga je er vanuit dat alle actuele parameters in een aaneengesloten stuk geheugen, zoals op een stack, worden gepushed. Besluit de compiler om (zoals .oisyn al schreef) een of meerdere parameters in registers te stoppen (of nog interessanter: je op een stack-less architectuur werkt :+ ), dan werkt je aanpak per definitie niet meer.

Wil je een breed portable oplossing, dan zit er volgens mij weinig anders op om per platform & compiler een constructie te bedenken, en dan met #ifdef / #elif / #else / #endif icm. macro's die je platform en compiler aanduiden, de platform-specifieke code te enablen. Lijkt me persoonlijk vanuit economisch oogpunt niet aantrekkelijk, maar goed, ik ken je project en de motieven van je opdrachtgever natuurlijk niet.
Punkie schreef op vrijdag 21 september 2007 @ 12:54:
Ik kan, in C, eigenlijk geen manier bedenken om iets ander dan een va_list als argument te gebruiken om een variabel aantal argumenten door te geven. Bestaat er een alternatief?
In algemene zin: wat denk je van het doorgeven van een pointer naar een zelf-geconstrueerde array van paren (type, value), waar je je actuele parameters in stopt? Is wat knutselwerk, maar dan heb je wel op ieder moment volledige controle over de complete parameterverzameling.

  • Punkie
  • Registratie: Oktober 2005
  • Laatst online: 07-11 20:36
Verwijderd schreef op vrijdag 21 september 2007 @ 13:40:
[...]
Ter verduidelijking: ap wijst naar een stuk geheugen waar de parameter waarden staan, en niet de adressen van die parameters. Dat is ook de reden dat voor het itereren naar de volgende parameter ap += sizeof(*) natuurlijk niet werkt.
In deze specifieke implementatie, welke ik niet zelf uitgevonden heb maar uit een library heb geplukt, is argptr wel degelijk een verwijzing naar de parameter adressen en staan deze in een aaneengesloten stuk geheugen, niet? Natuurlijk kan je er niet op betrouwen dat dit in andere librarys ook het geval is.
Tevens ga je er vanuit dat alle actuele parameters in een aaneengesloten stuk geheugen, zoals op een stack, worden gepushed. Besluit de compiler om (zoals .oisyn al schreef) een of meerdere parameters in registers te stoppen (of nog interessanter: je op een stack-less architectuur werkt :+ ), dan werkt je aanpak per definitie niet meer.
Dit begrijp ik niet :( Als ik de waarde of het adress verander dan wijzig ik een stuk geheugen. Of deze ook in een register steekt of niet is voor mij toch niet van belang? De compiler zorgt dat de synchronisatie correct gebeurt.
Wil je een breed portable oplossing, dan zit er volgens mij weinig anders op om per platform & compiler een constructie te bedenken, en dan met #ifdef / #elif / #else / #endif icm. macro's die je platform en compiler aanduiden, de platform-specifieke code te enablen.
Als ik op een ander platform werk, dan kunnen de dataypes een andere size hebben. Daarvan kan ik toch abstraheren door het gebruik van sizeof?
Het is toch de gebruikte library die belangrijk is en niet de compiler. Als ik gcc gebruik maar met een library compileer waarbij de __STDC_VERSION__ C89 gebruikt, dan moet ik ook een andere implementatie gebruiken dan wanneer ik voor C99 zou gaan, niet?

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 20:54

.oisyn

Moderator Devschuur®

Demotivational Speaker

Libraries doen er niet toe, het gaat om implementaties. En idd, gcc C89 is in feite een andere implementatie dan C99, en kan derhalve een andere calling convention hebben. Net als dat gcc 4.0.1 een andere calling convention kan hebben als versie 4.0.2 (hoewel dat in de praktijk onwaarschijnlijk is)

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.

Pagina: 1