digitalmars.D.learn - C++ vs D: Default param values and struct to array casting
- Andrew Edwards (44/44) Sep 06 2019 C++ allows the for following:
- Andrew Edwards (31/45) Sep 06 2019 This is my thought on how to accomplish op overloading:
- Paul Backus (12/42) Sep 06 2019 You can use `static foreach` to eliminate the repetition in the
- Johan Engelen (7/19) Sep 06 2019 "(&a)[i]" is undefined behavior in C++. You cannot index into
- Andrew Edwards (6/27) Sep 06 2019 Thanks for the clarification and link. This is a example taken
- a11e99z (3/17) Sep 06 2019 https://dlang.org/spec/simd.html
- Andrew Edwards (8/10) Sep 06 2019 This didn't work two well because I wont be able to access the
- Paul Backus (4/9) Sep 06 2019 You can also use std.typecons.Tuple for this, since it provides
- =?UTF-8?Q?Ali_=c3=87ehreli?= (86/87) Sep 06 2019 Here is my attempt:
- Andrew Edwards (6/9) Sep 07 2019 Ali, this is awesome. It solves all 4 problems in on shot. I
- Maximillian (3/23) Sep 07 2019 Please could you tell what "goto case;" do here?
- Maximillian (8/9) Sep 07 2019 I see now "fall-through" [1].
- Jacob Carlborg (38/81) Sep 07 2019 In D you can use the built-in property `.tupleof` [1], available for
C++ allows the for following: struct Demo { float a, b, c, d; Demo() { a = b = c = d = 0.0f; } Demo(float _a, float _b, float _c, float _d) { a = _a; b = _b; c = _c; d = _d; } float operator[] (size_t i) const { return (&a)[i]; } //[3] float& operator[] (size_t i) { return (&a)[i]; } //[4] } void fun(float col[3]) { // do fun stuff } void moreFun(const Demo& d = Demo(0.1f, 0.3f, 7.5f, 1.5f)) // [1] { // you guessed it... more fun stuff } int main(int argv, const char** argc) { Demo inst = Demo(0.1f, 0.3f, 7.5f, 1.5f); fun((float*)&inst); // [2] moreFun(); return 0; } I'm seeking some pointers on how to define these in D so that I can instantiate them in D while still linking to the associated C++ library or object file for the implementation. The main points of contention are at [1] and [2] because I have no idea how to accomplish these. I assume that I can accomplish [3] and [4] with opIndex() and opIndexAssign(), however I'm not understanding the slicing of the memory address at the first member variable. I know that by indexing the memory address at the member variable we are able treat the struct as an array but i do not understand how to implement it in D. I read the documentation at https://dlang.org/spec/cpp_interface.html and https://dlang.org/articles/cpptod.html but it was not clear "to me" how to overcome these issues. Andrew
Sep 06 2019
On Friday, 6 September 2019 at 09:14:31 UTC, Andrew Edwards wrote:C++ allows the for following: struct Demo { float a, b, c, d; Demo() { a = b = c = d = 0.0f; } Demo(float _a, float _b, float _c, float _d) { a = _a; b = _b; c = _c; d = _d; } float operator[] (size_t i) const { return (&a)[i]; } //[3] float& operator[] (size_t i) { return (&a)[i]; } //[4] }This is my thought on how to accomplish op overloading: struct Test { float a, b, c, d; float opIndex(size_t i) in(i >= 0 && i <= 3) { final switch(i) { case 0: return a; case 1: return b; case 2: return c; case 3: return b; } } void opIndexAssign(float val, size_t i) in(i >= 0 && i <= 3) { final switch(i) { case 0: a = val; break; case 1: b = val; break; case 2: c = val; break; case 3: d = val; break; } } } Please advise if I've gone off the beaten path. It seems overkill for the two lines of code C++ requires so any suggestion is greatly appreciated.
Sep 06 2019
On Friday, 6 September 2019 at 09:28:57 UTC, Andrew Edwards wrote:This is my thought on how to accomplish op overloading: struct Test { float a, b, c, d; float opIndex(size_t i) in(i >= 0 && i <= 3) { final switch(i) { case 0: return a; case 1: return b; case 2: return c; case 3: return b; } } void opIndexAssign(float val, size_t i) in(i >= 0 && i <= 3) { final switch(i) { case 0: a = val; break; case 1: b = val; break; case 2: c = val; break; case 3: d = val; break; } } } Please advise if I've gone off the beaten path. It seems overkill for the two lines of code C++ requires so any suggestion is greatly appreciated.You can use `static foreach` to eliminate the repetition in the switch statements. For example: float opIndex(size_t i) in (i < typeof(this).tupleof.length) { final switch (i) { static foreach (fid, field; typeof(this).tupleof) { case fid: return field; } } }
Sep 06 2019
On Friday, 6 September 2019 at 09:14:31 UTC, Andrew Edwards wrote:C++ allows the for following: struct Demo { float a, b, c, d; Demo() { a = b = c = d = 0.0f; } Demo(float _a, float _b, float _c, float _d) { a = _a; b = _b; c = _c; d = _d; } float operator[] (size_t i) const { return (&a)[i]; } //[3]"(&a)[i]" is undefined behavior in C++. You cannot index into struct members like that. Of course it may work in certain cases, but UB is UB. Don't do it! I found a more detailed explanation for you: https://stackoverflow.com/questions/40590216/is-it-legal-to-index-into-a-struct -Johan
Sep 06 2019
On Friday, 6 September 2019 at 09:49:33 UTC, Johan Engelen wrote:On Friday, 6 September 2019 at 09:14:31 UTC, Andrew Edwards wrote:Thanks for the clarification and link. This is a example taken from a popular library I’m trying to port to D. I’m not trying to do it in C++ myself, just to understand how to interface to the code so that I can get a reference example compiled in D. AndrewC++ allows the for following: struct Demo { float a, b, c, d; Demo() { a = b = c = d = 0.0f; } Demo(float _a, float _b, float _c, float _d) { a = _a; b = _b; c = _c; d = _d; } float operator[] (size_t i) const { return (&a)[i]; } //[3]"(&a)[i]" is undefined behavior in C++. You cannot index into struct members like that. Of course it may work in certain cases, but UB is UB. Don't do it! I found a more detailed explanation for you: https://stackoverflow.com/questions/40590216/is-it-legal-to-index-into-a-struct -Johan
Sep 06 2019
On Friday, 6 September 2019 at 09:14:31 UTC, Andrew Edwards wrote:C++ allows the for following: struct Demo { float a, b, c, d; Demo() { a = b = c = d = 0.0f; } Demo(float _a, float _b, float _c, float _d) { a = _a; b = _b; c = _c; d = _d; } float operator[] (size_t i) const { return (&a)[i]; } //[3] float& operator[] (size_t i) { return (&a)[i]; } //[4] }https://dlang.org/spec/simd.html also probably u can do https://run.dlang.io/is/WMQE93
Sep 06 2019
On Friday, 6 September 2019 at 11:35:59 UTC, a11e99z wrote:https://dlang.org/spec/simd.htmlThis didn't work two well because I wont be able to access the members by name as C++ library expects. Will consider during refactoring.also probably u can do https://run.dlang.io/is/WMQE93Ended up using this since it provides for named access and solves the overloading requirement. Thanks, Andrew
Sep 06 2019
On Friday, 6 September 2019 at 18:39:47 UTC, Andrew Edwards wrote:You can also use std.typecons.Tuple for this, since it provides both named and indexed access: https://run.dlang.io/is/bB21sXalso probably u can do https://run.dlang.io/is/WMQE93Ended up using this since it provides for named access and solves the overloading requirement. Thanks, Andrew
Sep 06 2019
On 09/06/2019 02:14 AM, Andrew Edwards wrote:I'm seeking some pointers on how to define these in DHere is my attempt: struct Demo { // NOTE: The types and number of elements can be templatized and mixed-in like // mixin (makeMembers!T(N)); float a = 0; float b = 0; float c = 0; float d = 0; // Then these checks would be unnecessary static assert(a.sizeof == b.sizeof); static assert(b.sizeof == c.sizeof); static assert(c.sizeof == d.sizeof); // And this would be equal to N enum length = this.sizeof / a.sizeof; this(const(float)[] init...) { asSlice[0..init.length] = init; } auto asSlice() inout { // WARNING: As UNDEFINED BEHAVIOR as in the C++ code! return (&a)[0..length]; } ref asArray(size_t n = length)() inout { // WARNING: As UNDEFINED BEHAVIOR as in the C++ code! return *cast(float[n]*)(&a); } ref inout(float) opIndex(size_t i) inout { // [3] [4] return asSlice[i]; } } unittest { import std.algorithm; static assert (Demo.length == 4); void enforceMemberWiseEquality(Demo d, const(float)[] values) { switch (values.length) { case 4: assert(d.d == values[3]); goto case; case 3: assert(d.c == values[2]); goto case; case 2: assert(d.b == values[1]); goto case; case 1: assert(d.a == values[0]); goto default; default: break; } } const init = [1.0f, 2.0f, 3.0f, 4.0f]; // Test constructing with different number of expressions foreach (length; 0..init.length) { auto testInit = init[0..length]; const d = Demo(testInit); enforceMemberWiseEquality(d, testInit); assert(d.asArray[0..length].equal(testInit)); assert(d.asSlice[0..length].equal(testInit));} } extern (C++) void fun(float* col) { // do fun stuff } extern (C++) void moreFun(ref const(Demo) d) { // [1] // you guessed it... more fun stuff } // This is needed because operations inside asSlice are not allowed // at compile time. (That's why I could not make this variable 'static'. immutable(Demo) moreFun_default; shared static this() { moreFun_default = Demo(0.1f, 0.3f, 7.5f, 1.5f); } extern (C++) void moreFun() { // [1] // We need this function because D does not allow rvalue references. // Here, we're passing an lvalue. moreFun(moreFun_default); } int main() { Demo inst = Demo(0.1f, 0.3f, 7.5f, 1.5f); fun(inst.asSlice.ptr); // [2] moreFun(); return 0; } Ali
Sep 06 2019
On Friday, 6 September 2019 at 18:31:29 UTC, Ali Çehreli wrote:On 09/06/2019 02:14 AM, Andrew Edwards wrote:Ali, this is awesome. It solves all 4 problems in on shot. I definitely don't intend on using the undefined aspect in the future but it is welcomed at the moment. Much appreciated. AndrewI'm seeking some pointers on how to define these in DHere is my attempt:
Sep 07 2019
On Friday, 6 September 2019 at 18:31:29 UTC, Ali Çehreli wrote:... void enforceMemberWiseEquality(Demo d, const(float)[] values) { switch (values.length) { case 4: assert(d.d == values[3]); goto case; case 3: assert(d.c == values[2]); goto case; case 2: assert(d.b == values[1]); goto case; case 1: assert(d.a == values[0]); goto default; default: break; } }Please could you tell what "goto case;" do here? Max
Sep 07 2019
On Saturday, 7 September 2019 at 11:22:09 UTC, Maximillian wrote:Please could you tell what "goto case;" do here?I see now "fall-through" [1]. To be honest I like this feature in C and I was sad it didn't work in D, at least now I know how to solve it. :) Max. [1] https://dlang.org/spec/statement.html#GotoStatement "This is to set apart with C's error-prone implicit fall-through behavior. goto case; could be used for explicit fall-through:"
Sep 07 2019
On 2019-09-06 11:14, Andrew Edwards wrote:C++ allows the for following: struct Demo { float a, b, c, d; Demo() { a = b = c = d = 0.0f; } Demo(float _a, float _b, float _c, float _d) { a = _a; b = _b; c = _c; d = _d; } float operator[] (size_t i) const { return (&a)[i]; } //[3] float& operator[] (size_t i) { return (&a)[i]; } //[4] } void fun(float col[3]) { // do fun stuff } void moreFun(const Demo& d = Demo(0.1f, 0.3f, 7.5f, 1.5f)) // [1] { // you guessed it... more fun stuff } int main(int argv, const char** argc) { Demo inst = Demo(0.1f, 0.3f, 7.5f, 1.5f); fun((float*)&inst); // [2] moreFun(); return 0; } I'm seeking some pointers on how to define these in D so that I can instantiate them in D while still linking to the associated C++ library or object file for the implementation. The main points of contention are at [1] and [2] because I have no idea how to accomplish these. I assume that I can accomplish [3] and [4] with opIndex() and opIndexAssign(), however I'm not understanding the slicing of the memory address at the first member variable. I know that by indexing the memory address at the member variable we are able treat the struct as an array but i do not understand how to implement it in D.In D you can use the built-in property `.tupleof` [1], available for structs and classes. That will give you a tuple, not an array. To get an array you can just wrap it in square brackets: Demo inst = Demo(0.1f, 0.3f, 7.5f, 1.5f); auto tuple = inst.tupleof; // "tuple" is a tuple auto arr = [tuple]; static assert(is(typeof(arr) == float[])); Converting the tuple to an array like that only works then all of the elements of the tuple has the same type. Here's an example: struct Test { float a, b, c, d; float opIndex(size_t i) in(i >= 0 && i <= 3) { return [this.tupleof][i]; } float get(size_t i)() { return this.tupleof[i]; } } void main() { auto t = Test(1, 2, 3, 4); assert(t[3] == 4); assert(t.get!3 == 4); } If you can provide the index at compile time you don't need to create an array of the the tuple. The "get" method shows that technique. Unfortunately "opIndex" cannot take the index as a template parameter. Here's the runnable version [2]. [1] https://dlang.org/spec/struct.html#struct_instance_properties [2] https://run.dlang.io/is/7QYljZ -- /Jacob Carlborg
Sep 07 2019