Thread overview
Strange multithreading error
Oct 29, 2021
Ruby The Roobster
Oct 30, 2021
Ruby The Roobster
October 29, 2021

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:

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:

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:

/*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:

/*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.

October 29, 2021

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

void opAssign(shared Skeleton rhs) shared

-Steve

October 30, 2021

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

void opAssign(shared Skeleton rhs) shared

-Steve

Thank you, this solved my problem.