Implementing SmartPtr - compiler bug?
For reasons I won't go into (but should be fairly obvious), I am trying to write code that does not rely on the garbage collector. As such, I'm using reference counting structs allocated on a pool. To keep things sane, I'm trying to use RAII semantics, and to that end, a smart pointer that calls "incref" and "decref" when appropriate. The SmartPtr struct implements this(T*), this(SmartPtr), this(this), ~this(), opAssign(SmartPtr), opAssign(ref SmartPtr) and opAssign(T*). To the best of my understanding this should be enough to catch all relevant cases. All works fine except one case: Foo foo; SmartPtr!Foo[4] array; array[0] = foo; assert(foo.refcount == 1); array = array.init; assert(foo.refcount == 0, "This assert fails"); I am compiling this on dmd v2.065 Switching the init line to "array[] = SmartPtr!Foo.init" makes the second assert pass. To the best of my understanding, "array = array.init" should always be equivalent to "array[] = typeof(array[0]).init" for static arrays. Am I missing something? Is this a compiler bug? Thanks, Shachar
Re: Implementing SmartPtr - compiler bug?
just checked this with gdc and dmd head. interesting that a = a.init; works the same in gdc and in dmd: just calls destructors for structs. but doing a[] = S.init works different. gdc does the same as in the first case: just calling destructors for all array elements. but dmd head calls postblits before destructing for all array elements (and then calls destructors for this copied elements). i.e. first case: destructor: bff51e2b destructor: bff51e2a destructor: bff51e29 destructor: bff51e28 second case: postblit: bff9405c destructor: bff94028 postblit: bff9405d destructor: bff94028 postblit: bff9405e destructor: bff94028 postblit: bff9405f destructor: bff94028 destructor: bff9405f destructor: bff9405e destructor: bff9405d destructor: bff9405c besides, dmd calls destructor for the first array element four times (bff94028). i think that this is definitely compiler bug. if dmd insists on postbliting, it should at least call destructors for all array elements, not only for the first one four times. ;-) signature.asc Description: PGP signature
Re: Implementing SmartPtr - compiler bug?
On Monday, 27 October 2014 at 07:31:34 UTC, Shachar Shemesh wrote: For reasons I won't go into (but should be fairly obvious), I am trying to write code that does not rely on the garbage collector. As such, I'm using reference counting structs allocated on a pool. To keep things sane, I'm trying to use RAII semantics, and to that end, a smart pointer that calls "incref" and "decref" when appropriate. The SmartPtr struct implements this(T*), this(SmartPtr), this(this), ~this(), opAssign(SmartPtr), opAssign(ref SmartPtr) and opAssign(T*). To the best of my understanding this should be enough to catch all relevant cases. All works fine except one case: Foo foo; SmartPtr!Foo[4] array; array[0] = foo; assert(foo.refcount == 1); array = array.init; assert(foo.refcount == 0, "This assert fails"); I am compiling this on dmd v2.065 Switching the init line to "array[] = SmartPtr!Foo.init" makes the second assert pass. To the best of my understanding, "array = array.init" should always be equivalent to "array[] = typeof(array[0]).init" for static arrays. Am I missing something? Is this a compiler bug? Thanks, Shachar You have created dynamic array of SmartPtrs. In that case they now live on GS's heap and destructor calls are no longer deterministic (and in fact are not guaranteed at all). That is the very first things that gets C++ programmers coming to D (happened to me too). RAII does not work in D. At least not how C++ would expect it to.
Re: Where is a variable declared in a module allocated?
On Saturday, 25 October 2014 at 22:16:12 UTC, John Colvin wrote: On Saturday, 25 October 2014 at 21:52:13 UTC, MachineCode wrote: Where is a variable declared in a module allocated? is it same as a C's global? for example: module foo; int myvar; that is in thread local storage. __shared, shared or immutable cause the variable to be in classic global storage like in C. See: http://dlang.org/migrate-to-shared.html woops, sorry, that should be __gshared
Re: Implementing SmartPtr - compiler bug?
On Mon, 27 Oct 2014 09:11:33 + Szymon Gatner via Digitalmars-d-learn wrote: > You have created dynamic array of SmartPtrs. nope. it's stack-allocated array. signature.asc Description: PGP signature
Re: Implementing SmartPtr - compiler bug?
On Monday, 27 October 2014 at 09:21:14 UTC, ketmar via Digitalmars-d-learn wrote: On Mon, 27 Oct 2014 09:11:33 + Szymon Gatner via Digitalmars-d-learn wrote: You have created dynamic array of SmartPtrs. nope. it's stack-allocated array. Right, sorry. Tho I admit I made assumptions since that was not the full code.
Re: Reflections on isPalindrome
On Sunday, 26 October 2014 at 20:38:29 UTC, Nordlöw wrote: On Friday, 24 October 2014 at 22:29:12 UTC, Peter Alexander wrote: Further, I would like to extend isPalindrome() with a minimum length argument minLength that for string and wstring does I extended my algorithm with a minLength argument https://github.com/nordlow/justd/blob/master/algorithm_ex.d#L774 You could add an early `return false;` if the range has length and it is less than minLength.
Re: Implementing SmartPtr - compiler bug?
On 27/10/14 11:31, Szymon Gatner wrote: Right, sorry. Tho I admit I made assumptions since that was not the full code. I've opened a bug. It has a fully contained sample (that does not, in fact, implement smartptr). https://issues.dlang.org/show_bug.cgi?id=13661
Re: Problems with Mutex
On 10/26/14 7:13 PM, ketmar via Digitalmars-d-learn wrote: On Sun, 26 Oct 2014 22:53:07 + Neven via Digitalmars-d-learn wrote: Why cannot I globally have auto mutex = new Mutex? And why it works now when I put __gshared? this is because 'auto mutex = new Mutex' is not a global declaration, it's thread-local declaration. all D variables are thread-locals by default. i.e. you have one indepented Mutex for each thread. and you initialized it only in one thread, all other threads got unitialized Mutex objects. having thread-locals instead of globals by default can be confusing if you missed that in documentation. just use 'shared' or '__gshared' to get "real" globals. Just as a followup, for some reason Mutex is not callable as a shared object, so you have to make it __gshared. This is unfortunate, but it is the only way to do it right now. Because an actual mutex is thread safe, it is not a problem. -Steve
Re: Problems with Mutex
On Mon, 27 Oct 2014 09:24:13 -0400 Steven Schveighoffer via Digitalmars-d-learn wrote: > Just as a followup, for some reason Mutex is not callable as a shared > object, so you have to make it __gshared. that's due to difference betwen plain type and shared type. `shared(Mutex)` is not the same as simply `Mutex`. to use class as shared var you have to declare it as 'shared class'. or at least declare some of class methods as 'shared'. but for Mutex it's perfectly ok to use it as '__gshared' object. signature.asc Description: PGP signature
Re: Problems with Mutex
On Mon, 27 Oct 2014 09:24:13 -0400 Steven Schveighoffer via Digitalmars-d-learn wrote: > Just as a followup, for some reason Mutex is not callable as a shared > object, so you have to make it __gshared. > > This is unfortunate, but it is the only way to do it right now. > Because an actual mutex is thread safe, it is not a problem. p.s. generally you have to write code that can be used with shared vars with special accuracy. that's why D made a clear distinction between shared and thread variables and classes. you must explicitly declare your intention to use something as shared. generally this increases code safety and allows to writte better code. this can be somewhat unusual for those who used to use C/C++ before, as there is no such "guards" though. signature.asc Description: PGP signature
Re: Implementing SmartPtr - compiler bug?
On Monday, 27 October 2014 at 12:40:17 UTC, Shachar Shemesh wrote: On 27/10/14 11:31, Szymon Gatner wrote: Right, sorry. Tho I admit I made assumptions since that was not the full code. I've opened a bug. It has a fully contained sample (that does not, in fact, implement smartptr). https://issues.dlang.org/show_bug.cgi?id=13661 Strictly speaking, your opAssign is wrong, because it doesn't swap source and destination, as it should. This doesn't matter here, but it would for a smartptr. However, that opAssign isn't even called is certainly a bug.
Re: Problems with Mutex
On 10/27/14 9:34 AM, ketmar via Digitalmars-d-learn wrote: On Mon, 27 Oct 2014 09:24:13 -0400 Steven Schveighoffer via Digitalmars-d-learn wrote: Just as a followup, for some reason Mutex is not callable as a shared object, so you have to make it __gshared. This is unfortunate, but it is the only way to do it right now. Because an actual mutex is thread safe, it is not a problem. p.s. generally you have to write code that can be used with shared vars with special accuracy. that's why D made a clear distinction between shared and thread variables and classes. you must explicitly declare your intention to use something as shared. generally this increases code safety and allows to writte better code. But the result is that you can't use a mutex embedded in a shared object without casting (except for when it's the object's Monitor). Ironically, that's the one place you NEED a mutex. It's not a good situation. this can be somewhat unusual for those who used to use C/C++ before, as there is no such "guards" though. The 'shared' system is really underdesigned in D, and needs complete overhaul. The opposite of it (default unshared) is an extremely useful mechanism. -Steve
Re: Reflections on isPalindrome
On Monday, 27 October 2014 at 12:10:59 UTC, Marc Schütz wrote: You could add an early `return false;` if the range has length and it is less than minLength. See update :) Thanks!
Re: Implementing SmartPtr - compiler bug?
On Monday, 27 October 2014 at 14:04:53 UTC, Marc Schütz wrote: On Monday, 27 October 2014 at 12:40:17 UTC, Shachar Shemesh wrote: On 27/10/14 11:31, Szymon Gatner wrote: Right, sorry. Tho I admit I made assumptions since that was not the full code. I've opened a bug. It has a fully contained sample (that does not, in fact, implement smartptr). https://issues.dlang.org/show_bug.cgi?id=13661 Strictly speaking, your opAssign is wrong, because it doesn't swap source and destination, as it should. This doesn't matter here, but it would for a smartptr. However, that opAssign isn't even called is certainly a bug. Why would opAssign() swap things?
Re: Implementing SmartPtr - compiler bug?
On Monday, 27 October 2014 at 16:58:56 UTC, Szymon Gatner wrote: On Monday, 27 October 2014 at 14:04:53 UTC, Marc Schütz wrote: On Monday, 27 October 2014 at 12:40:17 UTC, Shachar Shemesh wrote: On 27/10/14 11:31, Szymon Gatner wrote: Right, sorry. Tho I admit I made assumptions since that was not the full code. I've opened a bug. It has a fully contained sample (that does not, in fact, implement smartptr). https://issues.dlang.org/show_bug.cgi?id=13661 Strictly speaking, your opAssign is wrong, because it doesn't swap source and destination, as it should. This doesn't matter here, but it would for a smartptr. However, that opAssign isn't even called is certainly a bug. Why would opAssign() swap things? This is necessary to make assignment exception safe (in the general case). It's known as the copy-and-swap idiom in C++. Here it's described in the D spec: http://dlang.org/struct#assign-overload For your example code it's not necessary, because there can never be exceptions, but depending on what the smart pointer does, it might throw in one of its members' opAssign, which could lead to a partially copied object.
Re: Problems with Mutex
On Monday, October 27, 2014 09:24:13 Steven Schveighoffer via Digitalmars-d- learn wrote: > Just as a followup, for some reason Mutex is not callable as a shared > object, so you have to make it __gshared. > > This is unfortunate, but it is the only way to do it right now. Because > an actual mutex is thread safe, it is not a problem. The reason that it's not shared is because Sean Kelly didn't want to make much of anything in druntime shared until shared was better ironed out, which keeps getting talked about but never done. - Jonathan M Davis
Re: Reflections on isPalindrome
On Monday, 27 October 2014 at 16:59:19 UTC, Nordlöw wrote: On Monday, 27 October 2014 at 12:10:59 UTC, Marc Schütz wrote: You could add an early `return false;` if the range has length and it is less than minLength. See update :) Thanks! And you can return true if length <= 1 Why bidirectional range only?
Need assistance translating this C++ template
Howdy, I stumbled across a tiny NES emulator written in C++ (http://bisqwit.iki.fi/jutut/kuvat/programming_examples/nesemu1/nesemu1.cc) that I feel compelled to make even tinier with some D magic. I am having trouble with a nested template in the code. The C++ code: // Bitfield utilities template struct RegBit { T data; enum { mask = (1u << nbits) - 1u }; template RegBit& operator=(T2 val) { data = (data & ~(mask << bitno)) | ((nbits > 1 ? val & mask : !!val) << bitno); return *this; } operator unsigned() const { return (data >> bitno) & mask; } RegBit& operator++ () { return *this = *this + 1; } unsigned operator++ (int) { unsigned r = *this; ++*this; return r; } }; My D implementation thus far: // To avoid type confusion... alias u8 = uint_least8_t; alias u32 = uint_least32_t; template RegBit(uint bitno, uint nbits = 1, T = u8) { T data; enum { mask = (1u << nbits) - 1u } // FIXME: Nested template frustration here... HELP void opAssign(T2 val) { data = (data & ~(mask << bitno)) | ((nbits > 1 ? val & mask : !!val) << bitno); return *this; } auto opCall() { return (data >> bitno) & mask; } ref RegBit opUnary(string s)() if (s == "++") { return *this = *this + 1; } ref RegBit opUnary(string s)(int) if (s== "++") { uint r = *this; ++*this; return r; } } Any push in the right direction would be greatly appreciated. I'm just trying to get a D implementation up and running before I start making this smaller and more intuitive.
Re: Need assistance translating this C++ template
On Mon, 27 Oct 2014 22:43:22 +, John wrote: > void opAssign(T2 val) Without looking at the rest of your code, looks like this line needs to be void opAssign(T2)(T2 val)
Re: Reflections on isPalindrome
On Monday, 27 October 2014 at 21:28:17 UTC, Andrea Fontana wrote: And you can return true if length <= 1 Thanks. Why bidirectional range only? popBack() only for http://dlang.org/phobos/std_range.html#isBidirectionalRange
Re: Need assistance translating this C++ template
On Monday, 27 October 2014 at 22:43:23 UTC, John wrote: The C++ code: // Bitfield utilities template struct RegBit { [...] template RegBit& operator=(T2 val) [...] }; My D implementation thus far: [...] template RegBit(uint bitno, uint nbits = 1, T = u8) { You're missing the struct here. Add some (template) parameters to a struct, and it becomes a struct template: struct RegBit(uint bitno, uint nbits = 1, T = u8) [...] // FIXME: Nested template frustration here... HELP void opAssign(T2 val) Similarly, add another set of (template) parameters to a function/method, before the runtime parameters, and it becomes a function/method template: void opAssign(T2)(T2 val) [...] } Those "slap another set of parameters on, and it's a template" syntaxes, are sugar for the longer "eponymous member" variant: struct Foo(T) {} is equivalent to template Foo(T) { struct Foo /* same name as the template */ { /* ... */ } } Works with structs, classes, functions, etc. So if you wanted to spell the templates out, it would look like this: template RegBit(uint bitno, uint nbits = 1, T = u8) { struct RegBit { /* ... other members of RegBit ... */ template opAssign(T2) { void opAssign(T2 val) { /* ... implementation of opAssign ... */ } } /* ... other members of RegBit ... */ } }
Re: Need assistance translating this C++ template
On Monday, 27 October 2014 at 23:19:42 UTC, anonymous wrote: On Monday, 27 October 2014 at 22:43:23 UTC, John wrote: The C++ code: // Bitfield utilities template struct RegBit { [...] template RegBit& operator=(T2 val) [...] }; My D implementation thus far: [...] template RegBit(uint bitno, uint nbits = 1, T = u8) { You're missing the struct here. Add some (template) parameters to a struct, and it becomes a struct template: struct RegBit(uint bitno, uint nbits = 1, T = u8) [...] // FIXME: Nested template frustration here... HELP void opAssign(T2 val) Similarly, add another set of (template) parameters to a function/method, before the runtime parameters, and it becomes a function/method template: void opAssign(T2)(T2 val) [...] } Those "slap another set of parameters on, and it's a template" syntaxes, are sugar for the longer "eponymous member" variant: struct Foo(T) {} is equivalent to template Foo(T) { struct Foo /* same name as the template */ { /* ... */ } } Works with structs, classes, functions, etc. So if you wanted to spell the templates out, it would look like this: template RegBit(uint bitno, uint nbits = 1, T = u8) { struct RegBit { /* ... other members of RegBit ... */ template opAssign(T2) { void opAssign(T2 val) { /* ... implementation of opAssign ... */ } } /* ... other members of RegBit ... */ } } Much appreciated! I saw I didn't even make it a struct shortly after posting, time to take a nap and restrain the caffeine intake. And thanks for the missing parameter, Justin.
Creation of 0MQ D project
Hey all, I've been playing around w/ D for a little while, and figured I would dive more into it with some side project stuff that has come up at work. The interface I am hooking up to is a zmq server, so I created a dub.json file ~ as follows: { "dependencies": { "zeromq": "~master", }, "libs": ["zmq"] } and tried to compile by calling 'dub'. I receive the following error: Building zeromq ~master configuration "library", build type debug. Running dmd... ../../.dub/packages/zeromq-master/deimos/zmq/zmq.d(96): Error: function deimos.zmq.zmq.zmq_strerror without 'this' cannot be const FAIL ../../.dub/packages/zeromq-master/.dub/build/library-debug-linux.posix-x86_64-dmd_2066-9416B6A4CCF6909BD83BEC040E325AC7/ zeromq staticLibrary Error executing command run: dmd failed with exit code 1. The 0mq library exists and is linkable: ls /usr/lib/libzmq.* /usr/lib/libzmq.a /usr/lib/libzmq.so /usr/lib/libzmq.so.4 /usr/lib/libzmq.so.4.0.0 The error looks to be related to the library code, but I figure I'm just doing something stupid in build -- as there seem to be no other topics about this. Any help would be appreciated, sorry for any glaring oversights!
Embedding D Shared Library in WSGI Web Server
Hi, I've written a modest shared library in D that I'd like to call directly from a Python web server (Linux/OS X, Apache, WSGI, Pyramid). I can call it reliably from within Python unit tests but on a running server, the library causes a SIGSEGV as soon as I try anything as complicated as a writeln call. Under Linux, I've tried using Pyd, CFFI and ctypes to bind the native .so to Python with the same result. I've tried calling Runtime.initialize();from an init function on server startup and from the D function being called as part of the web request. I've even tried writing a shim in C to make these calls and call through to the D. I've compiled with DMD and GDC with a variety of switches. Currently my build command looks like: dmd ./d/*.d -version=library -release -shared -O -inline -defaultlib=libphobos2.so -fPIC -oflibgusteau.so The extern(C) function takes a string and returns a string. I've tried holding onto a pointer to the returned string and passing in an adequately sized byte array from Python. Smaller strings seem to cause a crash with lower probability but typically the input and output strings are a few KB in size and consistently crash. I taken measures to ensure that they are correctly encoded and null terminated. I've also tried disabling the GC, calling terminate on function exit (after copying result into received buffer) and various other measures in an attempt to get something working. I'm guessing that the web server spawns/relies on a thread or process for each request and I've read that runtime initialization should be invoked from the main thread. Does this always mean the first thread that was created on process start up or does it just have to be consistent? If calling from a thread other than the one used to initialize, is that a problem? Are other threads created when GC is invoked which might last past the extern function being called and if so, can I prevent this by controlling collection explicitly? Thanks, John
Re: Creation of 0MQ D project
On Monday, 27 October 2014 at 23:56:11 UTC, Evan Lowry wrote: ../../.dub/packages/zeromq-master/deimos/zmq/zmq.d(96): Error: function deimos.zmq.zmq.zmq_strerror without 'this' cannot be const You found a bug in the binding. Line 96 of zmq.d [1]: const char* zmq_strerror(int errnum); Should be: const(char)* zmq_strerror(int errnum); I think until recently dmd would accept and ignore const on a free function, and that's how the bug got through. [1] https://github.com/D-Programming-Deimos/ZeroMQ/blob/79fb14b880c172e9aba9701366fd9bc15a8644b1/deimos/zmq/zmq.d#L96
Tagged enums why reserved words are not permitted ?
Hello ! I'm not sure if I'm missing something here but for a tagged enum it doesn't seem to make sense to forbid reserved keywords like: enum CrudOps {read, write, delete} The dmd compiler are complaining: -- cte.d(4): Error: basic type expected, not delete cte.d(4): Error: no identifier for declarator int cte.d(4): Error: type only allowed if anonymous enum and no enum type cte.d(4): Error: if type, there must be an initializer cte.d(4): Error: found 'delete' when expecting ',' -- It doesn't make sense to me because this kind of enum will not polute the global space and always need to beused with the tag: CrudOps.delete I'm missing something here ? Cheers !
Re: Tagged enums why reserved words are not permitted ?
On Tuesday, October 28, 2014 00:31:43 Domingo via Digitalmars-d-learn wrote: > Hello ! > > I'm not sure if I'm missing something here but for a tagged enum > it doesn't seem to make sense to forbid reserved keywords like: > > enum CrudOps {read, write, delete} > > The dmd compiler are complaining: > -- > cte.d(4): Error: basic type expected, not delete > cte.d(4): Error: no identifier for declarator int > cte.d(4): Error: type only allowed if anonymous enum and no enum > type > cte.d(4): Error: if type, there must be an initializer > cte.d(4): Error: found 'delete' when expecting ',' > -- > > It doesn't make sense to me because this kind of enum will not > polute the global space and always need to beused with the tag: > CrudOps.delete > > I'm missing something here ? keywords are always keywords wherever they are in the code and are only legal where those keywords are legal. I can see how you could come to the conclusion that the compiler shouldn't think that you're trying to use a keyword as an enum member, but the compiler does not take that into account. And I've never seen a language where it did (though one may exist out there somewhere). The thing that's been done in Phobos in this type of situation is to put an underscore on the end of the keyword, so you'd get enum CrudOps { read, write, delete_ } and while that may not be what you want, it's pretty much the best that you can do. - Jonathan M Davis
Re: Tagged enums why reserved words are not permitted ?
On Tue, 28 Oct 2014 00:31:43 + Domingo via Digitalmars-d-learn wrote: > Hello ! > > I'm not sure if I'm missing something here but for a tagged enum > it doesn't seem to make sense to forbid reserved keywords like: > > enum CrudOps {read, write, delete} > > The dmd compiler are complaining: > -- > cte.d(4): Error: basic type expected, not delete > cte.d(4): Error: no identifier for declarator int > cte.d(4): Error: type only allowed if anonymous enum and no enum > type > cte.d(4): Error: if type, there must be an initializer > cte.d(4): Error: found 'delete' when expecting ',' > -- > > It doesn't make sense to me because this kind of enum will not > polute the global space and always need to beused with the tag: > CrudOps.delete > > I'm missing something here ? > > Cheers ! D has no "context keywords". i.e. no keyword can be used as identifier, even if it's possible to distinguish between use cases. you can't, for example, use "body" identifier for variable/field name (and this annoys me alot). i believe that this was done so source code analyzing tools can stop tracking context while parsing D source. i.e. tool is not required to do semantic analysis to determine if "body" is identifier or function body start. i don't think that this will change. and this is the reason, for example, for adding "@property", "@safe", "@trusted" and so on instead of "property", "safe", "trusted": D taken some valuable keywords already and nobody wants to lose even more names. ;-) signature.asc Description: PGP signature
Re: Tagged enums why reserved words are not permitted ?
On Tue, Oct 28, 2014 at 12:31:43AM +, Domingo via Digitalmars-d-learn wrote: > Hello ! > > I'm not sure if I'm missing something here but for a tagged enum it > doesn't seem to make sense to forbid reserved keywords like: > > enum CrudOps {read, write, delete} > > The dmd compiler are complaining: > -- > cte.d(4): Error: basic type expected, not delete > cte.d(4): Error: no identifier for declarator int > cte.d(4): Error: type only allowed if anonymous enum and no enum type > cte.d(4): Error: if type, there must be an initializer > cte.d(4): Error: found 'delete' when expecting ',' > -- > > It doesn't make sense to me because this kind of enum will not polute > the global space and always need to beused with the tag: > CrudOps.delete [...] Not true: CrudOps x; with (CrudOps) { x = read; ... // etc. } T -- In a world without fences, who needs Windows and Gates? -- Christian Surchi
Re: Creation of 0MQ D project
On Tuesday, 28 October 2014 at 00:21:20 UTC, anonymous wrote: Line 96 of zmq.d [1]: const char* zmq_strerror(int errnum); Should be: const(char)* zmq_strerror(int errnum); Yep, this seemed to do the trick cleanly. S'all compiling and the examples provided in the repo run. Can submit a pull request, if no-one else has one lined up. Much thanks!
Re: Creation of 0MQ D project
On 10/27/2014 09:02 PM, Evan Lowry wrote: > On Tuesday, 28 October 2014 at 00:21:20 UTC, anonymous wrote: >> Line 96 of zmq.d [1]: const char* zmq_strerror(int errnum); >> Should be: const(char)* zmq_strerror(int errnum); > > Yep, this seemed to do the trick cleanly. S'all compiling and the examples > provided in the repo run. Can submit a pull request, if no-one else has one > lined up. > > Much thanks! From the original commit that caused that, it seems that const(char)* was meant for that statement..? -- Matt Soucy http://msoucy.me/ signature.asc Description: OpenPGP digital signature
Cheap Kitchens in North London
Cheap Kitchens in North London. Thirty Ex Display Kitchens To Clear. w w w. e x d i s p l a y k i t c h e n s 1. c o. u k £ 595 Each with appliances.Tel 01616-694785