By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
429,435 Members | 2,033 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 429,435 IT Pros & Developers. It's quick & easy.

Why no local functions ?

P: n/a
Hello all.

I program C++ since a lot of time now and I still don't know this simple
thing: what's the problem with local functions so they are not part of
C++ ?

There surely are many people who will find them very helpfull. gcc has
them as a non-standard option, but only when compiling C language code,
so I'm afraid there might be some obscure reason why local functions are
not so easy to be dealt with in C++, which I do not yet know.

Anyone knows what is the problem with supporting local functions in C++ ?

Thank you
Timothy Madden
Romania
Nov 27 '05 #1
Share this Question
Share on Google+
23 Replies


P: n/a
What do you mean by 'local' functions? Functions at file scope?

Nov 27 '05 #2

P: n/a
* Timothy Madden:

I program C++ since a lot of time now and I still don't know this simple
thing: what's the problem with local functions so they are not part of
C++ ?


C++ supports local functions in the form of member functions of local
classes.

Such functions do not have access to the enclosing scope's variables,
and in particular they do not have access to the enclosing function's
arguments.

The main reason for that restriction is probably that local functions
la Pascal are inefficient (they require a linked stack structure, look
up "Dijkstra displays"), not really of any help in solving problems, and
can be easily emulated if you _really_ need them.

Another restriction is that, as I recall, they do not have linkage, e.g.
you cannot use a locally defined function as a template parameter.

I'm not sure of the reason for that latter restriction.

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
Nov 27 '05 #3

P: n/a
da********@warpmail.net wrote:
What do you mean by 'local' functions? Functions at file scope?


No, I think he means Pascal like local functions.

e.g.

int f()
{
int i;

void f() // A
{
i = 2; // links to int f()::i
}

f(); // links to int f()::void f()

return i; // returns 2
}
Nov 28 '05 #4

P: n/a
Gianni Mariani wrote:
da********@warpmail.net wrote:
What do you mean by 'local' functions? Functions at file scope?


No, I think he means Pascal like local functions.

e.g.

int f()
{
int i;

void f() // A
{
i = 2; // links to int f()::i
}

f(); // links to int f()::void f()

return i; // returns 2
}


Yes, I mean Pascal like local functions. Local functions would indeed
have direct access to all already declared variables and symbols in the
enclosing function, and could be nested on many levels.

They were so cool when I was programming in Pascal and I was sorry to
give them up to switch to C++.

Timothy Madden
Romania
Nov 28 '05 #5

P: n/a
Alf P. Steinbach wrote:
* Timothy Madden:
I program C++ since a lot of time now and I still don't know this simple
thing: what's the problem with local functions so they are not part of
C++ ?

C++ supports local functions in the form of member functions of local
classes.

Such functions do not have access to the enclosing scope's variables,
and in particular they do not have access to the enclosing function's
arguments.

The main reason for that restriction is probably that local functions
la Pascal are inefficient (they require a linked stack structure, look
up "Dijkstra displays"), not really of any help in solving problems, and
can be easily emulated if you _really_ need them.

Another restriction is that, as I recall, they do not have linkage, e.g.
you cannot use a locally defined function as a template parameter.

I'm not sure of the reason for that latter restriction.


Yes, exactly. C++ already has static functions in local classes. Why
should it not directly have local functions ?

Would you please explain me why a 'linked stack structure' is
inefficient ? I think this are compiler internals and after all anybody
is free not to use such local functions. Having me to emulate them would
also be inefficient.

How do you find emulating local functions easy ? The only way I know of
is with many arguments passed by reference to a normal C++ function.

I do not like the restriction of having only global or namespace symbols
as possible template arguments, but I can see a reason for it: It allows
a programmer to separate template declarations (in headers) from
template definitions (in special source files) and then only use
explicit template instantiaton, all over an entire project or program.
Explicit instantiations would be all placed in dedicated source files,
that so have guaranteed visibility to all template arguments ever used
(template arguments are allways global or extern symbols). A programmer
might want to take this presumably difficult aproach (to using
templates) as a first step in preparation for switching to a compiler
that supports export, or because the full template definitions are based
on too much code that normaly has to be all included in headers.

By the way: what popular compilers do you know of that support export ?

Thank you
Timothy Madden
Romania
Nov 28 '05 #6

P: n/a
Timothy Madden wrote:
I program C++ since a lot of time now and I still don't know this simple
thing: what's the problem with local functions so they are not part of C++
?


Because a language should reserve (most) syntax until it can achieve some
new effect with that syntax.

The best feature for a private method would be a block closure, like this:

void foo() {
int bar = 42;
void thread() {
doSomethingTo(&bar);
}
beginThread(thread);
}

In that example, the inner function thread() can refer to the outer
function's local variable. Without that effect, the nested function syntax
is only syntactic sugar.

However, the C languages use a definition that assists implementors to store
each function invocation's local variables on a hardware stack. This helps
compete with Assembler. Without languages as fast as C++, we would have the
odious choice between rapid development in slow languages like Smalltalk or
Python, or slow development in rapid Assembly code.

In my example, thread()'s access to bar persists after the function foo()
returns. This breaks the deal that 'bar' can live on a hardware stack.
Compiler implementors must find alternative representations for variables,
and must taint every variable used in a closure. The compiler may or may not
know that beginThread() might allow thread() to call after foo() returns, so
the compiler must chose pessimism, and the variable bar would have a lot of
overhead.

Incidentally, in terms of design without regard to performance, block
closures allow a variable like bar to have the longest lifespan with the
narrowest scope, so block closures are good things. We can acheive them with
reference-counted smart pointers. Very-high level languages that provide
block closures give their variables this representation too.

--
Phlip
http://www.greencheese.org/ZeekLand <-- NOT a blog!!!
Nov 28 '05 #7

P: n/a
* Timothy Madden:
Alf P. Steinbach wrote:
* Timothy Madden:
I program C++ since a lot of time now and I still don't know this simple
thing: what's the problem with local functions so they are not part of
C++ ?

C++ supports local functions in the form of member functions of local
classes.

Such functions do not have access to the enclosing scope's variables,
and in particular they do not have access to the enclosing function's
arguments.

The main reason for that restriction is probably that local functions
la Pascal are inefficient (they require a linked stack structure, look
up "Dijkstra displays"), not really of any help in solving problems, and
can be easily emulated if you _really_ need them.

Another restriction is that, as I recall, they do not have linkage, e.g.
you cannot use a locally defined function as a template parameter.

I'm not sure of the reason for that latter restriction.


Yes, exactly. C++ already has static functions in local classes. Why
should it not directly have local functions ?


That's a good question, if you mean local functions with the same (lack
of) access. But it's no big deal to wrap it in a class.

Would you please explain me why a 'linked stack structure' is
inefficient ?
It has to be linked... ;-)

I'm not sure exactly how inefficient this is.

How do you find emulating local functions easy ? The only way I know of
is with many arguments passed by reference to a normal C++ function.
You can collect those many arguments in one argument.

By the way: what popular compilers do you know of that support export ?


Comeau. Also it's said there's an undocumented compiler switch for the
Intel compiler where you can turn on the 'export' feature. And
apparently Borland tried it out as a beta feature but didn't go for it.

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
Nov 28 '05 #8

P: n/a
Phlip wrote:
Timothy Madden wrote:

I program C++ since a lot of time now and I still don't know this simple
thing: what's the problem with local functions so they are not part of C++
?

Because a language should reserve (most) syntax until it can achieve some
new effect with that syntax.

The best feature for a private method would be a block closure, like this:

void foo() {
int bar = 42;
void thread() {
doSomethingTo(&bar);
}
beginThread(thread);
}


Hey !!!!
That is a logic or semantic error!

My question is more about syntax allowed in C++.
The above code could also trigger a syntax error if compilers implement
the type for expression _&thread_ to be something like void (foo::*)(),
that is even if you take the function's address, the pointer type is
still 'pointer to nested function in foo'. Of course beginThread() has a
prototype stating it takes pointers to real functions, since it is
likely to be declared by the library, and foo is likely to be declared
latter in user code.

Timothy Madden
Romania
Nov 28 '05 #9

P: n/a
Timothy Madden wrote:
Hey !!!!
That is a logic or semantic error!


I can't tell if you read my post. I don't give a darn about the binding of
thread(). I discussed the binding of bar.

--
Phlip
http://www.greencheese.org/ZeekLand <-- NOT a blog!!!
Nov 28 '05 #10

P: n/a
Timothy Madden wrote:
Hello all.

I program C++ since a lot of time now and I still don't know this simple
thing: what's the problem with local functions so they are not part of
C++ ?


Local functions are useless if you cannot do indrection on them such
that the resulting function pointers carry with them the dynamic
environment instantiated from their lexical scope.

Even if you support downward funargs only, it complicates the
representation of a function pointer. The pointer must carry not only
the address, but additional information about how to locate the
environment for that function call.

If the C language had local functions, programmers would expect to be
able to just use them wherever functions are expected. That is to say,
to write code that accepts function pointer arguments which would work
either with regular functions or local functions.

The obvious way to do that is to represent regular old function
pointers the same way as the local function pointers, except that they
have a null environment.

This goes against the "don't pay for what you don't use" principle.

C doesn't have local functions, and C++ is based on C. Not enough
people were shouting loud enough for C++ to get local functions. And
C++ is also deliberately backward-compatible with C. If C++ had local
functions and pointers to local functions, there would still be a need
to have ordinary pointers to functions that are C compatible (i.e.
represented as straight machine addresses and nothing but, if that's
what the C compilers do on a given platform).

So if there are ever going to be local functions in C++, there will
have to be some special syntax for indirecting upon them, the same way
that there is special syntax (pointer ot member) for indirecting on
member functions.

There are similarities between closures and member functions. Member
functions need a ``this'' pointer to locate their environment: their
environment is an explicitly-defined class object. The environment of a
first class function is an instance of the lexical environment spun
into an object, which is also accessed by a kind of ``this'' pointer.

You can emulate closures in C++ using objects, in particular objects
that overload the () operator, which can be regarded as function
objects or functors.

C++ does have local classes: classes defined in a block scope. You can
write a local class which inherits from some functor base class.
Instantiate an object of that class in the same scope and pass it down.
You should even be able to return it. It's not exactly a closure,
because anything you want to "capture" has to be declared as a member
of that object, and initialized through the constructor or whatever
additional means. There is no visibility from the function bodies to
the lexical environment in the surrounding block.

class Function {
public:
virtual int call ()() = 0;
virtual ~Function { }
};

FunctionnBase *MakeCounter(int initialValue)
{
class Counter : public Function {
private:
int count;
public:
Counter(int startingValue) : count(startingValue) { }
int call() {
return ++count;
}
};

return new Counter(initialValue);
}

// ...

Function *counter = MakeCounter(10);
cout << counter->call() << endl; // outputs 10
cout << counter->call() << endl; // outputs 11 ...
I've worked on a project where this technique was used quite
successfully. Such local objects were used in a client middleware to
install handlers for a stateful comunication protocol with a server.
Depending on what commands you sent to the server and in what
situation, you had to handle different kinds of responses in different
ways.

Moreover, you didn't know from what context you were called, so you had
to save and restore your handling object. So the local object was
instantiated from a local class, pushed to the top of a stack of
handlers, and then popped later.

It's not nearly as nice as having real closures, but there it is. Real
closures in C++ would not be that much more convenient than the above,
because of the limitations of static typing and the lack of automated
memory management. What a closure would do is effectively write the
local class for you using the lexical environment as the specification,
so you could eliminate the class declaration and construction steps.

Nov 28 '05 #11

P: n/a
Timothy Madden wrote:
Hello all.

I program C++ since a lot of time now and I still don't know this simple
thing: what's the problem with local functions so they are not part of
C++ ?

There surely are many people who will find them very helpfull. gcc has
them as a non-standard option, but only when compiling C language code,
so I'm afraid there might be some obscure reason why local functions are
not so easy to be dealt with in C++, which I do not yet know.

Anyone knows what is the problem with supporting local functions in C++ ?

Thank you
Timothy Madden
Romania


The problem is function pointers.

typedef void (*func_ptr)();

func_ptr return_generator()
{
int n = 0;
int next_int()
{
return n++;
}
return &next_int;
}

return_generator returns a function pointer to a function that accesses
a variable local to return_generator. By the time next_int is executed
that variable will no longer exist. The only way around this would be to
place limits on functions pointers (this is what pascal does), drop the
stack model for local variables (this is what lisp does) or place limits
on local functions (what then is the point).

john
Nov 28 '05 #12

P: n/a

Phlip wrote:
Timothy Madden wrote:
I program C++ since a lot of time now and I still don't know this simple
thing: what's the problem with local functions so they are not part of C++
?
Because a language should reserve (most) syntax until it can achieve some
new effect with that syntax.

The best feature for a private method would be a block closure, like this:

void foo() {
int bar = 42;
void thread() {
doSomethingTo(&bar);
}
beginThread(thread);
}

In that example, the inner function thread() can refer to the outer
function's local variable. Without that effect, the nested function syntax
is only syntactic sugar.


That is not true. Your example shows a first-class closure. The
environment in which bar has the value 42 persists even after the
function foo() terminates, allowing the thread to run the function.

You can have ``second class'' closures that are still useful, which can
only be passed down.

Pascal's local functions are like that. They are more than syntactic
sugar, but less than full blown closures.
However, the C languages use a definition that assists implementors to store
each function invocation's local variables on a hardware stack.
If you support downward funargs only, everything can still be on the
stack.
This helps
compete with Assembler. Without languages as fast as C++, we would have the
odious choice between rapid development in slow languages like Smalltalk or
Python, or slow development in rapid Assembly code.
Python is slow because it's immature implementation technology. There
are implementations of these types of languages that have very good
compilers that put out native machine code.
In my example, thread()'s access to bar persists after the function foo()
returns. This breaks the deal that 'bar' can live on a hardware stack.
Correct. However, a decent compiler for a language that has closures
will perform a careful analysis of each lexical scope capsule. It will
identify all of the closures and classify them based on what power they
require. Some closures will disappear entirely. Some can be proved to
be downward funargs only, and some may be found which must be assumed
to escape.

Only those closures which escape break the deal about the hardware
stack.

Rule number one is to have a good compiler.
Compiler implementors must find alternative representations for variables,
and must taint every variable used in a closure. The compiler may or may not
After a closure has escaped that captures variable X, and if its body
modifies X, then any escaping function must be assumed to modify X by
means of calling that escaped closure. X can't be cached in a register
across external calls. Is this what taint means?
know that beginThread() might allow thread() to call after foo() returns, so
the compiler must chose pessimism, and the variable bar would have a lot of
overhead.


That's right. Because beginThread() is an external call, the closure
passed into it is an escaping closure. Therefore, the environment which
it captures must be allocated in an object that will survive the
termination of the surrounding block.

Even if thread's aren't involved, the external function could stash the
closure somewhere, like in some global variable, and then it could be
called later.

What if the programmer knows that the closure is downward only, but
it's not obvious to the compiler? A declaration mechanism can exist to
express that.

In Common Lisp, for instance, you can use (declare (dynamic-extent
....)). The gist of it is that it tells the compiler about objects that
do not escape and can thus be stack allocated. If they are passed down
somewhere, they are not stashed away, and they are not returned upward
either. By means of this, you can express that a closure is downward
only, and so the bindings that it captures can be on the stack.
``Dynamic extent'' is the Lisp word for what is known as ``automatic
storage duration'' in C.

Nov 28 '05 #13

P: n/a
Kaz Kylheku wrote:


C doesn't have local functions, and C++ is based on C. Not enough
people were shouting loud enough for C++ to get local functions.


Yep.
Local functions in *original' PASCAL became necessary to solve
scoping-problems. Original PASCAL had just one translation unit,
so everything had to be squeezed into it. That made programming
'source code libraries' a hard thing, since function name clashes
came into ones way every now and then.
Local functions solved that problem to some extent.

--
Karl Heinz Buchegger
kb******@gascad.at
Nov 28 '05 #14

P: n/a
John Harrison wrote:
Timothy Madden wrote:
Hello all.

I program C++ since a lot of time now and I still don't know this
simple thing: what's the problem with local functions so they are not
part of C++ ?

There surely are many people who will find them very helpfull. gcc has
them as a non-standard option, but only when compiling C language
code, so I'm afraid there might be some obscure reason why local
functions are not so easy to be dealt with in C++, which I do not yet
know.

Anyone knows what is the problem with supporting local functions in C++ ?

Thank you
Timothy Madden
Romania

The problem is function pointers.

typedef void (*func_ptr)();

func_ptr return_generator()
{
int n = 0;
int next_int()
{
return n++;
}
return &next_int;
}

return_generator returns a function pointer to a function that accesses
a variable local to return_generator. By the time next_int is executed
that variable will no longer exist. The only way around this would be to
place limits on functions pointers (this is what pascal does), drop the
stack model for local variables (this is what lisp does) or place limits
on local functions (what then is the point).

john


I don't think these details really are the problem with local functions.

To clarify, lets say that they work like member functions: they are
defined to be part of the enclosig class and they can only be used in
the context of their class. Memeber functions have a limit placed on the
type of their address, but they are still a good thing.

The same goes with local functions. The type 'pointer to local function'
also holds the enclosing function name. A pointer to global function is
allways compatible with a pointer to local function. However pointers to
global functions can be assigned addresses of global functions only.
When a generic 'pointer to function' is defined in a function, it is
implicitly considered a 'pointer to local function' type. When a generic
'pointer to function' type is defined at global or namespace scope, it
is of course a nomal 'pointer to global function' type.

A function call through a pointer to local function can only be made
while enclosing function is still executing (and has not returned), and
only by other functions also local to the enclosing function or nested
in the enclosing function. The type for the pointer can be exposed and
used outside encosing function, when the function is visible, but no
function call through the pointer can be made outside.

For example:

void RawDataProcessor(int iProcessClass);

void (RawDataProcessor::*PartialProcessor)(int iCrtLevel, int
ProcessSubclass) = NULL;

void RawDataProcessor(int iProcessClass)
{
double a = (iProcessClass + 10) / 3.35;
void SimpleProcessor(int iCrtLevel, int iSubclass)
{
a = (a + 0.7*iCrtLevel) / ((iSubClass + 2)*1.02);
}
void CompoundProcessor(int iCrtLevel, int iSubclass)
{
if (iCrtLevel && iSubclass)
{
iSubclass--;
a = (a + 1.1) / 0.98;
CompoundProcessor(iCrtLevel-1, iSubclass);
}
else
SimpleProcessor(iCrtLevel, iSubclass);
}

void DeriveCodedProcessLevel(int iLevelCode, int iClass)
{
int iLevel = iLevelCode*2 - 3;
::PartialProcessor(iLevel, iClass);
}

if (iProcessClass > 7)
::PartialProcessor = CompoundProcessor;
else
::PartialProcessor = SimpleProcessor;

DeriveCodedProcessLevel(iProcessClass, iProcessClass);

count << "Processor reaches " << a << endl;
}

Here SimpleProcessor and Compound Processor form the internal workings
of public RawDataProcessor, should not be fully exposed to its outside,
and also are not that big as to make RawDataProcessor difficult to read.

Thank you
Timothy Madden
Romania
Nov 28 '05 #15

P: n/a
Kaz Kylheku wrote:
Timothy Madden wrote:
Hello all.

I program C++ since a lot of time now and I still don't know this simple
thing: what's the problem with local functions so they are not part of
C++ ?

Local functions are useless if you cannot do indrection on them such
that the resulting function pointers carry with them the dynamic
environment instantiated from their lexical scope.

Even if you support downward funargs only, it complicates the
representation of a function pointer. The pointer must carry not only
the address, but additional information about how to locate the
environment for that function call.

If the C language had local functions, programmers would expect to be
able to just use them wherever functions are expected. That is to say,
to write code that accepts function pointer arguments which would work
either with regular functions or local functions.

The obvious way to do that is to represent regular old function
pointers the same way as the local function pointers, except that they
have a null environment.

This goes against the "don't pay for what you don't use" principle.


You are talking about things way to far from practical implementations.

It is of course impractical to carry the dynamic environment (all used
local variables) with every function pointer. I would say it is not even
semanticaly correct. I may want to reenter enclosing function and call
through a pointer to my local functions, allready set, using the new,
current environment.

But local functions should only be called by the enclosing function, by
other local functions or by other nested functions in the same enclosing
function. And the type of a pointer to a local function is the one that
carries the name of enclosing function. This a pointer to a global
function can only be assigned the address a global function. However a
pointer to local function should, in principle, legaly receive the
address a global function.

Any generic pointer-to-function type defined within a function is
implicitly a pointer to local function (as opposed to a pointer to
global function), unless the programmer explicitly defines the pointer
type as pointing to global functions.

Thank you.
Timothy Madden
Romania
Nov 28 '05 #16

P: n/a

Kaz Kylheku wrote:
After a closure has escaped that captures variable X, and if its body
modifies X, then any escaping function must be assumed to modify X by
means of calling that escaped closure. X can't be cached in a register
across external calls.


Worse, it can't even be on the processor stack. You'd need a GC-like
mechanism (because nested functions can have deeper nested functions).
Suddenly, what looks like a simple integer on the stack has become
a complex beast.

Now, if you want this complexity, it's not too difficult to get. But
you
have to use a few more words to indicate you want the complexity.
(Use a tr1::shared_ptr, allocate memory, pass it to the function).
The only thing you could add is syntactic sugar. I'd say the risk for
accidental use is too big to make it easier.

Regards,
Michiel Salters

Nov 28 '05 #17

P: n/a
Timothy Madden wrote:
Kaz Kylheku wrote:
Timothy Madden wrote:
Hello all.

I program C++ since a lot of time now and I still don't know this simple
thing: what's the problem with local functions so they are not part of
C++ ?

Local functions are useless if you cannot do indrection on them such
that the resulting function pointers carry with them the dynamic
environment instantiated from their lexical scope.

Even if you support downward funargs only, it complicates the
representation of a function pointer. The pointer must carry not only
the address, but additional information about how to locate the
environment for that function call.

If the C language had local functions, programmers would expect to be
able to just use them wherever functions are expected. That is to say,
to write code that accepts function pointer arguments which would work
either with regular functions or local functions.

The obvious way to do that is to represent regular old function
pointers the same way as the local function pointers, except that they
have a null environment.

This goes against the "don't pay for what you don't use" principle.


You are talking about things way to far from practical implementations.

It is of course impractical to carry the dynamic environment (all used
local variables) with every function pointer. I would say it is not even
semanticaly correct.


Well then, go to, say, comp.lang.lisp and tell those people that. :)
I may want to reenter enclosing function and call
through a pointer to my local functions, allready set, using the new,
current environment.
That's a terrible idea; basically you are breaking encapsulation. A
first class function is an unbreakable capsule consisting of code and
the environment that the code needs.
But local functions should only be called by the enclosing function, by
other local functions or by other nested functions in the same enclosing
function. And the type of a pointer to a local function is the one that
carries the name of enclosing function.


Shoehorning this type information into the function pointer is
shortsighted. Firstly, it adds severe restrictions on programming,
making it impossible for instance to write libraries that work with any
local function regardless of its parent.

Secondly, local functions are enclosed by blocks, not by functions.
Unless the static type reflects this, it's too weak to achieve its
purpose:

Example:

void foo()
{
{
int a, b;
void local_x() { ... }
}
{
float c, d;
void local_y() { ... }
}
}

If &local_x and &local_y have the same manifest type, and if you allow
a separation between function pointers and their environment, then you
create the possibility of calling local_y with the environment that is
apporopriate for local_x, or vice versa!!! Oops.

If you want to restrict this statically, you have to extend the type
system so that the type of the function pointer &local_x is something
like void (foo::<unique_sym>::*)(). Eww!

Or else be forced to name blocks? Hmm, that's one step closer to
allowing just local classes. Why not just use the class keyword then
and require a constructor. :)

block bbb {
private:
int x, y,
bbb() : x(42), y(30) { }
void local_y() { ... }
}

The difference between a block and a class: the block keyword, and no
need for a semicolon after the closing brace. Bahaha.

Here is a sane design.

Have two types of function pointers. The regular old kind, and local
pointers. The local function pointers are actually objects that carry
the environment. They are unbreakable capsules. Assigning a global
pointer to a local pointer is allowed. The implicit conversion results
in a function object with a null environment, which is fine since the
target function doesn't expect any environment. Of course, the type
signature has to match!!! The reverse assignment is not allowed without
a cast.

A special syntax could be used to denote these new function pointers
that carry an environment. The C++ virtual keyword could be used.

int (* virtual ptr_x)() = &local_x;

The &local_x expression produces a function that carries the
environment that surrounds the definition of local_x. And of course,
some anonymous lambda thing would be provided as well:

int (* virtual_ptr)(double) = int lambda (double x) { /* ... */ }

or whatever.

And so now you can write a library function that works just with static
functions:

void qsort(void *base, size_t, size_t, int (*cf)(const void *, const
void *));

or you can write one that works with local functions /and/ with static
ones:

void lqsort(void *base, size_t, size_t, int (* virtual cf)(const void
*, const void *));

If you don't use local functions, you don't pay for them. Function
pointers remain C compatible. Function linkage for regular functions
remains C compatible. etc.

There is the question of memory management. I'd just leave that
explicit, in the spirit of C++. If you take a local pointer with &, it
uses the global operator new. Or, maybe a local operator new that is in
scope. :) When you don't need the function any more, you delete it
with the delete operator:

// Make closure:
int (* virtual ptr)() = &local_x;

// Don't need it any longer
delete ptr;

Of course, all C++ destructors are properly run on the objects in that
environment.

If people want to simulate garbage collection of closures, they can
wrap these local function pointers pointers in a smart class that does
reference counting.

closure<int (* virtual)()> c(&local_x); // smart ptr created,
refcount 1

Nov 28 '05 #18

P: n/a
> func_ptr return_generator()
{
int n = 0;
int next_int()
{
return n++;
}
return &next_int;
}

return_generator returns a function pointer to a function that accesses
a variable local to return_generator. By the time next_int is executed
that variable will no longer exist.


Same argument can be used to forbid local variables in c++

--
Vladimir

Nov 28 '05 #19

P: n/a
Kaz Kylheku wrote:
Timothy Madden wrote:
Kaz Kylheku wrote:
Timothy Madden wrote:
Hello all.

I program C++ since a lot of time now and I still don't know this simple
thing: what's the problem with local functions so they are not part of
C++ ?
Local functions are useless if you cannot do indrection on them such
that the resulting function pointers carry with them the dynamic
environment instantiated from their lexical scope.
[...]
I may want to reenter enclosing function and call
through a pointer to my local functions, allready set, using the new,
current environment.

[...]
But local functions should only be called by the enclosing function, by
other local functions or by other nested functions in the same enclosing
function. And the type of a pointer to a local function is the one that
carries the name of enclosing function.

[...]
Shoehorning this type information into the function pointer is
shortsighted. Firstly, it adds severe restrictions on programming,
making it impossible for instance to write libraries that work with any
local function regardless of its parent.

Secondly, local functions are enclosed by blocks, not by functions.
Unless the static type reflects this, it's too weak to achieve its
purpose:
[...] Here is a sane design.

Have two types of function pointers. The regular old kind, and local
pointers. The local function pointers are actually objects that carry
the environment. They are unbreakable capsules. Assigning a global
pointer to a local pointer is allowed. The implicit conversion results
in a function object with a null environment, which is fine since the
target function doesn't expect any environment. Of course, the type
signature has to match!!! The reverse assignment is not allowed without
a cast.

A special syntax could be used to denote these new function pointers
that carry an environment. The C++ virtual keyword could be used.

And so now you can write a library function that works just with static
functions:
[...] or you can write one that works with local functions /and/ with static
ones: [...] If you don't use local functions, you don't pay for them. Function
pointers remain C compatible. Function linkage for regular functions
remains C compatible. etc.

There is the question of memory management. I'd just leave that
explicit, in the spirit of C++. If you take a local pointer with &, it
uses the global operator new. Or, maybe a local operator new that is in
scope. :) When you don't need the function any more, you delete it
with the delete operator:

[...]

Ok what you are talking about is very different from my ideea.

I find the LISP-like concept of local functions to actualy implement
function objects. Every invocation of address-of operator on the same
local function creates a new object, potentialy different from the
previous. The refered local function becomes a major part of the pointer
type. I think the address-of operator becomes just too complicated in
this case and I wonder what the benefits would be. Then there is the
problem of an environment that refuses to go out of scope, since it
stays stored in some global function pointer, even after the enclosing
function returns. I think this might be too much of a change in the way
C++ programmers think today. It is just a too dangerous tool, even for
programmers that are not beginers, and I wonder if anyone will find it
appropriate. It actualy changes the language into something else.

Suppose you have a pointer in the enclosing function, that you also have
a reference to the pointed object, and you happen to explictly delete
the pointer upon the return of the enclosing function. This will
auto-magicaly make the encapsulated environment not valid anymore,
simply because its original copy went out of scope. I suspect there may
be many other such situations.

If ever implemented I see them like RTTI is now (more of an extension
that I never used and that mostly indicates a design flow when it
actualy gets to be used).

What I was thinking of were Pascal-like local functions, with all the
improvements appropriate in a modern language.
Pointers to local functions are simply pointers to first instruction in
the function's generated code (if the C++ implementation compiles the
program text). The language guaranties they can only be called from an
instance of the block that defines them. This way local functions
directly use the surrounding environment, while it is still alive and in
place. If environment changes between calls to a local function, the
function's behaviour is imediately affected.

Defining a function in a block other than directly a function body has
only limited uses. The exact type of a pointer to a block-local function
can not be fully expressed by the language. However the compiler does
uniquely identify the block-local function pointer type. This type,
internal to the compiler, is implicitly used when a simple
pointer-to-function declaration is encountered in program text, while
still in the block, and it can be given an alias with a normal typedef
declaration. However the basic rule remains: a local function can only
be called while still in scope, even if it is called through a function
pointer.

Thank you
Timothy Madden
Romania
Nov 28 '05 #20

P: n/a
Vladimir wrote:
func_ptr return_generator()
{
int n = 0;
int next_int()
{
return n++;
}
return &next_int;
}

return_generator returns a function pointer to a function that accesses
a variable local to return_generator. By the time next_int is executed
that variable will no longer exist.

Same argument can be used to forbid local variables in c++


Not really. It's one thing to refer to something with a pointer only to
find it doesn't exist. That would happen, as you say, if you returned a
pointer to a stack object, or if you had a pointer to a deleted heap
object. But in my code above the next_int function refers to the n
variable by *name*, yet it doesn't exist when the function is executed.
That's quite a different situation.

john
Nov 29 '05 #21

P: n/a
John Harrison wrote:
Vladimir wrote:
func_ptr return_generator()
{
int n = 0;
int next_int()
{
return n++;
}
return &next_int;
}

return_generator returns a function pointer to a function that accesses
a variable local to return_generator. By the time next_int is executed
that variable will no longer exist.

Same argument can be used to forbid local variables in c++


Not really. It's one thing to refer to something with a pointer only to
find it doesn't exist. That would happen, as you say, if you returned a
pointer to a stack object, or if you had a pointer to a deleted heap
object. But in my code above the next_int function refers to the n
variable by *name*, yet it doesn't exist when the function is executed.
That's quite a different situation.


It isn't.

If ptr is a pointer to a structure that has been deallocated, then
ptr->n is also a reference by name. There is no difference. Variables
are just ``members'' of the lexical environment data structure, and,
under the hood, the pointer to that environment can become bad. It's
just another object in the machine.

By the way, you can cause that dangling local variable situation in C
by executing a longjmp into a function where the context was saved with
setjmp, but which since terminated.

The longjmp itself is formally undefined, but in practice, things can
get farther than that.

Nov 29 '05 #22

P: n/a
Mi*************@tomtom.com writes:
Kaz Kylheku wrote:
After a closure has escaped that captures variable X, and if its body
modifies X, then any escaping function must be assumed to modify X by
means of calling that escaped closure. X can't be cached in a register
across external calls.


Worse, it can't even be on the processor stack. You'd need a GC-like
mechanism (because nested functions can have deeper nested functions).
Suddenly, what looks like a simple integer on the stack has become
a complex beast.

Now, if you want this complexity, it's not too difficult to get. But
you
have to use a few more words to indicate you want the complexity.
(Use a tr1::shared_ptr, allocate memory, pass it to the function).
The only thing you could add is syntactic sugar. I'd say the risk for
accidental use is too big to make it easier.


The times I have missed local functions i C++, I couldn't have cared less
about access to the outer functions local variables, I just pass what
I need access to as arguments.

What can't be passed as arguments is access rights to the private
data of a class.

Typical example:

class Foo {
public:
/* Interface here */
private:
/* implementation details here */
friend std::ostream& operator << (std::ostream& os, const Foo& f);
};

namespace {
void
output_helper1(std::string& s, const Foo& f, /* more */)
{
/* here we don't have any access to the Foo's private data */
}
}
std::ostream&
operator << (std::ostream& os, const Foo& f)
{
std::string output_string;
/* very complex logic here */
output_helper1(output_string, f, /* more arguments */);
output_helper2(output_string, f, /* more arguments */);
output_helper3(output_string, f, /* more arguments */);

os << output_string;
return os;
}

/Niklas Norrthon
Nov 29 '05 #23

P: n/a
Kaz Kylheku wrote:
John Harrison wrote:
Vladimir wrote:
func_ptr return_generator()
{
int n = 0;
int next_int()
{
return n++;
}
return &next_int;
}

return_generator returns a function pointer to a function that accesses
a variable local to return_generator. By the time next_int is executed
that variable will no longer exist.
Same argument can be used to forbid local variables in c++


Not really. It's one thing to refer to something with a pointer only to
find it doesn't exist. That would happen, as you say, if you returned a
pointer to a stack object, or if you had a pointer to a deleted heap
object. But in my code above the next_int function refers to the n
variable by *name*, yet it doesn't exist when the function is executed.
That's quite a different situation.

It isn't.

If ptr is a pointer to a structure that has been deallocated, then
ptr->n is also a reference by name.


I wasn't thinking of p->n I was thinking of *p.

john
Nov 29 '05 #24

This discussion thread is closed

Replies have been disabled for this discussion.