4 ms·
Having the pointer "doing double-duty as a pointer and a counter" reduces the potential for bugs. If you do otherwise, then you have more variables to think abo
by burfog 10y ago
Having the pointer "doing double-duty as a pointer and a counter" reduces the potential for bugs. If you do otherwise, then you have more variables to think about and they could get out of sync.
It does match the hardware. There is no cache invalidation.
C programmers commonly think in terms of assembly. They might not envision a particular kind of CPU, but low-level operations are in their mind. The better programmers actually check the assembly sometimes. They know that a "do" loop is normally smaller than a "while" loop because the "while" loop gets converted into a "do" loop inside an "if" that handles the case of no iteration.
The _Generic keyword is resolved by the compiler, so there is no branching. One can also use a macro. Macros can be the normal function-like type of course, but there is so much more. Macros can be used to define functions. Macros can be used with repeated inclusion of the same header file, allowing the creation of many different versions of code. This lets you generate indexed_cata__size_t and indexed_cata__uint64_t and so on, which you would then hook up via a macro called indexed_cata that uses _Generic to dispatch (with zero run-time overhead) to the correct implementation.
I probably don't really get what you're looking for with the continuation stuff. Wikipedia compares it to setjmp/longjmp and goto, which doesn't sound too nice. This is all completely off-the-wall alien to a C programmer. It's hard to get excited about learning to understand something that is associated with really slow programming languages.
Being "able to create a function at runtime" is normally incompatible with having a small high-performance runtime. In C you have a choice: fake it, or do things the awesome way. You can fake it with macros, possibly including ones that expand out to define functions. The awesome way is to JIT, and yes I've done this: allocate a chunk of memory, write out some binary instruction code, deal with any permissions/cache/pipeline issues, and then call it as a function or use a bit of inline assembly to reach it. Oh, there is another way that might be more your style: create C code on-the-fly, feed it into the compiler to generate a library, dlopen the library, and call your new function.
Yes, you can return a struct. What you don't yet get is any syntax to automatically split that apart in the caller. The caller will end up with a struct. Hopefully the next C revision will improve this.
Expressing my loop in an alternate way is likely to generate code that is bigger and slower. This particular case is trivial enough that gcc 4.8.4 can optimize it down to the exact same bytes, and a newer gcc probably just substitutes a call to strcpy unless you pass -ffreestanding to stop that optimization. Normally it makes a difference.
- lmm 10y ago> Having the pointer "doing double-duty as a pointer and a counter" reduces the potential for bugs. If you do otherwise, then you have more variables to think about and they could get out of sync. If you're just adding 1 to both each time then it's pretty hard to get that wrong. I think storing the original pointer and subtracting at the end has more potential to go wrong. > I probably don't really get what you're looking for with the continuation stuff. Wikipedia compares it to setjmp/longjmp and goto, which doesn't sound too nice. It's not well-explained - the terminology is arcane, people use the word to mean different things. But done right it can give you the performance and flexibility of goto but with the clarity of regular function calls, and all your control constructs can just be library functions. > It's hard to get excited about learning to understand something that is associated with really slow programming languages. Shrug. Almost all programming languages are fast enough these days. Correctness is often much more of an issue. > Being "able to create a function at runtime" is normally incompatible with having a small high-performance runtime. In C you have a choice: fake it, or do things the awesome way. You can fake it with macros, possibly including ones that expand out to define functions. The awesome way is to JIT, and yes I've done this: allocate a chunk of memory, write out some binary instruction code, deal with any permissions/cache/pipeline issues, and then call it as a function or use a bit of inline assembly to reach it. Oh, there is another way that might be more your style: create C code on-the-fly, feed it into the compiler to generate a library, dlopen the library, and call your new function. I don't need anything that complicated. Just the ability to partially apply a function would do. Or even ordinary classes. Which I mean sure, I can emulate by passing a struct consisting of a function pointer and the first argument as data, but that's going to be awful to read.
- burfog 10y agoFunctional programming throws away the "clarity of regular function calls" by abusing them or their syntax for control flow. It's an unreadable mess. Having control constructs be library functions seems to be an extension of this, making the situation much worse. I write code to emulate the modern multi-GHz PC and other things running at about a GHz. It's timing sensitive, so the emulator needs to keep up, and we really want to emulate SMP on one core. My coworkers write code to treat a binary executable as a giant equation, solving it to find inputs which cause crashes. We pay lots of money for giant rooms full of computers running our code. All programming languages are way too slow. I'd switch to FORTRAN if it was 2x faster. I'm using C99 with lots of non-standard extensions and inline assembly. We use SSE intrinsics to vectorize things by hand. Meanwhile, people like you are making my web browser too damn slow. It's 2016, and it feels like 1996. Other than a slight increase in pixels and always getting 24-bit color, there has been no improvement in speed despite the hardware being something like 1000 times faster. WTF. I paid good money for that hardware because I wanted things faster. I didn't buy that hardware to be wasted. Your code is not special. Each programmer tests his code in isolation, giving it the whole machine, but out in the wild it must share the machine with other code and/or with numerous instances of itself. Sometimes gcc's optimizer will partially apply a function. I've seen the result via disassembler. This beats partially applying functions by hand. For example, the function may do different things if a certain parameter is NULL. The compiler makes a version of the function for when the parameter is NULL, and another for when it is not. Passing a struct with a function pointer and argument seems a little annoying for a trivial situation, but it makes lots of sense when there are multiple functions and multiple arguments. For the trivial case, one would normally just pass the function pointer and argument separately. I think I've written code that matches your "partially apply a function" idea pretty well, using macros. Like this: There exist functions for reading/writing memory with names like read_mem_8 and read_mem_32, for several different sizes. I want to make wrapper functions called read8 and read32 and so on. The wrapper functions check for alignment, check a TLB, check for breakpoints, and then call the wrapped function or do the access directly. I make a macro that expands out to the function I want whenever I "call" the macro. No, I don't mean a regular function-like macro. Mine actually creates a real function. I invoke the macro itself at top level, outside of any function, right after creating the macro. I invoke it for each size that I want. Macro expansion generates a function that I can later call. Invoking it looks like so: define_read(8) define_read(16) define_read(32) define_read(64) define_write(8) define_write(16) define_write(32) define_write(64) Note the lack of a semicolon. These macros expand to actual functions that I can call. I could even take the address of the resulting functions if I wanted to, not that I do. Invoking the result of the macro expansion looks like so: write32(cpu,addr,val); For bigger things, one would put the template-like version of the function in a .h file and then include it repeatedly, each time with different choices. Like so: #define CHOSEN_BIT_WIDTH 32 #define CHOSEN_ENDIANNESS ENDIAN_BIG #include "def_stuff.h" #undef CHOSEN_BIT_WIDTH #undef CHOSEN_ENDIANNESS // ... and then again with different choices