View mode: basic / threaded / horizontal-split · Log in · Help
April 16, 2012
"No stack address"
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
Re: "No stack address"
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
Re: "No stack address"
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
Re: "No stack address"
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
Re: "No stack address"
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
Re: "No stack address"
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
Re: "No stack address"
On 4/17/12, Somedude <lovelydear@mailmetrash.com> wrote:
> Do you have any idea where this is explained

rdmd --help
Top | Discussion index | About this forum | D home