[HN Gopher] Proposal for C23: Improve type generic programming [...
___________________________________________________________________
Proposal for C23: Improve type generic programming [pdf]
Author : HexDecOctBin
Score : 48 points
Date : 2021-01-13 14:20 UTC (8 hours ago)
(HTM) web link (www.open-std.org)
(TXT) w3m dump (www.open-std.org)
| qppo wrote:
| Type inference is great and undeserving of the "bah humbug" type
| comments. Not a fan of it in function return types but that's
| easy to avoid in code review or linting (both of which you need
| plenty in C).
|
| The closure proposal is much more interesting. I skimmed the
| discussion and it points out many of the benefits and some syntax
| proposals, but I'm wondering about memory management.
|
| In C++ a lambda may not be convertible to a function pointer if
| the captured context is non zero sized, and if the captured
| context is larger than the size of a pointer it _must_ use some
| kind of dynamic allocation. C++ has scoped destructors, making
| implementation of that relatively straightforward (free the
| context when the lambda goes out of scope), but what would C
| require? Defer statements? Require a free() without matching call
| to malloc?
|
| Closures in C are still possible using void* as a user context,
| which is common both for closure semantics in libraries as well
| as implementing closures in languages transpired to or
| interpreted in C. The memory management is explicit, the types
| remain explicit (except for the closed over context), albeit a
| bit verbose.
|
| As much as I love functions as types and programming with them
| I'm confused how this is a positive feature for C, which lacks
| some of the semantics that other languages have making closures
| sensible.
| jcranmer wrote:
| So this proposal retains the C++ feature of not allowing you to
| do anything with the captured context. Semantically, it's as if
| you created the captured variables as new function-local
| variables when you initialized the function call, and the
| wording makes it UB to modify the captured variables via a
| pointer.
|
| Where the capture context lives in memory and how it is
| allocated and deallocated is not precisely specified by the
| specification, but there specification does permit it to be
| handled via C++-style scoped destruction. That C has no way for
| you to manually write this code does not preclude
| implementations from using that logic internally, especially
| since most C compilers are also C++ compilers and handling C
| lambdas as identically to C++ lambdas as possible would have
| some benefits.
| MauranKilom wrote:
| > In C++ a lambda may not be convertible to a function pointer
| if the captured context is non zero sized, and if the captured
| context is larger than the size of a pointer it must use some
| kind of dynamic allocation
|
| You are mixing things up here. A lambda will never require
| memory allocation, regardless of its size:
| https://godbolt.org/z/Mebn4h
|
| _std::function_ can 't get around memory allocation for
| sufficiently large functor objects, but that is a matter of how
| libraries implement type erasure for functors and has nothing
| to do with the lambda expressions of the core language.
| favorited wrote:
| In the section on lambdas, there is discussion of the
| "Objective-C blocks" language extension alongside C++ lambdas.
| They missed that the extension is to C, not Objective-C.
|
| I'm not suggesting they standardize Clang's language extension,
| but I think it's worth noting that a similar feature has actually
| been implemented for the C language itself, rather than a higher-
| level one.
| pjmlp wrote:
| Given WG14 views on existing practices, it also fails me why
| they haven't looked into it.
| squid_demon wrote:
| Please leave C alone. Thanks much.
| markus_zhang wrote:
| Yes, and please leave C++ alone too, at least for 20 years.
| guerrilla wrote:
| You can always set your compiler to accept only older
| standards...
| Jtsummers wrote:
| Thanks to the sheer number of C programs, no compiler
| supporting later C standards will _drop_ support for prior
| standards unless it 's explicitly targeting the later standard
| _and that standard is fundamentally breaking with earlier
| ones_. And even then, the major players won 't because they
| still have to support every program ever written in C.
|
| So any changes to C at this point will not break your ability
| to use whichever prior C standard you happen to be interested
| in.
| pjmlp wrote:
| MSVC 2019 enters the bar with support for C89, C11 and C17.
| Jtsummers wrote:
| Though they never actually _dropped_ C99 support, they
| never had _full_ C99 support to begin with. C has long been
| a second class citizen (compared to C++) under MS 's
| development tools.
| pjmlp wrote:
| They had C99 support to the extent required by ISO C++
| compliance.
|
| The backtrack from "we don't need C anymore" is most
| likely driven by, WSL, Azure Sphere OS, Azure RTOS, and
| above all probably some vocal customers with enough power
| to make it matter.
|
| Even the Windows kernel team dropped the COM based
| userspace drivers framework, replacing it for a C based
| one.
|
| In any case, everything from C99 that became optional in
| C11 isn't planned to ever be supported.
| emteycz wrote:
| Except when you come to a new company and find out they're
| using the new glory all over the place and insist you do too.
| Jtsummers wrote:
| Well, if you join a team you join whatever they're using.
| Which may not even be C. If you want control, you have to
| lead the team in the company or run your own company.
| That's part of being a "company man". You forfeit control
| over the kinds of things you work on and many decisions
| about what and how they're made unless you have the right
| ear or the right level of authority.
| emteycz wrote:
| And that's why the commenter wants people to not touch C
| - if C doesn't change then the workplace won't change
| too.
| UncleMeat wrote:
| Or companies will leave C behind.
| Jtsummers wrote:
| I mean, you can try and hold back the tide but the world
| changes. Take control of your section of it if you want
| control, but you can't stop change. Perhaps try to become
| as relatively unpopular as Common Lisp so that no new
| standards are ever made (though the community continues
| to extend the language environment with things like
| QuickLisp and Alexandria). Or as truly unpopular as
| JOVIAL so that there really are no more changes.
| emteycz wrote:
| Not everyone has the means to become their own employer.
| I agree with you about control and I myself am in
| business for this reason, but suggesting that they should
| simply use whatever they like doesn't address the
| problem.
|
| Blowing up language is not nice even for me as a
| businessman - now I have to place special care about
| who's coding what and how, something I didn't need to do
| up until now (in case of C).
| Jtsummers wrote:
| > Blowing up language is not nice even for me as a
| businessman - now I have to place special care about
| who's coding what and how, something I didn't need to do
| up until now (in case of C).
|
| This part is fair, but I've never worked (professionally)
| in a language that had a BDFL who controlled its
| extension in a deliberate fashion. It's always been
| design-by-committee languages. As a consequence, since I
| began, we've always had coding standards that limited
| what portions we could use for which projects (usually
| with options to try out novel parts of the
| language/standard library with extra reviews). And this
| is even true with C, though the restrictions weren't as
| severe (working in embedded, typically mandating
| no/limited use of malloc/free and no recursion, direct or
| indirect).
| flohofwoe wrote:
| Some sort of type inference would make a lot of sense to close
| some left-over holes from C99:
|
| E.g. why does this work: struct bla_t bla = {
| .x=1, .y=2, .z=3 };
|
| But when I want to assign to an existing variable a "type hint"
| is needed: bla = (struct bla_t) { .x=1, .y=2,
| .z=3 };
|
| Why the (struct bla_t)? The compiler knows the type of "bla"
| after all.
|
| Also this would be nice to create a zero-initialized struct
| value: const struct bla_t bla = {};
|
| ...this would basically be a C99 designated initialization
| without any initializers (it works as a language extension in
| gcc and clang, but is an error in MSVC).
| ephaeton wrote:
| There's a time and place for everything. Regarding the time, it
| is and has been the time for this since lisp emanated. But C is
| not the place, IMO. I don't want to be left to wonder about
| sizeof(anything) in C.
|
| "Lisp Programmers know the value of everything and the cost of
| nothing."
|
| C programmers ought to know the cost and sizeof everything...
|
| I hope the committee sees it like this as well.
|
| I also believe the feature (auto) makes sense for C++ with its
| verbose and lengthy types nobody but your compiler cares about
| (say, e.g., multimap::equal_range). For C? Get out of here..
|
| this passes, and suddenly zig becomes a whole lot more
| interesting ;) (but zig also has type inference you say? Well,
| it's a consistent proposition, and not a tacked-on "feature".)
| cataphract wrote:
| > I also believe the feature (auto) makes sense for C++ with
| its verbose and lengthy types nobody but your compiler cares
| about (say, e.g., multimap::equal_range). For C? Get out of
| here..
|
| It's more than convenience. auto is effectively mandatory in
| many cases where the return type of templates/functions is
| private to the implementation (lambdas being the most common
| example, but libraries like hana make extensive use of such
| private types).
| scatters wrote:
| C has VLAs, so sizeof is a runtime operation. That's already
| more complex than C++, where sizeof is fixed at compile time.
| flohofwoe wrote:
| VLA has been "dropped" from C11 though (made optional), so
| it's essentially been degraded to a compiler-specific
| language extension.
|
| IMHO VLAs were a "misfeature" that shouldn't have slipped
| into the standard in the first place.
| wahern wrote:
| With some slight tweaking variable modified types are the
| simplest and cleanest way forward to supporting
| arrays/slices as function parameters. void
| foo(size_t n, char buf[n]) { size_t m = sizeof buf;
| }
|
| In a saner world n and m should be equal, but in a cruel
| twist it doesn't do what you'd expect. I don't understand
| why the committee crippled the semantics here when adding
| VLAs and variable modified types. Never too late to fix
| things, though.
| FrozenVoid wrote:
| C needs operator overloading. Something like this:
| https://github.com/FrozenVoid/Infodump-DB/blob/943900c2dbf2e...
| fctorial wrote:
| What you actually want is function overloading, which C can't
| support since C functions map directly to elf symbols, and all
| assembly functions are vararg with all arguments being WORDs.
| (That's how it is in elf, *nix and x64, don't know much about
| other platforms)
| FrozenVoid wrote:
| _Generic macros provide similar functionality to function
| overload(which in C++ involves name mangling, that will never
| be accepted for C)
| https://en.cppreference.com/w/c/language/generic
| lldbg wrote:
| In C++, name mangling is used to solve this issue
| mjburgess wrote:
| You don't need a vtable for overloading; that is just runtime
| polymorphism ("ad hoc polymorphism".
|
| Function overloading can be done compile time, eg.,
| parametrically: add(3.0, 1.0) -> compile to
| add_float32_float32 add(1, 2) -> compile to
| add_int32_int32
| fctorial wrote:
| As I said, `add` function in C maps to a symbol named `add`
| in assembly. You're demangling the function name, which is
| what C++ does.
| mjburgess wrote:
| Sure, you're right.
|
| I guess we'd need C to have a special syntax to compile
| in something like a vtable
|
| add_f32_f32(x, y) calls add(MAGIC_NO, x, y)
|
| and add has a big switch on MAGIC_NO for selecting
| implementation...
|
| You'd need a keyword for this, "multi int add(int a, int
| b) ..."
| jcelerier wrote:
| such complications where this is what C++ gives you
| natively
| HexDecOctBin wrote:
| Not necessarily, they could standardize it as a syntax like
| _Generic, to make it so that the user has to choose a unique
| name for each function, but then they can all be called by a
| common name.
| jws wrote:
| Clang has C function overloading. I use it in a reasonable
| sized code base. It really cleans up the source code, you end
| up skipping a lot of the type name or package name parts of
| function names which accumulate in larger projects leaving a
| much cleaner source to read. 100% recommend.
| pantalaimon wrote:
| No, I'd say that operators will always result in a primitive
| operation is actually a feature - everything more complex
| warrants a proper function.
| HexDecOctBin wrote:
| I'd say they should probably go the other way, defining even
| built-in operators like:
|
| operator+(int a, int b) __gcc_internal_int_sum(a, b)
|
| This way, we can change (through overriding) the way these
| operations work (rounding, overflow, coercing, etc.), without
| having to define non-idiomatic functions to do so. A lot of
| undefined behavior can be made user-defined this way.
| gmueckl wrote:
| There should be a middle ground. The main issue with
| operator overloading is that every overload exists in the
| global scope. Thus, you get issues and surprises with
| unknown or competing overloads. Sometimes, a workable
| solution would be to have overloads restricted to a kind of
| named scope that the calling code can enter explicitly.
| Maybe something like operator_context(mz_vector_ops) { ...
| } which makes all operators defined in the context of
| my_vector_ops apply to expressions within the block, but
| nowhere else.
| FrozenVoid wrote:
| Consider arbitrary precision, rationals, interval
| arithmethic, countless algebraic types, matrices, vectors
| with all of them requiring typing and composing long
| functions versus intuitive formula:
| mpz_mul(res,x,y); mpz_mul(res,res,z); vs
| x*y*z;
|
| ```
| jjoonathan wrote:
| Math libraries are an exception. You want this:
| d + A*B*(c*(a*b))
|
| Not this: vector_add(mv_multiply(matrix_mul
| tiply(A,B),vector_dot(a,b)*c),d)
| wtetzner wrote:
| It'd be cool to have the Haskell feature of putting
| backticks around a binary function so you can use it infix,
| e.g.: ((A `mmul` B) `mvmul` (a `vdot` b)
| * c) `vadd` d
|
| Still not as nice as operators, but not terrible either.
| jcelerier wrote:
| so are expression grammar libraries, etc
| flohofwoe wrote:
| You can get this with clang's vector extension (https://cla
| ng.llvm.org/docs/LanguageExtensions.html#vectors-...), and
| this would make a lot of sense to add to the C standard.
|
| IMHO vector math is about the only place where operator
| overloading is acceptable in C++.
___________________________________________________________________
(page generated 2021-01-13 23:02 UTC)