Re: Interfacing with basic C++ class
On 9/28/22 12:57, Riccardo M wrote: > class MyClass { > public: > int field; > MyClass(int a) : field(a) {} Make the following function 'virtual': > int add(int asd) { virtual int add(int asd) { I think the C++ class does not get a vptr without a virtual function and apparently D expects this. > final int add(int asd); You don't need that 'final' anymore. > This is a very simplified version of the dlang official example of > interfacing with C++ classes. The example there uses virtual functions. That must be the difference. > I need to declare every class method as > "final" otherwise I get undefined references during linking. Is this > expected behaviour? Apparently, not with that added 'virtual'. Ali
Re: to delete the '\0' characters
On 9/23/22 11:37, Salih Dincer wrote: > * character**S** > * at the **END** > * of the **STRING** I think the misunderstanding is due to the following data you've posted earlier (I am abbreviating): 53 F6 6E 6D 65 64 65 6E 20 79 75 72 64 75 6D 75 6E 20 FC 73 74 FC 6E 64 65 20 74 FC 74 65 6E 20 65 6E 20 73 6F 6E 20 6F 63 61 6B 0 4F 20 62 65 6E 69 6D 20 6D 69 6C 6C 65 74 69 6D 69 6E 20 79 131 6C 64 131 7A 131 64 131 72 20 70 61 72 6C 61 79 61 63 61 6B 0 0 0 0 You must have meant there were multiple strings there (apparently on separate lines) but I assumed you were showing a single string with 0 bytes inside the string. (Word wrap must have contributed to the misunderstanding.) Ali P.S. With that understanding, now I think searching from the end for the first non-zero byte may be faster than searching from the beginning for the first zero; but again, it depends on the data.
Re: to delete the '\0' characters
On 9/22/22 14:31, Salih Dincer wrote: > string splitz(string s) > { >import std.string : indexOf; >auto seekPos = s.indexOf('\0'); >return seekPos > 0 ? s[0..seekPos] : s; > } If you have multiple '\0' chars that you will continue looking for, how about the following? import std; auto splitz(string s) { return s.splitter('\0'); } unittest { auto data = [ "hello", "and", "goodbye", "world" ]; auto hasZeros = data.joiner("\0").text; assert(hasZeros.count('\0') == 3); assert(hasZeros.splitz.equal(data)); } void main() { } Ali
Re: to delete the '\0' characters
On 9/22/22 08:19, Ali Çehreli wrote: > string noZeroes(string s) > { > return s.byCodeUnit.filter!(c => c != '\0'); > } That won't compile; the return type must be 'auto'. Ali
Re: to delete the '\0' characters
On 9/22/22 03:53, Salih Dincer wrote: > Is there a more accurate way to delete the '\0' characters at the end of > the string? I tried functions in this module: > https://dlang.org/phobos/std_string.html Just to remind, the following are always related as well because strings are arrays, which are ranges: std.range std.algorithm std.array >r ~= c; Stefan Koch once said the ~ operator should be called "the slow operator". Meaning, if you want to make your code slow, then use that operator. :) The reason is, that operation may need to allocate memory from the heap and copy existing elements there. And any memory allocation may trigger a garbage collection cycle. Of course, none of that matters if we are talking about a short string. However, it may become a dominating reason why a program may be slow. I was going to suggest Paul Backus' solution as well but I may leave the array part out in my own code until I really need it: string noZeroes(string s) { return s.byCodeUnit.filter!(c => c != '\0'); } Now, a caller may be happy without an array: auto a = s.noZeroes.take(10); And another can easily add a .array when really needed: auto b = s.noZeroes.array; That may be seen as premature optimization but I see it as avoiding a premature pessimization because I did not put in any extra work there. But again, this all depends on each program. If we were talking about mutable elements and the order of elements did not matter, then the fastest option would be to remove with SwapStrategy.unstable: import std; void main() { auto arr = [ 1, 0, 2, 0, 0, 3, 4, 5 ]; arr = remove!(i => i == 0, SwapStrategy.unstable)(arr); writeln(arr); } unstable works by swapping the first 0 that it finds with the last non-zero that it finds and continues in that way. No memory is allocated. As a result, the order of elements will not preserved but unstable can be very fast compared to .stable (which is the default) because .stable must move elements to the left (multiple times in some cases) and can be expensive especially for some types. The result of the program above is the following: [1, 5, 2, 4, 3] Zeros are removed but the order is not preserved. And very important: Don't forget to assign remove's return value back to 'arr'. ;) I know this will not work for a string but something to keep in mind... Ali
Re: Why this function just decides to not call another function and do its thing instead?
On 9/17/22 10:14, frame wrote: On Saturday, 17 September 2022 at 15:04:48 UTC, solidstate1991 wrote: And then instead just decides that the `localName` and `namespaceURI` pairs are not equal, and in those cases the Visual Studio debugger doesn't detect any entering into any of the `DOMString.equals` overrides, all while the debugger shows those strings are equal. `opEquals` probably was not called if an operand was null. It seems that `Attr.localName` can return null. Indeed. Copying from my /usr/include/dlang/dmd/object.d: /++ Implementation for class opEquals override. Calls the class-defined methods after a null check. Please note this is not nogc right now, even if your implementation is, because of the typeinfo name string compare. This is because of dmd's dll implementation. However, it can infer to @safe if your class' opEquals is. +/ bool opEquals(LHS, RHS)(LHS lhs, RHS rhs) if (is(LHS : const Object) && is(RHS : const Object)) { static if (__traits(compiles, lhs.opEquals(rhs)) && __traits(compiles, rhs.opEquals(lhs))) { // If aliased to the same object or both null => equal if (lhs is rhs) return true; // If either is null => non-equal if (lhs is null || rhs is null) return false; if (!lhs.opEquals(rhs)) return false; // If same exact type => one call to method opEquals if (typeid(lhs) is typeid(rhs) || !__ctfe && typeid(lhs).opEquals(typeid(rhs))) /* CTFE doesn't like typeid much. 'is' works, but opEquals doesn't (issue 7147). But CTFE also guarantees that equal TypeInfos are always identical. So, no opEquals needed during CTFE. */ { return true; } // General case => symmetric calls to method opEquals return rhs.opEquals(lhs); } else { // this is a compatibility hack for the old const cast behavior // if none of the new overloads compile, we'll go back plain Object, // including casting away const. It does this through the pointer // to bypass any opCast that may be present on the original class. return .opEquals!(Object, Object)(*cast(Object*) , *cast(Object*) ); } } Ali
Re: dub lint
On 9/15/22 16:14, Christian Köstlin wrote: > There is `dub run dscanner -- --defaultConfig` which creates a default > config in `~/.config/dscanner/dscanner.ini` (for linux and osx). Thanks! I love such features. It is so useful for a program to write out its configuration file. (My tools did that too.) > interestingly `dub lint --help` shows two different options for `--config`: > > ``` > dub lint --help > USAGE: dub lint [[@]] [] [-- > ] > .. > .. >--config=VALUEUse the given configuration file. > .. > .. >-c --config=VALUEBuilds the specified configuration. Neither of which is obvious to the newcommer at all. Not one bit! Even though I knew dscanner was the program 'dub lint' uses (perhaps because I noticed the message as it was being installed?) I did not think even once that 'dub lint's --config would refer to 'dscanner's config. As a dub user, I wouldn't know to go to a random program's web site. Even 'dub lint' doesn't mention dscanner: https://dub.pm/commandline.html#lint Ali
Re: dub lint
On 9/15/22 15:04, Ali Çehreli wrote: > Is there a way to silence specific 'dub lint' warnings? Answering myself, I don't think it's possible but luckily my catching an Error was in unittests only so I can do either of the following to skip unittest code when linting: a) Pass --skipTests to dscanner (what 'dub lint' runs behind the scenes) along with --styleCheck, which --skipTests requires dub lint -- --skipTests --styleCheck b) Pass --styleCheck indirectly through 'dub lint', which has its own spelling for it :), but --skipTests is still required of course: dub lint --style-check -- --skipTests Ali
Re: dub lint
On 9/15/22 14:32, Ali Çehreli wrote: > (However, like all linters it's not perfect but I still like having that > power.) The following code is flagged because it catches Error: unittest { try { assert(false); } catch (Error) { // Cool... } } [warn]: Catching Error or Throwable is almost always a bad idea. Is there a way to silence specific 'dub lint' warnings? For example, the code above is actually similar to assertThrown!Error, which knows what it's doing. :) https://dub.pm/commandline.html#lint Ali
dub lint
I've always thought of dub as a package manager and a build tool. But it actually makes it easy to use other tools: - dub lint: Runs some checks on your project. What I liked is how it removed the need to figure out how to install dscanner, which it uses behind the scenes. It installed dscanner and simply ran it. Cool... (However, like all linters it's not perfect but I still like having that power.) - dub dustmite: I haven't actually tried this but dub's help with using dusmite sounds great. Ali
Re: need help to translate C into D
On 9/13/22 04:07, test123 wrote: > On Tuesday, 13 September 2022 at 10:59:36 UTC, Dennis wrote: >> Side node, you can use `immutable` instead of `__gshared const`, it >> amounts to the same for global variables. > > because __enums_layout.ptr need to be part of other object, and this > const ptr cloud be included in multi objects. There may be valid reasons not to use 'immutable' but you can still do what you describe. There is an 'immutable' array below and its .ptr is being stored as 'const' inside objects of another struct. struct S { int i; string s; int[3] arr; } immutable S[] imm; shared static this() { // Initializing immutable at program initialization time: imm ~= S(42, "hello", [1,2,3]); imm ~= S(7, "world", [4,5,6]); } struct Other { const S * ptr; } void main() { auto o = Other(imm.ptr); } That works because 'const' can point to 'immutable' (and mutable and const). Ali
Re: Function attribute best practices
On 9/13/22 10:08, Paul Backus wrote: > Here's my attempt, covering all the attributes found under > [`MemberFunctionAttribute`][1] in the language spec: > > |Attribute|Affects |Inferred?| > |-||-| > |nothrow |Function|Yes | > |pure |Function|Yes | > |@nogc|Function|Yes | > |@safe|Function|Yes | > |@system |Function|Yes | > |@trusted |Function|No | > |@property|Function|No | > |@disable |Function|No | > |const|this|No | > |immutable|this|No | > |inout|this|No | > |shared |this|No | > |return |this|Yes | > |scope|this|Yes | > > In general, attributes with a 'Yes' in the 'Inferred?' column should not > be applied explicitly to functions that are subject to [attribute > inference][2]. This includes functions defined inside templates, as well > as nested functions and functions with an inferred return type (i.e., > `auto` functions). > > [1]: https://dlang.org/spec/function.html#MemberFunctionAttributes > [2]: https://dlang.org/spec/function.html#function-attribute-inference That is great! I think we can improve it with guidelines for when to write an attribute or not. For example, carried over from C++, I have this guideline: "put const to as many member function as you can." That guideline makes the function more useful because I can call it on mutable, const, and immutable objects. Great... Programmers can understand that. Now let's compare it to what the const attribute on a member function says (I did not find it in the spec; so making it up): "Makes the 'this' reference const." Although correct, it does not help the programmer. Or... What does pure mean? Does it tell the outside world that the function is pure or does it require to be called from pure code? I put that here because e.g. 'immutable' on a member function kind of does that: It requires to be called on an immutable object. Ok, not a fair comparison because there is no "pure object" but still, these are thoughts that I think programmers have in mind. (I do. :) ) Ali
Re: Function attribute best practices
On 9/12/22 09:39, Paul Backus wrote: > Yes. Except for `@trusted`, explicit attributes on template code are a > smell. Except for 'const' as well because some templates are member functions. And 'const' on a member function cannot be left to inference because it happens to be a part of the type of the function, which can be overloaded. Somebody needs to create a two dimensional table that shows what it means for each function attribute on a regular function, member function, and templates of those, and hopefully come up with some guidelines. I started thinking about it but will not have time these coming days. :/ Ali
Re: Function attribute best practices
On 9/12/22 11:29, Steven Schveighoffer wrote: > So you are thinking about this the wrong way I believe. Clearly. > When you put `pure` on a template function, you are saying "only > instantiations where this function can be pure are allowed". Makes sense. I was trying to put as many attributes as possible, being proud like, "look: my function is pure, @nogc, etc." Ali
Re: Function attribute best practices
On 9/12/22 10:29, H. S. Teoh wrote: write a unittest where you instantiate Foo with a deliberately-impure type Yes. A current on-topic thread on the difficulties of covering all corner cases: https://forum.dlang.org/thread/dmnfdqiplbldxkecp...@forum.dlang.org Ali
Re: Function attribute best practices
On 9/12/22 09:48, H. S. Teoh wrote: >> @nogc nothrow pure @safe >> unittest >> { >> // ... >> } >> >> No, it isn't because unless my unittest code is impure, I can't catch >> my incorrect 'pure' etc. on my member functions. > [...] > > Sure you can. The `pure unittest` code obviously must itself be pure > (otherwise it wouldn't compile). If Foo introduces impure behaviour, > then the unittest, being pure, wouldn't be allowed to call Foo's impure > methods, which is what we want. What's the problem? There was a problem until you and others put me straigth. :) What I meant was - if I put 'pure' etc. on my templatized code, - and then tested with a 'pure' unittest, I wouldn't know that the gratuitous use of my 'pure' on the member function was wrong. I would be fooling myself thinking that I smartly wrote a 'pure' member function and a 'pure' unittest and all worked. Wrong idea! :) Now I know I must leave attributes as much to inference as possible. Ali
Function attribute best practices
The following range Foo is trying to be helpful by adding as many attributes as it can ('const' is missing because ranges cannot be 'const' because at least popFront() needs to be mutable): import std.algorithm; struct Foo(R) { R r; int i; bool empty() @nogc nothrow pure @safe scope { return r.empty; } auto front() @nogc nothrow pure @safe scope { return r.front; } auto popFront() @nogc nothrow pure @safe scope { r.popFront(); } } auto foo(R)(R r) { return Foo!R(r); } int count; void main() { [ 1, 2 ] .map!((i) { ++count;// <-- Impure return i; }) .foo; } Of course there are compilation errors inside the member functions because e.g. r.front that it dispatches to is not pure (it touches the module variable 'count'): Error: `pure` function `deneme.Foo!(MapResult!(__lambda1, int[])).Foo.front` cannot call impure function `deneme.main.MapResult!(__lambda1, int[]).MapResult.front` (Other attributes would cause similar issues if e.g. the lambda were @nogc.) What are best practices here? Is this accurate: Because Foo is a template, it should not put any attribute on member functions? Or only member functions that use a member that depends on a template parameter? And non-members that are templates? It is scary because Foo works just fine until it is used with impure code. Is putting function attributes on unittest blocks for catching such issues? @nogc nothrow pure @safe unittest { // ... } No, it isn't because unless my unittest code is impure, I can't catch my incorrect 'pure' etc. on my member functions. Help! :) Ali
Re: Why I get delegate when passing address of function?
On 9/11/22 09:26, Ali Çehreli wrote: // This combines a class instance (which is a pointer behind the scene) // Combine with the class object In both places I meant "class variable". Ali
Re: Why I get delegate when passing address of function?
On 9/11/22 02:54, Injeckt wrote: > And what I should do to pass non-static function? You can combine your class object with other arguments and your thread function will know how to unwrap your class object to call its member function: import std.stdio; // I am not on Windows, so I am making it non-D another way // Let's assume this is what the library wants from us alias MyThreadFunc = uint function(void*); // Let's assume this is what the library provides extern (C) void libraryCreateThread(MyThreadFunc func, void* param) { writeln("The libraryCreateThread is starting our function"); func(param); } // This is my class class C { string s; // This is the function I want to be called void myClientThread(void* param) { writeln("The desired member function is here"); writeln("the member: ", s); // At this point we know the extra argument shoud be an // int (that's why main used one) auto arg = *cast(int*)param; writeln("the extra argument: ", arg); } } // We have to play along with what the library wants // It wants a 'function', so here is one: // (This could be a static member function) uint myNonMemberThreadFunc(void* param) { writeln("Our function is making the param useful..."); // See MyThreadArgs below auto args = *cast(MyThreadArgs*)param; writeln("... and calling our member function"); args.c.myClientThread(args.extraArg); return 0; } // This combines a class instance (which is a pointer behind the scene) // and any other argument struct MyThreadArgs { C c; void* extraArg; } void main() { auto c = new C(); c.s = "the class member"; // Assuming some extra argument int mainArg = 42; // Combine with the class object; this may have to be on the heap auto args = MyThreadArgs(c, ); // Do what the library wants libraryCreateThread(, cast(void*)); } > Error: function `_server.Server.ClientThread(void* param)` is not > callable using argument types `()`. too few arguments, expected `1`, got > `0` That looks like the same problem you had a couple of days ago: The name of the function is not a function pointer in D but is a call to it: - foo: The same thing as foo() - : 'function' Ali
Re: Dictionary of Templated Functions
On 9/10/22 15:35, jwatson-CO-edu wrote: > So, my solution > will be to construct a catch-all struct `Payload` and have that be my > argument type from which various functions can draw the data of their > choice. Two Phobos features may be helpful there: https://dlang.org/phobos/std_sumtype.html https://dlang.org/phobos/std_variant.html Ali
Re: Can you access the same classes from C++ and D and vise versa, or do the classes have to not form dependency cycle?
On 9/10/22 13:04, Daniel Donnell wrote: > https://dlang.org/spec/cpp_interface.html At DConf, Manu indicated that that page is outdated and that D's C++ support is actually a lot better. He kind-of-promised to update that page but I doubt it happened yet if ever. :) > one has to be compiled before the other, order depending on direction. I don't think that's correct. The page is unfortunately unclear: "the first with a C++ compiler, the second with a D compiler" could have better been written as "the C++ source file with a C++ compiler, and the D source file with a D compiler" Ali
Re: Using .require for struct types
On 9/10/22 09:33, Erdem Demir wrote: > DListOfA returnVal = temp.require("a", DListOfA());--> I wish I > could use ref DListOfA here But keeping a reference to a temporary would not work because the life of that temporary ends by the end of that expression (practically, at the semicolon). An option is to allocate the object dynamically with new (and store DListOfA* in the associative array). Then the GC would keep it alive as long its pointer was in the associative arrray. But a better option is to just forget about it because D already takes care of rvalues by blitting (bit-level copying) them by default. Everything just works... :) It is not expensive either. For example, your struct is very cheap to copy. But I am probably missing the reason why you want a ref there. Perhaps there are even better options. Ali
Re: Validate static asserts
On 9/9/22 10:35, Dennis wrote: > On Friday, 9 September 2022 at 16:41:54 UTC, Andrey Zherikov wrote: >> What's about new `compileOutput` trait that returns compiler output? >> ```d >> static assert(__traits(compileOutput, { }) == "message"); >> ``` > > As a compiler dev, that sounds terrifying. It would make basically every > change to dmd a breaking change. For that very reason, I wrote the function 'assertErrorStringContains()' a couple of days ago to ensure *my* strings were in the output: A precondition: void test_1(int i) in (i > 0, fooError("The value must be positive", i, 42)) { // ... } A unit test that ensures it fails and checks string pieces appear in the output: /* The .msg text of the error contains both the error string and the data that is included in the error. */ assertErrorStringContains(() => test_1(-1), [ "The value must be positive", "-1, 42" ]); Here is assertErrorStringContains: // Assert that the expression throws an Error object and that its string // representation contains all expected strings. void assertErrorStringContains(void delegate() expr, string[] expected) { bool thrown = false; try { expr(); } catch (Error err) { thrown = true; import std.algorithm : any, canFind, splitter; import std.conv : to; import std.format : format; auto lines = err.to!string.splitter('\n'); foreach (exp; expected) { assert(lines.any!(line => line.canFind(exp)), format!"Failed to find \"%s\" in the output: %-(\n |%s%)"( exp, lines)); } } assert(thrown); } Ali
Re: Validate static asserts
On 9/9/22 07:35, Andrey Zherikov wrote: > might not compile due to many different reasons I faced a related situation recently: My error string generation was buggy, which taught me that the compiler does not even compile the string part of 'static assert' in the 'true' case. The following program compiles! :) void main() { static assert (true, "hello" / WAT); } > Is there a way to validate static asserts in unit tests? I added and removed '&& false' to every 'static assert' condition manually one by one. :/ Perhaps a new compiler switch can compile every 'static assert' with an automatic 'false' and dump all their text to the output. Ali
Re: Storing a lambda alongside type-erased data
On 9/8/22 08:02, Paul Backus wrote: > This is actually pretty much exactly what VariantN does Great information, thanks! I am slowly getting up there. :) Ali
Storing a lambda alongside type-erased data
I am sure nothing is new here and I may have thought of this before but it was a revelation today. :) I've been trying to come up with a way of storing arbitrary number of objects of arbitrary types, which means I would be using a ubyte array. But then how do I use the data later without needing to remember its type perhaps with TypeInfo? I first considered registering data up front with something like SumType!(Tuple(int, string, double), Tuple(S, char)) but I couldn't make it work and it wasn't useful having to register valid sets of data like that. I looked at how std.variant.VariantN prints the correct type and failed to understand the magic there. :( Then I came up with storing a lambda that is created when the exact type is known. The following simple variant can carry arbitrary set of data because the data is provided as sequence template parameters (aka variadic). Note that set() member function is @nogc because the data is placed in an existing ubyte array. (That was the main motivation for this design.) (I left notes about 3 bugs in there, which can all be taken care of.) Then the stored lambda is used to print the data. (I am sure the lambda can do other things.) I chose 'function' in order to be @nogc. When not required, it could be a 'delegate' as well. import std; // Sorry :( struct V_(size_t size) { // This is where the data will reside; we have no idea on // what actual types of data will be used. ubyte[size] mem; // This is the lambda that remembers how to use the data // (printing to an output sink in this case.) void function(void delegate(in char[]), const(ubyte)*) dataToStr; // We can set any data into our data buffer void set(Args...)(Args args) @nogc nothrow pure { // Thank you, Tuple! :) alias Data = Tuple!Args; // Place the tuple of arguments // // BUG 1: Must consider alignment of Data // BUG 2: Must check that size is sufficient // BUG 3: The destructor of old data should be run //(optionally?) emplace(cast(Data*)(mem.ptr), Data(args)); // This is the interesting bit: Storing the lambda that // knows how to print this type. dataToStr = (sink, ptr) { // Cast back to the actual type. We know the type here. auto d = cast(Data*)(ptr); static foreach (i; 0 .. args.length) { if (i != 0) { sink(", "); } sink((*d)[i].to!string); } }; } void toString(scope void delegate(in char[]) sink) const { dataToStr(sink, mem.ptr); } } // A convenience function to avoid needing to specify the // template parameter. (The syntax is noisy otherwise.) auto V(size_t size = 1024)() { return V_!size(); } void main() { // Start with an empty variant auto v = V(); // Store some data in it v.set(42, "hello", 2.5); writeln(v); // Now set different types of data struct S { int i; } v.set(S(7), 'a'); writeln(v); } Ali
Re: Error "Unexpected '\n' when converting from type LockingTextReader to type int"
On 9/7/22 16:24, Synopsis wrote: > a- What is the difference with this syntax with the exclamation mark? > ```readf!"%s\n"(f1.num);``` That's the templated version, which is safer because it checks at compile time (important distinction) that the arguments and the format specifiers do match. > b- Do I need to put ```/n``` in every readf statement? Another option is to use a space character, which reads and skips any number of any whitespace character. I have written something about that here: http://ddili.org/ders/d.en/input.html And this one talks about readln, which may be more suitable in some cases: http://ddili.org/ders/d.en/strings.html And there is formattedRead: http://ddili.org/ders/d.en/strings.html#ix_strings.formattedRead > Forgive me if I'm asking silly questions. There is never a silly question. If a question came up, it is as legitimate as it gets. And welcome to programming! :) Ali
Re: Comparing slices with std.variant.Algebraic
On 9/5/22 01:58, anonymouse wrote: > array [1.7, 3.7, 5.7, 7.7, 9.7] in both cases, which is what is being > asserted by those two lines. None of those values can be represented precisely in a floating point type. Without looking at the code, I wonder whether the tests will pass if you can manage to use the following values instead, which can be represented precisely: [1.5, 3.5, 5.5, 7.5, 9.5] Ali
Re: Why do failed contracts don't dump stack backtrace in unittests?
On 9/4/22 09:35, Paul Backus wrote: // TODO: omit stack trace only if assert was thrown // directly by the unittest. Thank you but I mean... :) I can understand removing a backtrace from the eyes of an end user but the consumer of a unittest output is a developer, no? Ali
Why do failed contracts don't dump stack backtrace in unittests?
The program output is different whether an Error is thrown from main or from the unittest block: void foo(string s) in (s != "hello") { } unittest { foo("hello"); // No stack backtrace } void main() { foo("hello"); // Yes stack backtrace } Ali
Re: Best practice for dub registry package and module names
On 9/3/22 20:39, Ali Çehreli wrote: For example, there is fixedsizearray, which does not belong to any package: https://code.dlang.org/packages/fixedsizearray On the other hand, arsd-official:minigui does have a package. (And that answers a question: Dash character is acceptable in package and module names.) https://code.dlang.org/packages/arsd-official%3Aminigui How does that work? When the following dependency added to a user's project, "arsd-official:minigui": "~>10.9.1" does dub pull the entirety of arsd-official and then use minigui module from it? I think so, because minigui has dependencies to the same package which are (I think) not specifiable in dub. So, if I register the C package, B and A will be available under it and the users can use A and B. I think I need to get used to the feeling of wastefulness for pulling the entire package. :/ Ali
Re: Best practice for dub registry package and module names
On 9/3/22 20:04, rikki cattermole wrote: > This slightly smells, single module dub packages. > > What does each module do? The other issue is NIH because some of their functionality already exists. :/ A: Block of elements B: Expanding circular buffer C: Cache of elements I would like to register only C. However, B and A can be useful individually as well. My conflict stems from the fact that e.g. 'import block;' may collide with the user's own 'block'. There better be some namespacing, no? Ali
Best practice for dub registry package and module names
Let's say I have three modules that work together, which I want to register on dub: A, B, and C. Although the most interesting one is C and A and B are used in its implementation, A and B can be registered individually as well and be used as C's dependencies. I know package-less modules are more prone to name collisions; so I would like to give package names to these three modules. Should the package name be the same as each module? a.a, b.b, and c.c? Should the package be the author's name: acehreli.a, acehreli.b, and acehreli.c? Should they somehow reflect what they do: storage.a, buffer.b, and cache.c? What about module names? If the type is 'struct MyCache', should the module name be mycache, or my_cache, or my-cache? What else? Thank you, Ali
Re: Error while generate DNA with uniform()
On 9/3/22 14:18, Salih Dincer wrote: >uniform!"[]"(DNA.min, DNA.max); Even cleaner: uniform!DNA() :) Ali
Re: Error while generate DNA with uniform()
On 9/3/22 07:25, Steven Schveighoffer wrote: > There is probably a bug in generate when the element type is an `enum` > which somehow makes it const. Yes, Generator is missing an Unqual: https://issues.dlang.org/show_bug.cgi?id=23319 Salih had asked: >> Can we solve this issue with our own `generate()` structure? Yes, I did the following to determine that adding Unqual was a solution: - Copy generate() functions to your source file, - Copy the Generator struct to your source file, - Edit the definition of Generator's elem_ member as I hinted in the bug. Ali
Re: Constructors not working
I forgot to say that you don't need to write a constructor for most structs because Time's constructor-generated default constructor works like yours and with default arguments: struct Time { public int hours, minutes, seconds; // No constructor needed here. // Note 'return this;' as the last statement would be // mimicing how fundamental types like 'int' // work. However, I am getting the following warning when // I do that: // // Deprecation: returning `this` escapes a reference to // parameter `this` perhaps annotate the function with // `return` // // For simplicity, I will just return void in this code. // void opAssign(int secos) { assert(secos <= 86_400); hours = secos / 3600; minutes = (secos % 3600) / 60; seconds = secos % 60; } } import std.stdio; void main() { auto time = Time(360); time = 12_345; writeln(time); readln; } Ali
Re: Constructors not working
On 9/2/22 11:35, Svyat wrote: > Time time = 360; It seems to be more idiomatic to write it like this: auto time = Time(360); Or const, immutable, etc. const time = Time(360); // Now un-assignable But you would get the same compilation error. So, one way to work with it is to use default constructor arguments: this(int h, int m = 0, int s = 0) Then it would work with just 360. > .\testfortime.d(6):too few arguments, expected `3`, got `1`" At least that one is helpful. There are much more cryptic error messages out there. :) Ali
Re: Convert array of tuples into array of arrays.
On 8/31/22 07:34, musculus wrote: > Hi. I have an array of tuples that I would like to convert to an array > of arrays. I misunderstood as well and wrote the following program which makes separate arrays. You can make an array of arrays from those with the following syntax: auto arrayOfArrays = [ keys, values ]; Then I wrote a more general program after this first one: // Compiles slow but is convenient import std; auto makeTestTuple(int i) { return tuple!("key", "value")((i * 2).to!string, (double(i) / 10).to!string); } void main() { auto tuples = iota(10) .map!makeTestTuple .array; string[] keys; string[] values; writeln("Imperative:"); foreach (t; tuples) { keys ~= t.key; values ~= t.value; } writeln(keys); writeln(values); writeln("Alternative:"); keys = tuples.map!(t => t.key).array; values = tuples.map!(t => t.value).array; writeln(keys); writeln(values); writeln("More generic:"); keys = tuples.memberArray!"key"; values = tuples.memberArray!"value"; writeln(keys); writeln(values); } auto memberArray(string member, T)(T[] tuples) { return tuples.map!(t => mixin("t." ~ member)).array; } Here is the second program to have fun and "competitive advantage" with D. (That phrase came up recently, so I remembered it here. :) ) // Compiles slow but is convenient import std; auto makeTestTuple(int i) { return tuple!("key", "value", "somethingElse")( (i * 2).to!string, (double(i) / 10).to!string, i * i); } mixin template matchingMember(string name, T) { mixin (T.stringof ~ "[] " ~ name ~ ';'); } template TupleMemberArrays(T) { alias types = T.Types; alias fieldNames = T.fieldNames; enum memberCount = types.length; struct TupleMemberArrays { static foreach (i; 0 .. memberCount) { mixin matchingMember!(fieldNames[i], types[i]); } this(T[] tuples) { foreach (t; tuples) { static foreach (i; 0 .. memberCount) { mixin (format!q{ %s ~= t.%s = t[%s]; }(fieldNames[i], fieldNames[i], i)); } } } } } void main() { auto tuples = iota(10) .map!makeTestTuple .array; alias S = TupleMemberArrays!(ElementType!(typeof(tuples))); auto s = S(tuples); writeln(s.key); writeln(s.value); writeln(s.somethingElse); } That second program defines a type that matches the members of a tuple. Given a tuple with member names "key", "value", and "somethingElse"; and types string, string, int; it generates a struct similar to the following: struct YourAliasHere { string[] key; string[] value; int[] somethingElse; } Limitation: That implementation uses field names, which not all tuples use. But it can be changed to generate names file field0, field1, etc. when needed. Ali
Re: is it possible synchronized(null) ? i.e NO-OP
On the main forum, Paul Backus proposed a nested function as well as a scoped lock. On 8/26/22 10:13, mw wrote: >Object lock = (a particular condition) ? realLock : null; And I want to point out that "a particular condition" must not change between the check above and the following synchronized statement. > >synchronized(lock) { > // lots of complex code block here >} Ali
Re: Is it possible to return mutable and const range from a single method?
On 8/22/22 09:36, realhet wrote: > It gives the protection I was needed but is it possible to make this > prettier? > auto allParents(){ >struct ParentRange{ > A act; > @property bool empty() const{ return act is null; } > @property A front() { return act; } > void popFront(){ act = act.getParent; } >} >return ParentRange(getParent); > } > > auto allParents()const { >struct ConstParentRange{ > A act; > @property bool empty() const{ return act is null; } > @property const(A) front() inout { return act; } > void popFront(){ act = act.getParent; } >} >return ConstParentRange(cast()getParent); > } In other words, is it possible to combine the double implementation above? The following looks at typeof(this) to pick the type. My test asserts seem to pass: import std; class A{ inout(A) getParent() inout{ return null; } void nonConstMemberFunc() {} void constMemberFunc() const {} this(A p){ } } class B : A{ A parent; override inout(A) getParent() inout{ return parent; } struct ConstOrMutable(T) { // This alias is not strictly necessary; 'static if' // could define 'act' instead. static if (is (T == const)) { alias X = const(A); } else { alias X = A; } X act; @property auto empty() const { return act is null; } @property auto front() { return act; } void popFront() { act = act.getParent; } } auto allParents() inout { return ConstOrMutable!(typeof(this))(cast()getParent()); } this(A p){ super(p); parent = p; } } auto test(inout A a, void delegate(inout A) fun){ auto p = a.getParent; fun(p); } void main() { auto a = new A(null); auto b = new B(a); assert( __traits(compiles, b.constMemberFunc())); assert( __traits(compiles, b.nonConstMemberFunc())); const c = b; assert( __traits(compiles, c.constMemberFunc())); assert(!__traits(compiles, c.nonConstMemberFunc())); } Ali
Re: typeof(func!0) != typeof(func!0())
On 8/21/22 21:39, Andrey Zherikov wrote: > alias type = typeof(U().func!0); > pragma(msg, type); // pure nothrow @nogc ref @safe U() > return This is where the @property keyword makes a difference: @property auto ref func(int i)() { return this; } Now U().func!0 will be a call in your expression. But @property is not recommended (deprecated?). But I think std.traits.ReturnType is more explicit and does work in this case: import std.traits; alias type = ReturnType!(U().func!0); Ali
Re: Recommendation for parallelism with nested for loops?
On 8/18/22 18:49, Shriramana Sharma wrote: > Hello. I want to parallelize a computation which has two for loops An option is to add tasks individually but I am not sure how wise doing this and I don't know how to determine whether all tasks are completed. In any case, Roy Margalit's DConf 2022 presentation is very much on topic. :) http://dconf.org/2022/index.html#roym And the following program cannot show any benefit because the tasks are so short. import std.stdio; import std.parallelism; import std.conv; enum I = 1_000; enum J = 1_000; void main() { auto results = new int[I * J]; // In case you want a new TaskPool: // auto tp = new TaskPool(totalCPUs); // (And use tp. below instead of taskPool.) foreach (i; 0 .. I) { foreach (j; 0 .. J) { taskPool.put(task!foo(i, j, results)); } } // WARNING: I am not sure whether one can trust the results are // ready yet. (?) // // parallel() does call yieldForce() on each task but we don't seem // to have that option for tasks that are .put() into the pool. (?) enum toPrint = 10; writeln(results[0..toPrint]); writeln("[...]"); writeln(results[$-toPrint..$]); } void foo(size_t i, size_t j, int[] results) { results[i * J + j] = to!int(i * J + j); } Ali
Re: In-place extension of arrays only for certain alignment?
On 8/17/22 19:27, Steven Schveighoffer wrote: > On 8/17/22 10:09 PM, Ali Çehreli wrote: >> > IIRC, your data does not need to be sequential in *physical memory*, >> > which means you can use a ring buffer that is segmented instead of >> > virtually mapped, and that can be of any size. >> >> I thought about that as well. But I would like the sizes of blocks >> (Appenders?) be equal in size so that opIndex still can provide O(1) >> guarantee. (Compute the block + an offset.) > > It's still O(1). You only have 2 slices to worry about. Sometimes 2... I wanted to leave the sliding window width dynamic. So, there will be M buffers, not 2. If their lengths are not equal, opIndex must be O(M). M is expected to be small but still... M buffers of 'pageSize - (meta data).sizeof' each. BeerConf... Sure... :) Ali
Re: In-place extension of arrays only for certain alignment?
On 8/17/22 18:31, Steven Schveighoffer wrote: > 1. I highly recommend trying out the ring buffer solution to see if it > helps. The disadvantage here is that you need to tie up at least a page > of memory. I started to think holding on to multiple pages of memory should not matter anyway. If really needed, an array of Appenders could be used; when really really needed, they may come from a free list. Aside: I looked at Appender's implementation and saw that extending is one of its concerns as well. > 2. All my tests using the ring buffer showed little to no performance > improvement over just copying back to the front of the buffer. So > consider just copying the data back to the front of an already allocated > block. That makes sense as well. One worry would be types with copy constructors. (I am not sure whether D is still a language where structs can freely be moved around.) > IIRC, your data does not need to be sequential in *physical memory*, > which means you can use a ring buffer that is segmented instead of > virtually mapped, and that can be of any size. I thought about that as well. But I would like the sizes of blocks (Appenders?) be equal in size so that opIndex still can provide O(1) guarantee. (Compute the block + an offset.) > > -Steve Ali
Re: In-place extension of arrays only for certain alignment?
On 8/16/22 19:33, Steven Schveighoffer wrote: > Everything in the memory allocator is in terms of pages. A pool is a > section of pages. The large blocks are a *multiple* of pages, whereas > the small blocks are pages that are *divided* into same-sized chunks. Thank you. I am appreciating this discussion a lot. > When you want to allocate a block, if it's a half-page or less, then it > goes into the small pool, where you can't glue 2 blocks together. That's how it should be because we wouldn't want to work to know which blocks are glued. >> > The reason why your `bad` version fails is because when it must >> > reallocate, it still is only allocating 1 element. I will get back to this 1 element allocation below. >> That part I still don't understand. The same block of e.g. 16 bytes >> still has room. Why not use that remaining portion? > > It does until it doesn't. Then it needs to reallocate. I think my problem was caused by my test data being 16 bytes and (I think) the runtime picked memory from the 16-byte pool without any hope of future growth into adjacent block. Using a 16-byte block sounds like a good strategy at first because nobody knows whether an array will get more than one element. However, if my guess is correct (i.e. the first element of size of 16-bytes is placed on a 16-byte block), then the next allocation will always allocate memory for the second element. One might argue that dynamic arrays are likely to have more than a single element, so the initial block should at least be twice the element size. This would cut memory allocation by 1 count for all arrays. And in my case of 1-element arrays, allocation count would be halved. (Because I pay for every single append right now.) Of course, I can understand that there can be applications where a large number of arrays (e.g. a 2D array) may have zero-elements or one-element, in which case my proposal of allocating the first element on a e.g. 32-byte block would be wasteful. I think such cases are rare and we incur 1 extra allocation penalty for all arrays for that strategy. > Maybe some ASCII art? `A` is "used by the current slice", `x` is > "allocated, but not referenced by the array". `.` is "part of the block > but not used (i.e. can grow into this)". `M` is "metadata" > > ```d > auto arr = new ubyte[10]; // AA.. ...M > arr = arr[1 .. $]; // xAAA AA.. ...M > arr ~= 0; // xAAA AAA. ...M > arr ~= 0; // xAAA ...M > arr = arr[3 .. $]; // ...M > arr ~= cast(ubyte[])[1, 2, 3]; // AAAM // full! > arr ~= 1; // ...M // reallocated! > arr.ptr has changed > ``` That all makes sense. I didn't think the meta data would be at the end but I sense it's related to the "end slice", so it's a better place there. (?) > Metadata isn't always stored. Plus, it's optimized for the block size. > For example, any block that is 256 bytes or less only needs a single > byte to store the "used" space. That's pretty interesting and smart. > What is your focus? Why do you really want this "optimization" of gluing > together items to happen? This is about what you and I talked about in the past and something I mentioned in my DConf lightning talk this year. I am imagining a FIFO-like cache where elements of a source range are stored. There is a sliding window involved. I want to drop the unused front elements because they are expensive in 2 ways: 1) If the elements are not needed anymore, I have to move my slice forward so that the GC collects their pages. 2) If they are not needed anymore, I don't want to even copy them to a new block because this would be expensive, and in the case of an infinite source range, impossible. The question is when to apply this dropping of old front elements. When I need to add one more element to the array, I can detect whether this *may* allocate by the expression 'arr.length == arr.capacity' but not really though, because the runtime may give me adjacent room without allocation. So I can delay the "drop the front elements" computation because there will be no actual copying at this time. But the bigger issue is, because I drop elements my array never gets large enough to take advantage of this optimization and there is an allocation for every single append. > https://dlang.org/phobos/core_memory.html#.GC.extend Ok, that sounds very useful. In addition to "I can detect when it *may* allocate", I can detect whether there is adjacent free room. (I can ask for just 1 element extension; I tested; and it works.) (I guess this GC.extend has to grab a GC lock.) However, for that to work, I seem to need the initial block pointer that the GC knows about. (My sliding array's .ptr not work, so I have to save the initial arr.ptr). Conclusion: 1) Although understanding the inner workings of the runtime is
Re: Programs in D are huge
On 8/17/22 09:28, Diego wrote: > I'm writing a little terminal tool, so i think `-betterC` is the best > and simple solution in my case. It depends on what you mean with terminal tool bun in general, no, full features of D is the most useful option. I've written a family of programs that would normally be run on the terminal; I had no issues that would warrant -betterC. Ali
Re: In-place extension of arrays only for certain alignment?
Thank you for the quick response. On 8/16/22 12:31, Steven Schveighoffer wrote: > On 8/16/22 2:11 PM, Ali Çehreli wrote: >> Related to my DConf 2022 lightning talk, I am noticing that D >> runtime's in-place array extension optimization is available only for >> array data that are at certain memory alignments. >> > > No, it's based on 2 factors: > > 1. Is it a page-size-or-greater block? I assume the length of the new block. > 2. Is there a free page after it? Makes sense. > The reason why your `bad` version fails is because when it must > reallocate, it still is only allocating 1 element. That part I still don't understand. The same block of e.g. 16 bytes still has room. Why not use that remaining portion? Here is a simpler test with better-than-expected results. Array c is what I want to do. In this test it works and I don't even need to call assumeSafeAppend() (this must be what you call "end slice"): import std.stdio; void main() { ubyte[] a; a ~= 0; a.length = 0; a.assumeSafeAppend(); // Needed assert(a.capacity == 15); // Essentially, the same as above ubyte[] b; b ~= 0; b = b[0..0]; b.assumeSafeAppend();// Needed assert(b.capacity == 15); ubyte[] c; c ~= 0; c = c[1..$]; // c.assumeSafeAppend(); assert(c.capacity == 14); } > metadata (e.g. typeinfo for destruction and append capacity). I think it is 16 bytes total (on 64 bits): void* + size_t and I see this when I print .ptr: The change is always 0x10. > Note that once you reach page size, the optimization can happen, *even > if you are only appending to an end slice*. Here's something to try: > when the capacity is less than the "magic" number of elements, `reserve` > that number of elements. Then the "drop one element each loop" should > use the optimization. .reserve sounds promising but it will sometimes allocate memory and move elements even if I will not really need e.g. more than just one more element. (In my case, I may not know how many will be needed.) In other words, I really don't know how much to reserve. What I seem to need is this function: void increaseCapacityWithoutAllocating(T)(ref T[] arr) { // ... } Only the runtime seems to be able to implement that function. Can I call something in the runtime similar to how assumeSafeAppend() calls _d_arrayshrinkfit() in object.d? > > -Steve Ali
In-place extension of arrays only for certain alignment?
Related to my DConf 2022 lightning talk, I am noticing that D runtime's in-place array extension optimization is available only for array data that are at certain memory alignments. I used the following program to test it. In addition to repeatedly adding an element to an array, - 'version = neither' does not drop any element (this is "good" as well) - 'version = bad' case drops the front element (a moving window of 1) - 'version = good' case drops elements only when element data will hit a certain alignment value. Is this expected? import std.stdio; import std.range; // PLEASE UNCOMMENT ONLY ONE: // version = bad;// No in-place extension // version = good; // In-place extension happens // version = neither;// In-place extension happens mixin assertSingleVersionOf!("bad", "good", "neither"); void main() { struct S { ubyte[4] data; // Try different reasonable sizes. // For example, 5 will not work even // for the "good" case. } S[] arr; foreach (i; 0 .. 100_000) { const oldCap = arr.capacity; const oldPtr = arr.ptr; arr ~= S.init; if (arr.capacity != oldCap) { // The array needed to be extended... if (arr.ptr == oldPtr) { // ... but the pointer did not change writefln!"In-place extension -- element: %,s capacity: %,s -> %,s ptr: %s"( i, oldCap, arr.capacity, arr.ptr); } } version (neither) { // Do not remove any element; just extend } version (bad) { // Dropping 1 element inhibits in-place extension // (Many values other than 1 will inhibit as well.) arr = arr[1..$]; // Even this does not help arr.assumeSafeAppend(); } version (good) { // Dropping front elements equaling 2048 bytes works. // (Likely a GC magic constant.) enum magic = 2048; enum elementsPerPage = magic / S.sizeof; if (arr.length == elementsPerPage) { arr = arr[elementsPerPage..$]; } } } } // A useful template that has nothing to do with this problem. mixin template assertSingleVersionOf(args...) { import std.format : format; static assert (1 >= { size_t count = 0; static foreach (arg; args) { static assert (is (typeof(arg) == string)); mixin (format!q{ version (%s) { ++count; } }(arg)); } return count; }(), format!"Please pick only one or none of %(%s, %)"([args])); } Ali
Re: Exercise at end of Ch. 56 of "Programming in D"
On 8/14/22 19:23, Ali Çehreli wrote: > // BUG DUE TO WISHFUL THINKING: > override size_t toHash() const { >/* Since the 'points' member is an array, we can take > * advantage of the existing toHash algorithm for > * array types. */ >return typeid(points).getHash(); Thinking more about this, I think the above would still work if the Point type had a toHash() function that also ignored the color member. Adding just the following function makes the code work: class Point { int x; int y; Color color; // ... override size_t toHash() const { return x + y; } } The book is simply forgetting to show that function. (Otherwise, I never put any code without testing.) WARNING: Hash functions can be very tricky. I have a strong feeling that adding two integers is not an efficient one. Ali
Re: Exercise at end of Ch. 56 of "Programming in D"
On 8/14/22 18:47, johntp wrote: > I'm using DMD64 D Compiler v2.100.0 on linux mint. Same version here. > I copied the author's > solution and got the same thing. Wow! Are people actually working on those? :) At first, I couldn't even get the code to compile due to const-correctness issues with opCmp. :/ I used the following cast(): foreach (i, point; points) { immutable comparison = (cast()point).opCmp(rhs.points[i]); // ... } > Is there an errata page for the book? No. It is supposed to be corrected as mistakes are discovered. The online version should be the most recent. The bug was with trying to implement that unnatural "ignore the color" in the comparison logic but then toHash was taking advantage of existing array hashing code, which had no idea of the programmer's requirement: class Point { int x; int y; Color color; // IGNORES COLOR: override bool opEquals(Object o) const { const rhs = cast(const Point)o; return rhs && (x == rhs.x) && (y == rhs.y); } // ... } class TriangularArea { // BUG DUE TO WISHFUL THINKING: override size_t toHash() const { /* Since the 'points' member is an array, we can take * advantage of the existing toHash algorithm for * array types. */ return typeid(points).getHash(); } // ... } Here is a very inefficient implementation that still relies on existing array hashing functionality but after creating an array that does not contain the color field. I am pretty sure sort(tuples) is needed but at least the example works without it as well: override size_t toHash() const { auto tuples = points[].map!(p => tuple(cast()p.x, cast()p.y)).array; sort(tuples); return () @trusted { return typeid(tuples).getHash(); }(); } Writing it in an efficient way is left as en excercise because the auther is too lazy to learn how to do it. :p In case it's not clear, toHash() creates a new array which does not contain any color information and then sorts it and then needs to use the @trusted trick because toHash is @safe (apparently). Ali
Re: chain of exceptions, next method
On 8/13/22 15:59, kdevel wrote: > Quote from `src/druntime/src`: > > ``` > /** > * Returns: > * A reference to the _next error in the list. This is used when a new > * $(D Throwable) is thrown from inside a $(D catch) block. The > originally > * caught $(D Exception) will be chained to the new $(D Throwable) > via this > * field. > */ > @property inout(Throwable) next() @safe inout return scope pure > nothrow @nogc { return nextInChain; } > > ``` This automatic "combining" of exceptions happens for cleanup code like scope(exit). (I remember bug(s) for scope(failure).): import std.stdio; void foo() { // Bug? Should work for scope(failure) as well. scope (exit) { bar(); } throw new Exception("from foo"); } void bar() { throw new Exception("from bar"); } void main() { try { foo(); } catch (Exception exc) { for (Throwable e = exc; e; e = e.next) { writeln(e.msg); } } } The output: from foo from bar You can do the same by calling chainTogether(). Here is an excerpt from an old experiment of mine: try { foo(); } catch (Throwable exc) { Throwable.chainTogether(exc, new Exception(" ... ")); throw exc; } Ali
Re: "min" and "max"
On 8/9/22 17:03, pascal111 wrote: > They said " If at least one of the arguments is NaN, the result is an > unspecified value. That's called "unorderedness": http://ddili.org/ders/d.en/floating_point.html#ix_floating_point.unordered > as a beginner how can I guess what "NaNs" > means or if it refers to ranges?! You can use the index: ;) http://ddili.org/ders/d.en/ix.html There are a couple of entries for 'nan' there. Ali
Re: A look inside "filter" function defintion
On 8/2/22 05:39, pascal111 wrote: > I'm still stuck. Do you have a > down-to-earth example for beginners to understand this concept? I will refer to my explanation because down-to-earth has always been my goal. I hope i succeeded: http://ddili.org/ders/d.en/templates_more.html#ix_templates_more.eponymous%20template However, my chapters assume that previous chapters have already been read. Ali
Re: Breaking ";" rule with lambda functions
On 8/2/22 09:40, pascal111 wrote: > Maybe I'd wrong beliefs about lambda function. It's already in C++, so > it's a traditional feature Lambdas are a common feature of many programming languages. C++ got lambdas in their C++11 release, many years after D and many other languages had them. (It is not common for the C++ community to give credit. Most of their features are presented as C++ inventions when they are not. For example, almost the entirety of the C++11 features already existed in D (and other languages before D).) > but the problem is that I didn't use it > before because I didn't study C++ yet. C++ is the new-kid-in-the-block when it comes to lambdas. Getting back to lambda syntax, you don't have to use the shorthand => syntax. I try to explain how various syntaxes relate to each other: http://ddili.org/ders/d.en/lambda.html#ix_lambda.function,%20lambda My book is freely available and I think is better than the documentation you've shown earlier, which apparently included copies of my text: http://ddili.org/ders/d.en/index.html You can find most of your questions about keywords and syntax in the index section: http://ddili.org/ders/d.en/ix.html Ali
Re: char* pointers between C and D
On 7/25/22 06:51, ryuukk_ wrote: > On Monday, 25 July 2022 at 11:14:56 UTC, pascal111 wrote: >> const(char)[] ch1 = "Hello World!"; >> char[] ch2="Hello World!".dup; [...] > `ch1`is a string literal, just like in C, it is null terminated To be pedantic, ch1 is not the string literal but a slice of it. "Hello world" is the string literal and does have a '\0' at the end. > `ch2` is a GC allocated char array, it is NOT null terminated Yes. The only difference between ch1 and ch2 is that ch1 does not incur an allocation cost. Ali
Re: OK to do bit-packing with GC pointers?
On 7/22/22 09:50, Ben Jones wrote: > any problems with the GC? The slides don't seem to mention the GC but Amaury Séchet had given a presentation on bit packing: http://dconf.org/2016/talks/sechet.html Ali
Re: "chain" vs "~"
On 8/6/22 18:22, pascal111 wrote: > Why we use "chain" while we have "~": > > '''D > int[] x=[1,2,3]; > int[] y=[4,5,6]; > > auto z=chain(x,y); > auto j=x~y; > ''' To add to what has already mentioned, - chain can be used on ranges that are of different element types - as usual, some of the ranges may be generators - although obscure, one may sort in-place over multiple ranges (requires RandomAccessRange.) This program shows the first two points: import std; // Apologies for terseness void main() { auto ints = [ 10, 3, 7 ]; auto squares = iota(10).map!squared.take(5); auto doubles = [ 1.5, -2.5 ]; auto c = chain(ints, squares, doubles); // Different types but CommonType is 'double' here: static assert(is(ElementType!(typeof(c)) == double)); // Prints [10, 3, 7, 0, 1, 4, 9, 16, 1.5, -2.5] writeln(c); } auto squared(T)(T value) { return value * value; } And this one shows how one can sort in-place multiple arrays: import std; // Ditto void main() { auto a = [ 10, 3, 7 ]; auto b = [ 15, -25 ]; auto c = chain(a, b); sort(c); writeln(a); // Prints [-25, 3, 7] writeln(b); // Prints [10, 15] } Ali
Re: How do I initialize a templated constructor?
Here is another one that uses nested templates: import std.stdio; template TestArray(ulong element_n) { struct TestArrayImpl(Type) { int[element_n] elements; this(ulong number) { pragma(msg, "The type is: ", Type); writeln("Constructing with ", number); } } auto makeFor(string s)(ulong number) { return TestArrayImpl!(mixin(s))(number); } } void main() { auto ta = TestArray!10.makeFor!"int"(60); } Ali
Re: Acess variable that was set by thread
On 8/8/22 00:14, vc wrote: > i will like to hear thoughts even if it works > for me __gshared would work as well but I would consider std.concurrency first. Just a simple example: import std.stdio; import std.concurrency; import core.thread; struct Result { int value; } struct Done { } void run() { bool done = false; while (!done) { writeln("Derived thread running."); receiveTimeout(1.seconds, (Done msg) { done = true; }); } // Send the result to the owner // (I made assumptions; the thread may produce // results inside the while loop above.) ownerTid.send(Result(42)); } void main() { auto worker = spawn(); Thread.sleep(5.seconds); worker.send(Done()); auto result = receiveOnly!Result(); writeln("Here is the result: ", result); } Related, Roy Margalit's DConf 2022 presentation was based on traps related to sequential consistency. The video will be moved to a better place but the following link should work for now: https://youtu.be/04gJXpJ1i8M?t=5658 Ali
Re: How do I initialize a templated constructor?
On 8/7/22 22:38, rempas wrote: > I want to create it and be able to successfully initialize the template > parameters > of the constructor but until now, I wasn't able to find a way to > successfully do > that. The following method uses a convenience function but it's not really needed: import std.stdio; struct TestArray(ulong element_n, string type) { int[element_n] elements; mixin(type) member; pragma(msg, "The type is: ", typeof(member)); this(ulong number) { writeln("Constructing with ", number); } } auto makeTestArray(ulong element_n, string type)(ulong number) { return TestArray!(element_n, type)(number); } void main() { auto ta = makeTestArray!(10, "int")(60); } Ali
Re: Ranges
On 8/6/22 22:58, Salih Dincer wrote: > Ranges are not like that, all they do is > generate. You may be right. I've never seen it that way. I've been under the following impression: - C++'s iterators are based on an existing concept: pointers. Pointers are iterators. - D's ranges are based on an existing concept: slices. Slices are ranges. However, I can't find where I read that. Ali
Re: Ranges
On 8/7/22 08:34, pascal111 wrote: > Everyone knows that slices are not pointers D's slices are "fat pointers": In D's case, that translates to a pointer plus length. > that pointers are real work, Agreed. Pointers are fundamental features of CPUs. > but slices are like a simple un-deep technique that is appropriate for > beginners, That is not correct. Slices are designed by a C expert to prevent horrible bugs caused by C experts. Most C experts use slices very happily. > but after that in advanced level in programming, we should > use pointers to do same tasks we were doing with slices (the easy way of > beginners). That is an old thought. Today, we see that no matter how experienced, every person needs and appreciates help to prevent bugs. There are many cases of bugs killing people, jeopardizing expensive projects, loss of personal information, etc. Ali
Re: Ranges
On 8/6/22 14:10, pascal111 wrote: > the problem is that ranges in D lack the usage of pointers as > an essential tool to make all of ranges functions they need. If ranges > exist in C, they would use pointers, and this is There are a few cases where pointers provide functionality that ranges cannot: 1) Some algorithms don't make much sense with ranges. For example, most of the time find() can return just the element that we seek. In D, find() returns a range so that we can chain it with other algorithms. 2) Some algorithms like partition() better use three pointers. Other than that, ranges are superior to pointers in every aspect. (I resent the fact that some C++ "experts" used those two points to decide ranges are inferior and helped deprive the C++ community of ranges for a very long time. The same "experts" did the same with 'static if'.) > a powerful point in the account of C. I missed how you made that connection. Ali
Re: Ranges
On 8/6/22 09:33, Salih Dincer wrote: > the slices feel like ranges, don't they? Yes because they are ranges. :) (Maybe you meant they don't have range member functions, which is true.) D's slices happen to be the most capable range kind: RandonAccessRange. All of the following operations are supported on them as long as one imports std.array (or std.range, which publicly does so): - empty - front - popFront - save - back - popBack - indexed element access Slices have the optional length property as well (i.e. hasLength). Those operations are not supported by member functions but by free-standing functions. Ali
Re: Ranges
On 8/5/22 01:59, frame wrote: > On Thursday, 4 August 2022 at 22:14:26 UTC, Ali Çehreli wrote: > >> No element is copied or moved. :) >> >> Ali > > I know that :) And I know that. :) We don't know who else is reading these threads, so I didn't want to give wrong impression. Copying would happen if we added slicing on the left-hand side. However, I realized that the following fails with a RangeError: void main() { auto arr = [1, 2, 3]; arr[0..$-1] = arr[1..$];// <-- Runtime error } I suspect the length of the array is stamped too soon. (?) Should that operation be supported? Ali
Re: Ranges
On 8/4/22 11:05, frame wrote: > `popFront()` The function was this: void popFront() { students = students[1 .. $]; } > copies all > elements except the first one into the variable (and overwrites it), so > it moves the data forward. That would be very slow. :) What actually happens is, just the two variables that define a slice is adjusted. Slices consist of two members: struct __an_int_D_array_behind_the_scenes { size_t length; int * ptr; } So, students = students[1..$]; is the same as doing the following: students.length = (students.length - 1); students.ptr = students.ptr + 1; (ptr's value would change by 4 bytes because 'int'.) No element is copied or moved. :) Ali
Re: Ranges
On 8/4/22 06:08, pascal111 wrote: > In next code from > "https://www.tutorialspoint.com/d_programming/d_programming_ranges.htm;, That page seems to be adapted from this original: http://ddili.org/ders/d.en/ranges.html > we have two issues: > > 1) Why the programmer needs to program "empty()", "front()", and > "popFront()" functions for ranges The programmer almost never needs to implement those functions. Existing data structures and algorithms are almost always sufficient. (I did need to implement them but really rarely.) I tried to explain what those functions do. I don't like my Students example much because wrapping a D slice does not make much sense. Again, I just try to explain them. > while they exist in the language > library? The existing front, popFronh, etc. are only for arrays (slices). > it seems there's no need to exert efforts for that. Exactly. > "https://dlang.org/phobos/std_range_primitives.html; > > 2) "front()", and "popFront()" are using fixed constants to move forward > the range, while they should use variables. Well, 0 is always the first element and 1..$ are always the rest. Variables would not add any value there. However, the example could use the existing library function that you mention: > @property bool empty() const { >return students.length == 0; Better: import std.array : empty; return students.empty; > } > @property ref Student front() { >return students[0]; Better: import std.array : front; return students.front; > } > void popFront() { >students = students[1 .. $]; Better: import std.array : popFront; students.popFront(); But I think those implementations might confuse the reader. Ali
Re: Obsecure problem 2
On 8/4/22 00:57, pascal111 wrote: > I don't see my post. Some posts are blocked by the spam filter. (Apparently, your message did not have a sender name and cannot be passed because of that.) Ali
Re: Converting JSONValue to AssociativeArray.
On 8/1/22 15:47, Steven Schveighoffer wrote: You beat me to it. I used .object and .str: import std; void main() { JSONValue data = parseJSON(`{ "name": "Hype Editor", "hobby": "Programming" }`); writefln("%s", data); // This already sees the data as an AA but the type is JSONValue[string]": auto o = data.object; static assert(is(typeof(o) == JSONValue[string])); writeln("Accessing the data with .str:"); foreach (key, jsonValue; o) { writeln(" ", key, ": ", jsonValue.str); } // If that much is not sufficient, you can convert it to a // proper AA like this: auto aa_data = o .byKeyValue .map!(kv => tuple(kv.key, kv.value.str)) .assocArray; static assert(is(typeof(aa_data) == string[string])); writeln("Now it's string[string]:"); writefln(" %s", aa_data); } Ali
Re: Exclamation symbol "!" within functions syntax
On 7/27/22 04:00, pascal111 wrote: I noticed more than once that the exclamation "!" is used within functions typing, and it seems like an operator with new use, for example "to!int()", ".tee!(l => sum += l.length)", "enforce!MissingArguments...", so what dose it means? The binary ! operator is used for specifying template arguments. I have some explanation here: http://ddili.org/ders/d.en/templates.html#ix_templates.!,%20template%20instance Ali
Re: Particular exceptions names
On 7/26/22 16:43, pascal111 wrote: > In next example code, it used user-made exception, I am not sure I understand you correctly because the program you show throws Exception, which is not user-made at all. If you want to throw a particual exception that you define, you need to inherit that type from Exception. The following program show an example as well as 'enforce', which I prefer over explicit if+throw+else: import std.stdio; import std.format; class MissingArguments : Exception { this(string msg, string file = __FILE__, size_t line = __LINE__) { super(msg, file, line); } } void main(string[] args) { // if (args.length != 42) { // throw new MissingArguments(args.length); // } import std.exception : enforce; enforce!MissingArguments(args.length == 42, format!"Too few arguments: %s"(args.length)); // Program continues here... (No 'else' needed.) } Ali
Re: How to create Multi Producer-Single Consumer concurrency
On 7/13/22 02:25, Bagomot wrote: > How to do the same with `taskPool` instead of `spawnLinked`? You are hitting the nail on the head. :) std.parallelism, which taskPool is a concept of, is for cases where operations are independent. However, producer and consumer are by definition dependent, so it's a problem for std.concurrency, which involves message boxes. You can do the same with std.parallelism or core.thread but you would be implementing some of what std.concurrency already provides. The following are my understandings of these topics: http://ddili.org/ders/d.en/parallelism.html http://ddili.org/ders/d.en/concurrency.html http://ddili.org/ders/d.en/concurrency_shared.html The introduction section of the Concurrency chapter lists some differences. Ali
Re: Background thread, async and GUI (dlangui)
On 7/12/22 11:47, Bagomot wrote: > I now have a couple more questions about `Task`: > 1) How to run a non-static class method through `task`? > 2) How to use `taskPool` to run a series of tasks of the same type (from > question 1)? As a friendly reminder, these questions could be more useful in a separate forum thread. :) import std.stdio; import std.parallelism; import std.algorithm; import std.range; interface Animal { string song(); } class Dog : Animal { string voice_; this(string voice) { this.voice_ = voice; } string song() { return voice_ ~ " " ~ voice_; } } void main() { auto voices = [ "hav", "woof", "bark", "gav", "grrr" ]; auto dogs = voices.map!(voice => new Dog(voice)).array; // No need to specify this; just being silly... const workerCount = totalCPUs + 7; auto tp = new TaskPool(workerCount); scope (exit) tp.finish(); // a) Classic foreach loop foreach (dog; tp.parallel(dogs)) { writeln(dog.song); } // b) Adding individual tasks (could be a foreach loop) dogs.each!(dog => tp.put(task!(d => writeln(d.song))(dog))); } Ali
Re: null == "" is true?
On 7/12/22 10:11, Steven Schveighoffer wrote: > The algorithm to compare *any* arrays is first verify the lengths are > the same. Then for each element in the array, compare them. Since there > are 0 elements in both the empty string and the null string, they are > equal. Checking .empty() covered all of my uses cases. I think... :) void foo(string s) { import std.array; assert(s.empty); } void main() { // Literal null foo(null); // Zero-length and pointing at '\0' foo(""); // Fresh string a; foo(a); // Shrunk string b = "hello"; b.length = 0; assert(b.ptr !is null); foo(b); } Ali
Re: How can I match every instance of a template type (struct)?
On 7/12/22 06:34, rempas wrote: >static if (is(typeof(obj) == Test)) { printf("YES!!!\n"); } An alternative: import std.traits; static if (isInstanceOf!(Test, typeof(obj))) { printf("YES!!!\n"); } https://dlang.org/phobos/std_traits.html#isInstanceOf Ali
Re: Background thread, async and GUI (dlangui)
On 7/8/22 06:32, Bagomot wrote: > I do as in Ali's example, but > the GUI is still blocked: I don't know exactly why but something looks suspicious. > ```d > auto btn = new Button("Run"d); > btn.click = delegate(Widget src) { > auto request = Request("dlang.org"); That is a local variable which will die after the return statement... > auto downloadTask = task!download(); ... and that pointer will be invalid, still pointing on function call stack. At least, I would move the 'auto request' line up, next to 'btn' so that it lives long and that you can get the 'result' later on. But then, you need access to 'task' as well so that you can ensure it has finished with downloadTask.yieldForce(). > downloadTask.executeInNewThread; > return true; > }; Aside: What does the return value 'true' mean? I am not sure whether my explicit way of starting a thread (albeit with std.parallelism) is the right way here. As you've been suspecting, dlangui may provide async execution. (?) Ali
Re: Background thread, async and GUI (dlangui)
On 7/6/22 16:17, Ali Çehreli wrote: > I would consider std.parallelism And it looks more natural with a std.parallelism.Task: struct Progress { size_t percent_; void set(size_t downloaded, size_t total) { if (total != 0) { import core.atomic: atomicStore; const value = cast(size_t)(float(downloaded) / float(total) * 100); atomicStore(percent_, value); } } size_t get() const { import core.atomic: atomicLoad; return atomicLoad(percent_); } } struct Request { string url; string result; Progress progress; } void download(Request * request) { import std.net.curl: HTTP; auto http = HTTP(request.url); http.onProgress((size_t dl, size_t dln, size_t ul, size_t uln) { if (dl != 0) { request.progress.set(dln, dl); } return 0; }); http.onReceive((ubyte[] data) { request.result ~= (cast(char[])data); return data.length; }); http.perform(); } void main() { import std.parallelism : task; import std.stdio: writefln; auto request = Request("dlang.org"); auto downloadTask = task!download(); downloadTask.executeInNewThread; foreach (i; 0 .. 10) { writefln!"Doing work on the side (%s)"(i); writefln!"Checking download progress: %s%%"(request.progress.get()); import core.thread; Thread.sleep(100.msecs); } // Now we need the result before continuing: downloadTask.yieldForce(); writefln!"Downloaded %s bytes:\n%s"(request.result.length, request.result); } Ali
Re: Background thread, async and GUI (dlangui)
On 7/6/22 02:26, Bagomot wrote: > 1) How to make asynchronous HTTP requests with curl in order to receive > progress and status? And how do you know that the request is completed? I don't know how dlangui or others automate this but I had fun writing the following program that uses std.concurrency and std.net.curl: > 3) My application must perform some work in a separate thread (I do this > through inheritance of the worker from core.thread.Thread, is that > correct?). core.thread.Thread is low level. I would consider std.parallelism and std.concurrency first. (I use the latter for message passing in the program below.) import std.concurrency; import std.net.curl; import std.conv; import std.stdio; import std.algorithm; import std.range; import std.format; // Signifies that the main thread requests the getter to stop // serving. (Not used here.) struct Done {} // Represents download progress. struct Progress { size_t downloaded; size_t total; } // Represents a URL struct Url { string value; } // The thread entry point for the getter void getterFunc() { receive( (Done _) { // Not used here but this is for clean termination. return; }, (Url url) { // Received a URL to get. auto http = HTTP(url.value); // std.net.curl.HTTP will call this delegate with progress // information... http.onProgress((size_t dl, size_t dln, size_t ul, size_t uln) { if (dl != 0) { // ... and we will send it along to the main thread. ownerTid.send(Progress(dln, dl)); } return 0; }); // std.net.curl.HTTP will call this delegate with // downloaded parts... http.onReceive((ubyte[] data) { // ... and we will send it along to the main thread. ownerTid.send((cast(char[])data).to!string); return data.length; }); // Everything is set up. Let's do it. http.perform(); }, ); } void main() { auto getter = spawnLinked(); getter.send(Url("dlang.org")); string result; bool done = false; while (!done) { receive( (LinkTerminated msg) { // (This may or may not be expected.) stderr.writefln!"The getter thread terminated."; done = true; }, (Progress progress) { writeln(progress); if (!result.empty && (progress.downloaded == progress.total)) { done = true; } }, (string part) { result ~= part; }, ); } writefln!"Downloaded %s bytes:\n%s"(result.length, result); } Ali
Re: Calling readln() after readf
On 7/5/22 17:14, Gary Chike wrote: > So, in order to use `parse!int()`, I would need to separate it into two > statements with a variable acting as an intermediary: > ``` > auto input = readln(); > auto age = parse!int(input); Exactly. parse takes the input by reference (necessitating an lvalue) so that the input is consumed for the next step(s) of parsing. Ali
Re: How to call a function from a dll created with d ?
On 7/1/22 12:11, Vinod KC wrote: The following function is dimedll.testFunc: > ```d > module dimedll; // ... > export void testFunc() { > writeln("This is from dll"); > } > ``` We suspect the name of the file that defines main() is dime.d. > extern void testFunc(); That symbol belongs to this module, which is implied to be 'module dime'. > testFunc(); That's a call to dime.testFunc, which does not exist. With the provided information alone, the following is what I would do: 1) This dll must have a .di file, which should contain the following: // dimedll.di void testFunc(); (.di files can be generated by dmd with its -H command line switch.) 2) Provide dimedll.di as your library's interface file (a la "header file"). 3) The users of this dll should import that .di file (declaring the functions themselves won't work): import dimedll; void main() { // ... } Ali
Re: Better way to achieve the following
On 6/21/22 10:09, JG wrote: Suppose we are often writing something like ```d theFirstName[theFirstIndex].theSecondName[theSecondIndex].thirdName[theThirdIndex]=x; ``` One would like to something like ```d alias shortName = theFirstName[theFirstIndex].theSecondName[theSecondIndex].thirdName[theThirdIndex]; shortName = x; ``` but you can't alias an expression. An option is nested functions: ref shortName() { return theFirstName[theFirstIndex].theSecondName[theSecondIndex].thirdName[theThirdIndex]; } shortName = x; Ali
Re: Calling readln() after readf
On 6/20/22 07:00, Gary Chike wrote: > Would it be appropriate to forego `readf` > and read input as a string using `readln` ,benefiting from the `strip` > function, then convert to their appropriate datatype Makes sense. The following are related as well: https://dlang.org/library/std/conv/parse.html https://dlang.org/library/std/format/read/formatted_read.html Ali
Re: Calling readln() after readf
On 6/19/22 15:52, Gary Chike wrote: > On Saturday, 24 April 2021 at 22:13:45 UTC, Ali Çehreli wrote: >> On 4/24/21 7:46 AM, PinDPlugga wrote: >> ... >> As a general solution, you can use a function like this: >> >> auto readLine(S = string)(File file = stdin) { >> while (!file.eof) { >> auto line = file.readln!S.strip; >> if (!line.empty) { >> return line; >> } >> } >> >> return null; >> } >> >> Ali > > Hi Ali, > > Being a new D learner, I've noticed this behavior as well. Thank you for > providing the 'readLine' function! Would it be considered poor coding > style to intercept the stream between readf and readln with the > following statement? : > > ```d > auto catcher = readln.strip; > ``` The original program said "Please enter a number: ". If the program was interacting with a human and the human entered a number, then the rest of the line would be understood to be ignored and your method works. On the other hand, the readLine() function would not ignore the rest of the line and use it. But I think yours makes more sense. :) But when the program interacts with piped data, there may be multiple \n characters and all of those might have to be ignored before reading the next non-empty line. So you may want to do readln.strip multiple times? I don't know. It all depends on the use case. > P.S. I love your book on D! :) Thank you! :) > > Cheers, > > Gary Chike > Ali
Re: std.conv.to
On 6/17/22 10:04, Salih Dincer wrote: > Isn't foo and bar the same thing? I don't understand what's the > difference! >auto foo = to!Foo("123"); //?? >auto bar = Foo("321"); Yes, they are the same thing. The OP was looking for a generic way of converting any type to any other type as long as there is a way. (Think a function template.) std.conv.to can do that because it considers the constructors as well. Ali
Re: Creating DLL
On 6/16/22 09:32, Adam D Ruppe wrote: > This is why an explicit initialization call is the preferred method - > there, the time it is called is well-defined by the user after initial > loading is complete. Agreed but that excludes using the D runtime in 'static this' (and shared) blocks, right? If my explicit call to Initialize is in a 'shared static this', then I can have only one such block, right? Ali
Re: Creating DLL
On 6/16/22 09:07, Sergeant wrote: > May I ask one more question: why a code like this would work in > D-application but not in D-DLL? D programs generated by D compilers automatically initialize the D runtime. You can do the same with rt_init: pragma (crt_constructor) extern(C) int initialize() { // Can have any name return rt_init(); } And to deinitialize; pragma (crt_destructor) extern(C) int terminate() { // Can have any name return rt_term(); } Although, I haven't tested it with a DLL but with .so libraries on Linux... :/ This answer may be relevant: https://forum.dlang.org/post/t8diks$2l79$1...@digitalmars.com Ali
Re: map! evaluates twice
On 6/16/22 00:58, Salih Dincer wrote: > I guess the developed cached() and cache() are different things, > right? cache caches only the front element. https://dlang.org/library/std/algorithm/iteration/cache.html > I tried cached() cached() is supposed to cache as many elements as needed as long as there are ranges that still reference the elements. Technically, cached() does not exist because only a couple of people have seen a draft of it. :) Ali
Re: Consuming D libraries from other languages
On 6/15/22 10:37, Templated Person wrote: > It there any resources on how to build D static (`.lib` / `.a`) and > dynamic libraries (`.dll` / `.so`), and then use them from C? > > Do I need to link and initialize phobos somehow? Not Phobos but the D runtime. > What if I don't want to > use the D runtime? What happens with module level `this()` and > `~this()`? I had difficulty figuring out how everything fit together in a complicated scenario. (Different language runtimes calling each other and starting the D library.) However the following worked. > Is there a comprehensive guide on how to do this stuff? I gave a DConf presentation that answers some of your questions: https://dconf.org/2020/online/ Please click the 'November 22' tab on that page to expand "Exposing a D Library to Python Through a C API". But here are the links: Slides: https://dconf.org/2020/online/slides/ali-2.pdf Video: https://youtu.be/FNL-CPX4EuM Q: https://youtu.be/M5wtRgfAoAA Ali
Re: Closures over temporary variables
On 6/14/22 02:04, bauss wrote: > You have to do it like this: > > ``` > dgs ~= ( (n) => () { writeln(n); })(i); > ``` The same thing with a named function as well as with iota(): import std.range; import std.algorithm; import std.stdio; void main() { void delegate()[] dgs; auto makeDg(int i) { return () => writeln(i); } foreach (immutable i; 0 .. 3) { dgs ~= makeDg(i); } iota(3).each!(i => dgs ~= () => writeln(i)); foreach (dg; dgs) { dg(); } } Ali
Re: a struct as an multidimensional array index
On 6/11/22 13:36, z wrote: > i meant with the syntax in (1), the spec's documentation appears to say > they are equivalent in result with `new *type*[X][Y]` form. > > (1) https://dlang.org/spec/expression#new_multidimensional (3. multiple > argument form) Thank you. I see now: The values in parentheses are the lengths from the outermost to the innermost: new int[][][](5, 20, 30) // The equivalent of int[30][20][5] Although, I can see how it had to be that way so that when one used less number of lengths, the syntax always works from outer to inner in that new expression: new int[][][](5) // The equivalent of int[][][5] Ali
Re: a struct as an multidimensional array index
On 6/11/22 00:09, z wrote: > I rechecked and it should be `X Y Z` for static array, but `Z Y X` for > indexing/dynamic array creating with `new` How so? I wrote the following program: import std.stdio; void main() { enum X = 2; enum Y = 3; enum Z = 4; int[X][Y][Z] s; int[X][Y][] d = new int[X][Y][Z]; pragma(msg, typeof(s)); pragma(msg, typeof(d)); } It outputs the following for the static and the dynamic arrays: int[2][3][4] int[2][3][] Consistent elements, except the static array has a compile-time known length. Ali
Re: a struct as an multidimensional array index
On 6/11/22 04:16, Salih Dincer wrote: > I think D is very consistent with our feelings. That is, the order in > memory is in the form of rows x columns. Yet, there are no rows or columns because neither D nor C (nor C++) have multip-dimensional arrays. They all have arrays where elements are layed out in memory consecutively. The type of the elements and what they represent is entilery up to the programmer. > But yes, in reverse(column x > row) when you set it up statically. If you mean we can set up the memory in any way we want, I agree but again, since there are no mult-dimensional arrays, there cannot be the reverse of the order. > This sample code working on pointers > can be a proof: If it's prooving that elemets are side-by-side, then it's by spec. Here is an example where I have array where each element is a column: import std.stdio; import std.range; import std.algorithm; void main() { // I decide that this array represents // Three rows of two columns. int[][] arr; arr.length = 2; foreach (ref column; arr) { column.length = 3; } setFirstColumn(arr, 1); printArray(arr); } void setFirstColumn(int[][] arr, int value) { // The first element is my first column. arr[0][] = value; } void printArray(int[][] arr) { // Because stdout takes line-by-line, // we print a transposition. arr.transposed.writefln!"%-(%-(%s %)\n%)"; } You may think that the final transposition is a trick. No, it was needed only because stdout takes line-by-line. If I used a library like ncurses, I could have printed my array exactly the way I used it. The point is, there are no multi-dimensional arrays. Programmers use arrays as they need and sometimes the elements are arrays. Ali
Re: map! evaluates twice
On 6/10/22 13:47, Steven Schveighoffer wrote: > `map` calls the lambda for each call to `front`. If you want a cached > version, use `cache`: Others don't know but as I will likely show during a lightning talk at DConf, I am trying to finish a .cached range algorithm that caches all elements that are in use. (It must drop old elements so that an infinite range does not require infinite cache.) It is supposed to evaluate elements only once. Ali
Re: Run a command-line process with data sent, and retrieve the data.
On 6/10/22 10:40, Ali Çehreli wrote: >https://dlang.org/library/std/process/pipe_process.html I realized you may be happier with the following if all you need is stdout: https://dlang.org/library/std/process/execute.html https://dlang.org/library/std/process/execute_shell.html Ali
Re: Run a command-line process with data sent, and retrieve the data.
On 6/10/22 10:37, Chris Katko wrote: > I want to pipe in string data to a shell/commandline program, then > retrieve the output. But the documentation I read appears to only show > usage for 'Files' for stdin/stdout/stderr. > > ala something like this: > D > string input = "hello\nworld"; > string output; > runProcess("grep hello", input, output); > assert(output = "hello"); > https://dlang.org/library/std/process/pipe_process.html Ali
Re: Range to Nullable conversion
On 6/10/22 10:22, Antonio wrote: > Is there any alternative to ***range front*** that returns a Nullable > (i.e. **frontAsMonad** or **frontAsNullable**)? import std; // Spelling? :) auto nullablelize(R)(R range) { alias E = Nullable!(ElementType!R); struct Nullablelize { enum empty = false; auto front() { if (range.empty) { return E(); } else { return E(range.front); } } void popFront() { if (!range.empty) { range.popFront(); } } // Other range algorithms like save(), etc. here } return Nullablelize(); } void main() { // Wow! We can take 10 elements without error. :) writeln(iota(5) .filter!(i => i % 2) .nullablelize .take(10)); } Ali
Re: a struct as an multidimensional array index
On 6/10/22 08:13, z wrote: > arrays of arrays has different order for declaration and addressing, > and declaring array of arrays has different order depending on how you > declare it and wether it's static or dynamic array, *oof*) > > To give you an idea of the situation : > ```D > int[3][1] a;//one array of 3 int > writeln(a[0][2]);//first "column", third "row" > ``` I've written about this multiple times in the past but D's way is consistent for me. That must be because I always found C's syntax to be very illogical on this. To me, C's problem starts with putting the variable name in the middle: // C code: int a[1][3]; // Why? So, first, D moves the variable to its consistent place: after the type: int i; int[N] arr; Both of those are in the form of "type and then name". Good... And then, here is the consistency with arrays: "type and then square brackets". int[] dynamicArray; int[N] staticArray; So, here is where you and I differ: int[3][1] arr; // Ali likes int[1][3] arr; // z wants I like it because it is consistently "type and then square brackets". (It so happens that the type of each element is int[N] in this case.) If it were the other way, than array syntax would be inconsistent with itself. :) Or, we would have to accept that it is inside-out like in C. But of course I understand how it is seen as consistent from C's point of view. :) And this is consistent with static vs dynamic as well because again it's "type and then square brackets": int[1][] a; // A dynamic array of int[1] int[][3] b; // A static array of 3 int[]s Ali
Re: a struct as an multidimensional array index
On 6/10/22 08:01, Ali Çehreli wrote: > I still don't understand the reason though. The rows would be copied > without ref but should retain their type as bool[3], a static array. (?) Ok, now I see the very sinister problem: It is a disaster to combine static array lambda parameters with the laziness of range algorithms. The following program prints garbage because by the time writeln prints the elements, those elements are on invalid places on the stack: import std; void main() { int[3][3] arr; writeln( arr[] // Have to slice .map!(row => row[] // Have to slice .map!(element => element)) ); } The output is garbage element values. The programmer must take the parameter of the outer map by reference so that the elements are referring to actual elements in 'arr': .map!((ref row) => /* ... */ I don't think I realized this issue before, which may be caught by various combinations of safety compiler switches, which I haven't tried yet. Ali
'each' can take static arrays
I know static arrays are not ranges but somehow they work with 'each'. With map, I need to slice as 'arr[]': import std; void main() { int[3] arr; arr.each!(e => e);// Compiles // arr.map!(e => e); // Fails to compile arr[].map!(e => e); // Compiles } Why the inconsistency? Ali
Re: a struct as an multidimensional array index
On 6/10/22 07:38, Ali Çehreli wrote: > I played with that toString function but for some reason it prints all > Ts. (?) Fixed it by changing one of the lambdas to take by reference: void toString(scope void delegate(in char[]) sink) const { import std.algorithm; sink.formattedWrite!"%-(%-(%s %)\n%)"( elements[].map!((ref row) => row[].map!(column => column ? 'T' : 'f'))); // ^^^ } I still don't understand the reason though. The rows would be copied without ref but should retain their type as bool[3], a static array. (?) Ali