Re: Dscanner: is it possible to switch off style checks case-by-case?
On Thursday, 13 February 2020 at 17:15:50 UTC, mark wrote: I'm starting out with GtkD and have this function: void main(string[] args) { Main.init(args); auto game = new GameWindow(); Main.run(); } and this method: void quit(Widget widget) { Main.quit(); } When I run dscanner --styleCheck it reports: ./src/app.d(10:10)[warn]: Variable game is never used. ./src/app.d(22:22)[warn]: Parameter widget is never used. These are correct. However, is it possible to switch them off individually? Yes you can but in the present case the analysis is right because you can write void main(string[] args) { Main.init(args); new GameWindow(); Main.run(); } and for the other void quit(Widget) { Main.quit(); } or even void quit() { Main.quit(); } Otherwise here is an example of how you can tune the different checks: https://raw.githubusercontent.com/dlang/phobos/master/.dscanner.ini See also the last section of https://raw.githubusercontent.com/dlang-community/D-Scanner/master/README.md
Re: Building for multiple platforms
Since your project is already on GitHub, I think the easiest solution would be to use GitHub Actions [1] + setup-dlang action [2] + upload-release-asset action [3] to automate the whole process. [1]: https://help.github.com/en/actions [2]: https://github.com/mihails-strasuns/setup-dlang [3]: https://github.com/actions/upload-release-asset This looks very promising, thanks!
Re: Strange instruction sequence with DMD while calling functions with float parameters
On Friday, 14 February 2020 at 22:36:20 UTC, PatateVerte wrote: Hello I noticed a strange behaviour of the DMD compiler when it has to call a function with float arguments. I build with the flags "-mcpu=avx2 -O -m64" under windows 64 bits using "DMD32 D Compiler v2.090.1-dirty" I have the following function : float mul_add(float a, float b, float c); //Return a * b + c When I try to call it : float f = d_mul_add(1.0, 2.0, 3.0); I tested with other functions with float parameters, and there is the same problem. Then the following instructions are generated : //Loads the values, as it can be expected vmovss xmm2,dword [rel 0x64830] vmovss xmm1,dword [rel 0x64834] vmovss xmm0,dword [rel 0x64838] //Why ? movq r8,xmm2 movq rdx,xmm1 movq rcx,xmm0 // call 0x400 //0x400 is where the mul_add function is located My questions are : - Is there a reason why the registers xmm0/1/2 are saved in rcx/rdx/r8 before calling ? The calling convention specifies that the floating point parameters have to be put in xmm registers, and not GPR, unless you are using your own calling convention. - Why is it done using non-avx instructions ? Mixing AVX and non-AVX instructions may impact the speed greatly. Any idea ? Thank you in advance. It's simply the bad codegen (or rather a missed opportunity to optimize) from DMD, its backend doesn't see that the parameters are already in the right order and in the right registers so it copy them and put them in the regs for the inner func call. I had observed this in the past too, i.e unexplained round tripping from GP to SSE regs. For good FP codegen use LDC2 or GDC or write iasm (but loose inlining). For other people who'd like to observe the problem: https://godbolt.org/z/gvqEqz. By the way I had to deactivate AVX2 targeting because otherwise the result is even more weird (https://godbolt.org/z/T9NwMc)
Re: rt/aaA.d Line: 553
On Friday, 14 February 2020 at 23:28:39 UTC, Steven Schveighoffer wrote: On 2/14/20 5:57 PM, Ferhat Kurtulmuş wrote: One thing to learn, you can select a section of lines from github (click on first line, then shift-click on last line), then press the 'y' key, and it will generate a permanent link to those lines. https://github.com/dlang/druntime/blob/a581dc6d1d3bf796fcebd982221d0b3d6bbae437/src/rt/aaA.d#L553-L562 Thank you, I didn't know that.
Re: rt/aaA.d Line: 553
On 2/14/20 6:36 PM, Ferhat Kurtulmuş wrote: İf ((aa.used + 1)* GROW_DEN > aa.dim * GROW_NUM) aa.grow(ti.key); This call is expensive (it reallocates all the buckets and reinserts all the existing data into the new bucket), it should be avoided if in the end we will not be incrementing used. -Steve
Re: rt/aaA.d Line: 553
On Friday, 14 February 2020 at 23:19:31 UTC, Paul Backus wrote: On Friday, 14 February 2020 at 22:57:31 UTC, Ferhat Kurtulmuş wrote: findSlotInsert are called two times. Why not: if (++aa.used * GROW_DEN > aa.dim * GROW_NUM) aa.grow(ti.key); auto p = aa.findSlotInsert(hash); // only one call is enough? if (p.deleted) --aa.deleted; ... If I am not wrong this modification will not corrupt the current state of the hash table? `used` counts both filled and deleted buckets, so it shouldn't be incremented when changing a deleted bucket into a filled bucket. İf ((aa.used + 1)* GROW_DEN > aa.dim * GROW_NUM) aa.grow(ti.key); auto p = aa.findSlotInsert(hash); // only one call is enough? if (p.deleted) --aa.deleted; else ++aa.used;
Re: rt/aaA.d Line: 553
On 2/14/20 5:57 PM, Ferhat Kurtulmuş wrote: I hesitated to post this on the topic of druntime because probably I will learn something new if I am totally/stupidly wrong. There are no stupid questions, and this is the learn forum. So you are in the right place! In druntime/blob/master/src/rt/aaA.d Lines 553-562: One thing to learn, you can select a section of lines from github (click on first line, then shift-click on last line), then press the 'y' key, and it will generate a permanent link to those lines. https://github.com/dlang/druntime/blob/a581dc6d1d3bf796fcebd982221d0b3d6bbae437/src/rt/aaA.d#L553-L562 ... auto p = aa.findSlotInsert(hash); if (p.deleted) --aa.deleted; // check load factor and possibly grow else if (++aa.used * GROW_DEN > aa.dim * GROW_NUM) { aa.grow(ti.key); p = aa.findSlotInsert(hash); assert(p.empty); } ... findSlotInsert are called two times. Why not: if (++aa.used * GROW_DEN > aa.dim * GROW_NUM) aa.grow(ti.key); auto p = aa.findSlotInsert(hash); // only one call is enough? if (p.deleted) --aa.deleted; ... If I am not wrong this modification will not corrupt the current state of the hash table? A cursory reading: I think the case where you find the insert slot and the p.deleted is true, you can avoid the grow function. The grow function is much more expensive than findInsertSlot, so calling it twice is preferable. The reason we have the deleted status is because we want to reuse memory, but we can't free the memory if it had a dangling reference to it. The only time those deleted nodes turn into garbage is on a resize. HOWEVER, one case where we can avoid the double search is if there are no deleted nodes (we keep a count of them). This should be reasonably often in most cases because you insert nodes and don't remove them, or you grew the AA and all deleted nodes are purged. So maybe change to: Bucket *p; if(aa.deleted > 0 && (p = aa.findSlotInsert(hash)).deleted) --aa.deleted; else // everything else the same -Steve
Re: rt/aaA.d Line: 553
On Friday, 14 February 2020 at 22:57:31 UTC, Ferhat Kurtulmuş wrote: findSlotInsert are called two times. Why not: if (++aa.used * GROW_DEN > aa.dim * GROW_NUM) aa.grow(ti.key); auto p = aa.findSlotInsert(hash); // only one call is enough? if (p.deleted) --aa.deleted; ... If I am not wrong this modification will not corrupt the current state of the hash table? `used` counts both filled and deleted buckets, so it shouldn't be incremented when changing a deleted bucket into a filled bucket.
rt/aaA.d Line: 553
I hesitated to post this on the topic of druntime because probably I will learn something new if I am totally/stupidly wrong. In druntime/blob/master/src/rt/aaA.d Lines 553-562: ... auto p = aa.findSlotInsert(hash); if (p.deleted) --aa.deleted; // check load factor and possibly grow else if (++aa.used * GROW_DEN > aa.dim * GROW_NUM) { aa.grow(ti.key); p = aa.findSlotInsert(hash); assert(p.empty); } ... findSlotInsert are called two times. Why not: if (++aa.used * GROW_DEN > aa.dim * GROW_NUM) aa.grow(ti.key); auto p = aa.findSlotInsert(hash); // only one call is enough? if (p.deleted) --aa.deleted; ... If I am not wrong this modification will not corrupt the current state of the hash table?
Strange instruction sequence with DMD while calling functions with float parameters
Hello I noticed a strange behaviour of the DMD compiler when it has to call a function with float arguments. I build with the flags "-mcpu=avx2 -O -m64" under windows 64 bits using "DMD32 D Compiler v2.090.1-dirty" I have the following function : float mul_add(float a, float b, float c); //Return a * b + c When I try to call it : float f = d_mul_add(1.0, 2.0, 3.0); I tested with other functions with float parameters, and there is the same problem. Then the following instructions are generated : //Loads the values, as it can be expected vmovss xmm2,dword [rel 0x64830] vmovss xmm1,dword [rel 0x64834] vmovss xmm0,dword [rel 0x64838] //Why ? movq r8,xmm2 movq rdx,xmm1 movq rcx,xmm0 // call 0x400 //0x400 is where the mul_add function is located My questions are : - Is there a reason why the registers xmm0/1/2 are saved in rcx/rdx/r8 before calling ? The calling convention specifies that the floating point parameters have to be put in xmm registers, and not GPR, unless you are using your own calling convention. - Why is it done using non-avx instructions ? Mixing AVX and non-AVX instructions may impact the speed greatly. Any idea ? Thank you in advance.
static foreach over enum symbols
Hi all, I'm getting unexpected results while trying to process symbols from a module, some of which are enums. Depending on whether or not I comment out the first static foreach loop below, fullyQualifiedName gives me different results in the second loop. In either case, I'm surprised I can't grab the UDAs in the second static foreach loop. Any ideas what's going on? Test case: ---a.d--- module a; @Object enum x = "hello"; @Object enum y = "goodbye"; @Object struct z{} ---main.d--- template symbols(alias Mod){ import std.meta; alias toSymbol(alias T) = __traits(getMember, Mod, T); alias symbols = staticMap!(toSymbol, __traits(allMembers, Mod)); } void main(){ import std.traits; import std.meta; import a; //commenting this out changes the results below static foreach(sym; symbols!a){ pragma(msg, fullyQualifiedName!sym); pragma(msg, __traits(getAttributes, sym)); } pragma(msg, "\nget with UDAs\n"); pragma(msg, getSymbolsByUDA!(a, Object)); alias udaSyms = getSymbolsByUDA!(a, Object); pragma(msg, staticMap!(fullyQualifiedName, udaSyms)); static foreach(us; udaSyms){ pragma(msg, fullyQualifiedName!us); pragma(msg, __traits(getAttributes, us)); } } --- annotated output of dmd main.d: with the first loop commented out: get with UDAs tuple("hello", "goodbye", (z)) tuple("a.x", "a.y", "a.z") a.x tuple() //why is the UDA gone? a.y tuple() a.z tuple((Object)) and with the first loop: object tuple() main.main.sym //it's not a.x anymore, it's the name of the local var for static foreach? tuple() main.main.sym tuple() a.z tuple((Object)) get with UDAs tuple("hello", "goodbye", (z)) tuple("main.main.sym", "main.main.sym", "a.z") //and the results are changed here too? main.main.sym tuple() main.main.sym tuple() a.z tuple((Object))
Re: can't run D project on Visual studio
On 13/02/2020 15:54, Akomire Samson wrote: > I am having this error on running D project using Visual studio 2019 and > Visual D > > > Build Log > > Building Win32\Debug\LearningD.exe > > Command Line > > set PATH=C:\D\ldc2-1.19.0-windows-multilib\bin;C:\Program Files > (x86)\Microsoft Visual > Studio\2019\Community\VC\Tools\MSVC\14.24.28314\bin\HostX86\x64;C:\Program > Files (x86)\Microsoft Visual > Studio\2019\Community\Common7\IDE;C:\Program Files (x86)\Windows > Kits\10\bin;%PATH% > set LIB=C:\Program Files (x86)\Microsoft Visual > Studio\2019\Community\VC\Tools\MSVC\14.24.28314\lib\x86;C:\Program Files > (x86)\Windows Kits\10\Lib\10.0.18362.0\ucrt\x86 > set VCINSTALLDIR=C:\Program Files (x86)\Microsoft Visual > Studio\2019\Community\VC\ > set VCTOOLSINSTALLDIR=C:\Program Files (x86)\Microsoft Visual > Studio\2019\Community\VC\Tools\MSVC\14.24.28314\ > set VSINSTALLDIR=C:\Program Files (x86)\Microsoft Visual > Studio\2019\Community\ > set WindowsSdkDir=C:\Program Files (x86)\Windows Kits\10\ > set WindowsSdkVersion=10.0.18362.0 > set UniversalCRTSdkDir=C:\Program Files (x86)\Windows Kits\10\ > set UCRTVersion=10.0.18362.0 > "C:\Program Files (x86)\VisualD\pipedmd.exe" -deps > Win32\Debug\LearningD.dep ldc2 -m32 -g -d-debug -X > -Xf="Win32\Debug\LearningD.json" -of="Win32\Debug\LearningD.exe" > -L/PDB:"Win32\Debug\LearningD.pdb" -L/SUBSYSTEM:CONSOLE -L/noopttls > -od="Win32\Debug" LearningD.d > if %errorlevel% neq 0 goto reportError > if not exist "Win32\Debug\LearningD.exe" (echo > "Win32\Debug\LearningD.exe" not created! && goto reportError) > > goto noError > > :reportError > echo Building Win32\Debug\LearningD.exe failed! > > :noError > Output > > LINK : fatal error LNK1181: cannot open input file 'kernel32.lib' > Error: C:\Program Files (x86)\Microsoft Visual > Studio\2019\Community\VC\Tools\MSVC\14.24.28314\bin\HostX86\x64\link.exe > failed with status: 1181 > Building Win32\Debug\LearningD.exe failed! > > > > I will appreciate any help. Maybe you don't have a Windows SDK installed? With the settings as shown above, kernel32.lib would be expected to exist in "c:\Program Files (x86)\Windows Kits\10\Lib\10.0.18362.0\um\x86". If that looks ok, maybe it has to do with using LDC to do the linking. Does it work when using DMD instead? Or try compilation model "Separate compile and link".
Re: How to use labeled break in static foreach?
On 2/14/20 1:41 AM, cc wrote: import std.meta; enum A = AliasSeq!(1, 2, 3, 4); THREELOOP: static foreach (idx, field; A) { static if (field == 3) { pragma(msg, "Got a 3!"); break THREELOOP; } static if (idx == A.length - 1) { static assert(0, "Got no 3..."); } } What I'd like to achieve in this example is for compilation to fail if the given tuple doesn't contain a matching item. Is there an easy way to do this? Trying to break out of the static foreach gives me Error:enclosing label `THREELOOP` for `break` not found In this reduced example, I was able to accomplish this by setting some enum within the successful comparison body and then checking e.g. static if (!__traits(compiles, FOUND)) but this breaks down once I start dealing with multiple levels of scope. static foreach does not support break. Partly because the places where static foreach is allowed do not allow break statements. In your example above, if you are not in a function context, a break statement is not allowed. Note that a naked break statement inside a static foreach that's inside e.g. a switch statement needs a label to ensure the user understands they are breaking the switch, not the static foreach. foreach does allow breaks. If you use foreach on an AliasSeq, it will work. But again, you must be in a function context. But remember that static foreach and foreach implement all the bodies given. So every single one has to be compiled, even if it's not used. This can cause problems for things like return statements that make further code not executable. AND static foreach doesn't introduce a new scope (foreach does). Other than recursive templates, I can't think of a great way to do this. Your enum solution has limits, as you say. -Steve
Re: How to get to body of HTTP 500 error with std.net.curl.get()?
On Friday, 14 February 2020 at 00:24:27 UTC, Gregor Mückl wrote: Hi! I am trying to write a client for pretty... well... creatively designed web API. The server gives HTTP status 500 replies if the requests are malformed, but the actual error message is hidden in the body of the reply (an XML document!). std.net.curl.get() throws an exception in this case. But I would like to get the body to process the error message within. How can I do that? Thanks in advance, Gregor Hi Gregor, If I am not completely wrong, the exception has an attribute "msg" which should contain the body of the http response. Kind regards André
Re: How to use labeled break in static foreach?
this kind of thing doesn't work super well due to the nature of compile time. My suggestion is to put the checks and the implementation in separate things. void foo(T...)() { static bool checkHelper() { bool passed; static foreach(t; T) { static if(is(t == whatever)) { passed = false; } } return passed; } static assert(checkHelper()); // then foreach to do the rest of it } something like that. maybe returning string instead of bool to have an error message you print out with the static assert. i don't love this but it is the simplest way I know.