Toon posts:

[C/C++] Variable Arguments

Pagina: 1
Acties:

Verwijderd

Topicstarter
Ik heb een functie waar een variable aantal argument mee kan worden gegeven:

(kort samengevat...de belangrijkste code)
code:
1
2
3
4
5
6
7
8
9
10
11
12
char * maak_string(const char * fmt,...){
 va_list pa;
 va_start(pa,fmt);
 format_string(fmt,pa);
 va_end
 }

char * format_string(const char * fmt,va_list pa){
  char * buffer;
  vsnprintf(buffer,sizeofbuffer,fmt,pa); 
  return buffer;
  }


Het idee was dat format_string aan de hand van de argument list een string aan maakt. De bedoeling was dat vsnprintf 1 of meerdere keren word aan geroepen zodat buffer de juiste grootte kan worden gemaakt. Dit schijnt te werken op de meeste platforms.

Het probleem zit met op Linux x86-64 platform (GCC). Na de eerste vnsprintf blijkt de "pa" geen geldige informatie te bevatten. Nou kan dit ook wel kloppen, want dat wordt ook gezegd in de man pages:

"Consequently,the value of ap is undefined after the call. The application should call va_end(ap) itself afterwards."

De vraag waarom werkt het wel op 32 bit linux-gcc? Is de definitie van va_list net even anders waardoor er in vsnprintf met een copy van pa wordt gewerkt? Wat zegt de C/C++ standaard hier over?

  • bitrain
  • Registratie: Mei 2005
  • Laatst online: 15:38
Heb je misschien op je 64bit gcc4 draaien en op je 32bits gcc3.x? Ik weet niet, maar dit zou wel eens het probleem kunnen zijn.

Verwijderd

Topicstarter
bitrain schreef op vrijdag 27 mei 2005 @ 21:07:
Heb je misschien op je 64bit gcc4 draaien en op je 32bits gcc3.x? Ik weet niet, maar dit zou wel eens het probleem kunnen zijn.
de 64 bit is gcc-3.2 and de 32 bit is gcc-3.3

  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 09-04 22:08
va_lists zijn ingewikkelde dingen. Het lijkt me dat de layout veranderd is, al was het alleen maar omdat een void* 64 bits is geworden. Een int is natuurlijk nog steeds 32 bits. Bovendien heb je meer registers, dus je kunt meer argumenten in registers meegeven.

En inderdaad, een va_list is one-pass.

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


Verwijderd

Topicstarter
Een mogelijke oplossing is natuurlijk om een va_copy te doen voor elke vsnprintf, maar ja, dan heb je weer het geval dat dat niet overal werkt.

  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 09-04 22:08
Tja, in C zit niet heel erg veel ontwikkeling meer, en C++ gebruikt geen va_lists. Dus ga er inderdaad niet vanuit dat je va_copy zomaar hebt. Ik denk dat dat op Itanium een serieus probleem wordt.

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


  • c0deaddict
  • Registratie: Mei 2004
  • Laatst online: 10-01 12:11

c0deaddict

Don't be lame, be KLEI

MSalters schreef op vrijdag 27 mei 2005 @ 22:08:
va_lists zijn ingewikkelde dingen. Het lijkt me dat de layout veranderd is, al was het alleen maar omdat een void* 64 bits is geworden. Een int is natuurlijk nog steeds 32 bits. Bovendien heb je meer registers, dus je kunt meer argumenten in registers meegeven.

En inderdaad, een va_list is one-pass.
va_lists lastig :?
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
25
26
27
28
29
30
31
32
33
34
35
36
/*
 * clib/vargs.h
 * Copyright (c) 2004/2005 Jos van Bakel <j0s@home.nl>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License 
 * along with this program; if not, write to the Free Software Foundation
 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

#ifndef __VARGS_H__
#define __VARGS_H__

#include <types.h>

typedef char * va_list;

#define __va_rounded_size( type ) \
 ( ( ( sizeof( type ) + sizeof( int ) - 1 ) / sizeof( int ) ) * sizeof( int ) )

#define va_start( ap, lastarg ) \
  ( ( ap ) = _CHARP( &(lastarg) ) + __va_rounded_size( lastarg ) )

#define va_arg( ap, type ) \
  ( ( ap ) += __va_rounded_size( type ), *( ( type *)( ( ap ) - __va_rounded_size( type ) ) ) )

#endif /* __VARGS_H__ */


Thats all :D

... En aangezien sizeof( int ) op een 64-bit proc 8 bytes is moet het ook goed werken @ een 64 bittertje ...

[ Voor 4% gewijzigd door c0deaddict op 28-05-2005 14:16 ]


  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 09-04 22:08
sizeof(int) == 8? Weet je't zeker? AMD denkt daar anders over (http://www.amd.com/us-en/...s_to_AMD64_Technology.htm).

Verder klopt't ook niet. Voor AMD64 is va_list anders, nl
C++:
1
2
3
4
5
6
typedef struct {
unsigned int gp_offset;
unsigned int fp_offset;
void *overflow_arg_area;
void *reg_save_area;
} va_list[1];

In de 2003 GCC developers summit (PDF online) staat dus ook expliciet dat je de va_copy macro moet gebruiken (en die is niet standaard).

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


  • H!GHGuY
  • Registratie: December 2002
  • Niet online

H!GHGuY

Try and take over the world...

sizeof(int) is idd 4 op een AMD64 (ik heb het zelf getest).

wat die va_list betreft heb'k in de MSDN gelezen dat de implementaties in windows en unix verschillen... ik zoek het even op voor je.

http://msdn.microsoft.com..._.va_end.2c_.va_start.asp

dit samen gooien met wat MSAlters zegt zal je denk'k wel naar de juiste oplossing leiden.

[ Voor 39% gewijzigd door H!GHGuY op 29-05-2005 10:02 ]

ASSUME makes an ASS out of U and ME


  • c0deaddict
  • Registratie: Mei 2004
  • Laatst online: 10-01 12:11

c0deaddict

Don't be lame, be KLEI

MSalters schreef op zaterdag 28 mei 2005 @ 22:34:
sizeof(int) == 8? Weet je't zeker? AMD denkt daar anders over (http://www.amd.com/us-en/...s_to_AMD64_Technology.htm).

Verder klopt't ook niet. Voor AMD64 is va_list anders, nl
C++:
1
2
3
4
5
6
typedef struct {
unsigned int gp_offset;
unsigned int fp_offset;
void *overflow_arg_area;
void *reg_save_area;
} va_list[1];

In de 2003 GCC developers summit (PDF online) staat dus ook expliciet dat je de va_copy macro moet gebruiken (en die is niet standaard).
Hmm, ok, maar waarom zo moeilijk ?
Als je nu "sizeof( int )" vervangt door "sizeof( void * )" dan zou het toch alsnog moeten werken op zowel 32-bit als 64-bit. Want de manier waarop argumenten op de stack worden gezet is toch op beide architecturen hetzelfde ?

Hmm, het ligt dus niet zo makkelijk als ik gedacht had :/
http://www.x86-64.org/documentation/abi-0.92.pdf

[ Voor 11% gewijzigd door c0deaddict op 29-05-2005 16:56 ]

Pagina: 1