[HN Gopher] Polymorphic Types in C [pdf]
___________________________________________________________________
Polymorphic Types in C [pdf]
Author : sigsev_251
Score : 92 points
Date : 2024-01-15 10:02 UTC (3 days ago)
(HTM) web link (open-std.org)
(TXT) w3m dump (open-std.org)
| vkazanov wrote:
| Wow, suggesting polymorphic types to WG14 is... brave!
| illegalmemory wrote:
| I had worked on something similar around 11-12 years back ( no
| way close to perfect or of any use :) )
| https://github.com/nautical/CBullet
|
| Basically you can do something like this in c :
|
| var value;
|
| value = data(100);
|
| // or
|
| value = data(0, 2, 3, 4, 5, 6);
| pjmlp wrote:
| The answer to most post-C89 features could be just use C++, but
| yeah, changing human nature is a lost fight.
| j16sdiz wrote:
| C++ is somewhat bloat.
|
| We need a "C++ lite".
|
| We do this by moving features from C++ to C, bit-by-bits, until
| C become bloated.
| pjmlp wrote:
| C would do better improving what matters, the lack of safe
| enumerations, strings and arrays, but even that they fail to
| copy from C++.
| lebubule wrote:
| Yes, please better support for arrays, mainly
| multidimensional. Fortran is good for that. With good
| arrays I think that strings comes naturally.
| uecker wrote:
| What is wrong with multi-dimensional arrays in C? It
| works far better than in C++:
|
| void foo(int X, int Y) { double a[X][Y]; .. }
|
| In fact, this is one reason I switched away from C++,
| because arrays are so bad in C++.
| pjmlp wrote:
| That is a CVE waiting to be exploited.
| uecker wrote:
| No.
| pjmlp wrote:
| It definitely is, given a nice combination of parameter
| values, stack sizes and careless programming.
| abcd_f wrote:
| > _somewhat_ bloat
|
| ... is somewhat understatement.
| logicprog wrote:
| It's really frustrating to watch this happen, first with
| using arrays to mean non void pointers, then with _Generic
| for dispatching on types, and now this. And the worst part to
| me is not necessarily that they're complicating or "bloating"
| C, because these _are_ necessary features, the worst part to
| me is that they seem to simultaneously recognize these
| features are necessary, and not want to commit to a full and
| proper implementation of them like e.g. proper generics or
| simple templates. Instead they create stuff like this, where
| they haven 't fully committed and so it's weird and awkward
| to use and doesn't feel fully integrated into the language,
| with less clear semantics, so in the end it ends up kind of
| _worse_ than C++, whereas what I wish they 'd do, if they're
| going to bring features from C++ to C, is just fully and
| properly _implement the feature_ , but make it as simple and
| limited and basic as possible -- the basic concept of
| templates and monomorphization makes sense to me, buy I'm not
| exactly sure how this works at compile time.
|
| It this point, if I want generics, I'd rather just use C++.
| uecker wrote:
| Note that a type system with polymorphic and dependent type
| system is far more powerful than generics or simple
| templates. In my opinion monomorphization is rather bad,
| because it creates a lot of compile time bloat while still
| not being very expressive from a type system point of view.
| vkazanov wrote:
| Many in this industry dislike C++'s approach to language
| design: "let's add everything and see what sticks".
| pjmlp wrote:
| C is just the same, it just happens that not many people feel
| like showing up on WG14 meetings and push for their
| proposals.
|
| Showing up at WG21 is much more appealing to most folks.
|
| Maybe compare how C23 compares to C89, and future roadmap.
| flohofwoe wrote:
| > Showing up at WG21 is much more appealing to most folks.
|
| ...and let's just hope it stays that way ;)
| flohofwoe wrote:
| C++ has added more new design warts on top of C than C ever had
| though (which means that C++ is now caught in an infinite cycle
| of trying to fix warts it had introduced itself a couple of
| years ago).
|
| At least in C there's a possibility to learn from C++'s
| mistakes, and only integrate the 'good parts' (and the C++
| template system certainly isn't one of those good parts).
| pjmlp wrote:
| Unfortunely doesn't look like it is happening any time soon.
| logicprog wrote:
| Yeah, that would be the ideal -- take just the simplest,
| cleanest version of C++'s good parts and integrated them. But
| lile the sibling post said, that's not what they're doing.
| Instead they're designing their own new versions of each
| feature that they add, and because it seems they're not
| willing to fully commit to new features or changing the
| language much, the features are always _weirder_ with _more_
| gotchas and _less_ baked than thr C++ versions. It isn 't so
| bad right now because the language is overall much smaller,
| but if they eventually grow to anything close to the size of
| C++ (and C++ isn't even that big of a language anymore
| compared to other modern languages) it'll actually be way
| worse.
| uecker wrote:
| Having switched from C++ to C, I think C is a lot less
| weird than C++. But I also observed that C++ programmers
| usually do not "get" C (but think they do, because "it is
| just a subset") and often think some parts are weird simply
| because they are different.
|
| In any case, if you need a clean subset of C++ (but nobody
| I have met really agreed about what this is) nobody is
| stopping you from defining one.
| logicprog wrote:
| > Having switched from C++ to C, I think C is a lot less
| weird than C++.
|
| Overall? Without a doubt. But these specific features
| they've been adding are implemented very unnecessarily
| strangely just to avoid fully committing to them, so that
| they end up in this design space where they may not be
| weirder than the full Lovecraftian horror of the
| equivalent C++ feature, but they are much weirder than
| the ideal/abstract concept of the equivalent C++ feature,
| which is what I wish they would have pulled in. Like,
| looking at this proposal, it isn't clear to me at all how
| the compiler is actually compiling or the runtime is
| executing these polymorphic function calls at all, in
| cases where they don't just collapse to void*. And it
| isn't clear to me how exactly type scoping and
| substitution works either. Whereas in concept at least
| templates are crystal clear and also easy to expand to do
| much more advanced macro-less metaprogramming, like D
| does.
|
| > and often think some parts are weird simply because
| they are different.
|
| I have way more experience with C than C++, but I'm
| usually a Rust programmer which works more like C++ so
| maybe that's where my bias is, but I really think this
| implementation is unnecessarily weird in general too --
| I've used a lot of languages.
| phkahler wrote:
| >> At least in C there's a possibility to learn from C++'s
| mistakes, and only integrate the 'good parts
|
| That's what Rust looked like in the beginning - a better C++.
| But that seems to be getting corrupted by a bunch of language
| designers too now.
| sigsev_251 wrote:
| Curious, why do you think Rust does not look like a better
| C++ (in the sense of not making the mistakes of it)? Is
| there something you find impractical?
| phkahler wrote:
| It still does look like a better C++, but I think it's
| being extended way too fast and might suffer the same
| shitificantion if they don't stop it.
| logicprog wrote:
| Yeah, I'm a huge Rust fan, but some of the decisions
| they've been making with the newer RFCs, especially
| around things like convenience syntax, and the fact that
| they now have async/await _and_ neutered coroutines,
| instead of having proper coroutines capable of doing both
| ala Kotlin, among other things, seem really iffy to me. I
| 'm afraid it'll turn into Scala or something soon, with a
| huge bag of redundant or partially overlapping features.
| crabbone wrote:
| I've been somewhat successful to use Ada instead. It has a lot
| of inconveniences of its own, but compared to C++ it's a
| language where it's hard to get things to work in any way, but
| once you get them to work, they are likely to do what you want,
| whereas in C++ it's easy to get things to compile, but then
| heaven only knows what that code will do.
|
| Also, Ada has a very decent interop with C. To the point that
| w/o any prior experience, I needed to use some functions from
| SQLite that weren't already exposed through Ada, and it was a
| pretty smooth sailing: very little "glue" code, all code that
| connected C and Ada was written in Ada.
| pjmlp wrote:
| Ada would be even better, unfortunely it is tainted by its
| past, and not an easy sell for most folks.
| tonyedgecombe wrote:
| My guess is this is what most people do. The few remaining
| holdouts are busy turning C into something that isn't C because
| they don't like things that aren't C.
| kitd wrote:
| There's libcello if you want to stay in C
|
| https://www.libcello.org/
| synergy20 wrote:
| which is inactive and the author has no intention to evolve
| it I think
| flykespice wrote:
| What about Ziglang?
| pjmlp wrote:
| In a way, Zig to me is Modula-2 with a revamped syntax for C
| minded folks, with a nice meta-programming story. In terms of
| language features.
|
| Personally I am not a big fan, because it doesn't have a good
| story for use-after-free (other than adopting similar
| analyser tooling like C derived languages), if I want to type
| @ all over the place I can use Objective-C, and the community
| doesn't seem very keen in supporting binary library
| distribution.
|
| That is me, we don't have to like all the same things.
|
| Even with UAF caveat, it still better than plain old C.
|
| Personally I find Odin more appealing than Zig.
| vbtemp wrote:
| I wish I had more than one upvote to give - what you just wrote
| is the truth of the matter.
| pgen wrote:
| Great, but I hate these leading underscores.
| injuly wrote:
| It's for backwards compatibility of C compilers (and lack of
| proper namespaces). All identifiers starting with an underscore
| are reserved for use by compiler intrinsics and such. Although
| most compilers don't complain if your variable names start with
| a leading underscore, it is recommended to not have such
| identifiers in your code.
| vkazanov wrote:
| They will probably come with macro-based aliases, the same way
| they did it elsewhere.
| Gibbon1 wrote:
| I feel increasingly annoyed with the idea that C can never have
| new keywords. As if that's some sort of huge problem with
| existing code bases.
| flohofwoe wrote:
| This is so that the new keywords don't collide with existing
| code (the combination of underscore followed by a capital
| letter is reserved). For instance until C23, 'bool' was
| actually called '_Bool' internally.
|
| Just as with stdbool.h before, there could be a stdlib header
| which wraps those internal names into something more human-
| friendly.
| uecker wrote:
| Me too.
| injuly wrote:
| Polymorphic types are certainly useful, and I wish C had a better
| way to go about it, but this proposal feels like an odd patch to
| C's type system. Especially this part:
|
| ``` Using a run-time value of type _Type T in _Var(T) can be
| allowed in general (and is useful), but needs to be restricted to
| contexts where full type information is not required at compile-
| time. ```
|
| Semantic rules that conditionally apply only in some contexts is
| a common tendency of the C++ standard that many C programmers
| often dislike.
| nxobject wrote:
| Here is another way in which this is a little patch-like. The
| author observes that:
|
| (a) it is desired that _Var(T)* and void* be compatible;
|
| (b) however. pointers-to-T for different T are not guaranteed
| to be compatible in general,
|
| as a consequence, it is NOT guaranteed that _Var(T)* is
| compatible with T*, which is a theoretical wart worthy of C++.
| I can see it becoming especially annoying if you're trying to
| introduce _Var(T) polymorphism into a code base that currently
| uses preprocessor macros that textually substitute types.
|
| Perhaps the better solution would be forgo giving void* any
| special status whatsoever. However, that means that this type
| of polymorphism can't be implemented using void* as a polyfill.
| uecker wrote:
| I don't think this is a deficiency, but an inherent property of
| powerful type systems. If you make them very expressive, then
| you can not have full type checking at compile time. On the
| other hand, if you restrict them so that you do full static
| type checking, than they are very limited.
| lerno wrote:
| The `any*` fat pointer in C3 is a version of this:
| https://c3-lang.org/anyinterfaces/ Once this is available the
| step to embedding some runtime type information isn't far.
| eps wrote:
| Clean, very nice.
| eddd-ddde wrote:
| I did not know about C3. I love C because is what I first learn
| to program, the simplicity, the complete lack of magic. Looks
| like I will love C3 as well:
|
| > Avoid "big ideas".
| keyle wrote:
| Interesting. And reading all the changes from C, it looks like
| what occasional C developers should always remind themselves
| when coming back to C. That's a useful list by itself.
| eps wrote:
| As a (not so) minor point -
|
| It's worth keeping in mind that code aesthetics is an important
| aspect of C codebases. There's a lot of C code that is
| exclusively lowercase, sans the macros. So introducing keywords
| like _Type and _Var will serve to hinder their adoption, because
| it'd make the code that much more "ugly". Just like what happened
| with _Generic - a reasonable feature, bad keyword selection ->
| barely any field use.
| isomorphic- wrote:
| The C specification mandates that new keywords use _Keyword
| naming conventions to ensure backwards compatability by not
| overriding potentially existing identifiers in codebases. That
| is why the C specification has reserved identifiers that begin
| with an underscore and either an uppercase letter or another
| underscore.
|
| Typically, a <stdkeyword.h> header is included that contain
| macros to provide the lowercased variants. I.E., this is how
| _Bool was implemented; <stdbool.h> provides the lowercased
| `bool` variant.
| eps wrote:
| C23 is scheduled to promote bool, alignof & co. to keywords,
| so the concern for using _Xxx keywords is recognized by the
| committee. They introduce _Xxx keywords, sometimes alias them
| to lowercase versions with macros and let this age. Then,
| some time after, they switch to the "primary spelling", which
| is how the lowercase versions are referred to.
|
| You can't easily lowercase _Type and _Var, so practically
| speaking it will take years before these features could be
| suitable for wide-spread adoption. Hence my original comment
| - given the friction, is it worth expanding the language this
| way at all then?
| lifthrasiir wrote:
| _Bool etc. came with convenience macros defined from
| <stdbool.h> and so on, but _Generic never did, suggesting
| that the underscored version was meant to stay forever that
| way. (Otherwise it should have been named as something like
| _Generic_switch and later renamed to generic_switch...)
| Maybe _Type and _Var are similarily intended.
| sigsev_251 wrote:
| I mean, _Generic is usually hidden behind a macro
| anyways, so it doesn't really show up in you text file
| all that much.
| Dwedit wrote:
| You can still use virtual function calls in plain C, you just do
| things the same way that C++ does things internally. Your first
| member of the struct is a Vtable, and you need to assign that
| member when you create the object. Your first parameter for the
| virtual method calls is a "this" pointer.
| binary132 wrote:
| Slight detail: there only needs to be one instance of the
| vtable per class, and objects of that type only need to contain
| a pointer to it.
| Dwedit wrote:
| And you can also make the vtable `const` so it goes into the
| read-only data section.
| huhtenberg wrote:
| By the same argument we don't really need C to begin with,
| because things can still be coded in assembly. It all boils
| down to convenience of reducing boilerplate. As the meme goes -
| "You don't need sneakers to run, but they sure help."
| phkahler wrote:
| And if I understand your point its that we should embrace
| higher level tools rather than trying to build abstractions
| in lower level tools. In that case OP should just use C++
| rather than trying to build an abstraction into C. That going
| higher level is actually why C++ was created in the first
| place.
| huhtenberg wrote:
| The point was that if there's a certain coding pattern that
| can be accommodated by adding a new language feature, it is
| worth considering. The fact that it's readily doable with
| some effort in some other way is not an argument against
| it.
|
| C++ started as a reasonable extension to C, but now it's
| just something else (nor does it even want to be seen
| related to C now anyway). Extending C with something new,
| without rushing, is a perfectly fine idea.
| o11c wrote:
| The problem with rolling your own virtual tables without
| compiler support is that devirtualization is generally not
| possible, even in fairly easy cases.
| brcmthrowaway wrote:
| Isn't this GObject?
| smcameron wrote:
| Is it just me, or are disillusioned C++ refugees who've gone back
| to C now trying to wreck it a second time? Probably just me.
| Since the 80's, C++ served as an excellent decoy to absorb
| craziness and keep C sane.
| zabzonk wrote:
| it is just you. why would anyone go back to C from C++? if you
| want to write C in C++, you can do that, with the advantages of
| stronger type checking.
| SV_BubbleTime wrote:
| You can use the C syntax in C++. But you are not using C,
| your compiler is still C++.
| zabzonk wrote:
| yes the compiler is the same (for example gcc or msc) but
| why would you care?
| burstmode wrote:
| Many people, Especially sice C++ has become a playfield for
| CS language theorists, who invent more and more
| overcomplicated language "features" with little to no
| practical use.
| pjmlp wrote:
| That is still Haskell.
|
| Getting features into WG21 still requires some effort to
| get them through, regardless of what people in the outside
| think, and yeah I do agree it could get some more
| direction.
|
| C++ may seem to get everything dumpped into it, however any
| language nerd that feels like diving into what the history
| of languages with similar age (Python, Perl, Ada, C#, Java,
| F#, OCaml,...) have across all their versions, standard
| library, main interpreter/compilers, .... will find out
| those aren't much better either.
| zabzonk wrote:
| nobody makes you use these features, but when or if you
| come to writing a library, you may well find them useful.
| uecker wrote:
| I switched back to C from C++. The reasons were that C
| removes a lot of unnecessary complexity which helps
| concentrating at the problem at hand. It also removes a lot
| of features that when used incorrectly makes the code much
| harder to understand. So if one often has to work with code
| written by unexperienced programmers (as I have to do), I
| found C++ very problematic. The stronger type checking is
| largely a myth.
| chlorion wrote:
| It's definitely not a myth.
|
| A trivial example is assigning pointers to incompatible
| types or passing them to incompatible function parameters
| not being an error. GCC 14 and Clang 16 are only just now
| making these errors by default. C++ never had this problem.
|
| Another example, is having type safe generic functions and
| classes. Using void* to pass around arbitrary types is
| something that you don't need to do in C++ because it
| supports generic programming.
|
| In C++ you can for example, pass an array of a specific
| length to a function, and make it a compile time error to
| pass an array of incorrect length, using std::array. In C
| arrays decay to pointers.
|
| static_case is also a thing in C++ that allows you to cast
| between types much more safely.
|
| There are so many things that I could probably spend
| several hours typing them out and I don't think that is
| really needed.
|
| C++ is certainly more type safe than C, whether or not you
| think the tradeoffs it makes are worth is something to
| debate for sure, but it gives you tools to write type safe
| code that simply don't exist in C.
|
| https://wiki.gentoo.org/wiki/Modern_C_porting#What_changed.
| 3...
| vbtemp wrote:
| It's not just you.
|
| The only thing I'm confused about is why more C standards keep
| coming out. C is what it is. Work with the archaic parts of it
| as need be. You use C for ultimate cross-platform compatibility
| (every exotic platform and its mother has an ANSI-C/C99
| compiler). If you're able to run on the most bleeding-edge C
| compiler supporting the most recent WG version of C... then why
| not just use another language? I say this as someone who loves
| C. If there are bits that feel old, you just create a DSL to
| get around these problems that compiles down to C... but you
| don't change C itself.. Just my perception working with all
| this for many years now.
| hgs3 wrote:
| For me the big problem with most C competitors is they go
| overboard. The only alternative I've tried that sparked my
| fancy was D with -betterC flag. It's unfortunate that it's
| not its own language, but rather a "profile" of a much much
| larger language.
| rwbt wrote:
| There's a lot of feature gap between C and C++. Many including
| me like to program in C but want just a _little_ more
| safeguards, syntax sugar and some established ideas (like
| defer) that improve the programming experience.
|
| Now there are languages like Odin, Zig and C3 that are trying
| to fill that niche but having C evolve slowly to accommodate
| such users is also a great idea.
|
| C99 added Designated Initializers which IMHO is probably the
| most innovative declarative syntax to initialize anything and
| it took C++ roughly 20 years to adopt such a thing (and then
| they had to nerf it).
| sfpotter wrote:
| What problem does this solve that isn't superficial? I don't see
| why it's important to include.
| sigsev_251 wrote:
| Generic libraries. There are programming domains in the
| embedded world where code reuse can shorten the development
| timeline in a significant way, which gives engineers more time
| to test and verify.
| sfpotter wrote:
| Example? Why not just use C++?
| sigsev_251 wrote:
| Because there are platforms with no, or really buggy C++
| support.
| sfpotter wrote:
| What is an example of an embedded systems project which
| truly benefits from a generic library in the way that you
| described?
| sigsev_251 wrote:
| Aviation would be my personal experience. We have to
| implement services on the aircraft for structured
| communication support with the ground tower. Those would
| really benefit from something like that, since we
| wouldn't have to implement a backend for every project
| and we could simply reuse the whole library everywhere.
| sfpotter wrote:
| I don't see how generic programming (a la polymorphic
| types as presented) is the only or even the best solution
| to this problem.
| phkahler wrote:
| That sounds like a someone else's problem. Find a
| platform that supports the language you need rather than
| try to make C into that language.
|
| It is not C's job to be everything to everyone.
| uecker wrote:
| 1. Type safety for generic interfaces such as qsort. 2.
| Language interoperability. Then you need to have a dynamic
| framework for constructing types and function calls.
| sfpotter wrote:
| If you're in a situation where you "truly need" type safe
| generic interfaces, why wouldn't you just use a different
| language?
|
| I don't understand your second point.
| uecker wrote:
| There is no other languages with the same advantages as C
| and a good type system.
| uecker wrote:
| The other point: For interoperability with other languages
| you often need to construct function calls at run-time. For
| this one needs to be able to describe types. Adding this by
| external libraries is always a pain because one has to
| carefully support the ABI of all supported architectures. A
| compiler can support this seamlessly. A _Typeof operator
| that returns a type that can be passed around and
| interrogated at run-time solves this in a very elegant and
| simple way.
| hgs3 wrote:
| Hi Martin, thank you for writing this proposal. This is just
| my two cents, but one-off void* functions, like qsort, are
| less of a pain point relative to generic containers. With
| generic containers it's common to have a collection of void*
| functions that must be consistently invoked with identical
| type T. Correct me if I'm wrong, but this proposal cannot
| genericize a struct field, i.e. it can genericize type 'T'
| but not 'T->someField'. The latter would be useful for
| something like 'vec_push(v,p)' where 'v->data[]' is the type
| T needed to determine if 'p' is a compatible type.
|
| Tangentially related, the macro-based containers you've
| written here [1] are the best answer for type-generic
| containers I've come across. One "gotcha" is the container
| name must be a valid C identifier otherwise it doesn't token
| paste correctly (see Example #2 of your REAMDE where you
| typedef'd string* as string_ptr to workaround this). Would
| you give consideration to a new preprocessor mechanism for
| concatenating a list of tokens into a single valid C
| identifier? i.e. Something like CONCAT(struct Foo *) would
| produce struct_Foo_Ptr? The result is guaranteed token paste-
| able.
|
| [1] https://github.com/uecker/noplate
| synergy20 wrote:
| IMHO, the C committee should just copy a subset from C++, in
| addition to having new features for free, you also make sure c
| and c++ are 100% compatible.
| up2isomorphism wrote:
| There is no point to make sure C/C++ 100% compatible and also
| you can not as of now.
| muragekibicho wrote:
| I use C at my day job and our company works with ANSI C. I
| believe it's C89. I was curious to find out - who's using C23 in
| prod? Please let me know in the comments. I'm super curious!
| ptek wrote:
| Embedded developer or are you porting software?
|
| Is it C89 or gnuc89? gnuc89 is the one that allows // for
| single line comments, I don't know what other features gnuc89
| adds over c89.
|
| I also call C89 ANSI C :)
|
| Are you guys using gcc or clang?
___________________________________________________________________
(page generated 2024-01-18 23:02 UTC)