-
October 3rd, 2019, 05:29 AM
#1
Pointer Madness
Way in over my head trying to pass a variable number of arguments ( pointers ) to a routine.
Arguments are passed in pairs, the variable type and then its address. When passing variables of type int things work, but byte and long types fail.
Usage
Code:
byte TestByte = 10;
va_func(1, 0, &TestByte); // Fails
int TestInt = 20;
va_func(1, 1, &TestInt); // Works
long TestLong = 30;
va_func(1, 2, &TestLong ); // Fails
Routine
Code:
va_func(int ArgCount, ... );
{
va_list Args;
va_start(Args, ArgCount);
for (int I = 0; I < ArgCount; I += 2)
{
/*
* Argument type
*/
int VarType;
VarType = va_arg(Args, int)
printf("Type %d\n", VarType );
/*
* Argument address
*/
long VarAddress;
VarAddress = va_arg(Args, int)
printf("Address %d\n", VarAddress);
/*
* Argument Value
*/
void *Ptr;
Ptr = (void *)VarAddress;
printf("Value %d\n", *Ptr);
.....Code to process arguments goes here, returning a new value (NewValue)
*Ptr = NewValue;
}
va_end(Args);
}
-
October 3rd, 2019, 08:04 AM
#2
Re: Pointer Madness
for va_arg(), you need to know the type of the argument for the 2nd parameter. It won't auto-detect. That's why, for example, in printf() you have to specify the type of each of the arguments in the format string - so that the correct usage of va_arg() is used to correctly obtain the value for each argument.
That's why as you are using int for va_arg(), the code only works when an int is passed.
Are you using c or c++? If c++, then it would be better to use templated variadic arguments. See https://en.cppreference.com/w/cpp/la...parameter_pack
Last edited by 2kaud; October 3rd, 2019 at 08:46 AM.
Reason: Changed web reference
All advice is offered in good faith only. All my code is tested (unless stated explicitly otherwise) with the latest version of Microsoft Visual Studio (using the supported features of the latest standard) and is offered as examples only - not as production quality. I cannot offer advice regarding any other c/c++ compiler/IDE or incompatibilities with VS. You are ultimately responsible for the effects of your programs and the integrity of the machines they run on. Anything I post, code snippets, advice, etc is licensed as Public Domain https://creativecommons.org/publicdomain/zero/1.0/ and can be used without reference or acknowledgement. Also note that I only provide advice and guidance via the forums - and not via private messages!
C++23 Compiler: Microsoft VS2022 (17.6.5)
-
October 3rd, 2019, 08:25 AM
#3
Re: Pointer Madness
I was thinking along those lines but was tripping up on the fact that I'm passing in pointers or addresses not values.
I assumed that by passing addresses they would all be of the same length / number of bytes based on the OS, a 32 bit OS would push four bytes on the stack no matter what the data type of the variable being passed. Or so I thought.
With addresses on the stack, not values, wouldn't va_arg always grab a 'pointers size' worth of data?
As for the language, C or C++, I'm not completely sure. I'm using AVR-GCC for micro controllers / embedded systems.
-
October 3rd, 2019, 08:46 AM
#4
Re: Pointer Madness
You can do this:
Code:
void va_func(int ArgCount, ...)
{
va_list Args;
va_start(Args, ArgCount);
for (int I = 0; I < ArgCount; I += 2)
{
//Argument type
const int VarType = va_arg(Args, int);
printf("Type %d\n", VarType);
//Argument address
const void* VarAddress = va_arg(Args, void*);
printf("Address %p\n", VarAddress);
//Argument Value
printf("Value %hhi\n", *(char*)VarAddress);
}
va_end(Args);
}
int main()
{
char TestByte = 10;
va_func(1, 0, &TestByte);
int TestInt = 20;
va_func(1, 1, &TestInt);
long TestLong = 30;
va_func(1, 2, &TestLong);
}
which displays on my computer:
Code:
Type 0
Address 0018FF3F
Value 10
Type 1
Address 0018FF38
Value 20
Type 2
Address 0018FF34
Value 30
BUT for the argument value, you need to know the type of the argument for the appropriate cast. It works here as the cast is to char and the values are all within the char range. However, if the value for the int and long were outside of the char range then it wouldn't. If you are using c++, that's why variadic templated functions are better.
PS
As a value referring to the type is passed as the first arg of the pair following the initial number of pairs value, consider:
Code:
void va_func(int ArgCount, ...)
{
va_list Args;
va_start(Args, ArgCount);
for (int I = 0; I < ArgCount; I += 2)
{
//Argument type
const int VarType = va_arg(Args, int);
printf("Type %d\n", VarType);
//Argument address
const void* VarAddress = va_arg(Args, void*);
printf("Address %p\n", VarAddress);
//Argument Value
switch (VarType) {
case 0:
printf("Value %hhi\n", *(char*)VarAddress);
break;
case 1:
printf("Value %i\n", *(int*)VarAddress);
break;
case 2:
printf("Value %li\n", *(long*)VarAddress);
break;
default:
printf("Unknown type %i\n", VarType);
break;
}
}
va_end(Args);
}
int main()
{
char TestByte = 10;
va_func(1, 0, &TestByte);
int TestInt = 2000;
va_func(1, 1, &TestInt);
long TestLong = 300000000;
va_func(1, 2, &TestLong);
}
which as expected displays:
Code:
Type 0
Address 0018FF37
Value 10
Type 1
Address 0018FF38
Value 2000
Type 2
Address 0018FF3C
Value 300000000
Last edited by 2kaud; October 3rd, 2019 at 10:22 AM.
Reason: PS
All advice is offered in good faith only. All my code is tested (unless stated explicitly otherwise) with the latest version of Microsoft Visual Studio (using the supported features of the latest standard) and is offered as examples only - not as production quality. I cannot offer advice regarding any other c/c++ compiler/IDE or incompatibilities with VS. You are ultimately responsible for the effects of your programs and the integrity of the machines they run on. Anything I post, code snippets, advice, etc is licensed as Public Domain https://creativecommons.org/publicdomain/zero/1.0/ and can be used without reference or acknowledgement. Also note that I only provide advice and guidance via the forums - and not via private messages!
C++23 Compiler: Microsoft VS2022 (17.6.5)
-
October 3rd, 2019, 10:10 AM
#5
Re: Pointer Madness
I'm starting to think I'm dealing with a compiler issue, no matter what I try I can't get this to work. I went with long vs int to see if the long variable would work knowing full well that it would break the int version, but still no go.
I tried using the code provided by 2kaud, thanks by the way, but the compiler doesn't like the syntax.
When trying to use va_arg with the data type of byte the compiler gets mad.
'byte {aka unsigned char}' is promoted to 'int' when passed through '...'
Which has me thinking that the compiler is always defaulting to int which would explain why its the only data type that works - heck, I don't know whats going on, such a bummer.
-
October 3rd, 2019, 10:20 AM
#6
Re: Pointer Madness
What compiler are you using? The code posted in #4 compiles OK and runs producing the given output using MS VS 2019.
All advice is offered in good faith only. All my code is tested (unless stated explicitly otherwise) with the latest version of Microsoft Visual Studio (using the supported features of the latest standard) and is offered as examples only - not as production quality. I cannot offer advice regarding any other c/c++ compiler/IDE or incompatibilities with VS. You are ultimately responsible for the effects of your programs and the integrity of the machines they run on. Anything I post, code snippets, advice, etc is licensed as Public Domain https://creativecommons.org/publicdomain/zero/1.0/ and can be used without reference or acknowledgement. Also note that I only provide advice and guidance via the forums - and not via private messages!
C++23 Compiler: Microsoft VS2022 (17.6.5)
-
October 3rd, 2019, 10:33 AM
#7
Re: Pointer Madness
Well for VS 2019, extending this scheme to also allow double consider:
Code:
void va_func(int ArgCount, ...)
{
va_list Args;
va_start(Args, ArgCount);
for (int I = 0; I < ArgCount; ++I) {
//Argument type
const int VarType = va_arg(Args, int);
//printf("Type %d\n", VarType);
//Argument address
const void* VarAddress = va_arg(Args, void*);
//printf("Address %p\n", VarAddress);
//Argument Value
switch (VarType) {
case 0:
printf("Value %hhi\n", *(char*)VarAddress);
break;
case 1:
printf("Value %i\n", *(int*)VarAddress);
break;
case 2:
printf("Value %li\n", *(long*)VarAddress);
break;
case 3:
printf("Value %f\n", *(double*)VarAddress);
break;
default:
printf("Unknown type %i\n", VarType);
break;
}
}
va_end(Args);
}
int main()
{
const char TestByte = 10;
const int TestInt = 2000;
const long TestLong = 300000000;
const double testDouble = 3.14159;
va_func(4, 0, &TestByte, 1, &TestInt, 2, &TestLong, 3, &testDouble);
}
which for me displays:
Code:
Value 10
Value 2000
Value 300000000
Value 3.141590
as expected.
All advice is offered in good faith only. All my code is tested (unless stated explicitly otherwise) with the latest version of Microsoft Visual Studio (using the supported features of the latest standard) and is offered as examples only - not as production quality. I cannot offer advice regarding any other c/c++ compiler/IDE or incompatibilities with VS. You are ultimately responsible for the effects of your programs and the integrity of the machines they run on. Anything I post, code snippets, advice, etc is licensed as Public Domain https://creativecommons.org/publicdomain/zero/1.0/ and can be used without reference or acknowledgement. Also note that I only provide advice and guidance via the forums - and not via private messages!
C++23 Compiler: Microsoft VS2022 (17.6.5)
-
October 3rd, 2019, 11:19 AM
#8
Re: Pointer Madness
Yea um - I'm using AVR-GCC for micro controllers / embedded systems.
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|