www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - C++ vs D: Default param values and struct to array casting

reply Andrew Edwards <edwards.ac gmail.com> writes:
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
next sibling parent reply Andrew Edwards <edwards.ac gmail.com> writes:
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
parent Paul Backus <snarwin gmail.com> writes:
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
prev sibling next sibling parent reply Johan Engelen <j j.nl> writes:
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
parent Andrew Edwards <edwards.ac gmail.com> writes:
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:
 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
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. Andrew
Sep 06 2019
prev sibling next sibling parent reply a11e99z <black80 bk.ru> writes:
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
parent reply Andrew Edwards <edwards.ac gmail.com> writes:
On Friday, 6 September 2019 at 11:35:59 UTC, a11e99z wrote:
 https://dlang.org/spec/simd.html
This 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/WMQE93
Ended up using this since it provides for named access and solves the overloading requirement. Thanks, Andrew
Sep 06 2019
parent Paul Backus <snarwin gmail.com> writes:
On Friday, 6 September 2019 at 18:39:47 UTC, Andrew Edwards wrote:
 also probably u can do https://run.dlang.io/is/WMQE93
Ended up using this since it provides for named access and solves the overloading requirement. Thanks, Andrew
You can also use std.typecons.Tuple for this, since it provides both named and indexed access: https://run.dlang.io/is/bB21sX
Sep 06 2019
prev sibling next sibling parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 09/06/2019 02:14 AM, Andrew Edwards wrote:

 I'm seeking some pointers on how to define these in D
Here 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
next sibling parent Andrew Edwards <edwards.ac gmail.com> writes:
On Friday, 6 September 2019 at 18:31:29 UTC, Ali Çehreli wrote:
 On 09/06/2019 02:14 AM, Andrew Edwards wrote:

 I'm seeking some pointers on how to define these in D
Here is my attempt:
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. Andrew
Sep 07 2019
prev sibling parent reply Maximillian <maxmill gmail.com> writes:
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
parent Maximillian <maxmill gmail.com> writes:
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
prev sibling parent Jacob Carlborg <doob me.com> writes:
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