On managed something and graphical nonsense

Friday, February 24, 2006

Fun with Singletons in C# 2.0 (Part 2)



And here we go again :)

Last entry we talked about the "Generic Singleton" and how to achieve such an artifact without partial specialization templating (aka full blown C++ templating engine). In this entry we will focus on allocators, what they do, what is their responsability, what we can do with them, and maybe some little history about their existence.

Allocators in one shape or another can be tracked to the very beginning of the programming endervour. There had always been a necesity to allocate resources, the most basic resource to allocate was memory; so in procedural language we relied either on the compiler to do stack allocation of memory or on specific constructions or library calls (as in C) to do the work. Using those approaches the specific allocation strategy was hidden from the programmer by those calls.

Sometimes things didnt worked as expected and those default allocation strategies fall short, mostly when a lot of space has to be allocated to be used in special ways; for example in mathematical code (memory must be aligned and consecutive to better use the cache of the processor) or in code with high performance requirements. At that moment most programmers relied in custom strategies to handle those situations doing just a single call to get all memory required in one step.

When object orientation got mainstream and languages like Smalltalk and C++, some objects were a little too big and costly to initialize, we again relied on allocators to recicle the memory we used or objects :) .

Then someone thought, what if we let the programmer specify the memory/object allocation strategy giving a binding to the underlaying allocation system. Then the allocators where given a name and an existence in its own. And light filled the sky and allocators where born :D

Ok, enough history. Now we are going to the nitty gritty details (I love this part). IMHO the best way to accomplish something is knowing where we want to go. Now lets suppose we have our "Static Allocation Singleton" from Part 1, now what if we write something like this:

/// <summary>
/// An StaticSingleton using an StaticAllocator used just
/// to simplify the inheritance syntax.
/// </summary>
public class StaticSingleton<T> : Singleton<T, StaticAllocator<T>> where T : class
{ }
So our static singleton is just a Singleton of type T with an StaticAllocator of type T. Cooool! Isnt it?? Now lets get our hands dirty. So by now we know that our allocators will be the ones that will be in charge of specifying our allocation strategy. Now our singleton not only depends on T but on our Allocator too. In C# code it should be something like this:


public class Singleton< T, Allocator >
where T : class
where Allocator : Allocator< T >
{
private static readonly Allocator<T> allocator;
/// <summary>
/// The Singleton implementation of the Instance method defer the creation policy to
/// its allocator, so this method just delegate the Instance retrieval to the
/// Instance method of the allocator.
/// </summary>
public static T Instance
{
get { return allocator.Instance; }
}
}

Even if you dont know already we have solved the Allocator Based Singleton problem. Where is the trick you might think? Well someone said (if anyone knows the source would be cool): "There are only a few problems in Computer Science that cannot be solved by an extra level of indirection." I applied that concept so now the Instance method just delegates the real allocation to the Allocator Instance :)

We have a big problem to solve yet, but we have solved it in the last singleton implementation. Guess who had been the lucky winner that will ensure that the allocator get created :D, yes our friend Reflection.


static Singleton()
{
ConstructorInfo constructor = typeof(Allocator).GetConstructor(
BindingFlags.Instance |
BindingFlags.NonPublic, null,
new Type[0], new ParameterModifier[0]
);

if (constructor == null)
throw new Exception("The allocator doesnt have a private/protected constructor.");

try
{
allocator = constructor.Invoke(new object[0]) as Allocator<T>;
}
catch (Exception e)
{
throw new Exception("The Singleton Allocator couldnt be constructed", e);
}
}

What if I create multiple allocators then can I create multiple instances of T? The answer is yes and no. Yes, you can create multiple instances of the allocator, and No they wont allow you to have multiple instances of T :) using the same trick that we used before.

Our allocator looks like this:

public abstract class Allocator<T>
where T : class
{
/// <summary>
/// The parameterless protected Constructor.
/// </summary>
protected Allocator()
{ }

/// <summary>
/// The property returns the only instance of the Singleton Object in question.
/// </summary>
/// <remarks>This property implementation must enforce the Single Object property
/// of Singletons throwing an exception.</remarks>
public abstract T Instance { get; }
}

So how do our StaticAllocator look? Well, almost the same as our original singleton :O

public class StaticAllocator<T> : Allocator<T> where T : class
{
static StaticAllocator()
{
ConstructorInfo constructor = typeof(T).GetConstructor(
BindingFlags.Instance |
BindingFlags.NonPublic, null,
new Type[0], new ParameterModifier[0]);

if (constructor == null)
throw new Exception("The object doesnt have a private/protected constructor.");

try
{
instance = constructor.Invoke(new object[0]) as T;
}
catch (Exception e)
{
throw new Exception("The StaticSingleton couldnt be constructed", e);
}
}

private StaticAllocator()
{ }

private static readonly T instance;

/// <summary>
/// The static allocator Instance property returns the instance created on class loading.
/// </summary>
/// <remarks>This means that the singleton is instantiated at the moment in which
/// a class has a reference to that type even if it never calls the Instance
/// method.</remarks>
public override T Instance
{
get { return instance; }
}
}

What if we want to do a LazyAllocator, well as in most textbooks we will leave that for the reader to implement by themselves (just dont tell anyone, you can get the source from here ;) ) So we did all this just to implement a LazyAllocator?? Hell no, what if I say you that you can have an abstract singleton to instance a concrete provider by itself just reading the concrete instance from a config file? Whould it be useful? Well I know of an specific scenario where it wont be only cool, but useful. When you are creating a Rendering Engine and the rendering provider could be Managed DirectX or OpenGL (and that cant be changed on the fly ;) ) then you just read the Renderer concrete class from a configuration file, and the user wont ever know what it is using as he is using an abstract class. :)

I must say that this entry was fun to write it, so I hope you enjoy reading it as much as I did writting it.

Greetings
Federico

1 Comments:

Post a Comment

Subscribe to Post Comments [Atom]



<< Home