and of course I got stuck cause I can't figure out a good way of doing some sort of generic function call type. I thought about variadic arguments immediately, but I'm unsure about if it's possible to cast something like void(char) to void(...) without messing up the actual calls.
Example:
1 2 3 4 5 6 7 8 9 10 11 12 13
void addOne(int* a)
{
*a += 1;
}
int main()
{
void(generic_call*)(...);
generic_call = (void(*)(...))addOne; // is this Undefined Behavior?
int a = 0;
generic_call(&a); // Or is this Undefined Behavior..?
}
Function pointers in C are of a type based on the signature of the function, as with std::function.
Yeah, but that's possible because you're either in C++ and have template information to use or you're doing it for a specific type like in your example, right?
To add to what's said above, you need to "know" the correct type because the calling convention, number of and type of args and processing of possible return value needs an exact match.
So if you load a function/class from a shared lib/dll, and you know the signature, then you can do the cast.
The whole point of type safety is to catch your errors at compile time.
What you want to do is completely impossible in C and C++. The languages are simply not designed to support such dynamism on function calls.
Briefly, the language would need some way to represent the types of objects as objects unto themselves (i.e. metatypes, where typeof(int) is a value of Type, and typeof(std::string) is another value of the same class), so that they can be compared. You would be making every function call much more expensive, as well.
@kbw & @helios
eheh... 😅 now I'm kinda embarrassed lol.
I was just gonna make a function like
1 2 3 4
function call_sfunc(StatefulFunc *sfn, ...)
{
((void(*)(...))sfn)(...); // ummmmm don't remember how to do non-templated variadic stuff apparently...
}
If it wasn't undefined........
tho @helios I'm not entirely sure what you mean. What is it you think I'm attempting to do...? My stipulations were that it would be considered undefined behavior to
cast something like void(int) to void(char), but that casting from void(int) to void(...) would be ok, given that it isn't undefined behavior. Do the first doesn't really make any sense for the reasons you give, but the second...? Idk... doesn't seem like it would be involved in such a thing. I probably would have answered my own question if I knew more about how variadic functions work but like I never got that far, ya know? lol I don't remember what the point of this entire paragraph is anymore XD
If you just want to cast the pointer, that's no problem. The problem comes when you try to actually call the function.
but that casting from void(int) to void(...) would be ok, given that it isn't undefined behavior. Do the first doesn't really make any sense for the reasons you give, but the second...? Idk... doesn't seem like it would be involved in such a thing.
Think about it. If a void (*)(...) could work as a generic function pointer for any function returning no values, the compiler would need to generate at call site code that is compatible with any possible function signature on the other side of the pointer.
How many values should the caller push in each call? Do f() and g() need to do anything to support this behavior? What about this?
foo("bar");
If you say "well, if foo points to f then you should only call it with a single int, and if it points to g you should only call it with two", then you don't really want generic pointers, you want to select between any of a finite number of pointer types, and that's easily doable with either unions or class hierarchies.
I mean I was thinking more along the lines of having a caveman compiler read this:
1 2 3 4 5 6
void func(...)
int main()
{
func(3, 4);
}
and just blindly pushing 2 ints to the stack, no questions asked. Which is of course why I was like this is probably undefined behavior. like... like if I take your foo("bar") example, then the compiler would just push a char pointer, no questions asked. In this version of things, the actual call to a variadic function will always look exactly the same as a normal function call under the covers, and the function itself would handle the fact that it doesn't know it's own argument length...? Which seemed viable enough until just now when I remembered I had some things backwards lol um... yeah.
As per the standard, it is undefined behaviour. But,
Permissible undefined behavior ranges from ..., to behaving during translation or program execution in a documented manner characteristic of the environment... http://eel.is/c++draft/intro.defs#defns.undefined
In practice, we could do something like the equivalent of this in C (using macros, minus the perfect forwarding):