Path: news.eternal-september.org!eternal-september.org!.POSTED!not-for-mail From: bart Newsgroups: comp.lang.c Subject: Re: Loops (was Re: do { quit; } else { }) Date: Tue, 22 Apr 2025 00:33:48 +0100 Organization: A noiseless patient Spider Lines: 129 Message-ID: References: <20250413072027.219@kylheku.com> <87ecxmv4t4.fsf@nosuchdomain.example.com> <20250420200823.908@kylheku.com> <20250421113640.839@kylheku.com> <20250421125957.29@kylheku.com> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 7bit Injection-Date: Tue, 22 Apr 2025 01:33:50 +0200 (CEST) Injection-Info: dont-email.me; posting-host="d044c27b80dbbae3d03ebae8688adc0f"; logging-data="3443142"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX1+hognLeicIjKK4er25t7af" User-Agent: Mozilla Thunderbird Cancel-Lock: sha1:4qgHsy47xeC36JQ3NzXa3dzyZBs= In-Reply-To: <20250421125957.29@kylheku.com> Content-Language: en-GB On 21/04/2025 21:25, Kaz Kylheku wrote: > On 2025-04-21, bart wrote: >> On 21/04/2025 19:43, Kaz Kylheku wrote: >>> On 2025-04-21, bart wrote: >>>> On 21/04/2025 04:16, Kaz Kylheku wrote: >>>>> - Because they are not gathered in one place, not only is it less> >>>> readable, but we cannot use while write a macro such as: >>>>> >>>>> for_sqlite_hash (p, &pSchema->trigHash) { >>>>> if (some_condition_over(p)) >>>>> continue; // doesn't stupidly repeat for the same p! >>>>> } >>>> >>>> I can't write such macros at all. I'm not even sure what this does. >>> >>> Have you never worked with a large codebase written by someone other >>> than you? >> >> How large are we talking about? > > Several 100K to millions. Characters, lines or files of source code, or bytes of binary? > >> I've delved into largish apps in the context of getting my C compiler >> working. I would describe that experience as 'brutal'. > > Well, that brutal experience is the job of the career software engineer, > believe it or not. Very few work only on their own original code. > >> If you need to >> debug someone else's codebase, not to find bugs in that program, but to >> find why your implementation is failing, then you want as conservative a >> coding style as possible. >> >> >>> When you open a random file in an unfamiliar code base, pretty >>> much any function call that is not in the standard library triggers >>> the "I don't know what this does" response. >> >> Yes, a function call. Macros are in an entirely different, evil category. > > No, they aren't. They are just another definition to understand. I've seen a 33KB C file that expanded to 4MB after preprocessing. Macros are evil. > > They can be misused, and also used to make solutions that are more > complicated than some great ideas that don't involve macros. You can tell when there is abuse going on, because you find that instead of optimising doubling the speed of a program, it will make it 3-4 times as fast. Because layers of macros hide the fact that that there are also multiple layers of functions which need serious inlining. > So can anything: an open coded function can be misused to make some > complicated solution that can be more nicely done with macros. >> The Lua sources (only a mere 30Kloc), use macros extensively, but also >> have a habit of giving them ordinary looking names in lower case so that >> they look like function calls. > > So does ISO C; e.g. assert (expr); offsetof (type, member). So what? > Macros that provide syntax should blend into the language. These didn't provide syntax. Here's a nice example: op_arith(L, l_addi, luai_numadd); Innocent-looking isn't it? It expands to this: {TValue*v1=(&((base+((((int)((((i)>>((((0+7)+8)+1)))&((~((~(Instruction)0)<<(8)))<<(0)))))))))->val);TValue*v2=(&((base+((((int)((((i)>>(((((0+7)+8)+1)+8)))&((~((~(Instruction)0)<<(8)))<<(0)))))))))->val);{StkId ra=(base+(((int)((((i)>>((0+7)))&((~((~(Instruction)0)<<(8)))<<(0)))))));if(((((v1))->tt_)==(((3)|((0)<<4))))&&((((v2))->tt_)==(((3)|((0)<<4))))){lua_Integer i1=(((v1)->value_).i);lua_Integer i2=(((v2)->value_).i);pc++;{TValue*io=((&(ra)->val));((io)->value_).i=(((lua_Integer)(((lua_Unsigned)(i1))+((lua_Unsigned)(i2)))));((io)->tt_=(((3)|((0)<<4))));};}else{lua_Number n1;lua_Number n2;if((((((v1))->tt_)==(((3)|((1)<<4))))?((n1)=(((v1)->value_).n),1):(((((v1))->tt_)==(((3)|((0)<<4))))?((n1)=((lua_Number)(((((v1)->value_).i)))),1):0))&&(((((v2))->tt_)==(((3)|((1)<<4))))?((n2)=(((v2)->value_).n),1):(((((v2))->tt_)==(((3)|((0)<<4))))?((n2)=((lua_Number)(((((v2)->value_).i)))),1):0))){pc++;{TValue*io=((&(ra)->val));((io)->value_).n=(((n1)+(n2)));((io)->tt_=(((3)|((1)<<4))));};}};};}; From this, I had to find the bug in my compiler. (Lua is an interpreter; I write interpreters and they are several times faster than Lua for equivalant inputs. I don't need to use such tricks.) >> Please humour me: What Does It Do? > > It is intended to condense an interation that *you* open-coded, in this > example upthread: > > p = sqliteHashFirst(&pSchema->trigHash); > while (p != NULL) > { > sqlite3DeleteTrigger(db, (Trigger*)sqliteHashData(p)); > p = sqliteHashNext(p) ) > } > > So although I didn't show the definition, I think, of course the intent that it > does this (i.e. is relevant to the discussion thread) and not something else. > > for_sqlite_hash (p, hashptr) stmet > > must initialize p using sqliteHashFirst(hashPtr); This sounds a very specific type of macro, just to replace a simple loop. And it is simple; those identifiers make it look scary. This is the loop: p = a; while (p) { stmt; p = next(); // which turns out to be p = p->next } Given that, do you /really/ need that extra layer of complexity via that macro? > If I had to write 17 loops over SQLite hashes, I'd rather type > for_sqlite_hash(p, hash) than > for (p = sqliteHashFirst(hash); p; p = sqliteHashNext(hash)). I guess that answers that question! I'd use macros only as a last resort; you would use them at the first opportunity.