|
Quote: it can eliminate virtual calls to a target class altogether if that virtual can be made constexpr?
That's about right. Peter Dimov and Vassil Vassilev started this one with: Allowing Virtual Function Calls in Constant Expressions
Their key argument was: Since in a constant expression the dynamic type of the object is required to be known (in order to, for example, diagnose undefined behavior in casts), the restriction is unnecessary and artificial.
This argument will probably propagate to all corners of the language were it applies, because that only makes sense.
Quote: I don't know if I should be elated or terrified for what they've done to C++
Elated.
While not every (if any?) compiler implementation currently provide full C++ 20 support, things are improving.
Compile-time expression evaluation and meta-template programming are immensely useful, but until now you could only do useful stuff in ways that were hard to understand, requiring all sorts of template trickery.
With C++ 20 I can do meta-template programming more easily, and even more important: easily explain what I do to other people.
With constexpr functions, the need for most of the template trickery that has evolved over the last decades goes away, and once that door was opened people started to ask reasonable questions, such as:
I know that the compiler knows which implementation of a virtual function will be called at runtime, and if that is the case, it would be awfully nice if I could use that function in a constant evaluated context, so how about we put that into the standard?
And, now that we have ranges, I guess many C++ developers will do pretty sophisticated meta-template programming without even realizing it - and suddenly it will not be scary anymore, because we are all doing it.
Espen Harlinn
Senior Architect - Ulriken Consulting AS
The competent programmer is fully aware of the strictly limited size of his own skull; therefore he approaches the programming task in full humility, and among other things he avoids clever tricks like the plague.Edsger W.Dijkstra
modified 13-Mar-21 19:30pm.
|
|
|
|
|
Espen Harlinn skrev: And, now that we have ranges, I guess many C++ developers will do pretty sophisticated meta-template programming without even realizing it - and suddenly it will not be scary anymore I will still be scared. And let's not pussyfoot around: the goal of template metaprogramming is to eliminate the need to run programs. Simply compiling them will enough.
|
|
|
|
|
Quote: Simply compiling them will enough.
There is a surprising amount of truth to that.
When I look at stuff I've done previously, I'm surprised by the amount of things that can be computed ahead of time.
Espen Harlinn
Senior Architect - Ulriken Consulting AS
The competent programmer is fully aware of the strictly limited size of his own skull; therefore he approaches the programming task in full humility, and among other things he avoids clever tricks like the plague.Edsger W.Dijkstra
|
|
|
|
|
If they can get rid of the code bloat and eliminate all the stuff i never use at run time once the compiler is done with it rather than leaving orphaned code in my binaries I'll be happy, since I do a lot of IoT development where RAM, program space, and CPU cycles are all at a premium. I feel like I need metaprogramming a lot but I often can't afford it.
Real programmers use butterflies
|
|
|
|
|
Quote: If they can get rid of the code bloat
With the latest compilers there should not be much, if any, of that.
Quote: I feel like I need metaprogramming a lot
you do
Quote: I often can't afford it.
and you can …
While coding I still find that I am continuously backsliding towards my pre C++ 20 ways of doing things. That is my fault, and not an inherent deficiency of C++ 20.
When I succeed at getting into "C++ 20" mode, shedding much of the mental baggage accumulated over more than 25 years of C++ development, the machine-code generated by the compiler is usually superior to the machine-code generated for code developed in pre "C++ 20" mode.
When I reason about code in "C++ 20" mode, how things will be laid out in memory comes more into focus, while in pre "C++ 20" mode there is more emphasis on the flow of execution - this is obviously very subjective, since this is me reviewing my own ways of thinking. What I think is interesting, is that the actual execution paths for the code I develop when I manage to get into "C++ 20" mode is shorter than before, sometimes significantly so.
For me, altering how I reason about coding is pretty d**n hard work. Reasoning about code in terms of compile-time computations and type selection, is not all that hard - making my brain pick solutions based on that, while actually trying to solve something else is, and that is what I mean by "C++ 20" mode.
Espen Harlinn
Senior Architect - Ulriken Consulting AS
The competent programmer is fully aware of the strictly limited size of his own skull; therefore he approaches the programming task in full humility, and among other things he avoids clever tricks like the plague.Edsger W.Dijkstra
|
|
|
|
|
I don't have access to the C++20 standard on my cross-compiler toolchain for my embedded devices - precisely where I need it the most.
The best I can do right now is C++17.
Real programmers use butterflies
|
|
|
|
|
Quote: The best I can do right now is C++17
Which makes it harder, perhaps even unpalatable … personally, I was never a great fan of std::enable_if<>, even if it performs something very useful. Concepts nearly made it into C++ 11, and I don't think anybody expected 9 more years to pass before we got a decent alternative to std::enable_if<> …
Espen Harlinn
Senior Architect - Ulriken Consulting AS
The competent programmer is fully aware of the strictly limited size of his own skull; therefore he approaches the programming task in full humility, and among other things he avoids clever tricks like the plague.Edsger W.Dijkstra
|
|
|
|
|
 I'm excited to hack my toolchain to enable C++20 but I just haven't gotten to it yet.
On the bright side, it gives me a chance to get up to speed.
I just now caught up to C++11 variadic templates which allows me to do a lot more metaprogramming (or at least more easily) than I used to be able to do.
I'm doing it for now, without using STL for it just because I want to make sure I get the mechanics of any of the library I rely on. The standards docs are just never that helpful to me for expressing concepts and rationales but I can learn those by implementing them myself.
I'm doing pretty well with it right now. I've got a pixel template struct, that takes a series of channel traits that describe "channels" that make up a pixel (usually color channels R, G, B, but also it supports other color models, grayscales, monochromes) etc, - you can specify the bit depth and layout of the bits for the whole thing, including endianness.
I needed it because "device drivers" on IoT devices don't exist, and LCD screens and JPEGs and PNGs each have very different binary pixel formats. And LCD screens come in a variety of binary pixel bitmap formats at a variety of capabilites.
Keep in mind I'm not using iostreams below because it adds static initialization code and other cruft that's interfering with my ability to examine the output in assembly
And I can't afford to mess with any of this at run time. So I've made a little something to give me rich compile time pixel information I can use to do things like resolve the shifts I need to do pixel channel manipulation (like changing the "green" color component of a 16-bit rgb565be value (big endian) which is six bits squashed in the middle and then (if you're on an LE platform) flipped.
Anyway, I'm still working on it, but what I have so far incurs no overhead for anything.
So:
typedef channel_traits<channel_name::R, channel_kind::color, uint8_t, 5> red_5bit_color_channel_t;
typedef channel_traits<channel_name::G, channel_kind::color, uint8_t, 6> green_6bit_color_channel_t;
typedef channel_traits<channel_name::B, channel_kind::color, uint8_t, 5> blue_5bit_color_channel_t;
typedef pixel<uint16_t, color_model::rgb, false, red_5bit_color_channel_t, green_6bit_color_channel_t, red_5bit_color_channel_t> rgb565be_t;
rgb565be_t p;
constexpr static const int index = 1;
using ch = rgb565be_t::get_channel<index>;
printf("index: %llu ",(unsigned long long)ch::index);
printf("name: %s, ",ch::name);
printf("type: %s, ",to_string(ch::kind));
printf("int-type size: %llu, ",(unsigned long long)sizeof(ch::int_type));
printf("min: %llu, ",(unsigned long long)ch::min);
printf("max: %llu, ",(unsigned long long)ch::max);
printf("bit-depth: %llu, ",(unsigned long long)ch::bit_depth);
printf("bits_to_left: %llu, ",(unsigned long long)ch::bits_to_left);
printf("bits_to_right: %llu, ",(unsigned long long)ch::bits_to_right);
printf("mask: %llx, ",(unsigned long long)ch::mask);
printf("pixel_mask: %llx\r\n",(unsigned long long)ch::pixel_mask);
Ouput
index: 1 name: G, type: color, int-type size: 1, min: 0, max: 63, bit-depth: 6, bits_to_left: 5, bits_to_right: 5, mask: 3f, pixel_mask: 7e0
Assembly
.LC0:
.string "index: %llu "
.LC1:
.string "G"
.LC2:
.string "name: %s, "
.LC3:
.string "color"
.LC4:
.string "type: %s, "
.LC5:
.string "int-type size: %llu, "
.LC6:
.string "min: %llu, "
.LC7:
.string "max: %llu, "
.LC8:
.string "bit-depth: %llu, "
.LC9:
.string "bits_to_left: %llu, "
.LC10:
.string "bits_to_right: %llu, "
.LC11:
.string "mask: %llx, "
.LC12:
.string "pixel_mask: %llx\r\n"
main:
push rax
mov esi, 1
mov edi, OFFSET FLAT:.LC0
xor eax, eax
call printf
mov esi, OFFSET FLAT:.LC1
mov edi, OFFSET FLAT:.LC2
xor eax, eax
call printf
mov esi, OFFSET FLAT:.LC3
mov edi, OFFSET FLAT:.LC4
xor eax, eax
call printf
mov esi, 1
mov edi, OFFSET FLAT:.LC5
xor eax, eax
call printf
xor esi, esi
mov edi, OFFSET FLAT:.LC6
xor eax, eax
call printf
mov esi, 63
mov edi, OFFSET FLAT:.LC7
xor eax, eax
call printf
mov esi, 6
mov edi, OFFSET FLAT:.LC8
xor eax, eax
call printf
mov esi, 5
mov edi, OFFSET FLAT:.LC9
xor eax, eax
call printf
mov esi, 5
mov edi, OFFSET FLAT:.LC10
xor eax, eax
call printf
mov esi, 63
mov edi, OFFSET FLAT:.LC11
xor eax, eax
call printf
mov esi, 2016
mov edi, OFFSET FLAT:.LC12
xor eax, eax
call printf
xor eax, eax
pop rdx
ret
Real programmers use butterflies
|
|
|
|
|
While I haven't looked at the implementation of your templates, the generated assembly speaks for itself. Cool
Quote: I'm excited to hack my toolchain to enable C++20 but I just haven't gotten to it yet.
I like clang and LLVM, but haven't dived into the internals. It is possible to do interesting things with them without knowing much about the internals.
Quote: I just now caught up to C++11 variadic templates which allows me to do a lot more metaprogramming (or at least more easily) than I used to be able to do.
There are interesting projects implemented using C++ 11 meta-template programming, but I find that much of the code is pretty hard to follow.
Quote: I'm doing it for now, without using STL for it just because I want to make sure I get the mechanics of any of the library I rely on.
Good for you, and it is also a great way to discover that any occurrence of code-bloat has little (if anything?) to do with the C++ language.
Apart from RTTI and exceptions there should be no overhead compared to C. Personally I use exceptions extensively for error handling, and also I like to think that I write fast code, so I have tried to determine if there is a real performance hit incurred by enabling exception and found that at long as an exception is not thrown, it makes no measurable difference to the performance of the code. On the other hand, I have found that several layers of:
auto rc = SomeFunction();
if( rc != NO_ERROR )
{
return rc;
}
have a negative impact on performance. Usually not much, but measurable. It also makes code a lot less readable.
Quote: I'm not using iostreams
Neither am I …
Quote: Anyway, I'm still working on it, but what I have so far incurs no overhead for anything.
Cool, keep up the good work
Espen Harlinn
Senior Architect - Ulriken Consulting AS
The competent programmer is fully aware of the strictly limited size of his own skull; therefore he approaches the programming task in full humility, and among other things he avoids clever tricks like the plague.Edsger W.Dijkstra
|
|
|
|
|
Espen Harlinn wrote: it is also a great way to discover that any occurrence of code-bloat has little (if anything?) to do with the C++ language.
Haha I just got into a thing here on the lounge the other day where I was talking about how, any code you can produce in C i can produce the *precise* equivalent in C++. The reason I said that was because some people think C++ isn't as efficient as C, but you can literally do everything you can do with C with C++, even if a little bit of it has to be done in a different way - which will still generate the same machine code.
C++ isn't bloated, I tell people. Generic programming and metaprogramming make it *easy* to introduce code bloat, but if used judiciously it can actually make your code smaller than what you would have coded manually. Of course, you already know all of this, but I'll be writing an article about it soon.
Espen Harlinn wrote: Apart from RTTI and exceptions there should be no overhead compared to C. Personally I use exceptions extensively for error handling, and also I like to think that I write fast code,
I think the issue here is that exceptions make your stack bigger, but it has been a long time since I have dived into exceptions. I avoid them on machines where I'm working with as little as 4kB of RAM.
The truth is on these little machines, there's little to do under a lot of error cases other than abort, for several reasons - no operating system, so nothing to drop down to. often as not it's a hardware issue which is catastrophic anyway. I mean, aside from checking user interface stuff, and making sure disks are inserted, I usually let the things *run til fail* and don't if/check everything. If the hardware's going to go all elephant, it didn't help me to abort() 3 instructions earlier than my machine would have anyway, if that makes sense. Hence, I don't do if()s everywhere. But this makes things *harder* to code not easier. It's easier to error check than to determine when it's relatively safe not to.
I've never used RTTI and because I lack imagination I can't think of a use case for it that isn't better solved either with metaprogramming and SFINAE or C# depending on what you're doing, so I can't speak to it.
Real programmers use butterflies
|
|
|
|
|
|
I'm writing that article now. =)
I'm not too interested in making the stack smaller on the x64 because I can't think of a great use case for that.
I'm interested in making it smaller on the AMTel AVR 8-bit monsters, the 32-bit Tensilica XTensa LX6 variants, and the ARM Cortex-M line processors because there are a number of issues primarily around the low cost, and low capabilities of these processors. There's usually so little ram (8kB to 320kb depending on the system) that the programs themselves are run off of flash PSRAM which is slow, and so at least on the XTensa it will cache potions of the program in RAM that are called frequently. This is like a much less sophisticated L1 cache system but even more important because flash I/O over an SPI serial bus makes you want to get out and push.
The problem is you only have 320kB to work with (307kB in the wild, I've found after it "boots") and you can't waste it on nonsense. Including stack canaries.
But I also can't have a lot of code around because of the cache issue. I at least need great locality, and the the degree that I'm not sure how exceptions interfere with I am assuming they will negatively impact it. It's more caution in my ignorance than anything, but even if I was more confident, I don't like the extra hidden stuff that exceptions introduce in terms of code flow and stack space, and potentially locality and goddess knows what else (what will throwing an exception from an ISR routine do?!) I'd just as soon write my code so as not to use them.
Real programmers use butterflies
|
|
|
|
|
I too am still using C++17 - and you can still use constexpr in a lot of places, just not quite as many as with C++20 (and you don't have concepts). In my latest project (it's not exactly embedded, but it's an emulator running on Windows for an embedded processor with attached UARTs and other devices), I have things like a CRC with lookup table that is generated at compile-time using constexpr functions (which means those functions can be unit tested and debugged at runtime by passing non-constexpr parameters), a whole set of bitmask and bitfield register functions that do as much work as possible at compile-time to ensure that a) bit-mask constants can be used in a constexpr context, and b) bit-field registers do as little as possible at runtime.
Java, Basic, who cares - it's all a bunch of tree-hugging hippy cr*p
|
|
|
|
|
Stuart Dootson wrote: a whole set of bitmask and bitfield register functions that do as much work as possible at compile-time to ensure that a) bit-mask constants can be used in a constexpr context
This is exactly what I'm doing right now with my pixel library. Made extra difficult for the fact that in certain cases (like with 1 bit monochrome) I am not reading and writing on byte boundaries, but *bit* boundaries
mono1_t::int_type value = mono1_t::get_pixel((const uint8_t*)bitmap, index);
Mono is the easy case.
Consider an 18-bit color format (shockingly common)
rgb666be_t::int_type value = rgb666be_t::get_pixel((const uint8_t*)bitmap, index);
where index = 17.
And I'm trying to compute everything needed to do this stuff at compile time.
These ones are fun.
Real programmers use butterflies
|
|
|
|
|
honey the codewitch wrote: Made extra difficult for the fact that in certain cases (like with 1 bit monochrome) I am not reading and writing on byte boundaries, but bit boundaries
Same here - I've got control registers like this:
Bit Number | Label | Value |
---|
31..13 | Not Used | - | 12 | Table Entry Lock | 0 = Unlocked 1 = Locked | 11..9 | Not Used | - | 8 | Page Access | 0 = Enabled 1 = Disabled | 7..6 | Data Bus Width | 01b = 16-bit 10b = 32-bit | 5..0 | Wait States Setting | 000000b = 0 Wait States 000001b = 1 Wait State … 111110b = 62 Wait States 111111b = 63 Wait States
|
I'm modelling each bit-field with a struct templatised on its bit locations, each register with a templatised list of bit-fields - something like this:
using bf1 = bit_field_t<21, 10>;
using bf2 = bit_field_t<31, 24>;
using bf3 = bit_field_t<0, 0>;
using bf4 = bit_field_t<1, 1>;
using bf5 = bit_field_t<2, 2>;
using bf6 = bit_field_t<3, 3>;
using bf7 = bit_field_t<4, 8>;
using bf9 = bit_field_t<6, 6>;
using reg1_t = bit_field_register_t<bf1, bf2, bf3, bf4, bf5, bf6, bf7>;
using reg2_t = bit_field_register_t<bf1, bf2, bf3, bf4, bf5, bf6, bf9>;
(Why all the types? So the compiler can detect attempts to access incorrect bit-fields).
Each field of a bit-field register can be manipulated like so:
int foo(reg1_t *r1) {
r1->insert<bf1>(123);
return r1->extract<bf7>();
}
and that code compiles to the following on AMD64 targets:
mov eax, dword ptr [rdi]
mov ecx, eax
and ecx, -4193281
or ecx, 125952
mov dword ptr [rdi], ecx
shr eax, 4
and eax, 31
ret
This isn't intended for memory-mapped registers (as I said - it's an emulation of that), but could probably be used for that with a few mods.
Java, Basic, who cares - it's all a bunch of tree-hugging hippy cr*p
|
|
|
|
|
That's cool. I'm sitting here super impressed by the compiler right now as I haven't done any hand optimizations to this yet except special casing when the pixel is represented natively (can be cast directly to the destination buffer):
constexpr inline static int_type at(const uint8_t* bitmap,size_t index) {
if(native_int) {
const size_t offs = index;
return order_guard(*(((int_type*)bitmap)+offs));
}
const size_t ofsb=((index*bit_depth)/8);
bitmap+=ofsb;
const size_t ofsm=(index*bit_depth)-(ofsb*8);
int_type result = 0;
uint8_t dat = *(bitmap);
for(size_t i = 0;i<bit_depth;++i) {
const size_t bit = 7-((ofsm+i)&7);
const uint8_t msk = (1<<bit);
const bool mov = bit==7;
result<<=1;
result|=(0!=(dat&msk));
if(mov) dat=*(++bitmap);
}
return order_guard(result);
}
... (below is from main)
typedef channel_traits<channel_name::V,channel_kind::luminosity,uint8_t,1> mono_1bit_luminosity_channel_t;
typedef pixel<color_model::none, false, mono_1bit_luminosity_channel_t> mono1_t;
uint16_t data=0xCCCC;
for(size_t x=0;x<16;++x) {
auto val = mono1_t::at((uint8_t*)&data,x);
printf("%d",val);
}
printf("\r\n");
return 0;
Nevertheless, the assembly is better than i expected (this is the entire main but it pretty much goes from .L2:
main:
push rbp
mov ebp, 1
push rbx
xor ebx, ebx
sub rsp, 24
mov WORD PTR [rsp+14], -13108
.L2:
mov rcx, rbx
mov rdx, rbx
mov eax, ebp
xor esi, esi
not rcx
shr rdx, 3
mov edi, OFFSET FLAT:.LC0
and ecx, 7
sal eax, cl
test BYTE PTR [rsp+14+rdx], al
setne sil
xor eax, eax
inc rbx
call printf
cmp rbx, 16
jne .L2
mov edi, OFFSET FLAT:.LC1
call puts
add rsp, 24
xor eax, eax
pop rbx
pop rbp
ret
Edit: VS Code (at least under Linux undoes clipboard copy commands when you use undo! )
I don't care that it's not optimized yet because I've set the code up where I can optimize by adding if statements which will always be settled at compile time.
Real programmers use butterflies
modified 15-Mar-21 10:35am.
|
|
|
|
|
But did you find the limerick?
Truth,
James
|
|
|
|
|
In a freak accident today, a photographer was killed when a huge lump of cheddar landed on him.
To be fair though, the people who were being photographed did try to warn him.
|
|
|
|
|
Cheesy. 
|
|
|
|
|
I would prefer stilton (and a glass of port to go with it) 
|
|
|
|
|
Andy stares right back at me,
Slack jawed sunglasses and Warhol effects
over Campbell's Soup SPI by way of TFT
it's everything i've come to expect
it's the last thing he'll ever see
the transmission stumbles,
the DC pin goes low
everything wrong is right again
and the data starts to flow
RST goes high and
and Andy's smirking face is erased in a blaze
of pain and
of collapsing transistor gates
the dance of bits in the end ruined like
the cadence and rhyme of this poem.
*smashes everything around me to punctuate the piece*
The wreckage[^]
Real programmers use butterflies
modified 12-Mar-21 15:17pm.
|
|
|
|
|
Computing by Warhol!
"I have no idea what I did, but I'm taking full credit for it." - ThisOldTony
"Common sense is so rare these days, it should be classified as a super power" - Random T-shirt
AntiTwitter: @DalekDave is now a follower!
|
|
|
|
|
Are all chicken coops militarily backed?
"I have no idea what I did, but I'm taking full credit for it." - ThisOldTony
"Common sense is so rare these days, it should be classified as a super power" - Random T-shirt
AntiTwitter: @DalekDave is now a follower!
|
|
|
|
|
Do you know feather that rumor is a load of spur-ious crop? It's always easy to peck on the military, their fife's and drum-sticks.
(Did Colonel Sanders influence your post?)
Ravings en masse^ |
---|
"The difference between genius and stupidity is that genius has its limits." - Albert Einstein | "If you are searching for perfection in others, then you seek disappointment. If you seek perfection in yourself, then you will find failure." - Balboos HaGadol Mar 2010 |
|
|
|
|
|
|