#include "cexn.h"
#include <stdio.h>
#include <string.h>

// You can define easily an exception handler (see below) 
exn_handler(handler);

int g(int a)
{
	return 42 + a;
}


void f()
{
	try
	{
		/*
		  Try the different exceptions. The "int one will be catched in
		  the directly enclosing block, the float one will be catched by
		  the registered handler, the char one will be catched by the default
		  handler and the functionnal one will be catched by the try block
		  in the main function.
  		*/
		throw(int,42); 
		// throw(float,3.14);
		// throw(char,'b');
		// throw(int(*)(int),g);
	}
	with
	{
		catch(int,a)
		{
			printf("an int exception : %d\n",a);
		}
		finally
		{
			printf("this block is always executed\n");
		}
	} rethrow;
	return;
}

int main()
{
	// You can register the handler function to be called with
	// the given exception type.
	register_handler(float,handler);
	register_handler(unsigned,handler);
	// And you can easily unregister a handler for a given type.
	unregister_handler(unsigned);
	// If you register a handler for a type with an already associated
	// handler, to new one replace to previous one.

	try
	{
		f();
	}
	with
	{
		catch(char*,a)
		{
			printf("a char* exception : \"%s\"\n",a);
		}
		catch(int(*)(int),t)
		{
			printf("a function pointer exception : %d\n",t(42));
		}
		finally
		{
			printf("this line is also always executed\n");
		}
	} catchall;

	// This exception will not be catched and will abort the program if raised.
	// throw(char*,"Uncatched exception !");
	return 0;
}

// This function is a generic exception handler which will print all the
// information passed to it by cexn, and which will try to print the exception
// itself if its type is known.
exn_handler(handler)
{
	printf("erreur de type <%s> levée dans la fonction %s du fichier %s à la ligne %d et rattrapée dans la fonction %s du fichier %s à la ligne %d.\n",type,r_fct,r_file,r_line,c_fct,c_file,c_line);
	printf("tentative d'affichage de l'exception : ");
	if (!strcmp(type,"int"))
		printf("%d\n",*(int*)exn);
	else if (!strcmp(type,"char"))
		printf("'%c'\n",*(char*)exn);
	else if (!strcmp(type,"float"))
		printf("%f\n",*(float*)exn);
	else if (!strcmp(type,"double"))
		printf("%f\n",*(double*)exn);
	else if (!strcmp(type,"char*"))
		printf("\"%s\"\n",*(char**)exn);
	else
		printf("%p\n",exn);
}

/*
Example of valid syntax for a try/with block:

You can give as much catch block as you wich.
One of the rethrow or catchall keyword must be present at
the end of the "with" block. The finally block, if any
must be the last one.
try
{
}
with
{
	catch()
	{
	}
	...
	finally
	{
	}
} rethrow;

The with block is optionnal. This syntax is only usefull
with a catchall at the end, but you can still use rethrow.
try
{
} catchall;

Even with catch and/or finally blocks, with is optionnal
try
{
}
catch
{
}
...
finally
{
} rethrow;

You can have a try block with zero catch blocks or no finally block.
And the with enclosing block is still optionnal.
try
{
}
finally
{
} rethrow;

The semi colon after rethrow or catchall is optionnal, but when there
is more than one catch/fianally block using it as well as using the with
enclosing block is more beautiful to my opinion, and more C-like.

An exception must be catched with the exact same name as the one with which
it has been raised. For exemple if you do:
typename int t;
throw(t,42);
This exception will be caught with the name t and not with the name int.
This is both a limitation and a functionnality (you can raise the same type
under different name if different handler must be called).
*/
