|
Gary R. Wheeler wrote: I am now responsible for a piece of code that's been part of our business for over thirty years...Function prototypes weren't used. Header files didn't define the functions or data in the corresponding .C source file. There were...
So at least 1992. So yes that was par for the course for C code then. Things like prototypes sort of came about probably associated with C++ (perhaps not entirely but for the same reason that C++ added them.) Someone used to the the older style would not have used them. Globals were something that any C app would be using. Single char variable names were common.
Gary R. Wheeler wrote: a group of #define's he'd added before #include <windows.h> which changed the definition of a number of values in the Windows headers which his comment (one of the few) claimed "Microsoft got wrong".
Could not speak specifically to that but as I recall there were a lot of problems with the early Microsoft C++. I know for a fact that for years after the ANSI C++ spec was released the Microsoft scored very poorly on implementing it. That was back when people could actually post comparisons of software without getting sued.
|
|
|
|
|
In some circumstances what you're saying could be true. This code was adapted to Windows in the early 2000's, so function prototyping was the norm. No excuse there.
jschell wrote: Could not speak specifically to that but as I recall there were a lot of problems with the early Microsoft C++. I know for a fact that for years after the ANSI C++ spec was released the Microsoft scored very poorly on implementing it. That was back when people could actually post comparisons of software without getting sued. This is a case where BK was redefining constants for arguments to and return values from Windows API functions, thereby guaranteeing they would not work as documented.
BK was an asshole.
Software Zen: delete this;
|
|
|
|
|
C gets you rather close to the machine, but it keeps you there. Meaning there isn't really a way in C NOT to hide intent as you'd be rather busy spelling out mechanics of the "how" explicitly, burying the intent.
I very much agree with you on C++ making it way easier to spell out the intent, letting the library do the how, or at least abstracting it away.
|
|
|
|
|
The one line C contest is holding on line 1. It would like a word.
Real programmers use butterflies
|
|
|
|
|
Quote: I'm poring over C code right now - C really isn't that much better, but fortunately you can do less with it. The code is evil. It's absolutely terrible to read, almost as if they were *trying* to hide intent.
How do you hide intent in C? No overloaded operators, no overloaded functions, no implicit calls to constructors that need explicit calls to destructors, no symbols with identical names in different namespaces ...
The amount of "magic happens implicitly behind the scenes" things in C is ridiculously small.
|
|
|
|
|
By making your code do something that is non-obvious.
Real programmers use butterflies
|
|
|
|
|
but to be honest, that can be done in any language.
I'm reading through "learning python" and just hit the description of formatting strings.
I don't know what that guy was smoking when they came up with their approach, but I want some.
Charlie Gilley
“They who can give up essential liberty to obtain a little temporary safety deserve neither liberty nor safety.” BF, 1759
Has never been more appropriate.
|
|
|
|
|
Yeah it can be done in any language it just seems like some languages make it easier to write incomprehensible code than others.
Real programmers use butterflies
|
|
|
|
|
charlieg wrote: but to be honest, that can be done in any language.
Err..no it can't.
I had a C program that had assembly code in a text literal. I would cast it to a function and the call it.
Pseudo code for that was something like the following
char* code = "\xCC\xFF...";
typedef int* myfunct(int i)
int result = (myfunct)(code)(45);
If I hadn't put a lot of comments around that, including the exact assembly, then a maintenance programmer, even an experienced one, would have spent a lot of time figuring that out.
|
|
|
|
|
I'm sorry, but what is your point?
You have special code, you knew it was special code, so you made it real obvious what you were doing.
Charlie Gilley
“They who can give up essential liberty to obtain a little temporary safety deserve neither liberty nor safety.” BF, 1759
Has never been more appropriate.
|
|
|
|
|
I totally get the love/hate relationship with any code, frankly. I work with legacy code, so loving and hating is an everyday thing for me
You are doing a good work. Keep it up
|
|
|
|
|
Well I think all midway languages (which to me is any compiled language between FORTRAN's readability and Assembler) is that readability will always be second to performance...although that should mean that a proportional amount of comments should be added to the code (at least a full page for the dark arts).
Some of the coolest code I've seen uses pointer arithmetic like hell to bypass C++ permissions (this was a videogame engine) and the only comment it had was:
/* Do not change this code or ponies will cry */
I had been tracing the source code through 7 different files when I was rewarded with a hearty laugh there. I don't know how readable it was to any other person, but since then, I knew that game engines, compilers and O.S. source code is never going to be readily accessible to just anyone (Despite our best comments). That is the nature of language, human, computer or otherwise.
|
|
|
|
|
Breaking encapsulation by offsetting from a classes base address seems like bad form, even for a game engine. That's what the "friend" keyword is for.
Of course I have some nasty nasty code in my SPI bus code because it interacts with the ESP32's SPI hardware registers directly for performance. It hurts.
Real programmers use butterflies
|
|
|
|
|
Lol
For the time it was written it was most certainly a hack. That portion was still in C++ 98 and the pointer redirection was for a scripting language internal to the engine (to directly call any method from any class in the engine)
SPI bus code...you reminded me of assembler code I found in our main Nintendo DSi Engine (we called coldbits) to directly write to the buffer of the image processor. It was not child's play but I don't think the use of the keyword asm in C++ is hackish (more like "Here be dragons" kinda warning)
|
|
|
|
|
The reason I don't like the asm keyword is I like my C++ code to be portable. I target a lot of IoT, and those devices come in a variety of architectures, even among the same lines of chips.
I've studied the output of GCC in many cases to where I can structure my C++ code to generate the asm I want while maintaining its higher level structure. Of course that doesn't work if you need to set specific registers and such. Still, I use a lot of compile-time tricks to achieve my goals in the real world, but the result tends to be a best of both worlds scenario - you get efficient, highly structured code.
Real programmers use butterflies
|
|
|
|
|
After having lived with Microsoft's version of C++, and the C++ source code of applications written by C developers, I have come to the point that I like encapsulation but inheritance is a multi headed hydra and usually not worth the effort. My experience has been that if you go past one level of inheritance, you are elephanting doomed.
Reading about objects and what not sounds nice, but when you get your nice inheritance hierarchy set up, in maintenance you realize that you have an inverted pyramid. Touch one base class, and it all falls down.
Now I would agree with you - if you are sharing pointers amongst objects, you have design issues.
Charlie Gilley
“They who can give up essential liberty to obtain a little temporary safety deserve neither liberty nor safety.” BF, 1759
Has never been more appropriate.
|
|
|
|
|
These days I don't use inheritance that much except for doing compile-time computation tricks.
I usually use templates rather than have strict base classes.
In GFX I expose a "caps" template structure that indicates the capabilities of an object. GFX calls certain methods on that object based on the values in that template structure, so for example, it can determine if the object supports reading, or certain optimized operations. Normally, you'd inherit from a base class to do this, but you can do similar using templates. Code size can get to be in issue depending on what you're doing though, but I like the flexibility.
Real programmers use butterflies
|
|
|
|
|
Inheritance for me mainly works best in a very contained environment. I suppose if I were responsible for a core set of code that would apply to multiple applications (like a GUI control set), it might make sense. My career has been spent developing one application after another, and rarely do they inherit from each other. Maybe basic concepts, but as soon as some other team member does not understand something, they code up their own solution and off we go. Inheritance broken.
Templates - well there be magic, but in fact, yet again, the ivory tower folks seem to come up with a pristine solution, and the folks that are shoveling $^&&^& in Dixie code something up they understand to get the job done.
Charlie Gilley
“They who can give up essential liberty to obtain a little temporary safety deserve neither liberty nor safety.” BF, 1759
Has never been more appropriate.
|
|
|
|
|
I just love templates, personally. I'm not as big a fan of the STL, but I don't have much occasion to use it since I often target the Arduino platform with my code, and its STL implementation is only partial on some of its targets. Part of the joy of coding C++ for me is seeing what I can schlep from runtime to compile time in order to increase performance and maintain flexibility.
I have a pixel template class that allows you to declare individual color channels and the bit depth of each, and compose a pixel with as many channels as you want, up to the machine's word size.
It will then allow you to modify the individual channels so you can set the red channel of an RGB pixel, or the U channel of a Y'UV pixel, and it will recompute the overall value.
If you use constants it will compute all of it at compile time, including getting the compiler to bit shift arbitrary bits an arbitrary direction.
Real programmers use butterflies
|
|
|
|
|
honey the codewitch wrote: but would it kill people to write readable code, or at least comment it with something *helpful*?
In my experience and based on the cries of anguish, gnashing of teething and pulling out of hair when I even suggest that comments have a place in code I am guessing that the answer is that yes it would kill them.
|
|
|
|
|
Comments *do* have a place in code - as a last resort. I mean, it's one thing to comment the description of functions and arguments as a header to the function, but i mean the code inside of it should have no more comments than are necessary, and each comment ultimately represents a failure to express intent clearly in code. Sometimes comments are necessary because code can be evil. But good code expresses intent clearly when it can, obviating the need for a comment. At least that's my take on it. Code should be self-evident wherever possible.
Real programmers use butterflies
|
|
|
|
|
I feel like if you can't program it in C/C++ then you don't really understand it. If the C/C++ answer is obvious then the others are trivial.
|
|
|
|
|
Yeah, but that's not what I was talking about.
More I'm talking about things like this.
if(pin_d1>31) {
b |= (((pins_h>>((pin_d1-32)&31))&1)<<1);
} else if(pin_d1>-1) {
b |= (((pins_l>>(pin_d1))&1)<<1);
}
if(pin_d2>31) {
b |= (((pins_h>>((pin_d2-32)&31))&1)<<2);
} else if(pin_d2>-1) {
b |= (((pins_l>>(pin_d2))&1)<<2);
}
if(pin_d3>31) {
b |= (((pins_h>>((pin_d3-32)&31))&1)<<3);
} else if(pin_d3>-1) {
b |= (((pins_l>>(pin_d3))&1)<<3);
}
if(pin_d4>31) {
b |= (((pins_h>>((pin_d4-32)&31))&1)<<4);
} else if(pin_d4>-1) {
b |= (((pins_l>>((pin_d4)&31))&1)<<4);
}
if(pin_d5>31) {
b |= (((pins_h>>((pin_d5-32)&31))&1)<<5);
} else if(pin_d5>-1) {
b |= (((pins_l>>(pin_d5))&1)<<5);
}
if(pin_d6>31) {
b |= (((pins_h>>((pin_d6-32)&31))&1)<<6);
} else if(pin_d6>-1) {
b |= (((pins_l>>(pin_d6))&1)<<6);
}
if(pin_d7>31) {
b |= (((pins_h>>((pin_d7-32)&31))&1)<<7);
} else if(pin_d7>-1) {
b |= (((pins_l>>(pin_d7))&1)<<7);
}
How many of these if statements will be compiled into the binary?
You can't know that just by looking at it, assuming this is C++.
In this case, none will, because they all evaluate constexpr values.
But those can be buried under templates, for example, and then the answer non-obvious.
Real programmers use butterflies
|
|
|
|
|
From 1985 to 2010, any decent run-of-the-mill-general-purpose coding standard worked pretty well for C and C++. But since C++11, every batch of new features also added more tools that were easy to misuse against the spirit if not the letter of said guidelines.
Nowadays, the only way to maintain good code with modern C++ is using modern standards following Clean-Code / SOLID principles. I've found the most important thing is keeping classes and functions small: just one purpose is the key. The best way to find out if your function or class is small enough is thinking about a name that describes it well: if a good description requires an 'or' or 'and' or 'then', then your class or function tries to fulfil more than one purpose and should be split!
GOTOs are a bit like wire coat hangers: they tend to breed in the darkness, such that where there once were few, eventually there are many, and the program's architecture collapses beneath them. (Fran Poretto)
|
|
|
|
|
"draw::text<>" has one purpose in my draw class in GFX, and has multiple overloads, the implementation of which spans several functions and one entire private header+cpp file. Because it renders truetype text. Even after factoring the function is half a screen on my display.
I agree with you in principle, but in practice I can't code that way. I have some wiring issues in my head that affect my short term memory, and if I over-factor I forgot how to navigate all the layers and calls. In some respects my condition can be debilitating. This is not one of those areas, but it does mean I have to adjust my approach. Cognitive load on the other hand, I can take for days. So give me a long function, as long as its purpose is singular.
But I understand that this is less than ideal, because I code professionally, and have coded on teams, from PM to lead to QA.
That's one reason I'm not a stickler for industry wide standards. I think a good coder can pick up a standard someone else uses, as long as that someone was consistent, and it's not awful - like there's sense to it. I mean, I have to consider myself and that I'm probably not the only one coding with a handicap of sorts.
As far as C++11 and beyond, I agree with you. But also I'm going to be difficult here - the introduction of variadic template arguments and things like that which facilitate metaprogramming and the metaprogramming itself creates its own kind of maintenance monster.
When you get used to reading "metaprogrammed" C++ it gets easier, but it's still a beast. I've had to reimplement a lot of what the STL hides at points because I target frameworks where the STL is partial if at all, and I don't know which parts are necessarily available, so I understand the sausage making therein.
On the other hand, you can do so much with it. Opening a dialogue with the compiler using your code, so that it can shape the generated code is just ... no other coding is like that. It sets C++ apart, and allows you to do some truly amazing things. In GFX I allow you to define pixels with arbitrary binary layouts and color formats. It then lets you manipulate all the individual color channels and if everything is const, it all resolves const. That means if you use a predefined color like "hot_pink" (an X11 standard color) it will resolve to a single scalar const value at compile time. You can even get the compiler to color blend and convert color formats (from like Y'UV to RGBA or BGR)
All that with C++14 and a somewhat byzantine header file (linked if you're curious) gfx/gfx_pixel.hpp at master · codewitch-honey-crisis/gfx · GitHub[^]
Anyway, you can't do that with C++ prior to 11 (14 in this above case), and you can't do it really in any other language, at least not as directly. (C# includes some code generation facilities now, but that's roundabout)
Real programmers use butterflies
|
|
|
|
|