[local_function] passing local functions as template parameters

Hello all, Can you please check if the following mechanism can be used to pass local functor classes as template parameters? PROBLEM If I understand the C++ standard right, local classes cannot be passed as template parameters. Because of that, if I am defining a functor to pass to `std::for_each` I have to define it outside the local scope, far from the `for_each` call, which is not ideal (from cplusplus.com): struct myclass { void operator() (int i) {cout << " " << i;} } myobject; int main () { // Ideally `myclass` would be defined here locally... vector<int> myvector; myvector.push_back(10); myvector.push_back(20); myvector.push_back(30); cout << "\nmyvector contains:"; for_each (myvector.begin(), myvector.end(), myobject); cout << endl; return 0; } The code above does not compile on GCC if `myclass` is defined locally within `main`'s scope (it still compiles on MSVC instead...). SOLUTION (?) The following mechanism allows to define the functor body locally (in a local function which also binds the in-scope parameter `x`) and then pass it as a template parameter (to `boost::bind()` and then to `std::for_each()`). The trick is for the local function to override the virtual body of a global class and then pass the local function as a pointer to the global class while dynamic binding will actually execute the locally overridden body code. #include <boost/function.hpp> #include <boost/bind.hpp> #include <iostream> #include <algorithm> #include <vector> template<typename F> struct lfn_global {}; template<typename A1> struct lfn_global<void (A1)> { virtual void body(A1) {} }; // Similarly define `lfn_global` for other function arities and return types. int main () { double x = 5.6; struct lfn_t: lfn_global<void (int)> { lfn_t(double const& bound_x): x(bound_x) {} void body(int i) { std::cout << " " << i + x; // Local func body. } private: double const& x; } lfn_obj(x); // x is const& bound from scope. boost::function<void (int)> lfn = boost::bind( &lfn_global<void (int)>::body, (lfn_global<void (int)>*)(&lfn_obj), _1); std::vector<int> v; v.push_back(10); v.push_back(20); v.push_back(30); std::cout << "v: "; std::for_each(v.begin(), v.end(), lfn); // I can pass the local functor! std::cout << std::endl; return 0; } This code compiles on both GCC and MSVC, it correctly prints "v: 15.6 25.6 35.6". If this indeed works, I can hide all the extra code it requires behind the `BOOST_LOCAL_FUNCTION...` macros. Can you please try this code on other compilers? Do you see any issue with this code? Thank you very much. -- Lorenzo

On 12/09/10 16:41, Lorenzo Caminiti wrote:
Hello all,
Can you please check if the following mechanism can be used to pass local functor classes as template parameters?
<snip>
SOLUTION (?)
The following mechanism allows to define the functor body locally (in a local function which also binds the in-scope parameter `x`) and then pass it as a template parameter (to `boost::bind()` and then to `std::for_each()`). The trick is for the local function to override the virtual body of a global class and then pass the local function as a pointer to the global class while dynamic binding will actually execute the locally overridden body code.
#include <boost/function.hpp> #include <boost/bind.hpp> #include <iostream> #include <algorithm> #include <vector>
template<typename F> struct lfn_global {}; template<typename A1> struct lfn_global<void (A1)> { virtual void body(A1) {} }; // Similarly define `lfn_global` for other function arities and return types.
int main () { double x = 5.6;
struct lfn_t: lfn_global<void (int)> { lfn_t(double const& bound_x): x(bound_x) {} void body(int i) { std::cout << " " << i + x; // Local func body. } private: double const& x; } lfn_obj(x); // x is const& bound from scope. boost::function<void (int)> lfn = boost::bind( &lfn_global<void (int)>::body, (lfn_global<void (int)>*)(&lfn_obj), _1);
std::vector<int> v; v.push_back(10); v.push_back(20); v.push_back(30);
std::cout << "v: "; std::for_each(v.begin(), v.end(), lfn); // I can pass the local functor! std::cout << std::endl;
return 0; }
This code compiles on both GCC and MSVC, it correctly prints "v: 15.6 25.6 35.6". If this indeed works, I can hide all the extra code it requires behind the `BOOST_LOCAL_FUNCTION...` macros.
Can you please try this code on other compilers?
Compiles fine on clang and icc.
Do you see any issue with this code?
Yes, by using boost::function you're introducing an extra virtual function call in each invocation of lfn. It's optimistic to assume that the compiler will figure out what's going on well enough to optimize all that away. OTOH, I looked at the assembly generated by icc and it does indeed seem to have arranged for both calls to be made directly, not through vtables, so it is possible, but oddly the calls are not inlined (I guess icc wasn't able to prove that there's only one call to lfn_t::body, which it should in principle have been able to), so lfn_t::body is still two calls away from main(). For definiteness, the piece of Boost.Function called through is a 54-byte function including one conditional branch. I think it would be worth rolling your own simpler wrapper; something along these lines: #include <boost/function.hpp> #include <boost/bind.hpp> #include <iostream> #include <algorithm> #include <vector> template<typename F> struct lfn_global {}; template<typename A1> struct lfn_global<void (A1)> { virtual void operator()(A1) {} }; // Similarly define `lfn_global` for other function arities and return types. template<typename F> struct ref_wrapper; template<typename A1> struct ref_wrapper<void (A1)> { typedef lfn_global<void (A1)> wrapped; explicit ref_wrapper(wrapped& f) : f_(f) {} void operator()(A1 a1) { f_(a1); } wrapped& f_; }; int main () { double x = 5.6; struct lfn_t: public lfn_global<void (int)> { lfn_t(double const& bound_x): x(bound_x) {} void operator()(int i) { std::cout << " " << i + x; // Local func body. } private: double const& x; } lfn_obj(x); // x is const& bound from scope. ref_wrapper<void (int)> lfn(lfn_obj); std::vector<int> v; v.push_back(10); v.push_back(20); v.push_back(30); std::cout << "v: "; std::for_each(v.begin(), v.end(), lfn); std::cout << std::endl; return 0; } (I would have used boost::ref, but that doesn't overload operator()) In this case icc chooses to inline the call to ref_wrapper::operator(), so that lfn_t::operator() is called directly from main (as before, that one is still not inlined). Anyway, the upshot of what I'm trying to say is: you may wish to make the compiler's life easier to head off efficiency concerns, and you should certainly do some profiling to measure the cost of a virtual function call in this context. But, all in all, it's a neat trick. John Bytheway

On Sun, Sep 12, 2010 at 1:40 PM, John Bytheway <jbytheway+boost@gmail.com> wrote:
On 12/09/10 16:41, Lorenzo Caminiti wrote:
Can you please try this code on other compilers?
Compiles fine on clang and icc.
Thanks a lot for checking this!
Do you see any issue with this code?
Yes, by using boost::function you're introducing an extra virtual function call in each invocation of lfn. It's optimistic to assume that the compiler will figure out what's going on well enough to optimize all that away.
OTOH, I looked at the assembly generated by icc and it does indeed seem to have arranged for both calls to be made directly, not through vtables, so it is possible, but oddly the calls are not inlined (I guess icc wasn't able to prove that there's only one call to lfn_t::body, which it should in principle have been able to), so lfn_t::body is still two calls away from main(). For definiteness, the piece of Boost.Function called through is a 54-byte function including one conditional branch.
I think it would be worth rolling your own simpler wrapper; something along these lines:
#include <boost/function.hpp> #include <boost/bind.hpp> #include <iostream> #include <algorithm> #include <vector>
template<typename F> struct lfn_global {}; template<typename A1> struct lfn_global<void (A1)> { virtual void operator()(A1) {} }; // Similarly define `lfn_global` for other function arities and return types.
template<typename F> struct ref_wrapper;
template<typename A1> struct ref_wrapper<void (A1)> { typedef lfn_global<void (A1)> wrapped; explicit ref_wrapper(wrapped& f) : f_(f) {} void operator()(A1 a1) { f_(a1); } wrapped& f_; };
int main () { double x = 5.6;
struct lfn_t: public lfn_global<void (int)> { lfn_t(double const& bound_x): x(bound_x) {} void operator()(int i) { std::cout << " " << i + x; // Local func body. } private: double const& x; } lfn_obj(x); // x is const& bound from scope. ref_wrapper<void (int)> lfn(lfn_obj);
std::vector<int> v; v.push_back(10); v.push_back(20); v.push_back(30);
std::cout << "v: "; std::for_each(v.begin(), v.end(), lfn); std::cout << std::endl;
return 0; }
(I would have used boost::ref, but that doesn't overload operator())
In this case icc chooses to inline the call to ref_wrapper::operator(), so that lfn_t::operator() is called directly from main (as before, that one is still not inlined).
Anyway, the upshot of what I'm trying to say is: you may wish to make the compiler's life easier to head off efficiency concerns, and you should certainly do some profiling to measure the cost of a virtual function call in this context. But, all in all, it's a neat trick.
I understand your concern and suggestion. If this trick works and it makes it into Boost.LocalFunction, I will definitely consider performance optimizations. -- Lorenzo

On 9/12/2010 12:15 PM, Lorenzo Caminiti wrote:
On Sun, Sep 12, 2010 at 1:40 PM, John Bytheway <jbytheway+boost@gmail.com> wrote: [...]
Anyway, the upshot of what I'm trying to say is: you may wish to make the compiler's life easier to head off efficiency concerns, and you should certainly do some profiling to measure the cost of a virtual function call in this context. But, all in all, it's a neat trick.
Yes, +1 on "neat trick". Based on other emails from Lorenzo, it looks like other similar libraries to proposed Boost.LocalFunction can either be encompassed by LocalFunction or trivially implemented in terms of it. I'm interested in the continued development. 2 very minor comments: First, I think the use of this_ is certainly an acceptable limitation, given that the alternative would be to use undefined (in, I'm guessing, a very practical sense) behavior. Second, I prefer the keyword "bind" (present) where you now use "bound" (past), but it's hardly a deal-breaker.
I understand your concern and suggestion. If this trick works and it makes it into Boost.LocalFunction, I will definitely consider performance optimizations.
I think this specific feature (ability to use a local function as a template parameter) could be very useful indeed. Overall, I still think there's a place for this kind of functionality (LocalFunction) alongside Boost.Phoenix. Question: Does LocalFunction provide any advantages over a named C++0x lambda, on those compilers that support such a construct? I can see the value of LocalFunction on compilers that don't have (or have buggy) C++0x lambdas, but what about those compilers that do have good support for C++0x lambdas? I apologize for drifting somewhat off-topic from the original subject... - Jeff

On Sun, Sep 12, 2010 at 9:17 PM, Jeffrey Lee Hellrung, Jr. <jhellrung@ucla.edu> wrote:
On 9/12/2010 12:15 PM, Lorenzo Caminiti wrote:
On Sun, Sep 12, 2010 at 1:40 PM, John Bytheway <jbytheway+boost@gmail.com> wrote:
[...]
Anyway, the upshot of what I'm trying to say is: you may wish to make the compiler's life easier to head off efficiency concerns, and you should certainly do some profiling to measure the cost of a virtual function call in this context. But, all in all, it's a neat trick.
Yes, +1 on "neat trick". Based on other emails from Lorenzo, it looks like other similar libraries to proposed Boost.LocalFunction can either be encompassed by LocalFunction or trivially implemented in terms of it. I'm interested in the continued development.
I also think that, if this trick really works, it's a major plus for Boost.LocalFunction. Defining the function locally, close to the `std::for_each()` call (or similar algorithms) is an important use case for C++0x lambdas (see http://en.wikipedia.org/wiki/C%2B%2B0x#Lambda_functions_and_expressions). This can now be essentially covered by Boost.LocalFunction keeping the usual C++ syntax for the function body definition -- but with the limitation that the local function cannot be defined within the `std::for_each()` call (at the expression level) but it can be defined just before such a call (at the scope level).
2 very minor comments: First, I think the use of this_ is certainly an acceptable limitation, given that the alternative would be to use undefined (in, I'm guessing, a very practical sense) behavior. Second, I prefer the keyword "bind" (present) where you now use "bound" (past), but it's hardly a deal-breaker.
It's trivial to change from `bound` to `bind`, or any other word (but no symbols) for that matter. I just need Boosters to voice their opinions (more) on what this "keyword" should be. I also think `bind` is more readable but the past form `bound` might be more consistent with Boost.Parameter `deduced`... BTW, shall I use `closure` instead of `bind`/`bound`? Would `closure` make more sense in this context?
I understand your concern and suggestion. If this trick works and it makes it into Boost.LocalFunction, I will definitely consider performance optimizations.
I think this specific feature (ability to use a local function as a template parameter) could be very useful indeed.
Overall, I still think there's a place for this kind of functionality (LocalFunction) alongside Boost.Phoenix.
Yes, as mentioned before, the advantage of Boost.LocalFunction is to retain actual C++ syntax within the function body definition (but Boost.Phoenix and Boost.Lambda allows for "function" definition also at the expression level).
Question: Does LocalFunction provide any advantages over a named C++0x lambda, on those compilers that support such a construct? I can see the value of LocalFunction on compilers that don't have (or have buggy) C++0x lambdas, but what about those compilers that do have good support for C++0x lambdas?
I am NOT a lambda expert but I have been studying the topic since I started Boost.LocalFunction. As far as I know, no there is no advantage in using Boost.LocalFunction over C++0x named lambdas. On the contrary lambdas can be defined also at the expression level while Boost.LocalFunction cannot. Boosters, please correct me if I am wrong on this (especially given my shallow understanding of lambdas). -- Lorenzo
participants (3)
-
Jeffrey Lee Hellrung, Jr.
-
John Bytheway
-
Lorenzo Caminiti