The Coming Software Apocalypse

For discussions about programming, programming questions/advice, and projects that don't really have anything to do with Puppy.
Message
Author
User avatar
Flash
Official Dog Handler
Posts: 13071
Joined: Wed 04 May 2005, 16:04
Location: Arizona USA

The Coming Software Apocalypse

#1 Post by Flash »

The Coming Software Apocalypse

A pretty good article about programming, from The Atlantic magazine, of all people.

jafadmin
Posts: 1249
Joined: Thu 19 Mar 2009, 15:10

#2 Post by jafadmin »

The results of using the lowest bidder. Same as it ever was ..

User avatar
tallboy
Posts: 1760
Joined: Tue 21 Sep 2010, 21:56
Location: Drøbak, Norway

#3 Post by tallboy »

A very interesting reflection on the programming 'industry'. Thank you, Flash.

tallboy
True freedom is a live Puppy on a multisession CD/DVD.

User avatar
6502coder
Posts: 677
Joined: Mon 23 Mar 2009, 18:07
Location: Western United States

#4 Post by 6502coder »

When I was programming on UNIX way back in the early '80s, I remember pointing out to my colleagues how amazingly primitive it was that we were still creating programs by laboriously typing characters into text files. Even then, I can hardly have been the first to make that observation.

I am skeptical of any suggestion that a breakthrough is anywhere in sight. Fundamentally, a correct program is a logical construct, a collection of (mostly implicit) assertions that since THIS is true, then THAT is true, and so on. The problem is that the logic is often necessarily convoluted, so it is easy to make subtle errors in reasoning. Writing a non-trivial bug-free program is at least as hard, if not much harder, than writing a law that is free of unintended consequences.

Back in '70s and '80s the computer scientist Ole-Johan Dahl (Turing Award winner in 2001) did some really interesting work on the question of how one proves that a program is correct, using the notion of "weakest precondition." On the one hand, it was great to see how one could, for example, prove that a for-loop terminated with the correct result. On the other hand, it was very sobering that so much of the apparatus of formal logic was required just to prove that a simple for-loop was correct, much less an entire non-trivial program.

Graphical aids like the ones described in the article are nice, but can only do so much. As long as programs continue to have to manipulate complex abstractions that have no simple visual rendering, programming will be hard work.

WIckedWitch
Posts: 276
Joined: Thu 29 Mar 2018, 22:13
Location: West Wales bandit country

#5 Post by WIckedWitch »

A subject dear to my heart. Many years ago, I was taught mathematics at a school that had, at the time, probably the best school mathematics department in the UK. Its maths course for UK A-levels went far beyond the written syllabus and covered a large part of what was then first year undergraduate mathematics. In fact, at one point, we were trained for UK A-level exams by being given Cambridge University Master's papers to attempt (which most of us found not unduly difficult).

Most important of all, we were taught that mathematics is not about numbers and algebra but about patterns and structure; and further that mathematics is the finest problem-solving tool that human intellect has yet devised.

Although circumstances and illness prevented my completing an undergraduate course (pure maths, applied maths, statistics and computing), I nevertheless brought a well-trained mathematical mindset to programming - and was immediately considered an utter freak by my colleagues.

Back in 1977, I was a founder member of the British Computer Society Specialist Group in Formal Aspects of Computing Science. By the time I retire, I thought, everyone will be using mathematics to get programs right. How wrong I was.

Largely because of an early excellent mathematical education, it is second nature to me to solve problems in mathematics before getting anywhere near writing code. For the past 25 years, I have worked on nothing but critical systems - and the mathematical incompetence of most software engineers leaves me, frankly, terrified.

Only yesterday, I received from a colleague an email about the desirability of strenuous testing of compilers for code in autonomous vehicles. It's a good idea - but the biggest risks in such vehicles arise from technical over-reach by gung-ho AI developers, some of whom, IMHO, shouldn't be trusted with so much as an abacus.

The software industry is riddled with ignorance about how to get software correct and dependable. I see very little impetus towards the thoroughgoing culture change that will be needed to ensure that self-driving cars stop killing people any time soon.

(end of self-righteous, opinionated rant)
Sometimes I post mindfully, sometimes not mindfully, and sometimes both mindfully and not mindfully. It all depends on whether and when my mind goes walkies while I'm posting :?

User avatar
tallboy
Posts: 1760
Joined: Tue 21 Sep 2010, 21:56
Location: Drøbak, Norway

#6 Post by tallboy »

There is a strong focus on Computational Mathematics today, for example at the UiO, Ole-Johan Dahl's old workplace. (uni in Oslo) Unfortunately, they now often use a language like Python to implement mathematical formulas to problems, instead of doing the opposite, like WickedWitch describes. Okay, you save chalk on 3 blackboard full of formulas, and can show nice 3-D curves and colored stress models of the result from the calculations, but I feel that the whole concept start off in the wrong end.
But then again, my own mathematical 'achievements' are not exactly worth mentioning at all... :lol:
True freedom is a live Puppy on a multisession CD/DVD.

WIckedWitch
Posts: 276
Joined: Thu 29 Mar 2018, 22:13
Location: West Wales bandit country

#7 Post by WIckedWitch »

tallboy wrote: But then again, my own mathematical 'achievements' are not exactly worth mentioning at all... :lol:
I don't think mathematical achievement is too important. It's the mathematical way of thinking that's the key thing. My own mathematical achievements are as near to zero as makes no difference but ever since I can remember being aware of my own cognitive style, it has been second nature to me to use mathematics as a way to describe and solve problems. It really annoys me when people who have studied mathematics at university try as hard as they can to run away from it when working as engineers. In my experience, software engineers are the worst offenders of this kind.
Sometimes I post mindfully, sometimes not mindfully, and sometimes both mindfully and not mindfully. It all depends on whether and when my mind goes walkies while I'm posting :?

jafadmin
Posts: 1249
Joined: Thu 19 Mar 2009, 15:10

#8 Post by jafadmin »

WIckedWitch wrote:
tallboy wrote: But then again, my own mathematical 'achievements' are not exactly worth mentioning at all... :lol:
I don't think mathematical achievement is too important. It's the mathematical way of thinking that's the key thing. My own mathematical achievements are as near to zero as makes no difference but ever since I can remember being aware of my own cognitive style, it has been second nature to me to use mathematics as a way to describe and solve problems. It really annoys me when people who have studied mathematics at university try as hard as they can to run away from it when working as engineers. In my experience, software engineers are the worst offenders of this kind.
(a -= (b = (a += b) - b))

:wink:

WIckedWitch
Posts: 276
Joined: Thu 29 Mar 2018, 22:13
Location: West Wales bandit country

#9 Post by WIckedWitch »

jafadmin wrote:
WIckedWitch wrote:
tallboy wrote: But then again, my own mathematical 'achievements' are not exactly worth mentioning at all... :lol:
I don't think mathematical achievement is too important. It's the mathematical way of thinking that's the key thing. My own mathematical achievements are as near to zero as makes no difference but ever since I can remember being aware of my own cognitive style, it has been second nature to me to use mathematics as a way to describe and solve problems. It really annoys me when people who have studied mathematics at university try as hard as they can to run away from it when working as engineers. In my experience, software engineers are the worst offenders of this kind.
(a -= (b = (a += b) - b))

:wink:
Assuming this is intended to be read as a fragment of C, I should first tell you that I am the original author of the MISRA C rules and was the first person in the UK (and quiet possibly the world) to configure a static analysis tool (QAC) to enforce them.

The result of evaluating the above expression is not defined by the ISO C standard. Among the reasons for this are:

1. In all contexts where expressions appear, they are preceded by and followed by sequence points. Between sequence points the order of evaluation of operators is not defined, hence, in the above expression, the net result of side-effects is not defined.

2. Only for certain logical operators does C define the order of evaluation of the operands of operators.

3. This is quite apart from any implicit conversions that may occur if a and b are not of assignment-compatible types, or even of mixed floating and integral types.

4. Not only are the relevant orders of evaluation not defined but the C standard imposes no requirement that they be done in the same order in the same expression.

5. The C standard permits the compiler to omit any evaluation provided that it produces no needed side-effect. Unfortunately the standard omits to define what it means for a side-effect to qualify as needed.

Any decent static checker will flag this expression as exactly the kind of thing that you don't want to write in C or any C-derived language for that matter.The only worthwhile use for code like this is to test the implementation-defined, unspecified and undefined behaviour of the compiler.

*** Side challenge: write a program to determine whether the left-hand-side or right-hand-side of an assignment operator is the first to be evaluated and print out, using the letters L and R what the order is ****

Coincidentally I received today a message from a colleague that he received from the engineering manager of a medical instruments company that I once worked for. Their programmers complain that using a static checker (in this case QAC) creates a lot of hard work. (Believe me, they are some of the worst programmers I've ever come across). In certain areas of software engineering, cultural bias trumps any amount of sound technical understanding. I was fired from that company for reporting them to a safety regulator for falsifying test reports (nuff said).

At least in semi-retirement, I can now choose which parts of the software engineering madhouse I visit.
Sometimes I post mindfully, sometimes not mindfully, and sometimes both mindfully and not mindfully. It all depends on whether and when my mind goes walkies while I'm posting :?

jafadmin
Posts: 1249
Joined: Thu 19 Mar 2009, 15:10

#10 Post by jafadmin »

Back in the 80's I was a budding C programmer, and a member on a few dialup BBS's that had to do with ASM and C programming.

On one BBS in Palo Alto a user started a thread proposing a contest that primarily related to chip programming. At the time, the amount of space on a chip for data/variables was quite small and engineers were constantly looking for ways to stretch the chip's limited resources.

The thread challenged users to swap 2 variables without using a third variable in the fewest lines of code. There were dozens of entries, with the closest winner doing it in 3 lines (McAfee Swap) by the time I first saw the thread.

My math instincts told me that it should be possible in a single line using algebra, so I approached the problem from that angle, and the result was what you see above. It took me two hours to conceive then debug. Needless to say, my solution won, hands down.

It has several advantages. When implemented as a macro, it becomes type agnostic and doesn't need a call stack. It works on all properly cast data types.

Code: Select all

/* Swap two values  -  cc Swap.c -o Swap

    Usage: ./Swap <First Number> <Second Number>

	- Jafadmin 1987
*/
#include <stdio.h>  // for printf()
#include <stdlib.h> // for atoi()

// This single-line macro swaps two variables of the 
//    same type without using a third variable.
#define Swap(a, b) (a -= (b = (a += b) - b))

int main(int ac, char *av[])
{
    int Left  = atoi(av[1]);  // We convert args to int's for
    int Right = atoi(av[2]);  //  demo purposes

    printf("\n\tLeft = %d, Right = %d\n"  , Left, Right);

    Swap(Left, Right);	// Call the macro

    printf(  "\tLeft = %d, Right = %d\n\n", Left, Right);
}
It goes without saying that the sum of a&b cannot exceed the size of the type. Upcasting is highly recommended. Easy peasy pointer swapping ..

I have a personal header file called "Cool.h" that has many tricks like this that I have come up with over the years.

WIckedWitch
Posts: 276
Joined: Thu 29 Mar 2018, 22:13
Location: West Wales bandit country

#11 Post by WIckedWitch »

As a means of swapping two scalar variables, your code makes ingenious use of multiple side-effects. But the results it produces will also be dependent on the values of the variables in cases where a and b are reals that differ widely in value. Also it would be asking for trouble if a and b were pointers into arrays with different cell sizes.

Try writing out the compound assignment operators in full and then consider that the order of evaluation of the operands of arithmetic operators is unspecified in C.

Note also that while it does not explicitly use a third variable, a compiler might well generate code that does use a third variable. A propos of one-liners and other concise coding tricks, you might find the book "Hacker's Delight" by H. S Warren an eye-opening read.

My solution to the order-of-evaluation problem is below:

#include <stdio.h>

static char ordeval[2] = "RR";
static int i = 0;

int main(void)
{
// in next statement, if the = operator is evaluated L to R, i.e. the
// lvalue is computed first, then 'L' goes into ordeval[0], whereas if
// evaluated R to L, then 'L' goes into ordeval[1]

ordeval = (++i, 'L');

return printf("Order of evaluation is %c%c\n", ordeval[0], ordeval[1]);

}

You might wish to consider whether this affects your confidence in your macro. What happens if, when evaluating the outermost -= operator, the compiler puts the value of a into a register before evaluating any other assignment in the expression? If you are relying on the innermost bracketted expression being evaluated first, this might not be a robust macro because the order of side effects is not defined by the C standard and different compilers may do it differently, or even the same compiler may do it differently under different compile-time options, or, albeit utterly perversely, the compiler might choose the order of side effects at random every time it compiles another expression. All of these possibilities are permitted by the ISO C standard. SPARK Ada gets round these problems by requiring all expressions to be such that they produce the same result under all possible orders of evaluation that the compiler permits.

(Sorry about scroodup indentation)
Last edited by WIckedWitch on Wed 25 Apr 2018, 22:58, edited 1 time in total.
Sometimes I post mindfully, sometimes not mindfully, and sometimes both mindfully and not mindfully. It all depends on whether and when my mind goes walkies while I'm posting :?

WIckedWitch
Posts: 276
Joined: Thu 29 Mar 2018, 22:13
Location: West Wales bandit country

#12 Post by WIckedWitch »

Here's another programming challenge - from a real safety-critical project that used C coding using X libraries to control air traffic control LED radar displays.

The main functional code was in SPARK Ada and machine-verified to be free from run-time undefinedness. A top-end static checker was used on the C code, which had to comply with MISRA C as far as possible.

To work properly, the static checker has to know, among other things, what macros are predefined by the compiler. These predefined macros must be identified to the static checker together with their defined values.

The challenge is to find means for automatically determining which macros are predefined, and what their values are, under given compiler options.

(Some eminent C experts told me this was impossible before I enlightened them.)
Sometimes I post mindfully, sometimes not mindfully, and sometimes both mindfully and not mindfully. It all depends on whether and when my mind goes walkies while I'm posting :?

jafadmin
Posts: 1249
Joined: Thu 19 Mar 2009, 15:10

#13 Post by jafadmin »

Keep in mind I was merely trying to win a friendly contest and provide proof of concept using an algebraic approach.

The original algorithm was done longhand and with more parenthesis forcing LtoR/RtoL evaluation. I distilled it intentionally to make it smaller and simpler for that viewing audience.

Once they got the gist of it, there were a dozen CS/SE's exploring all the real-time performance considerations and pitfalls, and providing peer review.

Also, chipset compilers do not create variables unless explicitly directed. Otherwise, the programmers would have no control over the provisioning of the code/data segments of the given chip. Also, one can check whether the compiler creates a third variable anyway by testing the output with 2 values that when combined, exceed the size of the type.

More robust: (a -= (b = ((a += b) - b)))

Rilly, rilly robust, in longhand: (a = (a - (b = ((a = (a + b)) - b))))

Thanks for the HD recommendation. Looks fun: http://hackersdelight.org/

.
Last edited by jafadmin on Thu 26 Apr 2018, 02:42, edited 9 times in total.

jafadmin
Posts: 1249
Joined: Thu 19 Mar 2009, 15:10

#14 Post by jafadmin »

WIckedWitch wrote: The challenge is to find means for automatically determining which macros are predefined, and what their values are, under given compiler options.

(Some eminent C experts told me this was impossible before I enlightened them.)
Hmm ..

wiak
Posts: 2040
Joined: Tue 11 Dec 2007, 05:12
Location: not Bulgaria

#15 Post by wiak »

tallboy wrote:There is a strong focus on Computational Mathematics today, for example at the UiO, Ole-Johan Dahl's old workplace. (uni in Oslo) n again, my own mathematical 'achieve
Ah... I have such fond memories of the ten months I spent at UofOslo - sharing flat with Norwegians at Sogn Studentby

I didn't learn much maths but some norske.

Wiak

WIckedWitch
Posts: 276
Joined: Thu 29 Mar 2018, 22:13
Location: West Wales bandit country

#16 Post by WIckedWitch »

OK - Here's how to find out what macros a C compiler predefines.

A predefined macro affects user-written code in at most two ways:

1. It can be tested to control conditional compilation.
2. It can supply a value that it used by the programmer.

The trick to discover inbuilt predefined macros is to scan all of the C compilers header files and extract from them (e.g. by using awk) a list of all items that match the syntactic pattern of a preprocessor identifier. Then you create a program of the following form;

#include <stdio.h>
int main(void)
{
#ifdef identifier-1
printf("identifier-1 has value ");
printf(#identifier-1);
printf("\n");
#endif

// then repeat the above ifdef/endif pattern for all the identifiers you have listed

return 0;
}

Assuming I've remembered the # stringify operator correctly, [*** - I HADN'T - SEE CORRECTION IN POST BELOW ***] running this program under given compiler options then tells you what predefined macros are given under those options and what their values are. I've used this technique successfully on some big code audits - one of them for forensic use in a large international litigation..

BUT

this is, alas, only a partial algorithm. It correctly identifies the values of all predefined macros from among identifiers that exist in the texts of header files but this does not do the whole job with gcc, which constructs predefined identifiers on-the-fly, so that they may not appear explicitly in those header files.

Various further ploys can be used to do the job completely for gcc, but they are all variations on the above pattern applied to different search domains.

Simples!
Last edited by WIckedWitch on Mon 07 May 2018, 18:34, edited 1 time in total.
Sometimes I post mindfully, sometimes not mindfully, and sometimes both mindfully and not mindfully. It all depends on whether and when my mind goes walkies while I'm posting :?

WIckedWitch
Posts: 276
Joined: Thu 29 Mar 2018, 22:13
Location: West Wales bandit country

#17 Post by WIckedWitch »

And now, folks, another one:

Write C code to determine the maximum of two integers without using an if, or other conditional statement.
OK. Here is the solution. I've done it both as a macro and a function. A decent optimising compiler will eliminate the two comparisons of a with 0 and produce pretty fast code for this.

Code: Select all

#include <stdio.h>

#define OPABS(a) ((a) * (((a)>=0) - ((a)<0)))

int opabs (int a) ( return a * ((a>=0) - (a<0)); } 

int main(void)
{
	printf("opabs(%i) = %i\n",  1, opabs( 1));
	printf("opabs(%i) = %i\n", -1, opabs(-1));
	
	printf("OPABS(%i) = %i\n",  1, OPABS( 1));
	printf("OPABS(%i) = %i\n", -1, OPABS(-1));
	
	return 0;
}
It's not perfect and could be tweaked a little further to help less powerful optimisers generate fast code from it.

The key thing, however, is that, at least at the source code level, there is just one single path through the code. Minimising the number of branches in the code is an extremely important thing in embedded systems that have to be shipped with test instrumentation in the finished product, as is required by some levels of DO 178.

An just for good measure, here is a clipping function done the same way:

Code: Select all

#include <stdio.h>

int opclip1(int a, int b, int i)
{
	return a*(i<a) + i*((i>=a)&&(i<=b)) + b*(i>b);
}

int opclip2(int a, int b, int i)
{
	int temparray[3] = {a, i, b};
	
	return temparray[((i>=a)&&(i<=b)) + ((i>b)<<1)];
}

int main(void)
{
	printf("opclip1(%i, %i, %i) = %i\n", 1, 3,  2, opclip1(1, 3,  2));
 	printf("opclip1(%i, %i, %i) = %i\n", 1, 3, -1, opclip1(1, 3, -1));
	printf("opclip1(%i, %i, %i) = %i\n", 1, 3,  4, opclip1(1, 3,  4));
	
	printf("\n");
	
	printf("opclip2(%i, %i, %i) = %i\n", 1, 3,  2, opclip2(1, 3,  2));
 	printf("opclip2(%i, %i, %i) = %i\n", 1, 3, -1, opclip2(1, 3, -1));
	printf("opclip2(%i, %i, %i) = %i\n", 1, 3,  4, opclip2(1, 3,  4));	

	return 0;
}
The first two arguments specify bounds. If the third argument is less than the lower bound, the function returns the lower bound. If the third argument exceeds the upper bound, then the function returns the upper bound. Otherwise the function returns the value of the third argument. Again, there are several different ways of doing this.

And finally, max and min functions in the same vein.

Code: Select all

#include <stdio.h>

int opmax(int a, int b)
{
	return ((a<b)*b + (!(a<b))*a);
}

int opmin(int a, int b)
{
	return ((a<b)*a + (!(a<b))*b);
}

int main(void)
{
	printf("\nMaximum of %i and %i is %i\n", 2, 3, opmax(2,3));
	printf("\nMinimum of %i and %i is %i\n", 2, 3, opmin(2,3));
	
	return 0;
}
Coding patterns like this can achieve huge reductions in path counts in embedded signal processing software. Radical limitation of path counts makes critical code much easier to test thoroughly. With one-path designs you can focus on the stronger forms of boundary-value testing without having to worry whether you've achieved full coverage on test metrics based on the control flow graph.


It's quite instructive to compare these coding patterns with the C snippets that technosaurus posted for fast/small static apps. Just goes to show how different are the results you get when you are focusing on different kinds of non-functional requirement. Technosaurus has given us fast code. My efforts focus on maximising the ease of strenuous testing for critical systems.

... we should probably both try to get out a little more ... :-)
Last edited by WIckedWitch on Sun 29 Apr 2018, 19:51, edited 4 times in total.
Sometimes I post mindfully, sometimes not mindfully, and sometimes both mindfully and not mindfully. It all depends on whether and when my mind goes walkies while I'm posting :?

WIckedWitch
Posts: 276
Joined: Thu 29 Mar 2018, 22:13
Location: West Wales bandit country

#18 Post by WIckedWitch »

And yet another one:

Write an ISO C conforming program to determine whether char is signed or unsigned. It's not difficult but it tests your understanding of the ISO C standard ...

... oh, and just for good measure, make the program work for any conforming compiler conforming to any version of the ISO C standard - and that's a hint as to the kind of coding trick you need.

Code: Select all

#include <stdio.h>

int main(void)
{
	char 	ch1			= 	(char)1;
	char 	ch2			= 	(char)5;
	int		charbits	=	2;

	while (ch1 != ch2)
	{
		// keep left shifting and adding 1 until ch1 and ch2 are equal
		// increment count of no. of bits by 2 each time round the loop
		// NOTE: *** This assumes that no. of bits in char is even ***
	
		ch1 = (ch1 << 2) + 1;
		ch2 = (ch2 << 2) + 1;
		charbits += 2;
	}
	
	printf("\nPlain char is ");
	
	// now shift ch1 left by 1 to put a 1 in the sign bit position
	// - BUT NOTE THAT ch1 DOES NOT CONTAIN ALL ONES - V.I. -
	// and test whether it now has a negative value - if so plain
	// char is signed and charbits says how many bits a char has
	
	if ((ch1 << 1) < 0)
	{
		printf("signed ");
	}
	else
	{
		printf("unsigned ");
	}
	
	printf("and has %i bits\n", charbits);
	
	return 0;
}
	
The key trick here is not to set all ones in the char variables you are manipulating. The reason for this is that, on targets using 1's complement integer representation, the C standard does not guarantee that something computed to be all ones is not stored as all zeros when actually assigned to a variable ('cos 1's complement has 2 representations of zero).

Happidaze, folks!
Sometimes I post mindfully, sometimes not mindfully, and sometimes both mindfully and not mindfully. It all depends on whether and when my mind goes walkies while I'm posting :?

WIckedWitch
Posts: 276
Joined: Thu 29 Mar 2018, 22:13
Location: West Wales bandit country

#19 Post by WIckedWitch »

AND FOR THE (POINTLESS) GRAND FINALE:

Not one-path programming but one-statement programming - a function that computes greatest common divisors and contains only a single return statement, all wrapped-up in a program whose main function itself contains only a single return statement.

Code: Select all

#include <stdio.h>

// one-liner function to compute gcd of two positive integers 
int gcd (int p, int q)
{	
	return (p % q == 0 ? q : gcd(q, p%q));	// assumes that p >= q > 0
}

int main(void)
{
	return printf("\ngcd(%i, %i) = %i\n", 35, 21, gcd(35, 21));
}
Wholly unsuitable for anything critical because of the recursion - but yet another merry example of one-x programming, whatever you choose x to be.:-)
Sometimes I post mindfully, sometimes not mindfully, and sometimes both mindfully and not mindfully. It all depends on whether and when my mind goes walkies while I'm posting :?

WIckedWitch
Posts: 276
Joined: Thu 29 Mar 2018, 22:13
Location: West Wales bandit country

#20 Post by WIckedWitch »

Here's a correction to my post about discovering C predefined macros:

the program should contain the defines;

Code: Select all

#define stringify_aux(s) #s
#define stringify(s) stringify_aux(s)
and then the pattern of each print statement should be;

Code: Select all

printf("<macro-name> = %s\n", stringify(<macro-name>));
Sorry for the cock-up. It comes from using several different languages. In the original incorrect example, I had used the C # operator absent mindedly as though it worked like $ in Tcl. Mea culpa.

I originally wrote this macro-discovery program almost 20 years ago and am now working on a revamp of it with the aim of making it work, as far as possible, for any C compiler on any platform.

Watch this space but don't hold your breath :-)
Sometimes I post mindfully, sometimes not mindfully, and sometimes both mindfully and not mindfully. It all depends on whether and when my mind goes walkies while I'm posting :?

Post Reply