Recently I have been making more of an effort to use the STLs functional features and write more generic code using templates. So far I have found writing quick little functors (after a quick mistake with function pointers) and the likes of std::for_each and std::transform very useful. Recently I have discovered that these functions take the functors by value and not by reference. Now I am sure that there is reason behind this (probably because it allows the compiler greater control to add functionality such as multi-threading behind the scenes) but is a little annoy when you aren’t using this.
The solution is to use a proxy functor, which stores the real functor by reference and passes on any calls. This can be copied easily as all it contains is the reference. Coming from my OO background (should have stuck with my functional roots) I want this proxy to inherit from std::unary_function and take one as a parameter (this is different from how the STL implements std::for_each etc. if you were wondering which just use templates). My solution is the following:
template<typename argument, typename result>
struct proxy_unary_function: public std::unary_function<argument, result> {
public:
proxy_unary_function(
std::unary_function<argument, result>& unary_function) :
unary_function_(unary_function) {
}
result operator()(argument arg) {
return unary_function_(arg);
}
private:
std::unary_function<argument, result>& unary_function_;
};
Pretty solid I thought although it needs a specialisation for the case where the result is void as you don’t want to return the result of a void function. I code up my functor which for this example I use a functor which returns the square of the output:
template<typename argument, typename result>
struct square: public std::unary_function<argument, result> {
public:
result operator()(argument arg) {
return arg * arg;
}
};
I’ve deliberately allowed both the argument and result types to be set independently so that the user can decide if they would like the argument to be passed by reference or value. Now all that is left is to test the code which is where I become a little stuck, because the following code doesn’t compile to my horror:
std::vector<int> vs;
square<int&, int> sq;
std::transform(vs.begin(), vs.end(), vs.begin(),
proxy_unary_function<int&, int>(sq));
With the error not being particular useful as always:
First Code Snippet:10:29: error:
no match for call to '(std::unary_function<int&, int>) (int&)'
This has had me stumped for a couple of hours and I’m not getting anywhere with. I know that I can solve it by writing my own std::unary_function replacement but I don’t like re-inventing the wheel (even if it could be slightly better… debatable). If I do then I end up with the following:
template<typename argument, typename result>
struct unary_function {
public:
virtual ~unary_function() {};
virtual result operator()(argument arg) = 0;
};
Moving everything to using my unary_function implementation allows everything to compile and work as expected. I can also use this as proof that I haven’t got any of my templates incorrect which could cause the above error as all I have done to use my implementation is remove the prefix std:: when selecting.
How does my implementation differ, well in one major way and that is that I specify that classes that inherit from my implementation must provide a function operator to satisfy my pure virtual function. The STL does not require this (which I find a little odd I must say!).
Looking further into the STL code, as I previously have said the implementations of std::for_each do not require a function of which has a base of std::unary_function and are generically typed instead. This must be for two reasons:
- Doing so would prevent the use of function pointers which have a different type to functors.
- Object slicing would make their use impossible.
Thats fine and great news, but it doesn’t really help me with my problem. From what I can see, the compiler is complaining that it cannot call the function operator. It doesn’t provide me with any alternatives or suggestions. Additionally it seems to believe that I’m not returning anything from the pass through function, although I know with compiler error messages this could just be indicative of the first problem.
So there is my trouble with functors.
Update – 15/01/2012
- After discussing this issue with colleagues at work, it seems that I overlooked a very simple issue with the problem code. The std::unary_function does not specify that you must implement the operator() function, then when the proxy_unary_function takes the unary_function it cannot call the operator() function upon it as that type does not have that interface. I’ve got more to follow up on this and hope to write a blog post on it soon.
- Additionally, the code from the post is now available on GitHub, you can find it here.