Re: Can anyone provide an example of how D templates are overridable by global symbols?
An example: a.d: ``` import core.stdc.stdio; void foo()() { version (Oops) printf(" foo - oops\n"); else printf(" foo\n"); } void doA() { printf("doA:\n"); foo!(); } ``` b.d: ``` import core.stdc.stdio; import a; void main() { printf("main:\n"); foo!(); doA(); } ``` ```bash $ dmd -c a.d -version=Oops $ dmd -c b.d $ dmd a.o b.o -of=ab $ ./ab main: foo - oops doA: foo - oops $ dmd b.o a.o -of=ba $ ./ba main: foo doA: foo ``` Each object file contains a foo!() instantiation (in a.o, the Oops version). No inlining, so the linker takes one of the weak definitions, and we end up with a consistent behavior for both calls - but the picked version is determined by the order of the object files. Now if the calls are inlined, the behavior might not be consistent anymore. So separate compilations with different compiler flags can cause observable differences.
Re: gdc or ldc for faster programs?
On Thursday, 27 January 2022 at 18:12:18 UTC, Johan Engelen wrote: But the language requires ODR, so we can emit templates as weak_odr, telling the optimizer and linker that the symbols should be merged _and_ that ODR can be assumed to hold (i.e. inlining is OK). Thanks! This was also my impression. But the problem is that Iain Buclaw seems to disagree with us. He claims that template functions must be overridable by global functions and this is supposed to inhibit template functions inlining. Is there any independent source to back up your or Iain's claim? The onus of honouring ODR is on the user - not the compiler - because we allow the user to do separate compilation. My own limited experiments with various code snippets convinced me that D compilers actually try their best to prevent ODR violation, so it isn't like users can easily hurt themselves: https://forum.dlang.org/thread/cstjhjvmmibonbajw...@forum.dlang.org Also module names are added as a part of function names mangling. Having an accidental clash of symbol names shouldn't be very likely in a valid D project. Though I'm not absolutely sure whether this provides a sufficient safety net.
Re: Can anyone provide an example of how D templates are overridable by global symbols?
On Thursday, 9 December 2021 at 21:06:54 UTC, Siarhei Siamashka wrote: On Thursday, 9 December 2021 at 20:53:52 UTC, Siarhei Siamashka wrote: How would one construct a simple example of a template symbol getting successfully overridden by a global symbol? Forgot to mention that a template function can be overridden by another function with the same name. But only as long as all of this happens in the scope of a single module. Here are a few examples (all of them successfully compile and run): ```D import std.stdio; T f(T)(T a, T b) { return a + b; } int f(int a, int b) { return a - b; } void main() { f(2, 1).writeln; // prints "1" } ``` ```D import std.stdio; int f(int a, int b) { return a - b; } T f(T)(T a, T b) { return a + b; } void main() { f(2, 1).writeln; // prints "1" } ``` ```D import std.stdio; import template_f; int f(int a, int b) { return a - b; } void main() { f(2, 1).writeln; // prints "1" } ``` ```D import std.stdio; import nontemplate_f; T f(T)(T a, T b) { return a + b; } void main() { f(2, 1).writeln; // prints "3" } ``` This mostly agrees with the following part of the D language specification: https://dlang.org/spec/module.html#name_lookup Except that having a template function and a non-template function with the same name within the same module scope doesn't seem to be explicitly documented in the D specification. But such name clash appears to be resolved in favor of a non-template function. And this behavior shouldn't inhibit functions inlining.
Re: Embarrassed to ask this question because it seems so trivial but genuinely curious...
On Thu, Jan 27, 2022 at 05:42:09PM +, WhatMeWorry via Digitalmars-d-learn wrote: > While studying Ali's book at chapter "Constructor and Other Special > Functions" and the below code snippet: [...] > // Original > //this(int i) const { writeln("a const object"); } > //this(int i) immutable { writeln("an immutable object"); } > //this(int i) shared { writeln("a shared object"); } > > const this(int i) { writeln("a const object"); } > immutable this(int i) { writeln("an immutable object"); } > shared this(int i) { writeln("a shared object"); } [...] > Assuming I can speak in correct programmer-ese: I was wondering why > the qualifiers were placed after the function parameter list (int i). > Just for fun, I moved to type qualifiers before the function > definitions "this" (like a return type?) and the output was exactly > identical. So I guess my question is, is this just a matter of > esthetics or is some more nuanced goal at work here? In method declarations, modifiers like const/immutable/shared play two distinct roles: 1) Qualifying the return type of the method; 2) Qualifying the implicit `this` parameter of the method. Historically, D has been rather lax about where qualifiers in the sense of (2) can go, so that's why: const this() is the same as this() const They both mean that the implicit `this` parameter (not to be confused with `this` as the name of the ctor) is const. Personally, though, I prefer the 2nd form, because the first form could potentially be ambiguous in non-ctor member functions: struct S { const int method(); } Does the const apply to `int` or to the implicit `this` parameter? It's not obvious. Better to write it this way: int method() const; // const applied to `this` const(int) method();// const applied to return type N.B. the parentheses in `const(int)` -- while D is lax in allowing you to write `const int`, that leads to the ambiguous situation above. My personal recommendation is to always parenthesize qualified types so that they are never ambiguous. T -- Mediocrity has been pushed to extremes.
Re: gdc or ldc for faster programs?
On Thursday, 27 January 2022 at 16:46:59 UTC, Ali Çehreli wrote: What I know is that weak symbols can be overridden by strong symbols during linking. Which means, if a function body is inlined which also has a weak symbol, some part of the program may be using the inlined definition and some other parts may be using the overridden definition. Thanks to separate compilation, they need not match hence the violation of the one-definition rule (ODR). But the language requires ODR, so we can emit templates as weak_odr, telling the optimizer and linker that the symbols should be merged _and_ that ODR can be assumed to hold (i.e. inlining is OK). The onus of honouring ODR is on the user - not the compiler - because we allow the user to do separate compilation. Some more detailed explanation and example: https://stackoverflow.com/questions/44335046/how-does-the-linker-handle-identical-template-instantiations-across-translation/44346057 -Johan
Re: Embarrassed to ask this question because it seems so trivial but genuinely curious...
On 1/27/22 12:42 PM, WhatMeWorry wrote: Assuming I can speak in correct programmer-ese: I was wondering why the qualifiers were placed after the function parameter list (int i). Just for fun, I moved to type qualifiers before the function definitions "this" (like a return type?) and the output was exactly identical. So I guess my question is, is this just a matter of esthetics or is some more nuanced goal at work here? For constructors, being on the front is not misleading. But for a member function that returns a value, take a look: ```d struct S { int * x; const int *foo() { return x; } } ``` What do you think happens here? The answer, is that this is a compiler error. The error is that the `const` applies to the `this` parameter and *not* the return value. So you are accepting a `const S`, and trying to return its member `x` as a plain `int *`. This is why we always recommend putting the `this` modifiers at the end of the function to make it visually clear that they don't apply to the return value. -Steve
Re: Embarrassed to ask this question because it seems so trivial but genuinely curious...
On Thursday, 27 January 2022 at 17:42:09 UTC, WhatMeWorry wrote: So I guess my question is, is this just a matter of esthetics or is some more nuanced goal at work here? It doesn't matter much for constructors, but in general, the problem with placing qualifiers in front is that it looks confusing: ```D struct S { immutable int[] f() { return []; } } ``` This reads as if it returns an `immutable(int[])`, but it doesn't, the `immutable` means that it can only be called on `immutable` instances of `S`.
Embarrassed to ask this question because it seems so trivial but genuinely curious...
While studying Ali's book at chapter "Constructor and Other Special Functions" and the below code snippet: import std.stdio; struct S { this(int i) { writeln("an object"); } // Original //this(int i) const { writeln("a const object"); } //this(int i) immutable { writeln("an immutable object"); } //this(int i) shared { writeln("a shared object"); } const this(int i) { writeln("a const object"); } immutable this(int i) { writeln("an immutable object"); } shared this(int i) { writeln("a shared object"); } } void main() { auto m = S(1); auto c = const(S)(2); auto i = immutable(S)(3); auto s = shared(S)(4); } Assuming I can speak in correct programmer-ese: I was wondering why the qualifiers were placed after the function parameter list (int i). Just for fun, I moved to type qualifiers before the function definitions "this" (like a return type?) and the output was exactly identical. So I guess my question is, is this just a matter of esthetics or is some more nuanced goal at work here?
Re: gdc or ldc for faster programs?
On Thu, Jan 27, 2022 at 08:46:59AM -0800, Ali Çehreli via Digitalmars-d-learn wrote: [...] > I see that template instantiations are linked through weak symbols: > > $ nm deneme | grep foo > [...] > 00021380 W _D6deneme__T3fooTiZQhFNaNbNiNfZv > > What I know is that weak symbols can be overridden by strong symbols > during linking. [...] Yes, and it also means that only one copy of the symbol will make it into the executable. This is one of the ways we leverage the linker to eliminate (merge) duplicate template instantiations. T -- Claiming that your operating system is the best in the world because more people use it is like saying McDonalds makes the best food in the world. -- Carl B. Constantine
Re: gdc or ldc for faster programs?
On 1/26/22 11:07, Siarhei Siamashka wrote: > On Wednesday, 26 January 2022 at 18:41:51 UTC, Iain Buclaw wrote: >> The D language shot itself in the foot by requiring templates to have >> weak semantics. >> >> If DMD and LDC inline weak functions, that's their bug. > > As I already mentioned in the bugzilla, it would be really useful to see > a practical example of DMD and LDC running into troubles because of > mishandling weak templates. I am not experienced enough to answer but the way I understand weak symbols, it is possible to run into trouble but it will probably never happen. When it happens, I suspect people can find workarounds like disabling inlining. > I was never able to find anything about > "requiring templates to have weak semantics" anywhere in the Dlang > documentation or on the Internet. The truth is some part of D's spec is the implementation. When I compile the following program (with dmd) void foo(T)() {} void main() { foo!int(); } I see that template instantiations are linked through weak symbols: $ nm deneme | grep foo [...] 00021380 W _D6deneme__T3fooTiZQhFNaNbNiNfZv What I know is that weak symbols can be overridden by strong symbols during linking. Which means, if a function body is inlined which also has a weak symbol, some part of the program may be using the inlined definition and some other parts may be using the overridden definition. Thanks to separate compilation, they need not match hence the violation of the one-definition rule (ODR). Ali
Re: Template sequence parameter and default value
On 1/26/22 18:49, Andrey Zherikov wrote: > I want something like this: > ```d > void foo(MODULES... = __MODULE__)() {} Two other options: 1) void foo(MODULES_...)() { static if (MODULES_.length == 0) { import std.meta : AliasSeq; alias MODULES = AliasSeq!__MODULE__; } else { alias MODULES = MODULES_; } } 2) void foo(MODULES_...)() { } void foo() { return foo!__MODULE__; } Ali
Re: Template sequence parameter and default value
On 1/27/22 10:21 AM, Andrey Zherikov wrote: So is it possible to get rid of the alias? I'm not sure it's worth the effort? Yours is a pretty straightforward solution. -Steve
Re: Template sequence parameter and default value
On Thursday, 27 January 2022 at 03:19:59 UTC, Jaime wrote: You can accomplish this by heading off the template sequence parameter with several default template parameters. If you need them all under one name, you can recombine them in the function body with std.meta.AliasSeq, the effective "kind" of a template sequence parameter. Example: ```d void foo(string FirstModule = __MODULE__, RestModules...)() { alias Modules = AliasSeq!(FirstModule, RestModules); // things } // foo!(module1, module2) => alias Modules = (module1, module2) // foo!() => alias Modules = (__MODULE__) ``` Unfortunately `string FirstModule` doesn't work if I specify the module: https://run.dlang.io/is/BZd0KB The closest solution I have is this: ```d void foo(MODULES...)() { writeln(MODULES.stringof); } alias foo(string MODULE = __MODULE__) = foo!(mixin(MODULE)); void main() { writeln(1); foo(); writeln(2); foo!(onlineapp); writeln(3); foo!(onlineapp,onlineapp); } ``` It prints this: ``` 1 tuple(module onlineapp) 2 tuple(module onlineapp) 3 tuple(module onlineapp, module onlineapp) ``` So is it possible to get rid of the alias?
Re: Order of object fields
On 1/27/22 8:13 AM, Steven Schveighoffer wrote: On Thursday, 27 January 2022 at 12:45:20 UTC, frame wrote: Is the order of fields guaranteed returned by `.tupleof` and `__traits(getMember,...)`, can I rely on this? I know that are different things, I mean just per each use case if I have more functions that traverses through all fields. Thx. Not for classes. The compiler is free to reorder if it wants to. Yes for structs. To clarify, the compiler can reorder the members to fit them better. But it will be the same order for any particular build of the object files. Any compile-time features that fetch the members should be in the same order as the compiler has decided to lay them out. It's just that it might not match the source code order. -Steve
Re: Order of object fields
On Thursday, 27 January 2022 at 12:45:20 UTC, frame wrote: Is the order of fields guaranteed returned by `.tupleof` and `__traits(getMember,...)`, can I rely on this? I know that are different things, I mean just per each use case if I have more functions that traverses through all fields. Thx. Not for classes. The compiler is free to reorder if it wants to. Yes for structs. -Steve
Order of object fields
Is the order of fields guaranteed returned by `.tupleof` and `__traits(getMember,...)`, can I rely on this? I know that are different things, I mean just per each use case if I have more functions that traverses through all fields. Thx.