|
Coming from the automation world I have noticed that my way to code usually differs a lot from other people that always coded in "high" languages.
Sometimes for the good, sometimes for the bad.
I do try to assimilate things in my own "style" when I find something that I like and / or surprises me in the good way. But I am not always successful with that on the long term.
So far, all the people that read my code could understand it and that was / is the most important to me. Sometimes I do things complicated, sometimes I do things way simpler than others.
At the end of the day, as long as it does what it has to do and how it has to do it... I don't really care.
[Addition] Something people have often told me: "Why didn't you use XXXXX for that?" because I tend to reinvent the wheel. Sometimes because I don't know that other function even existed, sometimes because I don't like how it works / performs.
M.D.V.
If something has a solution... Why do we have to worry about?. If it has no solution... For what reason do we have to worry about?
Help me to understand what I'm saying, and I'll explain it better to you
Rating helpful answers is nice, but saying thanks can be even nicer.
|
|
|
|
|
I completely agree, but I would equate it to a major net win.
If you give me one big function, that attacks a part of the problem efficiently, I never need comments or documentation to figure out what it does or why.
If you give me a composite design, I need proper documentation and comments beforehand, and I will need to trace your code while debugging, to:
A) figure out which part is fluff, and which part is functional necessity
B) compare it to common patterns, to distill the overall intent of the initial design
C) align the actual problem set with a stripped down version that discards most if not all of the design, before I will commit to maintaining it
23 years of higher level programming has lead me to the conclusion that design is not an artform, it's a tool that can be used to remove friction prior to replacing a component, or to promote form-over-function for selfish reasons.
In general, everything is overdesigned for selfish reasons.
|
|
|
|
|
I didn't start coding this way until my first psychotic event - meaning after I started seeing things that weren't there. That was in 2018. I went manic for a week, and I haven't been the same since.
I'm still dealing with the fallout to my brain, yet fascinated by it, while frustrated with living with it sometimes.
I started coding in 1986 when I was 8. I haven't really put it down for any serious length of time, so a lot of how I code is probably experiential but it did change significantly for me after I went over the high wall.
I can't code the way I used to anymore. I wouldn't be capable of it. It reminds me of whenever I go to order coffee and the fact that I could never do a barista's job is thrown into stark relief.
So part of it is a crutch, but it has its advantages, I think too, as you've reiterated above.
To err is human. Fortune favors the monsters.
|
|
|
|
|
Depends on whose navel it is. Once you have decided which meaning of navel you would use, everything else is easy, funny and weird.
"It is easy to decipher extraterrestrial signals after deciphering Javascript and VB6 themselves.", ISanti[ ^]
|
|
|
|
|
I am always fascinated by the way people code. It's a bit of a window into their minds - or a snapshot, really, since, as you say, we evolve.
I recently switched from 'readable' code to maximized use of the language after reading an article about the same. I enjoy putting a chunk of logic all in 'one line' of code where the language allows, and often it is much more readable that way for the one who knows the language well - for me it certainly is. One example is ternary operators. I also have begun to search for the most efficient way to accomplish a task when there are several, where "efficient" is measured in processing time. An example would be: when is it more efficient to use if-then instead of a switch (usually, sadly, since switch is so pretty in the code)?
Growth comes with much navel gazing
|
|
|
|
|
My guilty pleasure is complicated chains of template specializations - the ultimate in write-only code.
It's sooo powerful though. I am constantly drawn to the dark side. They have cookies.
To err is human. Fortune favors the monsters.
|
|
|
|
|
I swing the other way, so to speak. My code gets more modular, more factored as I age/gain experience. I too can now usually begin coding without much up-front thought, but I can do this because I explore the design in the language of code instead of some other language like diagrams, lists, etc. Abstraction is part of my design process. When I don't abstract, I get lost in the weeds.
|
|
|
|
|
I like the way you think. I'm not in IoT or embedded but videogames are similar (and it's rare that the code ever gets revisited after a year of patches).
As to readability, we had a saying some years ago "If you can't figure out what the code is doing, leave it alone, it's doing fine...a more worthy engineer will figure it out someday"
|
|
|
|
|
I haven't gotten any team I've worked with to buy in, but IMHO it depends what kind of coding style issue we're talking about:
1- Class, method, variable, function names... Identifiers should be logical and predictable. If your project uses getX() and setX(newX), or if it uses x() and x(newX), just do the same thing across the board! Rules like methods and functions should be verb expressions, etc.. Even whether or not to CamelCase acronyms -- is it convertToPdf() or convertToPDF()?
2- Layout: my feeling is more "Hell no!" Allow any sane code layout. The cost to the author to think about such things is too much higher than the cost to the reader to put up with them for such standards to make sense. (That said, my current team uses a plugin for Eclipse, Intellij and Jenkins to enforce the layout standard.)
Code complexity is somewhere in between, and I haven't really thought about as much as those two extremes. Code has to be maintainable. If your logic is hard to follow, the next developer to touch it is more likely to introduce a bug. Maybe I would be comfortable with longer methods IFF it means more comments are there as well.
|
|
|
|
|
#include <stdio.h>
template<int Pin> class foo {
constexpr const static int pin = Pin;
public:
constexpr inline static char test() __attribute((always_inline)) {
if(Pin==-1) {
return 'A';
} else {
return 'B';
}
}
static_assert(test()!=0,"test");
};
int main(int argc, char** argv) {
printf("%c\n",foo<-1>::test());
printf("%c\n",65);
return 0;
}
I'd like someone smarter than I am to explain to me why the first printf does not generate a mov esi, 65 or even movsx esi, 65 , but rather, 3 instructions that are seemingly redundant and yet don't get removed by the peephole optimizer, but I don't think that's going to happen.
The worst part is, I have a dozen libraries using a bus framework I wrote that relies on my bad assumptions about the code that is generated.
The upshot is the code is slow, and the only way to speed it up is to
A) rewrite it to not use templates
B) nix the ability to run multiple displays at once
But IT SHOULD NOT BE THIS WAY. I feel misled by the C++ documentation. But it was my fault for not checking my assumptions.
To err is human. Fortune favors the monsters.
modified 18-Sep-22 4:19am.
|
|
|
|
|
Is it because printf returns a value which has to be moved somewhere?
|
|
|
|
|
Nah, the printf is just forcing the code that I need to be put inside the binary.
That code executes before printf is called, and the register printf uses for its parameter is esi
Really, the second example generates the appropriate code mov esi, 65 which is all that is needed.
To err is human. Fortune favors the monsters.
|
|
|
|
|
|
Yeah, that's not really the issue I'm having though. I guess I wasn't clear. Let me see if I can explain it better.
See, if I put a constant in as per the second example of calling printf, it simply does mov esi, 65
int main(int argc, char** argv) {
constexpr const char a = 'A';
printf("%c\n",foo<-1>::test());
printf("%c\n",a);
return 0;
}
That's what I'd think it should do in the first example as well, printf or no.
It's using eax in the intermediary for reasons I can't fathom, and only when I call a forced inline function that should resolve to a compile time constant - and indeed *it does!* but it still generates extra instructions around it (fiddling with eax and al)
To bottom line it, why is the first example generating more code than the second example?
To err is human. Fortune favors the monsters.
|
|
|
|
|
honey the codewitch wrote: Yeah, that's not really the issue I'm having though.
That's why the code there is being generated. It's promoting the char to 32 bits. The language spec calls it "default argument promotion"
I have nothing more to add.
Good luck
|
|
|
|
|
But why is it only promoting it in one case?
Sorry, you don't have to answer. I know you said you have nothing left to add. It's just I'm still confused.
To err is human. Fortune favors the monsters.
|
|
|
|
|
No clue, you haven't even mentioned what compiler you are using. I can only point to the language standard documents.
All I can say is that it's well documented.
Read through the language spec, this was changed in C++11 and maybe the later specs, I don't feel like looking for you right now.
|
|
|
|
|
Tried on clang x86, gcc x86, gcc xtensa, gcc AVR.
To err is human. Fortune favors the monsters.
|
|
|
|
|
What release of gcc/clang are you using? According to Compiler Explorer I get the following with clang 5.0 with -O1 -std=C++17:
main: # @main
push rax
mov edi, .L.str
mov esi, 65
xor eax, eax
call printf
mov edi, .L.str
mov esi, 65
xor eax, eax
call printf
xor eax, eax
pop rcx
ret
.L.str:
.asciz "%c\n"
And x86-64 gcc 5.1 with the same flags gives:
.LC0:
.string "%c\n"
main:
sub rsp, 8
mov esi, 65
mov edi, OFFSET FLAT:.LC0
mov eax, 0
call printf
mov esi, 65
mov edi, OFFSET FLAT:.LC0
mov eax, 0
call printf
mov eax, 0
add rsp, 8
ret
Those are both pretty old compilers - the first of their lines to support C++17 AFAICT. Both produce the same code for each call. So maybe something in the compiler flags you're passing?
Keep Calm and Carry On
|
|
|
|
|
It probably has to do with the fact that I can't convince godbolt.org to allow me to remove their default compiler options and replace them with my own. I'm stuck with -o -whole-program or whatever. I used to be able to change it there somehow. Maybe someone exploited it and they turned off the feature.
To err is human. Fortune favors the monsters.
|
|
|
|
|
I get the same results using g++ 5.5.0 on my local linux box. That would be a CentOS 7 system on which I compiled g++-5.5.0 from source. So, still wondering if its maybe the flags you're using.
Keep Calm and Carry On
|
|
|
|
|
Looking at your output more carefully, your initial output is similar to mine.
Your final output is less optimized, probably having to do with your compiler flags.
To err is human. Fortune favors the monsters.
|
|
|
|
|
I get
main:
.LFB31:
.cfi_startproc
endbr64
subq $8, %rsp
.cfi_def_cfa_offset 16
movl $65, %edx
leaq .LC0(%rip), %rsi
movl $1, %edi
movl $0, %eax
call __printf_chk@PLT
movl $65, %edx
leaq .LC0(%rip), %rsi
movl $1, %edi
movl $0, %eax
call __printf_chk@PLT
movl $0, %eax
addq $8, %rsp
.cfi_def_cfa_offset 8
ret
with g++ -std=c++17 -O1 (g++ 9.4 on local linux box).
"In testa che avete, Signor di Ceprano?"
-- Rigoletto
|
|
|
|
|
I get that promotion is a thing. Again what I don't get is why it's only doing it in one case.
The thing is, all the documentation you've pointed me to suggests it should be doing the same thing in both cases.
But whatever, it doesn't matter because you're obviously tired of this.
To err is human. Fortune favors the monsters.
|
|
|
|
|
i think you should see the rule at C++11 7.16.1.4 paragraph 4
You are passing a function as an argument to a variadic function. That falls under the 'undefined behavior' description. This is why you get the default argument promotion.
As I said, just look it up and read the language spec.
honey the codewitch wrote: The thing is, all the documentation you've pointed me to suggests it should be doing the same thing in both cases. Well, there are two things you need to look at
- The rules of default argument promotion.
- The rules of passing arguments to variadic functions.
The spec clearly says that passing a function as an argument to a variadic function is undefined behavior. This is likely why you see the 'default argument promotion'.
It also mentions addressable object types and register storage.
printf("%c\n",65); This is passed in a register, this is defined behavior.
...
printf("%c\n",foo<-1>::test()); This is passed as a function. C11 7.16.1.4 paragraph 4 says this is undefined behavior.
I do see a proposal for C23 in the pipeline but it hasn't been voted on yet as far as I can tell.
I was wrong, looks like N2975 passed with 17 Yes, 0 No and two abstains.
You still haven't said what language version you are using or compiler.
modified 18-Sep-22 8:33am.
|
|
|
|
|