Unary Function
Is there anything called bottom-up learning? This is an example. Go through some source code - try to understand whats going on - ask questions.
#include <functional>
struct Check_state: public std::unary_function<int, bool>
{
explicit Check_state(int state = 0): m_state(state){}bool operator()(int x) const
{
return x == m_state;
}private:
int m_state;};
This piece of code shows a class that can be instantiated to create functors or function objects, as it overrides operaor(); the thing to notice is the fact that we are derving it from unary_function<>, lets ask questions and investigate the matter.
Why does stl have unary_function<Arg, Result> and why did we derive from it?
Well, unary_function<Arg, Result> is not just a unary function. It is the base class for an adaptable unary function. In this case we derived the functor from unary_function<Arg, Result> to make Check_state functor an adaptable unary function. By the way this particular example is really an example of adaptable predicate. ( A predicate is a boolean valued unary function i.e. a unary function that returns a bool - the truth or falsehood of some state or condition ).
Why do I need an adaptable unary function ?
As the name suggests, an adaptable unary function can be used by adaptors. Now that Check_state is an adaptable unary function, it can be used by adaptors.
What is the use of adaptors ?
An adaptor or more specifically a function object adaptor can be used to extend or specialize a function object. For example a negator is an adaptor that reverses the truth value of a predicate. The advantage of using such an adaptor is that you do not have to define a new class when you need a function object to do the reverse of your current, already defined function object does. So for the purpose of our example we have the adaptor unary_negator<T>, for the above Check_state class, we can have something like :
Check_state check_state(5); // instantiating Check_state to get a function object check_state, check_state(num) will be true whenever num is equal to 5.
std::unary_negate<Check_state> ncheck_state(state); //instantiating std::unary_negate<Check_state> to get the negator function object of check_state.
Here every time check_state(num) says true, ncheck_state(num) says false.
We rarely instantiate std::unary_negate<T> in real world, we always have a corresponding convenient function, in this case not1() - it’s just that we take advantage of the fact that normally you do not have to mention the type for a template function, compiler takes of it. gcc implementation of not1 looks like this :
template <class _Predicate>
inline unary_negate<_Predicate> not1(const _Predicate& __pred)
{
return unary_negate<_Predicate>(__pred);
}
Okay, now back to the starting point, so why do we need to have an adaptable unary function to be able to use adaptors ?
This is a ‘white box’ type question. We already know that we can use unary adaptors only with adaptable unary functions, and we can make a functor class adaptable by deriving it from the corresponding adaptable base class. These are the rules, but we have not yet explained why we need these rules. Lets check the implementation of unary_negate<T> :
template <class _Predicate>
class unary_negate : public unary_function<typename _Predicate::argument_type, bool>
{
protected:
_Predicate _M_pred;
public:
explicit unary_negate(const _Predicate& __x) : _M_pred(__x) {}bool operator()(const typename _Predicate::argument_type& __x) const
{
return !_M_pred(__x);
}
};
It is pretty simple, we keep an instance of _Predicate as _M_pred and do !_M_pred(__x) whenever the function object is invoked with __x. The only problem was to know the type of __x which we get from _Predicate::argument_type. Now this argument_type is actually a typedef defined in unary_function<A, R>. So now if you check the implementation of unary_function<A, R>, you see that it defines typedefs, argument_type and result_type. You need these typedefs if you want your functor to be adaptable ; deriving from unary_function<A, R> is just a convenient way of having those typedefs in your class.
template <class _Arg, class _Result>
struct unary_function
{
typedef _Arg argument_type; ///< @c argument_type is the type of the
/// argument (no surprises here)typedef _Result result_type; ///< @c result_type is the return type
};
Okay so is a normal c++ function taking a single argument, a unary function ?
Ofcourse and so is a function pointer taking a single argument. By the way, I think we really need an umbrella term covering all ‘calleables’. Though in some c++ literature ‘function object’ or ‘functor’ has been used as the generic term for all calleables, I think Stroustrup does not think so, he said ” An object of a class with an application operator is called a function-like object, a functor, or simply a function object. ” But to come back to our question : though a normal function or function pointer is a unary function, they are surely not adaptable unary functions as they dont have those typedefs ! We can get an adaptable unary function from a function pointer using the adaptor pointer_to_unary_function<T> or rather the convenient method ptr_fun() ( just like not1() above). Just check the implementation to see whats going on :
template <class _Arg, class _Result>
class pointer_to_unary_function : public unary_function<_Arg, _Result>
{
protected:
_Result (*_M_ptr)(_Arg);
public:
pointer_to_unary_function() {}explicit
pointer_to_unary_function(_Result (*__x)(_Arg))
: _M_ptr(__x) {}_Result
operator()(_Arg __x) const
{ return _M_ptr(__x); }
};
Journey
Agile is interesting because agile is human. One of of the practices that I think should be there in any agile methodology is ‘continuous training’. I abhor most of those corporate training sessions where you learn ‘advanced techniques’ of something, sitting in a classroom, five days in a row, six hours everyday, staring at the white board, trying to hide your yawns. I have reasons to believe that any learning programme should be as agile as any development programme. You learn something, you use your knowledge to solve real world problems, get stuck somewhere, learn some more, in an iterative continuous process.
But unlike projects the learning process never ends, you keep learning, keep exploring newer avenues, keep innovating … ( and get some awareness of some subtler kind of life beneath it all ?!)