Extending exceptions
Sometimes you have a function which needs to communicate back on failure through special exceptions as you want to program have the option to fix certain functions. For example a formula whose input parameter is bad (example: begin date is after the end date) or when the database throws up or anything.
Now you could do the following:
public class EndDateBeforeBeginDateException : Exception { }
class example
{
void SomethingWithTime(DateTime begin, DateTime end)
{
if (begin > end) throw new EndDateBeforeBeginDateException();
// ... do something with time ....
}
void DemonstrateFunctionTriesToCorrectInsteadOfFail()
{
DateTime input_begin = new DateTime();
DateTime input_end = input_begin.AddDays(-1); // WRONG DATE ON PURPOSE!
do
{
try
{
SomethingWithTime(input_begin, input_end);
}
catch (EndDateBeforeBeginDateException)
{
// Date was most likely wrong ask user
var r = MessageBox.Show("The end date was before the begin date."
+ "Do you want to switch them and try again?",
"Switch dates?",
MessageBoxButtons.YesNo);
if (r != DialogResult.No) { return; } // quit function
var temp = input_begin;
input_begin = input_end;
input_end = temp;
continue; // Try again now
}
} while (false);
}
}
But what if you have a lot of parameters. You don't want to create thousands of exceptions. Well, you can try using tagged exceptions which looks like this.
public class TaggedException<T> : Exception { }
struct _EndDateBeforeBeginDateTag{}
struct _SameDateTag {}
struct _TimeDoesNotExistTag {}
struct _ThinkOfATagOnYourOwn {}
class example
{
void SomethingWithTime(DateTime begin, DateTime end)
{
if (begin > end) throw new TaggedException<_EndDateBeforeBeginDateTag>();
if (begin == end) throw new TaggedException<_SameDateTag>();
// ... do something with time ....
}
void DemonstrateFunctionTriesToCorrectInsteadOfFail()
{
DateTime input_begin = new DateTime();
DateTime input_end = input_begin.AddDays(-1); // WRONG DATE ON PURPOSE!
do
{
try
{
SomethingWithTime(input_begin, input_end);
}
catch (TaggedException<_EndDateBeforeBeginDateTag>)
{
// Date was most likely wrong ask user
var r = MessageBox.Show("The end date was before the begin date."
+ "Do you want to switch them and try again?",
"Switch dates?",
MessageBoxButtons.YesNo);
if (r != DialogResult.No) { return; } // quit function
var temp = input_begin;
input_begin = input_end;
input_end = temp;
continue; // Try again now
}catch (TaggedException<_SameDateTag>)
{
// Force the user to change one date
}catch (TaggedException<_ThinkOfATagOnYourOwn>)
{
// Tag seems obvious enough *wink*
}
} while (false);
}
}
Now you only have one Exception in your code though you can quickly and without having to write much code.
(And if you are smart you store extra info the tag which is stored in the exception).
The same trick also works in C++ using templates.
The new and improved acyclic visitor with observer pattern (C++)
I have improved my “Acyclic Visitor with Observer” pattern (here is a link to the old one). Now you can create classes that are derived from two Acyclic visitors, something that was not possible with the previous version. On top of that I have made it less complex to use. On top of that they no longer have generic names (like “INotification”). The coolest thing is the registering. Previously you had to create the objects in the user code and also specify the class that should receive it. Now, it's simply done when you define the class.
Anyway just compare the code:
The old code
class HelloWorldReceiver : public IReceiver
{
public:
HelloWorldReceiver()
{
m_Translators.push_back( CTranslator<HelloWorldReceiver, WorldNote>() );
m_Translators.push_back( CTranslator<HelloWorldReceiver, HelloNote>() );
}
void Receive(WorldNote* aWorld) { }
void Receive(HelloNote* aHello) { }
};
The new code
class HelloWorldVisitor : public tAcyclicVisitor<HelloWorldVisitor>
public:
HelloWorldVisitor() {
RegisterAVObject<Hello>();
RegisterAVObject<World>();
}
void Receive(Hello* obj) { }
void Receive(World* obj) { }
};
And here is all the code you need to start using it, it's licensed as I promised in the previous post, but as you can see you all you need to do is keep the license in the code (all 21 lines).
// The MIT License
//
// Copyright (c) 2009 Wouter Lindenhof (http://limegarden.net)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef __ACYCLIC_VISITOR_H__
#define __ACYCLIC_VISITOR_H__
#include <list>
/**
* @brief if set to 1 it will allows the user to combine derived acyclic
* visitors, but this is likely to have a cost because of virtual inheritance.
*/
#define AV_ALLOW_VIRTUAL_INHERITANCE 1
/**
* @brief this is the base object
*/
class AVBaseObject
{
public:
virtual ~AVBaseObject() {}
};
class AcyclicVisitor;
class IAVTranslator
{
public:
virtual const bool Accept(AVBaseObject* object, AcyclicVisitor* visitor) const = 0;
};
class AcyclicVisitor
{
typedef std::list<IAVTranslator*> TransList;
protected:
TransList m_AV_Translators;
public:
virtual ~AcyclicVisitor() {
for( TransList::iterator it = m_AV_Translators.begin(); it != m_AV_Translators.end(); ++it )
delete (*it);
m_AV_Translators.clear();
}
void Visit(AVBaseObject* baseObject)
{
for( TransList::iterator it = m_AV_Translators.begin(); it != m_AV_Translators.end(); ++it )
(*it)->Accept(baseObject, this);
}
};
template <typename V, typename O>
class tAVTranslator : public IAVTranslator
{
public:
const bool Accept(AVBaseObject* object, AcyclicVisitor* visitor) const
{
V* original_visitor = dynamic_cast<V*>(visitor);
O* original_object = dynamic_cast<O*>(object);
if(original_object == 0 || original_visitor == 0) return false;
original_visitor->Receive(original_object);
return true;
}
};
template <typename V>
#if AV_ALLOW_VIRTUAL_INHERITANCE == 1
class tAcyclicVisitor : public virtual AcyclicVisitor
#else
class tAcyclicVisitor : public AcyclicVisitor
#endif
{
protected:
template <typename O> void RegisterAVObject()
{
m_AV_Translators.push_back(new tAVTranslator<V, O>());
}
};
#endif
I have also uploaded the entire example that I have used while testing so that you can some more interesting stuff.