
After few months of inactivity I came back to a local function syntax. Instead of trying to bind local variables, I implemented a couple of macros that simply assign a value to a function pointer: int BOOST_LOCAL_FUNCTION(char c) { std::cout << c << '\n'; return 1; } BOOST_LOCAL_FUNCTION_DECL(a) a('A'); // output: // A This code defines int (*a)(char) and assigns to it a pointer to the body of local function shown above. Half-preprocessed output is int (*sig16)() = 0; typedef struct { static BOOST_TYPEOF(sig16()) body(char c) { std::cout << c << '\n'; return 1; } } local20; BOOST_AUTO(a, &local20::body); a('A'); It's also possible to assign a pointer to any object compartible with int (*) (char): boost::function<int(char)> zz; int BOOST_LOCAL_FUNCTION(char c) { std::cout << c << c << '\n'; return 2; } BOOST_LOCAL_FUNCTION_ASSIGN(zz) zz('Z'); // output: // ZZ Proof of concept code is in the end of this message. Currently, it doesn't handle void return (Boost.Typeof limitation) and doesn't compile when arguments or return type is a dependant name. gcc 3.4 often crashes on local function defined inside templates. #include <iostream> #include <boost/function.hpp> #include <boost/typeof/typeof.hpp> #define BOOST_LOCAL_FUNCTION (*BOOST_PP_CAT(sig,__LINE__))() = 0; \ typedef struct { static BOOST_TYPEOF(BOOST_PP_CAT(sig,__LINE__)()) body #define BOOST_LOCAL_FUNCTION_ASSIGN(f) } BOOST_PP_CAT(local,__LINE__); \ f = &BOOST_PP_CAT(local,__LINE__)::body; #define BOOST_LOCAL_FUNCTION_DECL(f) } BOOST_PP_CAT(local,__LINE__); \ BOOST_AUTO(f, &BOOST_PP_CAT(local,__LINE__)::body); int main() { int BOOST_LOCAL_FUNCTION(char c) { std::cout << c << '\n'; return 1; } BOOST_LOCAL_FUNCTION_DECL(a) a('A'); boost::function<int(char)> zz; int BOOST_LOCAL_FUNCTION(char c) { std::cout << c << c << '\n'; return 2; } BOOST_LOCAL_FUNCTION_ASSIGN(zz) zz('Z'); }

What's the advantage to using int BOOST_LOCAL_FUNCTION(char c) { std::cout << c << '\n'; return 1; } BOOST_LOCAL_FUNCTION_DECL(a) over: struct { int operator()(char c)const { std::cout << c << '\n'; return 1; } } a; ?

Joseph Gauterin <joseph.gauterin <at> googlemail.com> writes:
What's the advantage to using int BOOST_LOCAL_FUNCTION(char c) { std::cout << c << '\n'; return 1; } BOOST_LOCAL_FUNCTION_DECL(a)
over:
struct { int operator()(char c)const { std::cout << c << '\n'; return 1; } } a;
Local struct can't be passed to templates: boost::function<int(char)> f = a; error: conversion from `main()::<anonymous struct>' to non-scalar type `boost::function<int ()(char), std::allocator<void> >' requested -- Alexander

On 11/27/07, Alexander Nasonov <alnsn@yandex.ru> wrote:
After few months of inactivity I came back to a local function syntax. Instead of trying to bind local variables, I implemented a couple of macros that simply assign a value to a function pointer:
int BOOST_LOCAL_FUNCTION(char c) { std::cout << c << '\n'; return 1; } BOOST_LOCAL_FUNCTION_DECL(a)
What is the advantage of this with respect to: struct unnamed { static void _ (char c) { std::cout << c << '\n'; return 1; }}; (&unnamed::_)('a'); It is ugly, but I find the BOOST_LOCAL_FUNCTION_DECL macros even uglier. The macros would make sense only if they made it easy to close around the environment. Lately I've been using local structures like the 'unnamed' above to create 'lambdas' for standard algorithms instead (or in addition to) Boost.Lambda. They work very well in practice: std::vector<std::vector<int> > v = ...; struct pred { static bool _ (std::vector<int> const& x, int max_sum) { return std::accumulate(x.begin(), x.end()) < y; }}; v.erase(std::remove_if(v.begin(), v.end(), boost::bind(&pred::_, _1, 10)), v.end()); Easier to use than lambda and, most importantly expecially with older compilers, compile faster! -- gpd

Giovanni Piero Deretta <gpderetta <at> gmail.com> writes:
int BOOST_LOCAL_FUNCTION(char c)
[ skiped because gmane believes I'm overquoting ]
struct unnamed { static void _ (char c) { std::cout << c << '\n'; return 1; }};
(&unnamed::_)('a');
It is ugly, but I find the BOOST_LOCAL_FUNCTION_DECL macros even uglier.
I disagree. It's not easy to recognise return type and arguments in your code. -- Alexander

I disagree. It's not easy to recognise return type and arguments in your code. What about this then:
struct local { static int a(char c) { std::cout << c << '\n'; return 1; } static int zz(char c) { std::cout << c << c << '\n'; return 2; } }; local::a('a') local::zz('Z') std::string str = "foo"; std::for_each(str.begin(),str.end(),&local::a);

Joseph Gauterin <joseph.gauterin <at> googlemail.com> writes:
I disagree. It's not easy to recognise return type and arguments in your code. What about this then:
struct local { static int a(char c) { std::cout << c << '\n'; return 1; } Not much better to my taste. They are inside a nested scope.
-- Alexander

(&unnamed::_)('a');
It is ugly, but I find the BOOST_LOCAL_FUNCTION_DECL macros even uglier.
I disagree. It's not easy to recognise return type and arguments in your code. -- Alexander
But isn't the only point of local functions, the fact that you can access context information (like, the arguments passed to your function)? Best, John -- http://John.Torjo.com -- C++ expert ... call me only if you want things done right

John Torjo <john.groups <at> torjo.com> writes:
But isn't the only point of local functions, the fact that you can access context information (like, the arguments passed to your function)?
Did you mean 'passed to your *outer* function'? To pass bound arguments you either 1. struct local { int& i; void body(char c) { i = c; } }; 2. struct local { static void body(int&, char c) { i = c; } The body of the former isn't static and can't be passed to a template. The former doen't have an opening bracket before your first unbound argument: int i; void BOOST_LOCAL_FUNCTION(i) char c) { } ... Note missing '(' in the first line. -- Alexander

Alexander Nasonov wrote:
John Torjo <john.groups <at> torjo.com> writes:
But isn't the only point of local functions, the fact that you can access context information (like, the arguments passed to your function)?
Did you mean 'passed to your *outer* function'?
Yup, that's what I meant. Best, John -- http://John.Torjo.com -- C++ expert ... call me only if you want things done right

AMDG Alexander Nasonov <alnsn <at> yandex.ru> writes:
int i; void BOOST_LOCAL_FUNCTION(i) char c) { } ...
Note missing '(' in the first line.
Hmmm, what about adding another parenthesis before? void (BOOST_LOCAL_FUNCTION(i) char c) I don't really like that either... How about allowing either: void BOOST_LOCAL_FUNCTION(BOOST_LOCAL_FUNCTION_BIND(i), char c) or void BOOST_LOCAL_FUNCTION(char c) depending on whether binding is needed? I haven't thought about it too hard, but I think it should be possible to handle both. void BOOST_LOCAL_FUNCTION(BOOST_LOCAL_FUNCTION_BIND((ref i)), char c) could expand to void // BOOST_LOCAL_FUNCTION result_type_getter(); typedef ... result_type; void* bound_args = 0; typedef struct { static result_type body //BOOST_LOCAL_FUNCTION_BIND (); } ignore; boost::tuple<int&> bound_args_impl; bound_args = &bound_args typedef struct { static result_type body(int&, end_of_bound_args_t, , char c) { // function body goes here } //BOOST_LOCAL_FUNCTION_DEF } impl; typedef BOOST_TYPEOF(impl::body) function_type; // now we can use TMP to figure out whether there // are any bound args, and if so what the tuple<...> // type is. In Christ, Steven Watanabe

Steven Watanabe <steven <at> providere-consulting.com> writes:
void BOOST_LOCAL_FUNCTION(BOOST_LOCAL_FUNCTION_BIND(i), char c)
or
void BOOST_LOCAL_FUNCTION(char c)
depending on whether binding is needed?
I haven't thought about it too hard, but I think it should be possible to handle both. void BOOST_LOCAL_FUNCTION(BOOST_LOCAL_FUNCTION_BIND((ref i)), char c) could expand to ...
Wow! I like it. I haven't implemented it yet but the sketch looks absolutely correct. Now the problem is convincing people to use TWOOOOOO(LOOOOOOONG macros ;) It's always possible to #define LFN BOOST_LOCAL_FUNCTION #define LFN_BIND(args) BOOST_LOCAL_FUNCTION_BIND(args) #define LFN_DECL(f) BOOST_LOCAL_FUNCTION_DECL(f) and then void LFN (LFN_BIND(i), char c) { } LFN_DECL(f) but I doubt that all will accept it. -- Alexander

AMDG Alexander Nasonov <alnsn <at> yandex.ru> writes:
Wow! I like it. I haven't implemented it yet but the sketch looks absolutely correct.
There's a little problem. How to get the arguments from the opening macro to the closing one without interfering with another local function. I think I've found a solution for that, too. The key is that we need to declare a variable if it has not yet been declared. We can test whether it has been declared by creating a global with the same name and using a sizeof expression. So, now we need some syntax that will be either a declaration or something else depending on a boolean condition. To do this we can take advantage of the ambiguity that requires the ::template syntax for dependent templates. typedef char no; struct yes { no dummy[2]; }; template<bool> struct conditional_declare; struct undeclared {}; template<> struct conditional_declare<true> { template<int> struct apply { void* value; friend void operator>(bool, const apply&) {} }; }; template<> struct conditional_declare<false> { static const int apply = 0; }; template<class T> yes is_declared(const T&); no is_declared(const undeclared&); undeclared local_function_bound_args; #define BOOST_LOCAL_FUNCTION_DECLARE_ARGS() \ conditional_declare<sizeof(is_declared(local_function_bound_args)) == \ sizeof(no)>::apply<0> local_function_bound_args int main() { BOOST_LOCAL_FUNCTION_DECLARE_ARGS(); // declares local_function_bound_args BOOST_LOCAL_FUNCTION_DECLARE_ARGS(); // turns into a pair of comparisons } In Christ, Steven Watanabe

Steven Watanabe <steven <at> providere-consulting.com> writes:
#define BOOST_LOCAL_FUNCTION_DECLARE_ARGS() \ conditional_declare<sizeof(is_declared(local_function_bound_args)) == \ sizeof(no)>::apply<0> local_function_bound_args
Does it work when a bound argument depends on a template parameter??? The syntax in this case is *template* conditional_declare<...>::apply<0> local_function_bound_args; which doesn't compile when *apply* is not a template. -- Alexander

AMDG Alexander Nasonov <alnsn <at> yandex.ru> writes:
Steven Watanabe <steven <at> providere-consulting.com> writes:
#define BOOST_LOCAL_FUNCTION_DECLARE_ARGS() \ conditional_declare<sizeof(is_declared(local_function_bound_args)) == \ sizeof(no)>::apply<0> local_function_bound_args
Does it work when a bound argument depends on a template parameter???
The syntax in this case is *template* conditional_declare<...>::apply<0> local_function_bound_args;
which doesn't compile when *apply* is not a template.
You are correct, it wouldn't work if conditional_declare<...> is a dependent type. However, void* is sufficient so it should be okay. In Christ, Steven Watanabe
participants (5)
-
Alexander Nasonov
-
Giovanni Piero Deretta
-
John Torjo
-
Joseph Gauterin
-
Steven Watanabe