www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Strange multithreading error

reply Ruby The Roobster <michaeleverestc79 gmail.com> writes:
I am currently writing a test program for a collision function, 
that involves multithreading so I can simultaneously check for 
collisions and move a skeleton at the same time.  Because of 
this, I had to use ```shared``` objects.  The specific objects I 
was using were declared in a file called "skeleton.d."  In a 
function I wrote for moving the skeletons, it uses operator 
overloading, which produces the following output:

```d
physics.d(85): Error: none of the `opOpAssign` overloads of 
`Point` are callable for `j` of type `shared(Point)`
physics.d(87): Error: none of the `opOpAssign` overloads of 
`Point` are callable for `k.start` of type `shared(Point)`
physics.d(88): Error: none of the `opOpAssign` overloads of 
`Point` are callable for `k.stop` of type `shared(Point)`
physics.d(90): Error: none of the `opOpAssign` overloads of 
`Point` are callable for `i.center` of type `shared(Point)`
physics.d(92): Error: none of the `opOpAssign` overloads of 
`Point` are callable for `tomove.center` of type `shared(Point)`
physics.d(112): Error: none of the `opOpAssign` overloads of 
`Point` are callable for `k` of type `shared(Point)`
physics.d(114): Error: none of the `opOpAssign` overloads of 
`Point` are callable for `j.start` of type `shared(Point)`
physics.d(115): Error: none of the `opOpAssign` overloads of 
`Point` are callable for `j.stop` of type `shared(Point)`
physics.d(117): Error: none of the `opOpAssign` overloads of 
`Point` are callable for `i.center` of type `shared(Point)`
physics.d(119): Error: none of the `opOpAssign` overloads of 
`Point` are callable for `ori.center` of type `shared(Point)`
physics.d(120): Error: none of the overloads of `opAssign` are 
callable using a `shared` object
skeleton.d(81):        Candidates are: 
`skeleton.Skeleton.opAssign(Skeleton rhs)`
skeleton.d(88):                        
`skeleton.Skeleton.opAssign(shared(Skeleton) rhs)`
physics.d(136): Error: mixin `physics.move.__mov__general__!"n"` 
error instantiating
```

This is clearly wrong, as type ```Point``` is not the same as 
type ```Skeleton```.

For reference, here are the files:

test.d:
```d
import physics;
void main()	{
	Skeleton cube;
	Face[] cubefaces;
	cubefaces.length = 1;
	cubefaces[0].lines ~= Line([Point(0,0.25,0), Point(0,0.5,0), 
Point(0,0.75,0)], Point(0,0,0), Point(0,1,0));
	cubefaces[0].lines ~= Line([Point(0.25,0,0), Point(0.5,0,0), 
Point(0.75,0,0)], Point(0,0,0), Point(1,0,0));
	cubefaces[0].lines ~= Line([Point(1,0.25,0), Point(1,0.5,0), 
Point(1,0.75, 0)], Point(1,0,0), Point(1,1,0));
	cubefaces[0].lines ~= Line([Point(0.25,1,0), Point(0.5,1,0), 
Point(0.75,1,0)], Point(0,1,0), Point(1,1,0));
	cubefaces[0].center = Point(0.5,0.5,0);
	cube.faces = cubefaces.dup;
	cube.center = Point(0.5,0.5,0);
	auto cube2 = cube;
	move(Point(99,0,0), 0, cast(shared(Skeleton))cube, 99);
	import std.stdio;
	import std.concurrency;
	spawn(&test1, cast(shared(Skeleton))cube2, 
cast(shared(Skeleton))cube);
	spawn(&move,Point(-99,0,0), 5, cast(shared(Skeleton))cube, 1);
	bool b = receiveOnly!bool;
	writeln(b);
}
void test1(shared ref Skeleton cube2, shared ref Skeleton cube)	{
	import std.concurrency;
	if(detectCollision(cast(shared(Skeleton[]))[cube2], 
cast(shared(Skeleton))cube, real.infinity))
		send(ownerTid(), true);
}
```

skeleton.d:

```d
/*skeleton.d by Ruby The Roobster*/
/*Version 1.0 Release*/
/*Module for representing skeletons in the D Programming Language 
2.0*/
/*This program is free software: you can redistribute it and/or 
modify
it under the terms of the GNU General Public License as published 
by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see 
<http://www.gnu.org/licenses/>.*/
/** Copyright: 2021, Ruby The Roobster*/
/**Author: Ruby The Roobster, michaeleverestc79 gmail.com*/
/**Date: October 1, 2021*/
/** License:  GPL-3.0*/
module skeleton;
/**Struct for representing a point.*/
public struct Point	{ //Point structure...
	///Point.x is the 'x' coordinate of the point.
	real x;
	///Point.y is the 'y' coordinate of the point.
	real y;
	///Point.z is the 'z' coordinate of the point.
	real z;
	this(real x, real y, real z)	{
		this.x = x;
		this.y = y;
		this.z = z;
	}
	void opAssign(Point rhs)	{
		this.x = rhs.x;
		this.y = rhs.y;
		this.z = rhs.z;
	}
	void opAssign(shared Point rhs)	{
		this.x = rhs.x;
		this.y = rhs.y;
		this.z = rhs.z;
	}

	void opOpAssign(string op)(Point rhs)	{
		mixin("this.x " ~ op ~ "= rhs.x;");
		mixin("this.y " ~ op ~ "= rhs.y;");
		mixin("this.z " ~ op ~ "= rhs.z;");
	}
	void opOpAssign(string op)(shared(Point) rhs)	{
			mixin("this.x " ~ op ~ "= rhs.x;");
			mixin("this.y " ~ op ~ "= rhs.y;");
			mixin("this.z " ~ op ~ "= rhs.z;");
	}
}
/**Struct for representing a face of a skeleton that is made out 
of lines.*/
public struct Face	{ //Face(of a 3D shape) structure...
	///Face.lines is an array of all the lines that connect to form 
the face.
	Line[] lines;
	///Face.center is the center point of the face.
	Point center;
	void opAssign(Face rhs)	{
		this.lines.length = rhs.lines.length;
		foreach(i;0 .. this.lines.length)	{
			this.lines[i] = rhs.lines[i];
		}
	}
	void opAssign(shared Face rhs)	{
		this.lines.length = rhs.lines.length;
		foreach(i;0 .. this.lines.length)	{
			this.lines[i] = rhs.lines[i];
		}
	}
}
/**Struct for representing a 3D skeleton.*/
public struct Skeleton	{ //Skeleton of a 3D structure...
	///Skeleton.faces is an array of the faces that make up the 
Skeleton.
	Face[] faces;
	///Skeleton.center is the center point of the skeleton.
	Point center;
	void opAssign(Skeleton rhs)	{
		this.faces.length = rhs.faces.length;
		foreach(i;0 .. this.faces.length)	{
			this.faces[i] = rhs.faces[i];
		}
		this.center = rhs.center;
	}
	void opAssign(shared Skeleton rhs)	{
		this.faces.length = rhs.faces.length;
		foreach(i;0 .. this.faces.length)	{
			this.faces[i] = rhs.faces[i];
		}
		this.center = rhs.center;
	}
}

/**Struct for representing a line composed of at least a starting 
point and an end point.
   *Notes:
   *This struct doesn't check to make sure that the line made is 
an actual line and assumes the user knows what they are doing.
*/
public struct Line	{ //Line struct...
	///Line.mid_points is an array containing all of the points that 
are neither start nor end points.
	Point[] mid_points;
	///Line.start is the start point of the line.
	Point start;
	///Line.end is the end point of the line.
	Point stop;
	void opAssign(Line rhs)	{
		this.start = rhs.start;
		this.stop = rhs.stop;
		this.mid_points.length = rhs.mid_points.length;
		foreach(i;0 .. this.mid_points.length)	{
			this.mid_points[i] = rhs.mid_points[i];
		}
	}
	void opAssign(shared Line rhs)	{
		this.start = rhs.start;
		this.stop = rhs.stop;
		this.mid_points.length = rhs.mid_points.length;
		foreach(i;0 .. this.mid_points.length)	{
			this.mid_points[i] = rhs.mid_points[i];
		}
	}
}
```

and finally, physiscs.d:

```d
/*physics.d by Ruby The Roobster*/
/*Version 0.35 testing*/
/*Module for basic physics in the D Programming Language 2.0*/
/*This program is free software: you can redistribute it and/or 
modify
it under the terms of the GNU General Public License as published 
by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see 
<http://www.gnu.org/licenses/>.*/
/** Copyright: 2021, Ruby The Roobster*/
/**Author: Ruby The Roobster, michaeleverestc79 gmail.com*/
/**Date: October 27, 2021*/
/** License: GPL-3.0*/
module physics;
public import skeleton;

package mixin template __mov__general__(string func)
{
	void __mov__general__(real accdec = 0)
	{
		import core.thread;
		Point moveby;
		bool b = false;
		auto ori = tomove;
			debug import std.stdio : writeln;
		if(speed > 1 || speed < -1)
		{
			moveby.x = moveto.x / speed;
			moveby.y = moveto.y / speed;
			moveby.z = moveto.z / speed;
			b = true;
		}
		else
		{
			moveby.x = moveto.x * speed;
			moveby.y = moveto.y * speed;
			moveby.z = moveto.z * speed;
		}
		while(!((tomove.center.x > moveto.x && moveto.x > 0) ^ 
(tomove.center.x < moveto.x && moveto.x < 0)))
		{
			static if(func == "a")
			{
				speed += accdec;
				if(b)
				{
					moveby.x = moveto.x / speed;
					moveby.y = moveto.y / speed;
					moveby.z = moveto.z / speed;
				}
				else
				{
					moveby.x = moveto.x * speed;
					moveby.y = moveto.y * speed;
					moveby.z = moveto.z * speed;
				}
			}
			else static if(func == "d")
			{
				speed -= accdec;
				if(b)
				{
					moveby.x = moveto.x / speed;
					moveby.y = moveto.y / speed;
					moveby.z = moveto.z / speed;
				}
				else
				{
					moveby.x = moveto.x * speed;
					moveby.y = moveto.y * speed;
					moveby.z = moveto.z * speed;
				}
			}
			foreach(i;tomove.faces)
			{
				foreach(k;i.lines)
				{
					foreach(j;k.mid_points)
					{
						j += moveby;
					}
					k.start += moveby;
					k.stop += moveby;
				}
				i.center += moveby;
			}
			tomove.center += moveby;
			Thread.sleep(dur!"msecs"(tbf));
			static if(func == "a")
			{
				speed += accdec;
			}
			else static if(func == "d")
			{
				speed -= accdec;
			}
			else
			{
			}
		}
		foreach(i;ori.faces)
		{
			foreach(j;i.lines)	
			{
				foreach(k;j.mid_points)
				{
					k += moveto;
				}
				j.start += moveto;
				j.stop += moveto;
			}
			i.center += moveto;
		}
		ori.center += moveto;
		tomove = ori;
	}
}

/**
   * move moves all the points in a skeleton to a specified point 
with a specified time gap between moving the points.
   * Params:
   *	moveto =	A point specifying the total amount to move along 
each axis.
   * 	tbf =	The time in miliseconds between 'frames'(a frame is 
one section of moving points before waiting a bit).  This gives 
an illusion of continuous motion.
   * 	tomove =	The skeleton being moved.
   * 	speed =	The speed at which to move the points.
   * Returns:
   * none
*/
pragma(inline, true) public void move(Point moveto, uint tbf, ref 
shared Skeleton tomove, real speed)
{
	mixin __mov__general__!"n";
	__mov__general__();
}

/**
   * accMove moves all the points in a skeleton to a specified 
point with a specified time gap between movements all while 
accelerating the speed.
   * Params:
   *	moveto = 	A point specifying the total amount to move along 
each axis.
   *	tbf = 	The time in miliseconds between 'frames'(a frame is 
one section of moving points before waiting a bit).  This gives 
an illusion of continuous motion.
   *	tomove = 	The skeleton being moved.
   *	speed  = 	The original speed at which the skeleton moves.
   *	accdec = 	The amount to increment the speed by each frame.
*/
pragma(inline, true) public void accMove(Point moveto, uint tbf, 
shared ref Skeleton tomove, real speed, real accdec = 0)
{
	mixin __mov__general__!"a";
	__mov__general__(accdec);
}
/**
   * decMove moves all the points in a skeleton to a specified 
point with a specified time gap between movements all while 
deaccelerating the speed.
   * Params:
   *	moveto = 	A point specifying the total amount to move along 
each axis.
   *	tbf = 	The time in miliseconds between 'frames'(a frame is 
one section of moving points before waiting a bit).  This gives 
an illusion of continuous motion.
   *	tomove = 	The skeleton being moved.
   *	speed  = 	The original speed at which the skeleton moves.
   *	accdec = 	The amount to decrement the speed by each frame.
*/
pragma(inline) public void decMove(Point moveto, uint tbf, shared 
ref Skeleton tomove, real speed, real accdec = 0)
{
	mixin __mov__general__!"d";
	__mov__general__(accdec);
}

public bool detectCollision(shared Skeleton[] towatch, shared 
Skeleton skele, real time = 0)
	in	{
		auto a = cast(ulong)time;
		assert(a == time || time == real.infinity,"Parameter time must 
always be a whole number or infinity!");
	}
	do	{
		mixin find!(["x", "y", "z"]);
		import std.datetime.stopwatch;
		auto sw = StopWatch(AutoStart.no);
		sw.start();
		scope(exit) sw.stop();
		while(sw.peek.total!"msecs" <= time || time == real.inf)
		{
			foreach(i;towatch)
			{
				foreach(j;i.faces)
				{
					foreach(k;j.lines)
					{
						foreach(l;k.mid_points)
						{
							foreach(m;skele.faces)
							{
								foreach(n;m.lines)
								{
									for(uint o; o < n.mid_points.length+1; o++)
									{
										if(switcho(l))
											return true;
										if(switcho(k.start))
											return true;
										if(switcho(k.stop))
											return true;
									}
								}
							}
						}
					}
				}
			}
		}
		return false;
	}

package mixin template find(string[] tofind)
{
	static foreach(i; tofind)	{
		mixin("real high" ~ i ~ ";");
		mixin("real low" ~ i ~ ";");
	}
	void find(Point[2] tof)
	{
		static foreach(i; tofind)
		{
			mixin("high" ~ i ~ " = tof[0]." ~ i ~ " >= tof[1]." ~ i ~ " ? 
tof[0]." ~ i ~ " : tof[1]." ~ i ~ ";");
			mixin("low" ~ i ~ " = tof[0]." ~ i ~ " <= tof[1]." ~ i ~ " ? 
tof[0]." ~ i ~ " : tof[1]." ~ i ~ ";");
		}
	}
}

package pragma(inline, true) bool switcho(Point toswitch)
{
	switch(o)
	{
			default:
				if(o == n.mid_points.length)
				{
					find([n.mid_points[o-1], n.stop]);
				}
				else
				{
					find([n.mid_points[o-1], n.mid_points[o]]);
				}
				if(toswitch.x <= highx && toswitch.x >= lowx && toswitch.y <= 
highy && toswitch.y >= lowy && toswitch.z <= highz && toswitch.z 
= lowz)
{ return true; } break; case 0: find([n.mid_points.start, n.mid_points[o]]); if(toswitch.x <= highx && toswitch.x >= lowx && toswitch.y <= highy && toswitch.y >= lowy && toswitch.z <= highz && toswitch.z
= lowz)
{ return true; } } } ``` Can anyone explain why it can't find the correct function? Thanks in advance.
Oct 29 2021
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On Friday, 29 October 2021 at 22:02:53 UTC, Ruby The Roobster 
wrote:
 I am currently writing a test program for a collision function, 
 that involves multithreading so I can simultaneously check for 
 collisions and move a skeleton at the same time.  Because of 
 this, I had to use ```shared``` objects.  The specific objects 
 I was using were declared in a file called "skeleton.d."  In a 
 function I wrote for moving the skeletons, it uses operator 
 overloading, which produces the following output:

 [...]
In order for a member function to be called on a shared object, the function has to be marked shared. Typically done like ```d void opAssign(shared Skeleton rhs) shared ``` -Steve
Oct 29 2021
parent Ruby The Roobster <michaeleverestc79 gmail.com> writes:
On Friday, 29 October 2021 at 23:32:38 UTC, Steven Schveighoffer 
wrote:
 On Friday, 29 October 2021 at 22:02:53 UTC, Ruby The Roobster 
 wrote:
 I am currently writing a test program for a collision 
 function, that involves multithreading so I can simultaneously 
 check for collisions and move a skeleton at the same time.  
 Because of this, I had to use ```shared``` objects.  The 
 specific objects I was using were declared in a file called 
 "skeleton.d."  In a function I wrote for moving the skeletons, 
 it uses operator overloading, which produces the following 
 output:

 [...]
In order for a member function to be called on a shared object, the function has to be marked shared. Typically done like ```d void opAssign(shared Skeleton rhs) shared ``` -Steve
Thank you, this solved my problem.
Oct 29 2021