|
Bruegels Fuckbooks posted:A great feature, but then people have to implement it. Behold: https://devblogs.microsoft.com/cppblog/c1114-stl-features-fixes-and-breaking-changes-in-vs-2013/ I think you are very confused and have learned little bits and pieces of things and came to some incorrect conclusions or something. Before C++ had variadic templates, everyone faked them in a few different ways. Microsoft's approach was not unusual or particularly ugly and did not cause compatibility issues that other approaches did not have. VC++ was late to implement a lot of C++11 features, but the primary cause of that was that there was a period where Microsoft was big on the idea that .NET would entirely replace native development and so didn't have anyone working on their C++ compiler. None of that has anything whatsoever to do with C variadic functions.
|
# ? Jun 25, 2020 22:55 |
|
|
# ? May 17, 2024 14:39 |
|
Plorkyeran posted:I think you are very confused and have learned little bits and pieces of things and came to some incorrect conclusions or something. Before C++ had variadic templates, everyone faked them in a few different ways. Microsoft's approach was not unusual or particularly ugly and did not cause compatibility issues that other approaches did not have. VC++ was late to implement a lot of C++11 features, but the primary cause of that was that there was a period where Microsoft was big on the idea that .NET would entirely replace native development and so didn't have anyone working on their C++ compiler. All of these statements are entirely correct and I would agree with you. Maybe I was a little unclear in my little earlier posts and I'll give you guys some context. I have noticed a pattern with variadic XXX - whether that XXX be functions or templates or whatever. Whenever I see that word, my heart dies a little inside because it reminds me of bugs I had to fix in legacy code when changing compiler versions early in my career. I remember spending days reading over how the VA_LIST poo poo worked in C and being like "jesus christ, who came up with that." Similarly, I encountered a C++ codebase where I had to upgrade a visual studio version for a codebase I wasn't super familiar with and having a very bad experience there (I wasn't complaining specifically about visual studio in this story, it was intended to be an illustration that this poo poo is complicated and can go wrong and that post was helpful and light went off when I read about it.). These are two separate, awful memories in my early career, interspersed with a couple of cases where variadic such and such didn't work on such on such platform for stupid reasons, and now I groan whenever I see the word variadic in a computer programming context.
|
# ? Jun 26, 2020 02:25 |
|
Lol just lol if you don't define 0, 1 and 2-arity overloads for all your reducer functions
|
# ? Jun 26, 2020 11:01 |
|
What is the big draw of variadic functions anyway? Academic circle jerk? Perceived efficiency gain if you don't count increased complexity against it?
|
# ? Jun 26, 2020 11:19 |
|
It's for printf (and scanf). There's a short list of other things that you'd use variadic functions for, but they're all pretty inconsequential - the only reason people bother with them is that variadic functions already exist to support printf and so you might as well use them.
|
# ? Jun 26, 2020 12:16 |
|
Jabor posted:It's for printf (and scanf). Right. So if you were designing it from the ground up, it would be a good time to go screw it, we're not having it. I can see it in bash, but that's just because I'm used to terminal commands with arbitrary modifiers. It doesn't have to be that way. I haven't written much C++, but as far as I can tell from the docs, it's just number formatting that you could do with a string library before you gave the string to printf. Perhaps left aligning and so on depends on whatever system thing printf is printf'ing to, but it's obviously something you could access outside printf, if printf can. Is printf in C very different from that?
|
# ? Jun 26, 2020 13:12 |
|
In all seriousness, the 0 and 1-arity variants let you avoid exception handling and providing default values when reducing on empty or single value lists. In nearly-JS pseudocode of summing a list of numbers: code:
It's not a world-shatteringly useful feature but it is nice to have. Edit: oops multi-arity isn't variadic, my bad. SAVE-LISP-AND-DIE fucked around with this message at 13:16 on Jun 26, 2020 |
# ? Jun 26, 2020 13:14 |
|
It's easier to just always use fold instead of reduce, i.e. provide an extra argument with the initial state. Depending on your language, you can just make it automatically default to the array's element type's default value. Pseudocode: code:
|
# ? Jun 26, 2020 14:31 |
|
Ola posted:Right. So if you were designing it from the ground up, it would be a good time to go screw it, we're not having it. I can see it in bash, but that's just because I'm used to terminal commands with arbitrary modifiers. It doesn't have to be that way. printf in C is the same thing. The complication is that C doesn't have strings.
|
# ? Jun 26, 2020 16:14 |
|
Ola posted:I haven't written much C++, but as far as I can tell from the docs, it's just number formatting that you could do with a string library before you gave the string to printf. Perhaps left aligning and so on depends on whatever system thing printf is printf'ing to, but it's obviously something you could access outside printf, if printf can. Is printf in C very different from that? Apart from your approach worsening the performance significantly, there is a bunch of things that are fundamentally variadic, e.g. tuples, and you need to handle it somehow. C's varargs are terrible because they lack compile time checking, not because variadic.
|
# ? Jun 26, 2020 16:46 |
|
The only thing you should ever actually write a new variadic function for is if you're creating a wrapper function around printf for debug logging or whatever.
|
# ? Jun 26, 2020 20:04 |
|
Xarn posted:Apart from your approach worsening the performance significantly, there is a bunch of things that are fundamentally variadic, e.g. tuples, and you need to handle it somehow. I don't think this follows. Tuples are foundational to most statically typed functional languages, and they usually do not support variadic functions (although Haskell lets you emulate them, sort of, with type classes). Athas fucked around with this message at 07:28 on Jun 27, 2020 |
# ? Jun 26, 2020 20:15 |
|
RPATDO_LAMD posted:The only thing you should ever actually write a new variadic function for The only variadic function I ever wrote allowed C code to call a C++ logging API like fprintf. Thank the powers for Stack Overflow cause I never would've sorted that out on my own.
|
# ? Jun 26, 2020 20:17 |
|
Athas posted:I don't think this follows. Tuples are foundational to most statically typed functional languages, and they usually do not support variadic functions (althoug Haskell lets you emulate them, sort of, with type classes). In a functional language you conceptually only have two arities: unary and nullary. All you in principle need for an arbitrary number of arguments is to construct a function that will return a value when done or "itself" (or a very similar function) when it has more args to consume. Variadic ends up being more a matter of if the type system lets you do all that without choking.
|
# ? Jun 26, 2020 21:41 |
|
Xarn posted:Apart from your approach worsening the performance significantly, there is a bunch of things that are fundamentally variadic, e.g. tuples, and you need to handle it somehow. C's varargs are terrible because they lack compile time checking, not because variadic. Tuples aren't fundamentally variadic, they can be hard limited to any number. "If you need a 17-tuple, it's time to make a type." the compiler might say. And how does it worsen performance? Does the string formatting inside printf happen for free?
|
# ? Jun 27, 2020 11:34 |
|
That's still variadic, you just placed a really low implementation limit on the N Anyway, the performance overhead is caused by having to allocate for every single argument. At least that's how I understood your proposed replacement for print(f) family of functions -- replace variadic printf with a function that puts together a format string and an array of already formatted strings (which need to be allocated).
|
# ? Jun 27, 2020 18:02 |
|
Athas posted:I don't think this follows. Tuples are foundational to most statically typed functional languages, and they usually do not support variadic functions (although Haskell lets you emulate them, sort of, with type classes). If I have a tuple, I have a variadic function, even if I have to wrap it inside a tuple first! This post brought to you by trying to have two variadic packs in a C++ function
|
# ? Jun 27, 2020 18:03 |
|
Product types aren't variadic. They're fundamentally different things.
|
# ? Jun 27, 2020 18:13 |
|
Tuples aren't variadic at all. Functional languages generally compile (a_1,...,a_n) to a uniquely defined n-tuple type (e.g. Haskell has mkTupleTy :: [Type] -> Type). Up to some high n, library implementations provide typeclass instances for your tuple. Above that, you really shouldn't be using tuples anyhow (or add your own). Even without that, all tuples with n>=2 can be represented by nested 2-tuples: (a_1, (a_2, (a_3, ...))). In fact, that's how generic representations of types do things; decompose into product (a,b)/sum Either a b/unit (). If a function expects a n-tuple, you cannot pass a m-tuple with m /= n [unless your tuple implementation is via dependent types and the function is polymorphic over n I guess]
|
# ? Jun 27, 2020 19:28 |
I'm forever impressed that Haskell people managed to get printf working in their super strict type system without variadic functions. (explanation for how it works) The type safe version is probably the one you should actually use, but IMO it's a little less interesting because it's more obvious how it works.
|
|
# ? Jun 27, 2020 20:16 |
|
It's pretty cool what you can do with types. I was binding Godot to Haskell a while back and implemented static inheritance with typeclasses. Unfortunately not the most idiomatic thing, but I need to rewrite that anyways once linear types are out. If anyone's curious, Godot.Api.Auto has the automatically generated typeclasses and instances, and Godot.Methods has method names. The Method typeclass matches names and classes into a signature, and HasBaseClass encodes inheritance itself. There's also a reflexive and transitive extension for HasBaseClass (:<) and some custom type errors so the typechecker doesn't complain when you mess up a method call. To some extent, this does belong in the thread here. But you have to make some sacrifices when binding an OOP game engine to Haskell. KaneTW fucked around with this message at 20:38 on Jun 27, 2020 |
# ? Jun 27, 2020 20:32 |
|
If your usual language doesn't have enough varargs for you, Scala allows multiple (curryable) parameter sections. So a normal declaration would be:code:
code:
code:
|
# ? Jun 27, 2020 21:58 |
|
wtf, Scala doesn't just autocurry all ordered parameters?
|
# ? Jun 27, 2020 22:30 |
|
Yeah, JVM limitations afaik?
|
# ? Jun 28, 2020 00:29 |
|
I might have my terminology wrong, but the way I understand variadic types is that a variadic type can be parametrized over an arbitrary number of types. This then makes e.g. tuple or variant variadic, while something like a pair (is always parametrized with two types) is not.
|
# ? Jun 28, 2020 09:51 |
|
Xarn posted:I might have my terminology wrong, but the way I understand variadic types is that a variadic type can be parametrized over an arbitrary number of types. This then makes e.g. tuple or variant variadic, while something like a pair (is always parametrized with two types) is not. It's a function that takes an arbitrary number of arguments. A pair has two arguments, but they can of course be the same type. Tuples might seem like they can have an arbitrary number of arguments, but there may be a hard limit somewhere in the stock language, which means it has none of the problems of implementing actual variadic stuff and every n-tuple is a rock solid type up to whatever limit there is for n. I like 17, it's a nice and arbitrary number. Also, isn't parsing a seemingly arbitrary number of arguments into an array of strings cheating, like Java or Bash? It's not completely variadic, it's a single array of strings. It does seem like academic circlejerk, but the circlejerking academics don't mind if you implement it safely. That is perhaps the best lesson learned from printf.
|
# ? Jun 28, 2020 11:32 |
|
In Java (at least in the printf case) it's an array of the top type, not an array of string. C doesn't have a top type, so that's out the window. I guess you could require every printf parameter to be a pointer to the actual thing you want to print (which is, when you look under the hood, effectively how Java does it), but that's cumbersome to use if you need to take addresses explicitly, and having the compiler magically do that for you isn't very in keeping with the C ideology.
|
# ? Jun 28, 2020 11:45 |
|
Jabor posted:In Java (at least in the printf case) it's an array of the top type, not an array of string. Yeah, I meant the command line arguments, (String[] args).
|
# ? Jun 28, 2020 11:55 |
|
Xarn posted:I might have my terminology wrong, but the way I understand variadic types is that a variadic type can be parametrized over an arbitrary number of types. This then makes e.g. tuple or variant variadic, while something like a pair (is always parametrized with two types) is not. I'm not even close to being an expert, but I think you can say that tuples are parametrized by a list of types, and then defined inductively on the structure of the list? So in c++ you pattern match on the variadic template arguments' head and tail. Or in a functional language you would use a GADT/inductive family. So I think technically a list of types is just one type (or sort, or whatever). But i think variadic functions are just functions that take a list of arguments, where you don't explicitly construct the list, so I dont know if the distinction matters... urea fucked around with this message at 16:21 on Jun 28, 2020 |
# ? Jun 28, 2020 16:14 |
|
A tuple is usually just shorthand for a perfectly ordinary type. If I have a C++ function that takes a std::tuple<int, float, string> parameter and I try to call if with a std::tuple<int, float, float>, I'll get a compile error because those types don't match. I could have just as well defined defined a struct type with those fields, the std::tuple type is just shorthand. C/C++-style variadic functions defer the type-checking to runtime, if it exists at all. If I call printf("int=%d, float=%f, string=%s\n", 5, 1.0f, 2.0f), I won't get a compile-time error* and in C/C++, there won't be a runtime type check, it's just undefined behavior. It acts something like a python-esque language where there's no static type checking, but also without guaranteed runtime errors for wrong types Java printf() effectively takes an array of Object, just with some syntactic sugar to create the array and box anything that wasn't Object already. That only works since it has a conversion from any type -> Object, and all Object have a ToString(), which is all printf() needs to use. You can't make a function that takes any number of nonhomogeneous types (i.e. something like python's struct.pack() that that takes a format string and some data and returns a byte array with those values packed into it) * for printf, you probably do get a warning on modern compilers, but that's just because they have special case code to recognize printf() formats specifically.
|
# ? Jun 28, 2020 21:12 |
|
If C was made today, I am convinced you could make a type safe, non-variadic "print to console" function that was every bit as a fast as printf. It was a fair mistake made at the time, there is no need to repeat it.Foxfire_ posted:You can't make a function that takes any number of nonhomogeneous types Which begs the question why do you want to make something that seems like you can, with terrible consequences if you get it wrong, instead of making it easy to use with clearly defined rules that says what you can or can't? The most likely answer is academic circlejerk, with "sins of the fathers" a close runner up. Even if you write C today, you don't have to acknowledge or emulate it in any way, you can write whatever you want as type safe as you should. PS: Foxfire_ posted:A tuple is usually just shorthand for a perfectly ordinary type. If I have a C++ function that takes a std::tuple<int, float, string> parameter and I try to call if with a std::tuple<int, float, float>, I'll get a compile error because those types don't match. I could have just as well defined defined a struct type with those fields, the std::tuple type is just shorthand. Is there not some limit of n for std::tuple<type 1, type 2, type 3 ... type n> in C++?
|
# ? Jun 28, 2020 21:38 |
|
Ola posted:If C was made today, I am convinced you could make a type safe, non-variadic "print to console" function that was every bit as a fast as printf. It was a fair mistake made at the time, there is no need to repeat it. There was a thing where somebody tried to compile a file with 1000 lines of println!("Hello world!"); and a VPS with even 1tb of ram was choking on it. quote:Is there not some limit of n for std::tuple<type 1, type 2, type 3 ... type n> in C++? It's not variadic-related but Rust still does that same thing with methods on char arrays. The language has no support for "length of the array" as a template parameter so they just have all the std lib implementations copy-pasted for "array of 1 character", "array of 2 characters", etc up to 32. RPATDO_LAMD fucked around with this message at 22:00 on Jun 28, 2020 |
# ? Jun 28, 2020 21:56 |
|
Ola posted:Is there not some limit of n for std::tuple<type 1, type 2, type 3 ... type n> in C++? It's defined recursively on the variadic template arguments, so if there were a limit, it would be based on the recursion limit of the compiler's template instantiator , not some fundamental limit.
|
# ? Jun 28, 2020 22:05 |
|
Print to console takes one string argument, the stock tuples go up to 17, boom, done. The world is a better place.
|
# ? Jun 28, 2020 22:07 |
|
Ola posted:Print to console takes one string argument, the stock tuples go up to 17, boom, done. The world is a better place. Stock tuples should only go up to 3 or 4 anything longer and you should define an actual named struct or record type or whatever.
|
# ? Jun 28, 2020 22:09 |
|
RPATDO_LAMD posted:Stock tuples should only go up to 3 or 4 I am willing to accept this compromise, even if 17 is the most arbitrary value of n known to mankind.
|
# ? Jun 28, 2020 22:23 |
|
Ola posted:I am willing to accept this compromise, even if 17 is the most arbitrary value of n known to mankind. Listen bud, it's 2^4th, then one more for good measure just because of all the whining if it were merely 16. I hope you don't want 18, because gently caress you if you do.
|
# ? Jun 28, 2020 22:28 |
|
I was being dumb above; Java gets variadic's of heterogeneous types exactly the same way python does: - Take all the excess parameters and dump them into a single array of generic objects - Do a runtime type test when they are accessed and fail then if they're wrong It abandons static type safety just like the C version (python never had static safety to begin with). C/C++ doesn't have a way to implement the runtime type test and has uglier syntax, but is otherwise the same. How do Rust/Haskell/etc.. that have static type-safe variadic's work?
|
# ? Jun 28, 2020 23:22 |
|
Ola posted:If C was made today, I am convinced you could make a type safe, non-variadic "print to console" function that was every bit as a fast as printf. It was a fair mistake made at the time, there is no need to repeat it. It wasn't a mistake at the time. Yes, using modern technology we can do better (cf. the Haskell example linked to earlier), but those approaches all depend on things that hadn't been invented when C was created. And given the relative costs of computer time and programmer time in the 1970s, printf's design was absolutely the right choice.
|
# ? Jun 28, 2020 23:44 |
|
|
# ? May 17, 2024 14:39 |
|
Foxfire_ posted:I was being dumb above; Java gets variadic's of heterogeneous types exactly the same way python does: Every printable type has to implement a function named fmt (inside the Display trait) that writes to a "formatter" object (basically just a character buffer). At compile time the variadic macro in println!("The cat is {} years old", 7) is transformed by And yes, the format string has to be known at compile time. code:
So it basically works the same way as C++'s iostream. RPATDO_LAMD fucked around with this message at 00:06 on Jun 29, 2020 |
# ? Jun 29, 2020 00:00 |