Jump to page: 1 2
Thread overview
C++ vs D: Default param values and struct to array casting
Sep 06, 2019
Andrew Edwards
Sep 06, 2019
Andrew Edwards
Sep 06, 2019
Paul Backus
Sep 06, 2019
Johan Engelen
Sep 06, 2019
Andrew Edwards
Sep 06, 2019
a11e99z
Sep 06, 2019
Andrew Edwards
Sep 06, 2019
Paul Backus
Sep 06, 2019
Ali Çehreli
Sep 07, 2019
Andrew Edwards
Sep 07, 2019
Maximillian
Sep 07, 2019
Maximillian
Sep 07, 2019
Jacob Carlborg
September 06, 2019
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
September 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.
September 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

September 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:
>> 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
September 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

September 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;
        }
    }
}

September 06, 2019
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

September 06, 2019
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
September 06, 2019
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
September 07, 2019
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


« First   ‹ Prev
1 2