Thread overview
"No stack address"
Apr 16, 2012
Somedude
Apr 16, 2012
Andrej Mitrovic
Apr 16, 2012
Somedude
Apr 16, 2012
Andrej Mitrovic
Apr 17, 2012
Somedude
Apr 17, 2012
Somedude
Apr 17, 2012
Andrej Mitrovic
April 16, 2012
I'm trying to compile a D source on win32 with DMD 2.059, and I get this:

PS E:\DigitalMars\dmd2\samples> rdmd xinoksort.d
OPTLINK (R) for Win32  Release 8.00.12
Copyright (C) Digital Mars 1989-2010  All rights reserved.
http://www.digitalmars.com/ctg/optlink.html
OPTLINK : Warning 23: No Stack
OPTLINK : Warning 134: No Start Address
PS E:\DigitalMars\dmd2\samples>

Clearly, the link phase didn't complete correctly:

-a---        16/04/2012     08:55      12600 xinoksort.d -a---        16/04/2012     21:12         56 xinoksort.exe -a---        16/04/2012     21:12        889 xinoksort.obj

Phobos wasn't linked. What happened ?
Does it link dynamically now ? How do I set the libpath (I'm using the
Powershell) ?
April 16, 2012
On 4/16/12, Somedude <lovelydear@mailmetrash.com> wrote:
> OPTLINK : Warning 134: No Start Address

This means you're missing a void main or int main function. You can pass  --main to rdmd to add it automatically (useful when e.g. unittesting).
April 16, 2012
Le 16/04/2012 21:51, Andrej Mitrovic a écrit :
> On 4/16/12, Somedude <lovelydear@mailmetrash.com> wrote:
>> OPTLINK : Warning 134: No Start Address
> 
> This means you're missing a void main or int main function. You can pass  --main to rdmd to add it automatically (useful when e.g. unittesting).

All right, now I have a bunch of questions.
Sorry, D newcomer here.

I hate to do this, but I'll simply dump the full code, it should save me a lot of questions.

/++
	Xinok Sort for the D Programming Language

	Written and tested for DMD 2.055 and Phobos

	Authors:  Xinok
	Date:     November 2011
	License:  Public Domain
	Web:      $(LINK http://www.sourceforge.net/projects/xinoksort)

	Todo:
	Parallelization - Write a separate function which runs xinok sort in
multiple threads

	Support for CTFE - Sorting arrays at compile time

	Unittest for ranges - Test implementation of xinok sort using range
primitives

	In / Out conditions and asserts
++/

module xinoksort;
import std.range;
import std.functional : binaryFun, not;
import std.algorithm : swap, swapRanges, copy, reverse, isSorted;
import std.traits : isCallable;
import core.exception : OutOfMemoryError;

/++
	This function will sort an array or range in-place,
	optionally using a custom predicate.

	Returns: Sorted input as SortedRange

	Throws: OutOfMemoryError if memory allocation fails

	Predicate:
	The predicate can be a string, function, or delegate.

	A string should represent the two elements to compare
	using the characters 'a' and 'b'.

	A function or delegate should take two arguments of the
	same type and return a bool.

	The default predicate for sorting is "a < b".

	Examples:
	---
	int[] arr = [20, 40, 60, 10, 30, 50];
	xinokSort(arr);

	// Sort in reverse order
	xinokSort!("a > b")(arr);

	// Use a function or delegate as predicate
	bool comp(int a, int b){ return a < b; }
	xinokSort!(comp)(arr);

	// Sort an array of strings, case insensitive
	string[] list;
	xinokSort!("icmp(a, b) < 0")(list);
	---
++/
SortedRange!(Range, pred) xinokSort(alias pred = "a < b", Range)(Range arr){
	typeof(Range[0])[] temp;

	if(!__ctfe){ // Allocate temporary memory
		size_t len = arr.length >= 1024*64 ? arr.length / 1024 : 64;
		while(temp is null){
			try temp.length = len;
			catch(OutOfMemoryError err){ // Reduce memory usage and try again
				len /= 2;
				if(len >= 8) continue;
				else throw err;
			}
		}
	}
	else temp.length = 1024;

	XinokSortImpl!(pred, Range).findRuns(arr, arr.length, temp);
	return assumeSorted!(pred, Range)(arr);
}

/++ Generate compare functions from predicate

    Example:
    ---
    mixin Compares!("a > b");
    ---
++/
template Compares(alias pred){
	static if(is(typeof(pred) : string)){
		alias binaryFun!(pred) less;
		alias binaryFun!(pred, "b", "a") greater;
		alias not!(binaryFun!(pred)) greaterEqual;
		alias not!(binaryFun!(pred, "b", "a")) lessEqual;
	}
	else static if(isCallable!pred){
		alias pred less;
		auto greater(T)(T a, T b){ return pred(b, a); }
		auto greaterEqual(T)(T a, T b){ return !pred(a, b); }
		auto lessEqual(T)(T a, T b){ return !pred(b, a); }
	}
	else static assert(false, "Invalid predicate");
}

/// Implementation of xinok sort for ranges
template XinokSortImpl(alias pred, Range){
	static assert(isRandomAccessRange!Range && isBidirectionalRange!Range
&& hasSlicing!Range, Range.toString ~ " is incompatible with xinok sort");

	alias typeof(Range[0]) T;
	mixin Compares!pred;

	/// Find runs in ascending or descending order and merge them
	size_t findRuns(Range arr, size_t t, T[] temp){
		enum min_run = 8;

		if(arr.length <= min_run){
			insertSort(arr);
			return arr.length;
		}

		if(t > arr.length) t = arr.length;

		// Find first run
		size_t lef = 2;
		if(lessEqual(arr[0], arr[1])){ // Ascending order
			while(lef < arr.length && lessEqual(arr[lef-1], arr[lef])) ++lef;
		}
		else{ // Descending order
			while(lef < arr.length && greater(arr[lef-1], arr[lef])) ++lef;

			// reverse(arr[0..lef]);
			auto tmp = arr[0..lef];
			while(tmp.length >= 2){
				swap(tmp.front, tmp.back);
				tmp = tmp[1..$-1];
			}
		}

		if(lef < min_run){ // Build run of min length
			insertSort(arr[0..min_run]);
			lef = min_run;
		}

		while(lef < t){ // Build run of length t
			size_t rig = findRuns(arr[lef..$], lef, temp) + lef;
			mergeRuns(arr[0..rig], arr[0..lef], arr[lef..rig], temp);
			lef = rig;
		}

		return lef;
	}

	/// Merge together two sorted runs
	void mergeRuns(Range arr, Range lef, Range rig, T[] temp){
		assert(arr.length == lef.length + rig.length);

		if(lef.length <= rig.length){
			if(lef.length == 0) return; // Nothing to do

			if(lef.length <= temp.length){ // Merge left to right
				copy(lef, temp[0..lef.length]);
				lef = temp[0..lef.length];

				foreach(ref v; arr){
					if(lessEqual(lef.front, rig.front)){
						v = lef.front;
						lef.popFront();
						if(lef.empty) break;
					}
					else{
						v = rig.front;
						rig.popFront();
						if(rig.empty) break;
					}
				}
				if(!lef.empty) copy(lef, arr[$-lef.length .. $]);
			}
			else{ // Range Swap
				if(lessEqual(lef.back, rig.front)) return; // Nothing to swap

				size_t c;
				{
					// Binary search
					size_t l = 0, u = lef.length - 1;
					immutable off = lef.length - 1;
					while(u - l > 1){
						c = (u - l) / 2 + l;
						if(greater(lef[c], rig[off-c])) u = c;
						else l = c;
					}
					c = greater(lef[l], rig[off-l]) ? l : u;

					// swapRanges(lef[c..$], rig[0..off-c+1]);
					foreach(i, ref v; lef[c..$]) swap(v, rig[i]);
				}

				mergeRuns(lef, lef[0..c], lef[c..$], temp);
				return mergeRuns(rig, rig[0..lef.length-c], rig[lef.length-c..$], temp);
			}
		}
		else{
			if(rig.length == 0) return; // Nothing to do

			if(rig.length <= temp.length){ // Merge right to left
				copy(rig, temp[0..rig.length]);
				rig = temp[0..rig.length];

				foreach_reverse(ref v; arr){
					if(greaterEqual(rig.back, lef.back)){
						v = rig.back;
						rig.popBack();
						if(rig.empty) break;
					}
					else{
						v = lef.back;
						lef.popBack();
						if(lef.empty) break;
					}
				}
				if(!rig.empty) copy(rig, arr[0..rig.length]);
			}
			else{ // Range Swap
				if(lessEqual(lef.back, rig.front)) return; // Nothing to swap

				size_t c;
				{
					// Binary search
					size_t l = 0, u = rig.length - 1;
					immutable off = lef.length - 1;
					while(u - l > 1){
						c = (u - l) / 2 + l;
						if(less(rig[c], lef[off-c])) l = c;
						else u = c;
					}
					c = less(rig[u], lef[off-u]) ? u : l;

					// swapRanges(lef[off-c..$], rig[0..c+1]);
					foreach(i, ref v; lef[off-c..$]) swap(v, rig[i]);
				}

				mergeRuns(rig, rig[0..c + 1], rig[c + 1..$], temp);
				return mergeRuns(lef, lef[0..$-1-c], lef[$-1-c..$], temp);
			}
		}
	}

	/// A simple insert sort used for obtaining min_run
	void insertSort(Range arr){
		T o; size_t j;
		for(size_t i = 1; i < arr.length; ++i) if(less(arr[i], arr[i-1])){
			j = i; o = arr[j];
			do{
				arr[j] = arr[j-1];
				--j;
			} while(j >= 1 && less(o, arr[j-1]));
			arr[j] = o;
		}
	}
}


/// Implementation of xinok sort for arrays
template XinokSortImpl(alias pred, T : T[]){
	alias T[] Range;
	mixin Compares!pred;

	/// Find runs in ascending or descending order and merge them
	size_t findRuns(Range arr, size_t t, T[] temp){
		enum min_run = 8;

		if(arr.length <= min_run){
			insertSort(arr);
			return arr.length;
		}

		if(t > arr.length) t = arr.length;

		// Find first run
		size_t lef = 2;
		if(lessEqual(arr[0], arr[1])){ // Ascending order
			auto p = arr.ptr + 2, e = arr.ptr + arr.length;
			while(p < e && lessEqual(p[-1], *p)) ++p;
			lef = p - arr.ptr;
		}
		else{ // Descending order
			auto p = arr.ptr + 2, e = arr.ptr + arr.length;
			while(p < e && greater(p[-1], *p)) ++p;
			lef = p - arr.ptr;

			// Reverse elements
			e = arr.ptr; --p;
			while(e < p) swap(*e++, *p--);
		}

		if(lef < min_run){ // Build run of min length
			insertSort(arr[0..min_run]);
			lef = min_run;
		}

		while(lef < t){ // Build run of length t
			size_t rig = findRuns(arr[lef..$], lef, temp) + lef;
			mergeRuns(arr[0..rig], arr[0..lef], arr[lef..rig], temp);
			lef = rig;
		}

		return lef;
	}

	/// Merge together two sorted runs
	void mergeRuns(Range arr, Range lef, Range rig, T[] temp){
		assert(arr.length == lef.length + rig.length);

		if(lef.length <= rig.length){
			if(lef.length == 0) return; // Nothing to do

			if(lef.length <= temp.length){ // Merge left to right
				// copy(lef, temp[0..lef.length]);
				temp[0..lef.length] = lef[];

				T* p = arr.ptr;
                T* l = temp.ptr, l_end = temp.ptr + lef.length;
                T* r = rig.ptr, r_end = rig.ptr + rig.length;
				while(true){
					if(lessEqual(*l, *r)){
						*p++ = *l++;
						if(l >= l_end) break;
					}
					else{
						*p++ = *r++;
						if(r >= r_end) break;
					}
				}
				while(l < l_end) *p++ = *l++;
			}
			else{ // Range Swap
				if(lessEqual(lef[$-1], rig[0])) return; // Nothing to swap

				size_t c;
				{
					// Binary search
					size_t l = 0, u = lef.length - 1;
					immutable off = lef.length - 1;
					while(u - l > 1){
						c = (u - l) / 2 + l;
						if(greater(lef[c], rig[off-c])) u = c;
						else l = c;
					}
					c = greater(lef[l], rig[off-l]) ? l : u;

					// Swap elements
					T* lp = lef.ptr + lef.length - 1;
					T* rp = rig.ptr + (off - c);
					T o;
					while(rp >= rig.ptr){
						o = *lp;
						*lp = *rp;
						*rp = o;
						--lp; --rp;
					}
				}

				mergeRuns(lef, lef[0..c], lef[c..$], temp);
				return mergeRuns(rig, rig[0..lef.length-c], rig[lef.length-c..$], temp);
			}
		}
		else{
			if(rig.length == 0) return; // Nothing to do

			if(rig.length <= temp.length){ // Merge right to left
				// copy(rig, temp[0..rig.length]);
				temp[0..rig.length] = rig[];

				T* p = arr.ptr + arr.length - 1, l = lef.ptr + lef.length - 1, r =
temp.ptr + rig.length - 1;
				while(true){
					if(greaterEqual(*r, *l)){
						*p-- = *r--;
						if(r < temp.ptr) break;
					}
					else{
						*p-- = *l--;
						if(l < lef.ptr) break;
					}
				}
				while(r >= temp.ptr) *p-- = *r--;
			}
			else{ // Range Swap
				if(lessEqual(lef[$-1], rig[0])) return; // Nothing to swap

				size_t c;
				{
					// Binary search
					size_t l = 0, u = rig.length - 1;
					immutable off = lef.length - 1;
					while(u - l > 1){
						c = (u - l) / 2 + l;
						if(less(rig[c], lef[off-c])) l = c;
						else u = c;
					}
					c = less(rig[u], lef[off-u]) ? u : l;

					// Swap elements
					T* lp = lef.ptr + lef.length - 1;
					T* rp = rig.ptr + c;
					T o;
					while(rp >= rig.ptr){
						o = *lp;
						*lp = *rp;
						*rp = o;
						--lp; --rp;
					}
				}

				mergeRuns(rig, rig[0..c + 1], rig[c + 1..$], temp);
				return mergeRuns(lef, lef[0..$-1-c], lef[$-1-c..$], temp);
			}
		}
	}

	/// A simple insert sort used for obtaining min_run
	void insertSort(Range arr){
		T o; T* p;
		foreach(c; arr.ptr + 1 .. arr.ptr + arr.length) if(less(*c, c[-1])){
			p = c; o = *p;
			do{
				*p = p[-1];
				--p;
			} while(p > arr.ptr && less(o, p[-1]));
			*p = o;
		}
	}
}

unittest{
	// Predicate test

	bool comp(uint a, uint b){ return a < b; }

	with(Compares!comp){
		assert(less(10, 20));
		assert(lessEqual(10, 20));
		assert(!greater(10, 20));
		assert(!greaterEqual(10, 20));

		assert(greater(20, 10));
		assert(greaterEqual(20, 10));
		assert(!less(20, 10));
		assert(!lessEqual(20, 10));

		assert(!less(10, 10));
		assert(!greater(10, 10));
		assert(lessEqual(10, 10));
		assert(greaterEqual(10, 10));
	}
}

unittest{
	// Sort test

	int[] arr = [2, 5, 8, 1, 4, 7, 3, 6, 9];
	xinokSort(arr);
	assert(arr == [1, 2, 3, 4, 5, 6, 7, 8, 9]);

	arr = [2, 5, 8, 1, 4, 7, 3, 6, 9];
	xinokSort!("a > b")(arr);
	assert(arr == [9, 8, 7, 6, 5, 4, 3, 2, 1]);

	bool comp(int a, int b){ return a > b; }
	arr = [2, 5, 8, 1, 4, 7, 3, 6, 9];
	xinokSort!(comp)(arr);
	assert(arr == [9, 8, 7, 6, 5, 4, 3, 2, 1]);

	// Stability test

	bool icmp(ubyte a, ubyte b){
		if(a >= 'a') a -= 'a' - 'A';
		if(b >= 'a') b -= 'a' - 'A';
		return a < b;
	}
	ubyte[] str =
cast(ubyte[])"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".dup;
	xinokSort!(icmp)(str);
	assert(str == "aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ");

	// CTFE test

	int[] foo(){
		int[] tmp = [20, 40, 60, 80, 10, 30, 50, 70, 90];
		xinokSort(tmp);
		return tmp;
	}
	//@ static assert(foo() == [10, 20, 30, 40, 50, 60, 70, 80, 90]);
}


All I want is compile and run this.
Everything is there, the unit test included. I thought that should have
been sufficient to be a runnable program.

Basically, if I type
PS E:\DigitalMars\dmd2\samples> rdmd --main xinoksort.d
Now it compiles and links without warning, and I get:

-a---        16/04/2012     08:55      12600 xinoksort.d -a---        16/04/2012     22:34       3621 xinoksort.d.deps -a---        16/04/2012     22:34      13264 xinoksort.exe -a---        16/04/2012     22:34      38763 xinoksort.obj

But running the exe crashes immediately at execution with "unauthorized instruction". Why ? Shouldn't it run, do nothing and quit nicely ?

And how do I execute the unit test ?
I'm sorry for all these questions, but it appears to be hard to find
these simple infos in the wiki and main website. I guess I'll update the
wiki to make it convenient to find them.
April 16, 2012
On 4/17/12, Somedude <lovelydear@mailmetrash.com> wrote:
> But running the exe crashes immediately at execution with "unauthorized instruction". Why ?

That's the old exectuable leftover from the previous compile. RDMD generates the exe in a temporary folder with a random name and runs it immediately. Because the main function doesn't do anything (RDMD provides an empty one via --main), the app exited immediately.

> And how do I execute the unit test ?

rdmd -unittest --main test.d
April 17, 2012
Le 17/04/2012 01:26, Andrej Mitrovic a écrit :
> On 4/17/12, Somedude <lovelydear@mailmetrash.com> wrote:
>> But running the exe crashes immediately at execution with "unauthorized instruction". Why ?
> 
> That's the old exectuable leftover from the previous compile. RDMD generates the exe in a temporary folder with a random name and runs it immediately. Because the main function doesn't do anything (RDMD provides an empty one via --main), the app exited immediately.
> 
>> And how do I execute the unit test ?
> 
> rdmd -unittest --main test.d

Thanks, it works. BTW, I couldn't find this information. Do you have any
idea where this is explained (in particular the --main switch) that I
overlooked ?
Anyway, I think I'll add this simple piece of info somewhere in the
wiki. I've already cleaned it up a little.
April 17, 2012
Le 17/04/2012 09:30, Somedude a écrit :
> Anyway, I think I'll add this simple piece of info somewhere in the wiki. I've already cleaned it up a little.

Ok, here it is: http://prowiki.org/wiki4d/wiki.cgi?HowTo/UnitTests
April 17, 2012
On 4/17/12, Somedude <lovelydear@mailmetrash.com> wrote:
> Do you have any idea where this is explained

rdmd --help