Re: Creating an array of immutable objects
On 2017-02-15 01:08, David Zhang wrote: Thanks for your answers. Out of curiosity though, how could something like this be done with classes instead? You mean if FileDesc was a class? It's basically the same. You would need: Mutable array: 1. Add a constructor which sets all immutable instance variables For immutable array: 0. Same as above 1. The constructor needs to be declared as "immutable" 2. The instances need to be created with "immutable new" In the example below, there are two constructors, one for creating mutable instances and one for immutable instances. This is only needed since I'm creating both a mutable and an immutable array with the same type. You only need one kind of constructor if you only need a mutable or an immutable array. module main; immutable string[] paths = [ "hello", "world" ]; class FileDesc { immutable string path; immutable uint index; this(string path, uint index) immutable { this.path = path; this.index = index; } this(string path, uint index) { this.path = path; this.index = index; } } // immutable array created at compile time immutable _fileDesc = paths .enumerate!uint .map!(t => new immutable FileDesc(t.value, t.index)) .array; // mutable array created at application start using allocators FileDesc[] _fileDesc2; auto makeFileDescs(const string[] paths) { return paths.enumerate!uint.map!(t => new FileDesc(t.value, t.index)); } static this() { import std.experimental.allocator; _fileDesc2 = theAllocator.makeArray!(FileDesc)(makeFileDescs(paths)); } -- /Jacob Carlborg
Re: Creating an array of immutable objects
Thanks for your answers. Out of curiosity though, how could something like this be done with classes instead?
Re: Alias type with different initialiser.
On Monday, February 13, 2017 22:59:11 John Colvin via Digitalmars-d-learn wrote: > Also, it's > generally a bad idea to define `.init` for any type as code > generally expects this to be the compiler-generated property > (e.g. a value of type Initial!(int, 1) not of type int). And there's actually a decent chance that at some point, it will become illegal precisely because of the problems that it causes to allow it. - Jonathan M Davis
Re: Alias type with different initialiser.
Thanks again. On Tuesday, 14 February 2017 at 14:08:31 UTC, John Colvin wrote: I would recommend making `template from(string moduleName)` global (maybe in a utils module?), there's no reason for it to be a member of Initial. Yes, I think there is high chance it will be part of Phobos, if chosen in favour of DIP 1005.
Re: Mallocator and 'shared'
On Tuesday, 14 February 2017 at 10:52:37 UTC, Johannes Pfau wrote: But if the interrupt accesses a variable and a normal function accesses the variable as well, the access needs to be 'volatile' (not cached into a register by the compiler; not closely related to this discussion) and atomic, as the interrupt might occur in between multiple partial writes. So the variable should be shared, although there's no multithreading (in the usual sense). Single publishing (loggers, singletons) doesn't need atomic load: https://forum.dlang.org/post/ozssiniwkaghcjvrl...@forum.dlang.org
Re: Policy-based design in D
On Tuesday, 14 February 2017 at 10:05:19 UTC, TheFlyingFiddle wrote: (snip) Oh, I didn't know you could name mixin template instantiations like that! Thanks for the tip, that makes things work nicely!
Re: Mallocator and 'shared'
On Tuesday, 14 February 2017 at 14:27:05 UTC, Kagamin wrote: On Monday, 13 February 2017 at 17:44:10 UTC, Moritz Maxeiner wrote: To be clear: While I might, in general, agree that using shared methods only for thread safe methods seems to be a sensible restriction, neither language nor compiler require it to be so; and absence of evidence of a useful application is not evidence of absence. Right, a private shared method can be a good use case for a thread-unsafe shared method. --- __gshared int f = 0, x = 0; Object monitor; // thread 1 synchronized (monitor) while (f == 0); // Memory barrier required here synchronized (monitor) writeln(x) // thread 2 synchronized (monitor) x = 42; // Memory barrier required here synchronized (monitor) f = 1; --- Not sure about this example, it demonstrates a deadlock. That's beside the point, but I guess I should've clarified the "not needed" as "harmful". The point was that memory barriers and synchronization are two separate solutions for two separate problems and your post scriptum about memory barriers disregards that synchronization does not apply to the problem memory barriers solve. On Tuesday, 14 February 2017 at 14:27:05 UTC, Kagamin wrote: My opinion on the matter of `shared` emitting memory barriers is that either the spec and documentation[1] should be updated to reflect that sequential consistency is a non-goal of `shared` (and if that is decided this should be accompanied by an example of how to add memory barriers yourself), or it should be implemented. I'm looking at this in terms of practical consequences and useful language features. So am I. On Tuesday, 14 February 2017 at 14:27:05 UTC, Kagamin wrote: What people are supposed to think and do when they see "guarantees sequential consistency"? I mean people at large. That's a documentation issue, however, and is imho not relevant to the decision whether one should, or should not emit memory barriers. It's only relevant to how the decision is then presented to the people at large. On Tuesday, 14 February 2017 at 14:27:05 UTC, Kagamin wrote: I agree, message passing is considerably less tricky and you're unlikely to shoot yourself in the foot. Nonetheless, there are valid use cases where the overhead of MP may not be acceptable. Performance was a reason to not provide barriers. People, who are concerned with performance, are even unhappy with virtual methods, they won't be happy with barriers on every memory access. You seem to be trying to argue against someone stating memory barriers should be emitted automatically, though I don't know why you think that's me; You initially stated that Memory barriers are a bad idea because they don't defend from a race condition, but they look like they do which I rebutted since memory barriers have nothing to do with race conditions. Whether memory barriers should automatically emitted by the compiler is a separate issue, one on which my position btw is that they shouldn't. The current documentation of `shared`, however, implies that such an emission (and the related sequential consistency) is a goal of `shared` (and just not - yet? - implemented) and does not reflect the apparently final decision that it's not.
Re: link.exe Error: 'Offset 38C42H Record Type 00C3'
On Tuesday, 14 February 2017 at 14:04:39 UTC, Adam D. Ruppe wrote: On Tuesday, 14 February 2017 at 09:00:37 UTC, realNoob wrote: Error 1: Previous Definition Different : _D4xaru8ezFilterFAAyaAyaZAAya That is saying the function module xaru; string[] ezFilter(string[], string); is defined twice. Could be something weird in your code, but I would guess the most likely cause is that there's some old file left around from a previous build that is confusing it. I'd try deleting all the .obj files in the directory then see if the build works again. I don't understand this error. look's like klingon language... Qapla' Yes, it is. you're right. Thank you for your kind reply!
Re: Mallocator and 'shared'
Am Tue, 14 Feb 2017 14:38:32 + schrieb Kagamin : > On Tuesday, 14 February 2017 at 10:52:37 UTC, Johannes Pfau wrote: > > I remember some discussions about this some years ago and IIRC > > the final decision was that the compiler will not magically > > insert any barriers for shared variables. > > It was so some years ago, not sure if it's still so. I suspect > automatic barriers come from TDPL book and have roughly the same > rationale as autodecoding. They fix something, guess if this > something is what you need. At least this thread is from 2012, so more recent than TDPL: http://forum.dlang.org/post/k7pn19$bre$1...@digitalmars.com I'm not sure though if there were any further discussions/decisions after that discussion. -- Johannes
Re: Mallocator and 'shared'
Am Tue, 14 Feb 2017 13:01:44 + schrieb Moritz Maxeiner : > > It's not supposed to. Also, your example does not implement the > same semantics as what I posted and yes, in your example, there's > no need for memory barriers. In the example I posted, > synchronization is not necessary, memory barriers are (and since > synchronization is likely to have a significantly higher runtime > cost than memory barriers, why would you want to, even if it were > possible). > I'll probably have to look up about memory barriers again, I never really understood when they are necessary ;-) > > > > I remember some discussions about this some years ago and IIRC > > the final decision was that the compiler will not magically > > insert any barriers for shared variables. Instead we have > > well-defined intrinsics in std.atomic dealing with this. Of > > course most of this stuff isn't implemented (no shared support > > in core.sync). > > > > -- Johannes > > Good to know, thanks, I seem to have missed that final decision. > If that was indeed the case, then that should be reflected in the > documentation of `shared` (including the FAQ). https://github.com/dlang/dlang.org/pull/1570 I think it's probably somewhere in this thread: http://forum.dlang.org/post/k7pn19$bre$1...@digitalmars.com >1. Slapping shared on a type is never going to make algorithms on that >type work in a concurrent context, regardless of what is done with >memory barriers. Memory barriers ensure sequential consistency, they >do nothing for race conditions that are sequentially consistent. >Remember, single core CPUs are all sequentially consistent, and still >have major concurrency problems. This also means that having templates >accept shared(T) as arguments and have them magically generate correct >concurrent code is a pipe dream. > >2. The idea of shared adding memory barriers for access is not going >to ever work. Adding barriers has to be done by someone who knows what >they're doing for that particular use case, and the compiler inserting >them is not going to substitute. > >However, and this is a big however, having shared as compiler-enforced >self-documentation is immensely useful. It flags where and when data >is being shared. http://forum.dlang.org/post/mailman.1904.1352922666.5162.digitalmar...@puremagic.com > Most of the reason for this was that I didn't like the old > implications of shared, which was that shared methods would at some > time in the future end up with memory barriers all over the place. > That's been dropped, [...] -- Johannes
Re: Mallocator and 'shared'
On Tuesday, 14 February 2017 at 10:52:37 UTC, Johannes Pfau wrote: I remember some discussions about this some years ago and IIRC the final decision was that the compiler will not magically insert any barriers for shared variables. It was so some years ago, not sure if it's still so. I suspect automatic barriers come from TDPL book and have roughly the same rationale as autodecoding. They fix something, guess if this something is what you need.
Re: Mallocator and 'shared'
On Monday, 13 February 2017 at 17:44:10 UTC, Moritz Maxeiner wrote: To be clear: While I might, in general, agree that using shared methods only for thread safe methods seems to be a sensible restriction, neither language nor compiler require it to be so; and absence of evidence of a useful application is not evidence of absence. Right, a private shared method can be a good use case for a thread-unsafe shared method. --- __gshared int f = 0, x = 0; Object monitor; // thread 1 synchronized (monitor) while (f == 0); // Memory barrier required here synchronized (monitor) writeln(x) // thread 2 synchronized (monitor) x = 42; // Memory barrier required here synchronized (monitor) f = 1; --- Not sure about this example, it demonstrates a deadlock. My opinion on the matter of `shared` emitting memory barriers is that either the spec and documentation[1] should be updated to reflect that sequential consistency is a non-goal of `shared` (and if that is decided this should be accompanied by an example of how to add memory barriers yourself), or it should be implemented. I'm looking at this in terms of practical consequences and useful language features. What people are supposed to think and do when they see "guarantees sequential consistency"? I mean people at large. I agree, message passing is considerably less tricky and you're unlikely to shoot yourself in the foot. Nonetheless, there are valid use cases where the overhead of MP may not be acceptable. Performance was a reason to not provide barriers. People, who are concerned with performance, are even unhappy with virtual methods, they won't be happy with barriers on every memory access.
Re: Alias type with different initialiser.
On Tuesday, 14 February 2017 at 11:34:22 UTC, Bastiaan Veelo wrote: On Monday, 13 February 2017 at 22:59:11 UTC, John Colvin wrote: Why not use a constructor instead of static opCall? I don't know, this comes from http://dlang.org/spec/struct.html#dynamic_struct_init. Your constructor looks a lot better. Am I missing a test case where static opCall would be called, but not the constructor? Why do the docs use static opCall? The docs are just trying to illustrate that opCall is only used for initialisation if the initialiser is of a different type. Constructors are always used for initialisation if they are there, they completely hide static opCall in that context. static opCall can return anything of any type, constructors implicitly return a reference to `this`. A constructor is the way to initialise a type and some code - such as std.conv.emplace - actually calls the constructor manually (it's accessible via .__ctor) to do so. static opCall doesn't necessarily represent an initialiser function, so generic code won't necessarily know to use it. Overall, static opCall is rarely the solution you need. Also, it's generally a bad idea to define `.init` for any type as code generally expects this to be the compiler-generated property (e.g. a value of type Initial!(int, 1) not of type int). Thanks. I can't remember what confused me to think that typeof(int1.init) had to be int. enum initial = val; [...] static assert(int1.initial == 1); // typeof(int1.initial) == int These lines have no purpose beyond illustration, right? they check that the implementation does have a member .initial and that it is equal to the value we requested as the initialiser. It also enforces that it is accessible at compile-time. I now have the following, featuring the novel Scherkl-Nielsen self-important lookup: /** Creates an type that is mostly $(PARAM T), only with a different initial value of $(PARAM val). */ struct Initial(T, T val) { private T _payload = val; alias _payload this; this(T v) { _payload = v; } // https://dlang.org/blog/2017/02/13/a-new-import-idiom/ private template from(string moduleName) { mixin("import from = " ~ moduleName ~ ";"); } void toString(scope void delegate(const(char)[]) sink, from!"std.format".FormatSpec!char fmt) { import std.array : appender; import std.format : formatValue; auto w = appender!string(); formatValue(w, _payload, fmt); sink(w.data); } } unittest { alias int1 = Initial!(int, 1); static assert(int1.init == 1); // typeof(int1.init) == int1 int1 i; assert(i == 1); int1 ii = 2; assert(ii == 2); assert(ii.init == 1); assert(int1.init == 1); void f(int val) { assert(val == 1); } f(i); int i0; assert(i0 == 0); i = i0; assert(i == 0); assert(i.init == 1); i0 = ii; assert(i0 == 2); assert(i0.init == 0); import std.string; assert(format("%6d", ii) == " 2"); } I would recommend making `template from(string moduleName)` global (maybe in a utils module?), there's no reason for it to be a member of Initial.
Re: link.exe Error: 'Offset 38C42H Record Type 00C3'
On Tuesday, 14 February 2017 at 09:00:37 UTC, realNoob wrote: Error 1: Previous Definition Different : _D4xaru8ezFilterFAAyaAyaZAAya That is saying the function module xaru; string[] ezFilter(string[], string); is defined twice. Could be something weird in your code, but I would guess the most likely cause is that there's some old file left around from a previous build that is confusing it. I'd try deleting all the .obj files in the directory then see if the build works again. I don't understand this error. look's like klingon language... Qapla'
Re: Alias type with different initialiser.
On Tuesday, 14 February 2017 at 10:49:19 UTC, Bastiaan Veelo wrote: On Tuesday, 14 February 2017 at 01:31:10 UTC, John Colvin wrote: On Monday, 13 February 2017 at 22:59:11 UTC, John Colvin wrote: [ snip ] sorry, made a typo, that should have been alias int1 = Initial!(int, 1); static assert(int1.initial == 1); // typeof(int1.initial) == int static assert(int1.init == 1); // typeof(int1.init) == int1 What is the difference between alias Initial!(int, 1) int1; and alias int1 = Initial!(int, 1); ? Or was the typo in the comments alone? Thanks. just a more modern style. I think the old style would have been deprecated if it wasn't for how much old code used it.
Re: Mallocator and 'shared'
On Tuesday, 14 February 2017 at 13:01:44 UTC, Moritz Maxeiner wrote: Of course, I just wanted to point out that Kagamin's post scriptum is a simplification I cannot agree with. As a best practice? Sure. As a "never do it"? No. Sorry for the double post, error in the above, please use this instead: Of course, I just wanted to point out that Kagamin's Thread unsafe methods shouldn't be marked shared, it doesn't make sense is a simplification I cannot agree with. As a best practice? Sure. As a "never do it"? No.
Re: Mallocator and 'shared'
On Tuesday, 14 February 2017 at 10:52:37 UTC, Johannes Pfau wrote: The compiler of course can't require shared methods to be thread-safe as it simply can't prove thread-safety in all cases. This is like shared/trusted: You are supposed to make sure that a function behaves as expected. The compiler will catch some easy to detect mistakes (like calling a non-shared method from a shared method <=> system method from safe method) but you could always use casts, pointers, ... to fool the compiler. You could use the same argument to mark any method as @trusted. Yes it's possible, but it's a very bad idea. Though I do agree that there might be edge cases: In a single core, single threaded environment, should an interrupt function be marked as shared? Probably not, as no synchronization is required when calling the function. But if the interrupt accesses a variable and a normal function accesses the variable as well, the access needs to be 'volatile' (not cached into a register by the compiler; not closely related to this discussion) and atomic, as the interrupt might occur in between multiple partial writes. So the variable should be shared, although there's no multithreading (in the usual sense). Of course, I just wanted to point out that Kagamin's post scriptum is a simplification I cannot agree with. As a best practice? Sure. As a "never do it"? No. On Tuesday, 14 February 2017 at 10:52:37 UTC, Johannes Pfau wrote: Am Mon, 13 Feb 2017 17:44:10 + schrieb Moritz Maxeiner : you'd still need those memory barriers. Also note that the synchronization in the above is not needed in terms of semantics. However, if you move you synchronized to the complete sub-code blocks barriers are not necessary. Traditional mutex locking is basically a superset and is usually implemented using barriers AFAIK. I guess your point is we need to define whether shared methods guarantee some sort of sequential consistency? My point in those paragraphs was that synchronization and memory barriers solve two different problems that can occur in non-sequential programming and because of Kagamin's statement Memory barriers are a bad idea because they don't defend from a race condition makes no sense (to me). But yes, I do think that the definition should have more background /context and not the current "D FAQ states `shared` guarantees sequential consistency (not implemented)". Considering how many years that has been the state I have personally concluded (for myself and how I deal with D) that sequential consistency is a non-goal of `shared`, but what's a person new to D supposed to think? On Tuesday, 14 February 2017 at 10:52:37 UTC, Johannes Pfau wrote: struct Foo { shared void doA() {lock{_tmp = "a";}}; shared void doB() {lock{_tmp = "b";}}; shared getA() {lock{return _tmp;}}; shared getB() {lock{return _tmp;}}; } thread1: foo.doB(); thread2: foo.doA(); auto result = foo.getA(); // could return "b" I'm not sure how a compiler could prevent such 'logic' bugs. It's not supposed to. Also, your example does not implement the same semantics as what I posted and yes, in your example, there's no need for memory barriers. In the example I posted, synchronization is not necessary, memory barriers are (and since synchronization is likely to have a significantly higher runtime cost than memory barriers, why would you want to, even if it were possible). On Tuesday, 14 February 2017 at 10:52:37 UTC, Johannes Pfau wrote: However, I think it should be considered a best practice to always make a shared function a self-contained entity so that calling any other function in any order does not negatively effect the results. Though that might not always be possible. Yes, that matches what I tried to express. On Tuesday, 14 February 2017 at 10:52:37 UTC, Johannes Pfau wrote: Am Mon, 13 Feb 2017 17:44:10 + schrieb Moritz Maxeiner : My opinion on the matter of `shared` emitting memory barriers is that either the spec and documentation[1] should be updated to reflect that sequential consistency is a non-goal of `shared` (and if that is decided this should be accompanied by an example of how to add memory barriers yourself), or it should be implemented. Though leaving it in the current "not implemented, no comment / plan on whether/when it will be implemented" state seems to have little practical consequence - since no one seems to actually work on this level in D - and I can thus understand why dealing with that is just not a priority. I remember some discussions about this some years ago and IIRC the final decision was that the compiler will not magically insert any barriers for shared variables. Instead we have well-defined intrinsics in std.atomic dealing with this. Of course most of this stuff isn't implemented (no shared support in core.sync). -- Johannes Good to know, thanks, I seem to have missed that final decision. If that was indeed
Re: Alias type with different initialiser.
On Monday, 13 February 2017 at 22:59:11 UTC, John Colvin wrote: Why not use a constructor instead of static opCall? I don't know, this comes from http://dlang.org/spec/struct.html#dynamic_struct_init. Your constructor looks a lot better. Am I missing a test case where static opCall would be called, but not the constructor? Why do the docs use static opCall? Also, it's generally a bad idea to define `.init` for any type as code generally expects this to be the compiler-generated property (e.g. a value of type Initial!(int, 1) not of type int). Thanks. I can't remember what confused me to think that typeof(int1.init) had to be int. enum initial = val; [...] static assert(int1.initial == 1); // typeof(int1.initial) == int These lines have no purpose beyond illustration, right? I now have the following, featuring the novel Scherkl-Nielsen self-important lookup: /** Creates an type that is mostly $(PARAM T), only with a different initial value of $(PARAM val). */ struct Initial(T, T val) { private T _payload = val; alias _payload this; this(T v) { _payload = v; } // https://dlang.org/blog/2017/02/13/a-new-import-idiom/ private template from(string moduleName) { mixin("import from = " ~ moduleName ~ ";"); } void toString(scope void delegate(const(char)[]) sink, from!"std.format".FormatSpec!char fmt) { import std.array : appender; import std.format : formatValue; auto w = appender!string(); formatValue(w, _payload, fmt); sink(w.data); } } unittest { alias int1 = Initial!(int, 1); static assert(int1.init == 1); // typeof(int1.init) == int1 int1 i; assert(i == 1); int1 ii = 2; assert(ii == 2); assert(ii.init == 1); assert(int1.init == 1); void f(int val) { assert(val == 1); } f(i); int i0; assert(i0 == 0); i = i0; assert(i == 0); assert(i.init == 1); i0 = ii; assert(i0 == 2); assert(i0.init == 0); import std.string; assert(format("%6d", ii) == " 2"); }
Re: Mallocator and 'shared'
Am Mon, 13 Feb 2017 17:44:10 + schrieb Moritz Maxeiner : > > Thread unsafe methods shouldn't be marked shared, it doesn't > > make sense. If you don't want to provide thread-safe interface, > > don't mark methods as shared, so they will not be callable on a > > shared instance and thus the user will be unable to use the > > shared object instance and hence will know the object is thread > > unsafe and needs manual synchronization. > > To be clear: While I might, in general, agree that using shared > methods only for thread safe methods seems to be a sensible > restriction, neither language nor compiler require it to be so; > and absence of evidence of a useful application is not evidence > of absence. The compiler of course can't require shared methods to be thread-safe as it simply can't prove thread-safety in all cases. This is like shared/trusted: You are supposed to make sure that a function behaves as expected. The compiler will catch some easy to detect mistakes (like calling a non-shared method from a shared method <=> system method from safe method) but you could always use casts, pointers, ... to fool the compiler. You could use the same argument to mark any method as @trusted. Yes it's possible, but it's a very bad idea. Though I do agree that there might be edge cases: In a single core, single threaded environment, should an interrupt function be marked as shared? Probably not, as no synchronization is required when calling the function. But if the interrupt accesses a variable and a normal function accesses the variable as well, the access needs to be 'volatile' (not cached into a register by the compiler; not closely related to this discussion) and atomic, as the interrupt might occur in between multiple partial writes. So the variable should be shared, although there's no multithreading (in the usual sense). > you'd still need those memory barriers. Also note that the > synchronization in the above is not needed in terms of semantics. However, if you move you synchronized to the complete sub-code blocks barriers are not necessary. Traditional mutex locking is basically a superset and is usually implemented using barriers AFAIK. I guess your point is we need to define whether shared methods guarantee some sort of sequential consistency? struct Foo { shared void doA() {lock{_tmp = "a";}}; shared void doB() {lock{_tmp = "b";}}; shared getA() {lock{return _tmp;}}; shared getB() {lock{return _tmp;}}; } thread1: foo.doB(); thread2: foo.doA(); auto result = foo.getA(); // could return "b" I'm not sure how a compiler could prevent such 'logic' bugs. However, I think it should be considered a best practice to always make a shared function a self-contained entity so that calling any other function in any order does not negatively effect the results. Though that might not always be possible. > My opinion on the matter of `shared` emitting memory barriers is > that either the spec and documentation[1] should be updated to > reflect that sequential consistency is a non-goal of `shared` > (and if that is decided this should be accompanied by an example > of how to add memory barriers yourself), or it should be > implemented. Though leaving it in the current "not implemented, > no comment / plan on whether/when it will be implemented" state > seems to have little practical consequence - since no one seems > to actually work on this level in D - and I can thus understand > why dealing with that is just not a priority. I remember some discussions about this some years ago and IIRC the final decision was that the compiler will not magically insert any barriers for shared variables. Instead we have well-defined intrinsics in std.atomic dealing with this. Of course most of this stuff isn't implemented (no shared support in core.sync). -- Johannes
Re: Alias type with different initialiser.
On Tuesday, 14 February 2017 at 01:31:10 UTC, John Colvin wrote: On Monday, 13 February 2017 at 22:59:11 UTC, John Colvin wrote: [ snip ] sorry, made a typo, that should have been alias int1 = Initial!(int, 1); static assert(int1.initial == 1); // typeof(int1.initial) == int static assert(int1.init == 1); // typeof(int1.init) == int1 What is the difference between alias Initial!(int, 1) int1; and alias int1 = Initial!(int, 1); ? Or was the typo in the comments alone? Thanks.
Re: Policy-based design in D
On Tuesday, 14 February 2017 at 06:48:33 UTC, TheGag96 wrote: Tonight I stumbled upon Andrei's concept of policy-based design (https://en.wikipedia.org/wiki/Policy-based_design) and tried to implement their example in D with the lack of multiple inheritance in mind. https://dpaste.dzfl.pl/adc05892344f (btw, any reason why certificate validation on dpaste fails right now?) The implementation isn't perfect, as I'm not sure how to check members of mixin templates so that you could verify whether print() and message() are actually where they should be. How would you do that? Is there any use for this kind of thing in D, and if so, what would it be? I've hardly dabbled in OOP patterns, but the abstraction seems kinda interesting. Something like this can be used to check if the mixin has a specific member: template hasMixinMember(alias mixin_, string member) { enum hasMixinMember = __traits(compiles, () { mixin mixin_ mix; static assert(__traits(hasMember, mix, member)); }); } struct HelloWorld(alias OutputPolicy, alias LanguagePolicy) if(hasMixinMember!(OutputPolicy, "print") && hasMixinMember!(LanguagePolicy, "message")) { mixin OutputPolicy; mixin LanguagePolicy; void run() { print(message()); } } Note: This method could fail if you do some compile-time reflection black magic inside the mixins. Could also do this: struct HelloWorld(alias OutputPolicy, alias LanguagePolicy) { mixin OutputPolicy output; mixin LanguagePolicy lang; void run() { output.print(lang.message()); } } If "output" / "lang" does not contain a particular member you will get a compile time error at the usage point (although it's not the best message).
Re: Creating an array of immutable objects
On 2017-02-14 01:59, David Zhang wrote: Hi, I have a struct with two immutable members, and I want to make an array of them. How do I to this? I'm using allocators for this. string[] paths; struct FileDesc { immutable string path; immutable uint index; } _fileDesc = /*something*/; You can't use alloc.makeArray because it requires a range with which to initialize the array, and while I can produce an array of paths, I don't know how to merge both a range of paths and indices. Lockstep doesn't work. I also tried using emplace and an allocated byte array, but it gave me random values and was (I think) unnecessarily complicated. What am I missing? Here are two examples, one creating an immutable array at compile time. The other one creating a mutable array at application startup using allocators: import std.algorithm; import std.range; immutable string[] paths = [ "hello", "world" ]; struct FileDesc { immutable string path; immutable uint index; } // immutable array created at compile time immutable _fileDesc = makeFileDescs(paths).array; // mutable array created at application start using allocators FileDesc[] _fileDesc2; auto makeFileDescs(const string[] paths) { return paths.enumerate!uint.map!(t => FileDesc(t.value, t.index)); } static this() { import std.experimental.allocator; _fileDesc2 = theAllocator.makeArray!FileDesc(makeFileDescs(paths)); } -- /Jacob Carlborg
link.exe Error: 'Offset 38C42H Record Type 00C3'
Hello. I got a problem this: [LDC(ldmd2)] -- Success. C:\MyFolders\src>ldmd2 --version LDC - the LLVM D compiler (1.1.0): based on DMD v2.071.2 and LLVM 3.9.1 built with LDC - the LLVM D compiler (1.1.0) Default target: x86_64-pc-windows-msvc Host CPU: amdfam10 http://dlang.org - http://wiki.dlang.org/LDC ... skip ... C:\MyFolders\src>ldmd2 xaru.d -Dddocs xaru.d(169): Deprecation: module core.stdc.wchar_ is not accessible here, perhaps add 'static import core.stdc.wchar_;' Using Visual C++: C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC [DMD] -- Fail! C:\MyFolders\src>dmd --version DMD32 D Compiler v2.072.2 Copyright (c) 1999-2016 by Digital Mars written by Walter Bright C:\MyFolders\src>dmd xaru.d -Dddocs xaru.d(60): Deprecation: Implicit string concatenation is deprecated, use "\uc624\ud1a0\ucf54\ub178\ucf54+\uc5d4\uc194\ub85c\uc9c0," ~ "36" instead xaru.d(169): Deprecation: module core.stdc.wchar_ is not accessible here, perhaps add 'static import core.stdc.wchar_;' OPTLINK (R) for Win32 Release 8.00.17 Copyright (C) Digital Mars 1989-2013 All rights reserved. http://www.digitalmars.com/ctg/optlink.html xaru.obj(xaru) Offset 38C42H Record Type 00C3 Error 1: Previous Definition Different : _D4xaru8ezFilterFAAyaAyaZAAya Error: linker exited with status 344806024 I don't understand this error. look's like klingon language...