Amiga.org
Operating System Specific Discussions => Amiga OS => Amiga OS -- Development => Topic started by: Jose on September 11, 2005, 11:15:13 AM
-
Hi. Suppose I have a function that has a variable number of arguments. How do I call a second function passign it those arguments in the very same way they were passed to the 1st one.
For example..
void SomeFunction (int a, ...);
void SecondFunc (int nr, ...);
How do I call the 2nd function from the 1st one passing it the first's varargs...?
I have an idea of making va_list global and initializing it in the first function. The second one would then only have to call va_arg. I could also pass a pointer to va_arg I guess.
Would this work or it's not very safe (calling a 2nd function will mess with the stack..) ?
-
You could do it like AmigaOS does it:
void SomeFunctionA (int a,int *b)
{
/* whatever it does, do it here */
}
VARARGS68K void SomeFunction (int a,...)
{
va_list ap;
va_startlinear (ap,a);
return (SomeFunctionA (a,va_getlinearva (ap,int *)));
}
void SecondFuncA (int a,int *b)
{
/* do some stuff */
SomeFunction (a,b);
/* do some stuff */
}
VARARGS68K void SecondFunc (int a,...)
{
va_list ap;
va_startlinear (ap,a);
return (SecondFunc (a,va_getlinearva (ap,int *)));
}
Bye,
Thomas
-
Er...I meant I could also pass a pointer to va_list not va_arg...
-
Hmm... where do you get these:
va_startlinear
va_getlinearva
Can't find them anywhere..
-
Ahh, it's in the headers...
Why not just use va_start anyway ?
-
When you use:
return (SecondFunc (a,va_getlinearva (ap,int *)));
Wouldn't this be the same as:
return (SecondFunc (a,va_arg (ap,int *)));
And will SecondFunc (actually I think you meant SecondFuncA), supposing it was declared as void SecondFuncA (int a,int *b,...)
, be able to get other subsequent values, like:
va_list ap;
int d;
int e;
va_start (ap,b);
d = va_arg (ap, int)
e = va_arg (ap, int)
;
I think you're just calling SomeFuncA and SecondFuncA with the first argument in the variable argument list but I want them to be able to get any subsequent value...
That or I'm just getting all this wrong...
-
What I wanted to say is, you should *avoid* varargs. Do not use varargs. Write your function *without* varargs. Make your functions accept a pointer to an array which contains the arguments.
Then, in order to call the function with varargs, create a stub which fetches a pointer to the varargs array and sends it to the actual function.
That's how it always was in AmigaOS. AmigaOS functions never accepted varargs. The actual function always only accepted an array of tag items. But there is a small varargs stub which calles the original function passing a pointer to the varargs array.
Getting this pointer is very easy on 68k but difficult on PPC because the PPC does not allocate all arguments on the stack.
You should read the docs before you start to program. There is a chapter about varargs in one of the PDF files.
AFAIK using varargs outside the originating function is not supported.
Bye,
Thomas
-
Hi. I'd have no problems with that except in this particular function I'm dealing with, actually all my functions that accept an array accept a pointer to it.
I want to be able to allocate the exact needed memory to the concatenation of various strings, using one of them as template like printf does. How I'm doing this is having sprintf or one of the other similar exec functions accept various created substrings from the format string (actually it's the same one wich is temporarily NULL terminated before it's end), each one with the successive % format commands. Then I check the size of the substring or number (%d, %ld, etc. ) and continue till the end adding to the total till I get the total memory needed to hold the whole resulting string. After that I just need to allocate the needed memory and call sprintf or one alike function to produce the wanted final string using the template string. And this is where the problem lies, sprintf is a varargs function.
I'm not at home and can't check from here, but maybe there's some exec function that accepts an pointer to a format string, that would be perfect... :-)
:pint:
-
@Jose
The problem with what you're doing is that you basically end up duplicating sprintf functionality.
This is how I'd do it:
a) Use RawDoFmt twice. First time just count the total size of the string. Allocate buffer, RawDoFmt again, now poking chars to the buffer.
or
b) Use RawDoFmt with dynamically expanding buffer putchproc. You'll prolly end up with slightly larger buffer than necessary, but there will be just one RawDoFmt call.
-
@Piru
Yes! :-D The a) option sounds perfect. I had forgot that RawDoFmt outputs one char at a time so I can just count the size of the final string... Dynamically expanding buffer would probably involve a linked list type of thing, not really needed for what I want to do.
-
Hi. If I don't use varargs and use a pointer to an array I'd have to dynamically allocate an array everytime I wanted to create a string using other strings (for example a list of errors) and variables.
I read this is a big problem for OS4 compatibility, is there a way around this still using varargs?
What about MOS, doesn it have the same problem ?
The bellow code is some example I made last night. Using Piru's suggestion the code size more than halved (was almost 200 lines).
However the bloody thing doesn't work. I couldn't find the bug, so since the code size is small and it's probably a stack related problem that I wouldn't find anyway I decided to post it here in hope some benevolent soul finds it :roll:
#include <exec/memory.h>
#include <exec/types.h>
#include <stdio.h>
BOOL AllocAndCpyStr (char *CtrlStr, char **dest, ...);
void CountChrs (char ch, LONG *StringSize);
void CpyChr (char ch, char *dest);
int main (int argc, char **argv)
{
char *a = "Error a";
char *b = "Error b";
char *c = 0;
/* .. Other function code...*/
/* Make information strings without having to allocate an array everytime for each */
/* Suppose both error a and b happened. Allocate string with info without having make */
/* an array with pointers a and b plus other data we want to put into final string */
/* Also serves to test AllocAndCpyStr */
if (AllocAndCpyStr ("%s %c %s \n Test number is %d", &c, a, ' ', b, 44))
printf ("Sucess, concatenation and allocation result is:\n %s\n",c);
else
printf ("Failed\n");
if (c)
FreeMem (c, strlen (c) + 1);
exit (0);
}
/* printf() like, except it copies resulting string to it's own allocated chunk, whose ptr is put in *dest */
/* uses StrToRawDF */
BOOL AllocAndCpyStr (char *CtrlStr, char **dest, ...)
{
LONG StringSize = 0;
BOOL Result = FALSE;
/* Check total size */
RawDoFmt (CtrlStr, &dest + 1, (void (*)())CountChrs, &StringSize);
/* Allocate space 4 strings */
if (!(*dest = (char *)AllocMem (StringSize, MEMF_ANY | MEMF_CLEAR)))
{ printf ("Out of memory in report!\n");
goto END;
}
printf ("Allocated space (resulting string size) was %ld bytes\n\n", StringSize);
Result = TRUE;
/* Copy over strings */
RawDoFmt (CtrlStr, &dest + 1, (void (*)())CpyChr, *dest);
END:
return (Result);
}
/* Count nr. of times it's called (= string size) */
void CountChrs (char ch, LONG *StringSize)
{ ++*StringSize;
}
void CpyChr (char ch, char *dest)
{ *dest++ = ch;
}
-
&dest + 1
This does not work on PPC code. I told you there is a chapter about varargs in the OS4 docs. Why don't you read it ? I even gave you an example how it is done in OS4. Why don't you look at it ?
Bye,
Thomas
-
Basically your CountChrs and CpyChr didn't use correct registers. Another bug was that you used 16-bit format codes %c and %d instead of 32-bit %lc and %ld (remember I warned you about the difference?). Here's the fixed code with MorphOS varargs included (code tested with SAS/C and ppc-morphos-gcc):
#include <exec/memory.h>
#include <exec/types.h>
#ifdef __MORPHOS__
#include <exec/rawfmt.h>
#endif
#include <proto/exec.h>
#include <stdio.h>
#include <strings.h>
BOOL AllocAndCpyStr (char *CtrlStr, char **dest, ...);
int main (int argc, char **argv)
{
char *a = "Error a";
char *b = "Error b";
char *c = 0;
/* .. Other function code...*/
/* Make information strings without having to allocate an array everytime for each */
/* Suppose both error a and b happened. Allocate string with info without having make */
/* an array with pointers a and b plus other data we want to put into final string */
/* Also serves to test AllocAndCpyStr */
if (AllocAndCpyStr ("%s %lc %s \n Test number is %ld", &c, a, ' ', b, 44))
printf ("Sucess, concatenation and allocation result is:\n %s\n",c);
else
printf ("Failed\n");
if (c)
FreeMem (c, strlen (c) + 1);
return 0;
}
#ifndef __MORPHOS__
static const UWORD CountChrs[] =
{
0x5293, /* addq.l #1,(a3) */
0x4E75, /* rts */
};
static const UWORD CpyChr[] =
{
0x16C0, /* move.b d0,(a3)+ */
0x4E75, /* rts */
};
#endif
/* printf() like, except it copies resulting string to it's own allocated chunk, whose ptr is put in *dest */
/* uses StrToRawDF */
BOOL AllocAndCpyStr (char *CtrlStr, char **dest, ...)
{
LONG StringSize = 0;
BOOL Result = FALSE;
/* Check total size */
#ifdef __MORPHOS__
va_list args;
va_start(args, dest);
VNewRawDoFmt (CtrlStr, (APTR) RAWFMTFUNC_COUNT, (STRPTR) &StringSize, args);
va_end(args);
#else
RawDoFmt (CtrlStr, &dest + 1, (void (*)(void))CountChrs, &StringSize);
#endif
/* Allocate space 4 strings */
if (!(*dest = (char *)AllocMem (StringSize, MEMF_ANY | MEMF_CLEAR)))
{ printf ("Out of memory in report!\n");
goto END;
}
printf ("Allocated space (resulting string size) was %ld bytes\n\n", StringSize);
Result = TRUE;
/* Copy over strings */
#ifdef __MORPHOS__
va_start(args, dest);
VNewRawDoFmt (CtrlStr, (APTR) RAWFMTFUNC_STRING, *dest, args);
va_end(args);
#else
RawDoFmt (CtrlStr, &dest + 1, (void (*)(void))CpyChr, *dest);
#endif
END:
return (Result);
}
Here's an alternative way of handling the varargs problem. This version in fact doesn't use varargs at all but temporary LONG array. This method, while is much more portable, also consumes some more resources, and it's as elegant as true varargs. While a return value from macro is needed, this method is GCC (and compatibles) only.
#include
#include
#include
#include
#include
#ifdef __PPC__
BOOL _AllocAndCpyStr (char *CtrlStr, char **dest, LONG *args);
#define AllocAndCpyStr(str,dst,args...) \
({LONG __array[] = {0, args}; _AllocAndCpyStr(str, dst, __array+1);})
#else
BOOL AllocAndCpyStr (char *CtrlStr, char **dest, ...);
#endif
int main (int argc, char **argv)
{
char *a = "Error a";
char *b = "Error b";
char *c = 0;
/* .. Other function code...*/
/* Make information strings without having to allocate an array everytime for each */
/* Suppose both error a and b happened. Allocate string with info without having make */
/* an array with pointers a and b plus other data we want to put into final string */
/* Also serves to test AllocAndCpyStr */
if (AllocAndCpyStr ("%s %lc %s \n Test number is %ld", &c, a, ' ', b, 44))
printf ("Sucess, concatenation and allocation result is:\n %s\n",c);
else
printf ("Failed\n");
if (c)
FreeMem (c, strlen (c) + 1);
return 0;
}
static const UWORD CountChrs[] =
{
0x5293, /* addq.l #1,(a3) */
0x4E75, /* rts */
};
static const UWORD CpyChr[] =
{
0x16C0, /* move.b d0,(a3)+ */
0x4E75, /* rts */
};
/* printf() like, except it copies resulting string to it's own allocated chunk, whose ptr is put in *dest */
/* uses StrToRawDF */
#ifdef __PPC__
BOOL _AllocAndCpyStr (char *CtrlStr, char **dest, LONG *args)
#else
BOOL AllocAndCpyStr (char *CtrlStr, char **dest, ...)
#endif
{
LONG StringSize = 0;
BOOL Result = FALSE;
/* Check total size */
#ifdef __PPC__
RawDoFmt (CtrlStr, args, (void (*)(void))CountChrs, &StringSize);
#else
RawDoFmt (CtrlStr, &dest + 1, (void (*)(void))CountChrs, &StringSize);
#endif
/* Allocate space 4 strings */
if (!(*dest = (char *)AllocMem (StringSize, MEMF_ANY | MEMF_CLEAR)))
{ printf ("Out of memory in report!\n");
goto END;
}
printf ("Allocated space (resulting string size) was %ld bytes\n\n", StringSize);
Result = TRUE;
/* Copy over strings */
#ifdef __PPC__
RawDoFmt (CtrlStr, args, (void (*)(void))CpyChr, *dest);
#else
RawDoFmt (CtrlStr, &dest + 1, (void (*)(void))CpyChr, *dest);
#endif
END:
return (Result);
}
-
@Thomas
:lol: I knew you were gonna say something...
That's just a scratch to try it out. I'll read that varargs chapter in the OS4 docs later..
I don't even have the OS4 docs though, but I want my code to run on OS4 and MOS so sooner or later I'll have to deal with it.
@Piru
Wow! I'll have a look at it this evening, thanks.
-
@Piru
"Basically your CountChrs and CpyChr didn't use correct registers."
Why? I took care to have them receive the arguments as described in the RawDoFmt docs.
:-? I'm on 68K so &dest+1 shouldn't be a problem here.
-
@Jose
Why? I took care to have them receive the arguments as described in the RawDoFmt docs.
I didn't see that at least...
/* Count nr. of times it's called (= string size) */
void CountChrs (char ch, LONG *StringSize)
{ ++*StringSize;
}
void CpyChr (char ch, char *dest)
{ *dest++ = ch;
}
How does that make sure 'ch' is in D0 and 'StringSize'/'dest' in A3?
This is something you'd have to do:
/* Count nr. of times it's called (= string size) */
#if defined(__GNUC__) && defined(mc68000)
void CountChrs (char ch __asm("d0"), LONG *StringSize __asm("a3"))
#elif defined(__SASC)
void __asm CountChrs (register __d0 char ch, register __a3 LONG *StringSize)
#else
#error unsupported compiler/cpu combo
#endif
{ ++*StringSize;
}
#if defined(__GNUC__) && defined(mc68000)
void CpyChr (char ch __asm("d0"), char *dest __asm("a3"))
#elif defined(__SASC)
void __asm CpyChr (register __d0 char ch, register __a3 char *dest)
#else
#error unsupported compiler/cpu combo
#endif
{ *dest++ = ch;
}
Now you perhaps understand why I prefer direct UWORD arrays instead of trying to trick the compiler into doing the right thing (tm)...
I tried to explain it in the other thread (http://www.amiga.org/forums/showthread.php?t=17754).
I'm on 68K so &dest+1 shouldn't be a problem here.
It isn't.
-
Got it! And IIRC VBCC uses yet another syntax for registers...
-
VBCC uses __reg("d0").
Bye,
Thomas