Saturday, April 08, 2006

C++ : In a bind with function objects part IV

Just to recap let’s look at the relevant parts of class B again in it’s entireity…


class B
{
public:
void Update()
{
Graphic &graphic = GetGraphic();

std::for_each(
m_A_UpdateHandlers.begin(),
m_A_UpdateHandlers.end(),
std::bind2nd(
std::mem_fun_ref(&A_UpdateHandler::Update),
&graphic)
);
}

private:
void Init()
{
Control *control;

for(APtrContainer::iterator it = m_APCollection.begin();
it!= m_APCollection.end(); ++it)
{
A &a = *it;
std::string &name = a.GetName();
control = GetControlByName(name);
if(control)
{
const int threshold = a.GetThreshold();
m_A_UpdateHandlers.push_back(
A_UpdateHandler(*control, threshold));
}
}
}


Control *GetControlByName(std::string);
Graphic &GetGraphic() const;
...
private:
APtrContainer m_APCollection;
A_UpdateHandlerContainer m_A_UpdateHandlers;
};


The first thing we notice is that design constraints imposed by the standard during the refactor have forced me to separate the processing of class A objects into an initialisation phase and an update phase. The potentially expensive call to GetControlByName is only done once per object in the initialisation phase as is the check to see if it returns a NULL value. Additionally, if the control indeed is NULL nothing gets added to the handler container. In the worst case, if no controls are found the update function will do nothing! Finally the update function in the handler that does the actual work is succinct and to the point, only expressing the minimum it needs to in order to get the job done. Everything required prior to this call has been handled already and is either passed in or cached where appropriate


void A_UpdateHandler::Update(Graphic *graphic) const
{
bool value = m_Control.GetCurrentValue() > m_Threshold;
graphic->doSomething(value);
}


All of these things have improved the code in terms of legibility, ease of maintenance and run-time efficiency, what more do you want!?


A couple of finer points are worth noting here before we end the last part in this series. std::bind2nd is obviously a templated function. The compiler implicitly deducts what types to use to instantiate the function using the types of the parameters. However, std::bind2nd defines the type of the second parameter as a reference type to this type. For this reason we must pass in a pointer to the Graphic object since references to references are illegal in C++ and your local friendly compiler will tell you as much if you attempt it! Also, when using std::mem_fun_ref in combination with std::bind2nd like this the member function must be const as std::bind2nd (and std::bind1st for that matter) don’t allow the use of non-const adaptors as parameters. In our case of using a wrapper class, which acts as a kind of functor with a constant state that we don’t need to change, this is not an issue, then again, there’s always that lovely keyword mutable.

No comments: