Topic: closures and properties
Author: Edward Diener <eddielee@abraxis.com>
Date: 1999/03/04 Raw View
John Maddock wrote:
> I don't think a closure could point to a regular global function -
> what would the hidden this pointer point to? Or to put it another way
> - a closure that pointed to a regular function would be a regular
> function pointer.
My thought was that a hidden "this" pointer of 0 would mean a regular global function. The
reason for this suggestion is that you could specify such a Borland style "closure" and
point it to a regular global function or an object's member function at run time depending
on the logic of your program. I do not think of this as a big extra but it would certainly
seem implementable considering that the way that Borland seems to now implement their
closures is as a "this" pointer and and the member function's address. If this dual ability
for Borland style closures were not also added to the language, I would not think it a big
issue, but if it were it would add even a little more flexibility to this concept.
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: John_Maddock@compuserve.com (John Maddock)
Date: 1999/03/03 Raw View
>that as a very useful addition to the C++ language. And another poster, John Maddock,
>made the point that if the C++ standard library were changed to allow Borland style
>"closures" to be function objects, that would further simplify the use of the standard
>library, particularly the STL.
Almost, my point was that no changes are necessary at all, once
closures become *types*, just as regular function pointers are, then
they automatically become function objects, just as regular function
pointers are: there is no need to change anything in the STL at all
"it just works".
There are also a couple of other points:
I don't think a closure could point to a regular global function -
what would the hidden this pointer point to? Or to put it another way
- a closure that pointed to a regular function would be a regular
function pointer.
Closures seemlessly take care of mutliple calling conventions - just
as function pointers do already. Yes I know this is nothing to do
with the standard, but multiple calling conventions are a fact of life
for many of us, and anything that limits usage to __cdecl functions
would be less than useful for most of us. Current implimentations
using templates, would need a separate template for each calling
convention (I assume), multiplying the number of templates required by
a factor of 3 or 4 again, sure you could write a script to churn out
the variations, but it's going to be one big header :-)
Finally, I have one major gripe about template implimentations: its
almost always quicker to write a bespoke function object, than it is
to figure out which template to use, I have the same gripe about most
of the STL "binders" BTW, I know that they're there, but by the time
I've worked out which one I need, I could have written a bespoke
object (which is a total non-brainer of an exercise).
John Maddock
http://ourworld.compuserve.com/homepages/John_Maddock/
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: "Andrei Alexandrescu" <alexandrescua@micromodeling.com>
Date: 1999/02/28 Raw View
Dave Abrahams wrote in message <79si51$3hu$1@news.harvard.net>...
>Edward Diener wrote in message <36C0CE15.63898A2D@abraxis.com>...
>>
>>I would like to see the implementation of the closure template and of the
>bind
>>function. It's a cop out to me saying that you leave this as an exercise to
>>the reader. If you do have a workable implementation using templates let's see
>>it. I'm assuming the bind function is completely templatized also.
>
>Yes.
>
>Unfortunately, I don't have time to write this out just now and test it to
>prove that I haven't made any serious gaffes. I wouldn't do it this way
>anyway, because I find the mix-in class approach much simpler and cleaner.
>Still, I do think I know how the template approach could work. Let's leave
>this as a gadfly-of-the-week problem (with apologies to Herb Sutter) for the
>time being and see if anyone can come up with an answer.
Dave's right, what he does it's reasonably simple to implement. See
Leroy's code much above in this same thread. He uses an abstract inner
class and a pimpl to that class.
But there are a couple of problems, though:
- number of arguments - as Lisa Lippincott said in a previous thread,
it can be raised arbitrarily high through nasty duplication
- source code bloating - event the preprocessor cannot help you in a
decent way.
Dave's solution in particular is poorer than what template magic can
buy us, since it's not generic in respect of the return type and the
type of the arguments. This is doable, thatnks to partial template
specialization. But even the greatest C++ template magician cannot
genericize over the number of parameters. I hate that.
As another poster suggested, I would like someone to make a motion to
allow variable template argument list.
During my attempts to implement closures, I noticed that in order to
create closures I felt in need the following features:
*** 1. The ability to define a template that takes an 'array of
types', say:
template <typename<n> T>
class Something
{
};
where n is an implementation-defined integral number.
*** 2. The ability to define and call function/method signatures
passing the array of types instead of the explicit types:
template <typename|<n>| T>
class Something
{
void MyMethod(T); // takes all the parms of the template
// for Something<int, char> the signature would be
// void MyMethod(int, char);
};
*** 3. The ability to take individual elements and slices of the 'type
array':
template <typename|<n>| T>
class Something
{
T|<0>| MyMethod(T|<1, n - 1>|);
// for Something<int, char, double> the signature would be
int MyMethod(char, double);
};
*** 4. The ability to stop the recursion that could arise from using
the feature above, so specialization based upon n is needed:
template <typename|<0>| T>
class Something
{
void MyMethod();
};
(Please note that for avoiding any ambiguity I've used the tokens '|<'
and '>|'. They were proposed in a stage of the standardization process
as brackets for template types in place of '<' and '>'.)
I find this pack of features complicated enough. There should be
compelling reasons and evidence in the user community that it is
needed. I would love to see it (or something similar) in the language.
What I would not like to see, would be an incarnation of it that is
incomplete, or easier to misuse than to use properly.
Andrei
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: "Andrei Alexandrescu" <alexandrescua@micromodeling.com>
Date: 1999/02/28 Raw View
I agree 100% with you. (What an easy life for those guys who say: "I
agree with everything everyone has said")
Just like you, I would see Borland-style closures coming naturally in
C++. (Yes, I also toyed with them nowadays.)
This is especially natural because in the expression:
(Object.*Method)(arguments)
one may expect Object.*Method to be an expression of a distinct type.
Right now having that expression and type inaccessible is obvously
non-consistent with the rest of the language.
My point in talking about variable-parameters for templates is that
this is not the first time I felt in need for them. For instance, many
a programmer's dream is the vararg typesafe min/max/avg/sum functions.
Others want vararg constructors for vector, others want typesafe
vararg printf, and so on. A clever vararg templatized solution would
enable all, plus closures of course. Of course what I hinted to is
more than sure flawed as it's not the result of a prolonged study,
plus I am nowhere.near an expert.
Nonetheless, what costs me to propose a syntax? :o) What about using
the class keyword? This comes natural:
void (class::*MyClosure)(int) = Object->Method;
MyClosure(6);
But now there is something that comes to mind: should a closure as
described above be able to store a regular pointer to function? I
mean:
extern void Global(int);
MyClosure = Global;
MyClosure(10); // calls Global(10)
And now I start thinking: But I'd like to create on my own a
generalized functor that acts like a closure - and I end up willing
again varargs for templates... :o}
Andrei
Edward Diener wrote in message <36D8DB9D.C9CFDB9A@abraxis.com>...
>Andrei Alexandrescu wrote:
>
>> During my attempts to implement closures, I noticed that in order
to
>> create closures I felt in need the following features:
>>
>> *** 1. The ability to define a template that takes an 'array of
>> types', say:
>> template <typename<n> T>
>> class Something
>> {
>> };
>> where n is an implementation-defined integral number.
>>
>> *** 2. The ability to define and call function/method signatures
>> passing the array of types instead of the explicit types:
>> template <typename|<n>| T>
>> class Something
>> {
>> void MyMethod(T); // takes all the parms of the template
>> // for Something<int, char> the signature would be
>> // void MyMethod(int, char);
>> };
>>
>> *** 3. The ability to take individual elements and slices of the
'type
>> array':
>> template <typename|<n>| T>
>> class Something
>> {
>> T|<0>| MyMethod(T|<1, n - 1>|);
>> // for Something<int, char, double> the signature would be
>> int MyMethod(char, double);
>> };
>>
>> *** 4. The ability to stop the recursion that could arise from
using
>> the feature above, so specialization based upon n is needed:
>> template <typename|<0>| T>
>> class Something
>> {
>> void MyMethod();
>> };
>
>Despite the fact that you want all these features to be added to the language to
>support a template-based methodology for emulating Borland style "closures", or
>"bound member pointers" if that name better suits what it is, and I would like a
>single addition to the language that directly supports "bound member pointers" as a
>generalized type, you still believe that using templates to support this idea is
>best. I am not laughing at you but I do have to chuckle at your sheer stubborness
>and of all those who can not see the absolute simplicity and elegance of what
>Borland has done. Maybe you should take a look at it sometime, just to see how easy
>and simple this idea is to specify and use. I admit that it is an extension to C++
>and specified as such, as "__closure" in C++ Builder, and therefore currently
>non-portable, but I am still dumfounded at the inability of others to even
>acknowledge that this solution is easy to use and understand, even if they would not
>support its inclusion in a future specification of C++. As I have said before in
>this thread, sometimes it is easier to add a possibly needed feature to a language
>in a simple manner rather than implement it in a far more complex way using current
>language constructs. Whether that is the case with "bound member pointers", the
>future will decide.
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: Edward Diener <eddielee@abraxis.com>
Date: 1999/02/28 Raw View
Andrei Alexandrescu wrote:
> During my attempts to implement closures, I noticed that in order to
> create closures I felt in need the following features:
>
> *** 1. The ability to define a template that takes an 'array of
> types', say:
> template <typename<n> T>
> class Something
> {
> };
> where n is an implementation-defined integral number.
>
> *** 2. The ability to define and call function/method signatures
> passing the array of types instead of the explicit types:
> template <typename|<n>| T>
> class Something
> {
> void MyMethod(T); // takes all the parms of the template
> // for Something<int, char> the signature would be
> // void MyMethod(int, char);
> };
>
> *** 3. The ability to take individual elements and slices of the 'type
> array':
> template <typename|<n>| T>
> class Something
> {
> T|<0>| MyMethod(T|<1, n - 1>|);
> // for Something<int, char, double> the signature would be
> int MyMethod(char, double);
> };
>
> *** 4. The ability to stop the recursion that could arise from using
> the feature above, so specialization based upon n is needed:
> template <typename|<0>| T>
> class Something
> {
> void MyMethod();
> };
Despite the fact that you want all these features to be added to the language to
support a template-based methodology for emulating Borland style "closures", or
"bound member pointers" if that name better suits what it is, and I would like a
single addition to the language that directly supports "bound member pointers" as a
generalized type, you still believe that using templates to support this idea is
best. I am not laughing at you but I do have to chuckle at your sheer stubborness
and of all those who can not see the absolute simplicity and elegance of what
Borland has done. Maybe you should take a look at it sometime, just to see how easy
and simple this idea is to specify and use. I admit that it is an extension to C++
and specified as such, as "__closure" in C++ Builder, and therefore currently
non-portable, but I am still dumfounded at the inability of others to even
acknowledge that this solution is easy to use and understand, even if they would not
support its inclusion in a future specification of C++. As I have said before in
this thread, sometimes it is easier to add a possibly needed feature to a language
in a simple manner rather than implement it in a far more complex way using current
language constructs. Whether that is the case with "bound member pointers", the
future will decide.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: Roman Lechtchinsky <wolfro@cs.tu-berlin.de>
Date: 1999/03/01 Raw View
Andrei Alexandrescu wrote:
> During my attempts to implement closures, I noticed that in order to
> create closures I felt in need the following features:
Hmmm, how about a list of types?
class Nil
{};
template<typename Head, typename Tail>
class Cons
{};
> *** 1. The ability to define a template that takes an 'array of
> types', say:
> template <typename<n> T>
> class Something
> {
> };
> where n is an implementation-defined integral number.
Make it just
template<typename T> class Something {...};
and instantiate it on Cons only. If you want the compiler to check this
you can leave the general case undefined and specialize the template for
Cons and Nil.
> *** 2. The ability to define and call function/method signatures
> passing the array of types instead of the explicit types:
> template <typename|<n>| T>
> class Something
> {
> void MyMethod(T); // takes all the parms of the template
> // for Something<int, char> the signature would be
> // void MyMethod(int, char);
> };
This will be
template<typename T> class Something; // no definition
template<>
class Something<Nil>
{
void MyMethod();
};
template<typename T0>
class Something< Cons<T0,Nil> >
{
void MyMethod(T0);
};
template<typename T0, typename T1>
class Something< Cons<T0, Cons<T1,Nil> > >
{
void MyMethod(T0,T1);
};
...
You still need one class for every possible number of arguments but at
least the user doesn't see it.
> *** 3. The ability to take individual elements and slices of the 'type
> array':
> template <typename|<n>| T>
> class Something
> {
> T|<0>| MyMethod(T|<1, n - 1>|);
> // for Something<int, char, double> the signature would be
> int MyMethod(char, double);
> };
An easy one:
template<typename T0>
class Something< Cons<T0,Nil> >
{
T0 MyMethod();
};
template<typename T0, typename T1>
class Something< Cons<T0, Cons<T1,Nil> > >
{
T0 MyMethod( T1 );
};
etc.
> *** 4. The ability to stop the recursion that could arise from using
> the feature above, so specialization based upon n is needed:
> template <typename|<0>| T>
> class Something
> {
> void MyMethod();
> };
template<>
class Something<Nil>
{
void MyMethod();
};
Actually, this could be used to embed a complete
referentially-transparent functional language in C++ (the programs will
be run at compile-time):
struct Nil
{
typedef Nil Value;
};
template<class Head, class Tail>
struct Cons
{
typedef Cons<Head,Tail> Value;
};
template<int n>
struct Int
{
enum {val=n};
typedef Int<n> Value;
};
template<typename T1, typename T2> struct Add;
template<int n1, int n2>
struct Add< Int<n1>, Int<n2> > : public Int<n1+n2> {};
template< typename<typename T1, typename T2> F, typename T0, typename L>
struct Reduce;
template< typename<typename T1, typename T2> F, typename T0>
struct Reduce<F,T0,Nil> : public T0 {};
template< typename<typename T1, typename T2> F, typename T0, typename
Head, typename Tail>
struct Reduce<F,T0, Cons<Head,Tail> > : public F<Head,
Reduce<F,T0,Tail>::Value> {};
Now, Reduce<Add,Int<0>, Cons< Int<1>, Cons< Int<2>,Nil > > >::Value::val
is 3. Unfortunately, my compilers choke on the higher-order functions
(i.e. templated template parameters) but a version of Sum which doesn't
use them even works:
template<typename L> struct Sum;
template<>
struct Sum<Nil> : public Int<0> {};
template<typename H, typename T>
struct Sum< Cons<H,T> > : public Add< H, typename Sum<T>::Value >
{};
Conclusion: don't take this post too seriously ;)
Bye
Roman
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: Edward Diener <eddielee@abraxis.com>
Date: 1999/03/01 Raw View
Andrei Alexandrescu wrote:
> I agree 100% with you. (What an easy life for those guys who say: "I
> agree with everything everyone has said")
> Just like you, I would see Borland-style closures coming naturally in
> C++. (Yes, I also toyed with them nowadays.)
Glad to see that you understand my point about the usefulness of this addition.
> My point in talking about variable-parameters for templates is that
> this is not the first time I felt in need for them. For instance, many
> a programmer's dream is the vararg typesafe min/max/avg/sum functions.
> Others want vararg constructors for vector, others want typesafe
> vararg printf, and so on. A clever vararg templatized solution would
> enable all, plus closures of course.
I think vararg templates are a poor emulation of Borland style "closures" but may be
useful in other situations. To me the main vararg functions from "C", the printf and
scanf series, have been more than adequately replaced by C++ iostreams. I think you might
look at this methodology, chaining parameters by returning a reference to the original
object, as a better way to do some of the other things that you desired with vararg
templates. The C++ iostreams replacement for printf, scanf is also type safe whereas
varargs methodology is not.
> Nonetheless, what costs me to propose a syntax? :o) What about using
> the class keyword? This comes natural:
>
> void (class::*MyClosure)(int) = Object->Method;
> MyClosure(6);
This is one possibility. Another one is
void (::*MyClosure)(int) = Object->Method;
MyClosure(6);
or, of course one can add the "closure" keyword to the language similar to the way that
Borland added "__closure":
void (closure *MyClosure)(int) = Object->Method;
MyClosure(6);
I have no strong preference in this area.
> But now there is something that comes to mind: should a closure as
> described above be able to store a regular pointer to function? I
> mean:
>
> extern void Global(int);
> MyClosure = Global;
> MyClosure(10); // calls Global(10)
I'd like to see this also, simply because C++ still supports functions outside classes,
unlike Java, so it seems a Borland style "closure" should be able to point to those as
well. I'd like to see Borland style "closures" also be able to point to class static
functions, which are just a variation of global functions after all. But even if Borland
style "closures" were able to point to only an object instance's methods, I still see
that as a very useful addition to the C++ language. And another poster, John Maddock,
made the point that if the C++ standard library were changed to allow Borland style
"closures" to be function objects, that would further simplify the use of the standard
library, particularly the STL.
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: Petter Urkedal <petter@matfys.lth.se>
Date: 1999/02/11 Raw View
Christopher Eltschka wrote:
>
> Petter Urkedal wrote:
>
> > So, if you'd excuse me I'll use the term `local namespace' (or
> > maybe `embedded namespace' is better?)
>
> This doen't get it as well. You cannot
> - make pointers/references to namespaces
Short answer: We can if we say so...
Long answer: A namespace belongs to one implicit `object', namely
the collection of all global variables. To make pointers/references
does not make sense, since there is only one such object. When a
namespace is embedded in a class, there is an unspecified number of
objects which determines run-time bindings of the variables. Access
to a variable in a local namespace therefore requires a pair
(object, member), just as for a class. So embedded namespaces would
have pointers and references. Please, see this in context of the
follwing:
If the local namespaces were supported in the language,
struct A {
namespace ns { /* ... */ };
};
A::ns would be a class. So why use the keyword `namespace'?
A member function inside A will see `ns' as a namespace. And
`inside A' is exaclty the place we use this keyword. If we
used a keyword suggesting a `class' it should define something
which, in the place it is used, looks like a class, which an
embedded class does not.
That is, I agree that an embedded class is a class from the
outside, so I shall withdraw my objection about this term. Still
that the keyword for defining the embedded class should _not_
suggest a class, since it will not be seen at a class at the
place of definition. And according to my view of C++ `namespace'
expresses exactly what we need here.
(OTOH, if an ambedding was defined outside the class (and not even
qualifying the definition with `classname::', the keyword should
suggest a class. But this is not feasible.)
> - derive namespaces from "real" classes
> (very important for function-embedded classes/whatever!)
> - define operators on namespaces
>
> The idea is something which looks like a class from outside
> (including their own scope), however it's just part of it's
> scope from inside (whatever that scope may be).
OK, from the outside it looks like a class. From the inside it
looks like a namespace. Or what is the difference between a
`scope' and a `namespace'?
> Maybe just "embedding" is a good choice. I'll use that term in
> the following. I strongly oppose to "local namespace".
If my arguments for `namespace' does not appeal to the audience ;-).
> > And I may point out that strong local classes have an even wider
> > generality. But that's another discussion then.
>
> I don't see any application of strong local classes that cannot
> be achieved with normal classes containing references. Indeed,
> the changes to programs using references are minimal. Moreover,
> the very definition doesn't allow the compiler to optimize more
> than a good compiler will do today with the hand-made solution.
The basis of the claim was that a strong local class would
reduce to an embedding in the case it has no data members;
except that embeddedings may have local variables. But
these may be put into the surrounding class, anyway.
Though, I can see the your viewpoint, as well. It is a matter
of convenience. Because of the special case for local classes,
the two embeddings and local classes will both provide the needed
mechanism for efficient properties.
> Could you please give an (implementation-independant,
> conceptional) definition of "in the context of", as used here?
I'll try: By saying that a statement in a text is in a context,
we mean that its meaning depends on a larger portion of the text.
That is, the context is the surrounding text of a statement, but
the term also indicates a dependence on on this text. So,
applied to a local class inside a class, it means that an object
of the local class will depend on a particular object of its
`context' class. When saying `context' I therefore mean to
emphasize dependence rather than containment.
> To sum up, as I understand both concepts now:
>
> Your "strong local classes"
> [...]
Verified. But notice the `special case' I keep mentioning.
> My "embeddings"
> [...]
If the `namespace' keyword was used, derivation of embeddings is
replaced by a using-directive.
> I see the following advantages of embeddings over strong local
> classes:
>
> - Since they are embedded into a certain scope, they need just
> one this pointer, namely pointing to that scope [...]
But the `special case'...
> - Since they don't have a sizeof and objects cannot be created
> outside the enclosing scope, they can be extended in derived
> classes, causing an effect similar to "virtual data members".
> [...]
Do you propose that a member function of a class which uses an
embedding shall be modified in a derived class which redefines the
embedding? This would be similar to having member functions
be redefined in a derived class to reflect the new overloading of
other member functions. That is, `statically resolved virtual
functions'.
> - Since embeddings just refer to their scope, whatever
> that may be, they can also be used to access the local
> context of a function outside of that function, by deriving
> an embedding in function scope from a normal class
> in another scope, and handing out a base class pointer.
> I don't see a way to accomplish this with strong local classes.
This would be a further extension of the concept, right? But an
embedding in a function would have to be dynamically allocated,
otherwise it loose it's contents when the function returns. And
then we need memory management... Or maybe a sophisticated
stack mechanism?
--
[- http://matfys.lth.se/~petter/ -]
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: Christopher Eltschka <celtschk@physik.tu-muenchen.de>
Date: 1999/02/12 Raw View
Petter Urkedal wrote:
>
> Christopher Eltschka wrote:
> >
> > Petter Urkedal wrote:
> >
> > > So, if you'd excuse me I'll use the term `local namespace' (or
> > > maybe `embedded namespace' is better?)
> >
> > This doen't get it as well. You cannot
> > - make pointers/references to namespaces
>
> Short answer: We can if we say so...
>
> Long answer: A namespace belongs to one implicit `object', namely
> the collection of all global variables. To make pointers/references
> does not make sense, since there is only one such object. When a
> namespace is embedded in a class, there is an unspecified number of
> objects which determines run-time bindings of the variables. Access
> to a variable in a local namespace therefore requires a pair
> (object, member), just as for a class. So embedded namespaces would
> have pointers and references. Please, see this in context of the
> follwing:
>
> If the local namespaces were supported in the language,
>
> struct A {
> namespace ns { /* ... */ };
> };
>
> A::ns would be a class. So why use the keyword `namespace'?
> A member function inside A will see `ns' as a namespace. And
> `inside A' is exaclty the place we use this keyword. If we
> used a keyword suggesting a `class' it should define something
> which, in the place it is used, looks like a class, which an
> embedded class does not.
No, even "inside A", it will be a local class. Only
"inside A::ns", it may look like a namespace - however,
this is not quite correct either. It looks exactly like
your strong local class from there.
If an embedding has its own data, it may make sense to
have two instances of the embedding. This is not possible
with namespaces. It's however normal class behaviour.
An embedding can derive from either a normal class
(not from it's own surrounding class, of course),
or from another (non-surrounding) embedding.
>
> That is, I agree that an embedded class is a class from the
> outside, so I shall withdraw my objection about this term. Still
> that the keyword for defining the embedded class should _not_
> suggest a class, since it will not be seen at a class at the
> place of definition. And according to my view of C++ `namespace'
> expresses exactly what we need here.
>
A namespace of which there can be more than one instance,
which can inherit from normal clsases and from other namespaces,
override virtual functions, and from which other namespaces can be
derived.
Or a class which can only instantiated in the same scope in which
it was defined, and whose methods can access the variables of
their scope, which can be extended in derived class scopes,
and of which no size can be taken.
Now, what sounds more reasonable?
> (OTOH, if an ambedding was defined outside the class (and not even
> qualifying the definition with `classname::', the keyword should
> suggest a class. But this is not feasible.)
Probably the best way really is to make a completely new name
for it, like "embedding". This way, there's no problem with
incompatible expectations.
[...]
>
> > > And I may point out that strong local classes have an even wider
> > > generality. But that's another discussion then.
> >
> > I don't see any application of strong local classes that cannot
> > be achieved with normal classes containing references. Indeed,
> > the changes to programs using references are minimal. Moreover,
> > the very definition doesn't allow the compiler to optimize more
> > than a good compiler will do today with the hand-made solution.
>
> The basis of the claim was that a strong local class would
> reduce to an embedding in the case it has no data members;
However, this gives implementation problems to which I cannot
see a solution.
A member function depends on the layout of the object. Now,
if under some circumstances, the address of the context object
is taken from a reference, and under other contexts it is
calculated directly from the address of the object, how should
the member function know this? Note that the caller may not
know it as well, since this could be called through a pointer.
One solution would be to store the context in the pointers
instead - but that would again create an overhead, this time
for the pointers. Also, if you derive the strong local class
from a normal class (or would you disallow this?), the base
class pointer (and therefore, any class pointer) must be able
to carry such a context.
One additional effect is that for the optimisation to take place,
sizeof must be disallowed as well. Otherwise, you couldn't
get the same size for "embedded" and "free" strong local
classes, without at least reserving the slot for the context
reference.
> except that embeddedings may have local variables. But
> these may be put into the surrounding class, anyway.
If a strong local class has its own data members, they
cannot be part of the context class, since you don't know
how many instances you have (if you restrict yourself
to "embedded" instances, you do know, by the class
definition; however, you explicitly want "free" instances
as well).
If a strong local class has no data members, it doesn't have
it's own state, but only refers to the context class. In this
case, it's 100% equivalent to a reference of an embedding
with no data members. Only the declaration syntax is a little
different:
class SLC
{
public:
local class X
{
void foo() { ++bar; }
}
int bar;
};
class EMB
{
public:
embedding X
{
void foo() { ++bar; }
} x;
int bar;
};
SLC slc;
EMB emb;
void f()
{
slc.X lx; // instance of strong local class slc.X
lx.foo(); // calls void SLC::X::foo(), which increments slc.bar
EMB::X& ex=emb.x; // reference to embedding emb.x
ex.foo(); // calls void EMB::X::foo(), which increments EMB::bar
}
Note that the effect is exactly the same. The EMB code is
admittedly a little bit more verbose. OTOH every C++ programmer
will understand this line immediatly, unlike the SLC syntax
which must be learned.
>
> Though, I can see the your viewpoint, as well. It is a matter
> of convenience. Because of the special case for local classes,
> the two embeddings and local classes will both provide the needed
> mechanism for efficient properties.
Because of the member function problem, I can't see how the
optimisation could work.
[... explanation of your usage of "context" ...]
Ok, I think I understand it now.
>
> > To sum up, as I understand both concepts now:
> >
> > Your "strong local classes"
> > [...]
>
> Verified. But notice the `special case' I keep mentioning.
>
> > My "embeddings"
> > [...]
>
> If the `namespace' keyword was used, derivation of embeddings is
> replaced by a using-directive.
I re-quote my last sentence:
However, pointers/references to
them or to base classes of them (which can be full classes)
can be used as usual.
Look at the part in brackets. Besides, the "using" wouldn't
get the virtual method overriding semantics right (I mean,
one can define it any way one likes; however it's contrary
to the normal use). Probably it should just be a simple
"reopening".
What is the really interesting part is deriving from full
classes. This would be a sort of "partial multiple
inheritance", since only the embedding would inherit;
OTOH it's embedded into its class.
>
> > I see the following advantages of embeddings over strong local
> > classes:
> >
> > - Since they are embedded into a certain scope, they need just
> > one this pointer, namely pointing to that scope [...]
>
> But the `special case'...
... can IMHO not work.
>
> > - Since they don't have a sizeof and objects cannot be created
> > outside the enclosing scope, they can be extended in derived
> > classes, causing an effect similar to "virtual data members".
> > [...]
>
> Do you propose that a member function of a class which uses an
> embedding shall be modified in a derived class which redefines the
> embedding? This would be similar to having member functions
> be redefined in a derived class to reflect the new overloading of
> other member functions. That is, `statically resolved virtual
> functions'.
I don't quite understand what you mean, but I can make an example
of what I mean:
class A
{
public:
A(): i(0) {}
embedded X
{
virtual void foo() { ++i; }
} x;
embedded Y
{
virtual void foo() { i*=2; }
} y;
int get_i() { return i; }
protected:
int i;
};
class B: public A
{
public:
embedded A::X // We override A::X functions
{
void foo() { --i; }
};
embedded Y: A::Y // this is a different B::Y, derived from A::Y
{
void foo() { i*=5; }
} z;
};
A a;
B b;
void change(A& aa)
{
aa.x.foo();
cout << aa.get_i() << ", ";
aa.y.foo();
cout << aa.get_i() << endl;
};
int main()
{
change(a); // prints 1, 2
change(b); // prints -1, -2 (not 1, 2 and not -1, -5)
}
>
> > - Since embeddings just refer to their scope, whatever
> > that may be, they can also be used to access the local
> > context of a function outside of that function, by deriving
> > an embedding in function scope from a normal class
> > in another scope, and handing out a base class pointer.
> > I don't see a way to accomplish this with strong local classes.
>
> This would be a further extension of the concept, right?
Well, it's the extension which made me go away from the
"property" name. As I already said: The embedding is embedded
into it's scope, whatever that may be.
> But an
> embedding in a function would have to be dynamically allocated,
> otherwise it loose it's contents when the function returns.
Of course it looses its context on return from the function,
just like any other local object. The goal is not to make
the function context outlive the function, but only to give
other functions or objects limited access to the function's
local variables through a well defined interface. Just as you
can give unrestricted access to a single variable through
pointers (which get invalid on function return as well).
Well, it seems the complete thread subjects becomes relevant
again ;-)
> And
> then we need memory management... Or maybe a sophisticated
> stack mechanism?
This would only be needed if an outlive-the-scope semantics
should be implemented - but this is somewhat anti-C++.
With normal C++ semantic rules (i.e. pointers/references
to local objects become invalid on function return), you
don't need any sophisticated memory management.
Here again the class nature of embeddings is exposed:
An embedding object in a function is just a local object
that gets destructed of its scope. Only from inside the
embedding, you see the difference.
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: Edward Diener <eddielee@abraxis.com>
Date: 1999/02/10 Raw View
Dave Abrahams wrote:
> Edward Diener wrote in message <36BF74DD.FE050CBB@abraxis.com>...
> >
> >I think that if you honestly look at
> >the way that closures have been implemented by Borland in C++ Builder ( see
> my
> >explanation in another post ), it would be hard for you to say that it is
> not
> >an elegant compiler extension to the language, even if you don't support
> its
> >inclusion in a future version of C++.
>
> Sorry, I don't think it is so elegant. You can do the exact same job with
> templates.
>
> Here's your example rewritten using only built-in language support. I'm
> leaving the implementation of the classes and functions used to implement
> closures as an exercise for the reader. It doesn't look so bad to me...
>
> closure<void (*)()> VoidClosurePrototype;
> closure<int (*)(int)> IntClosurePrototype;
>
> A a;
> B b;
> VoidClosurePrototype.bind(a, A::VoidFunction); //
> OK
> VoidClosurePrototype.bind(b, B::AnotherVoidFunction); // OK
> IntClosurePrototype.bind(a, A::IntFunction);
> // OK
> IntClosurePrototype.bind(b, B::AnotherIntFunction); // OK
> VoidClosurePrototype.bind(a,A::IntFunction); //
> Error
> IntClosurePrototype.bind( b, B::AnotherVoidFunction); // Error
I would like to see the implementation of the closure template and of the bind
function. It's a cop out to me saying that you leave this as an exercise to the
reader. If you do have a workable implementation using templates let's see it.
I'm assuming the bind function is completely templatized also.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: "Dave Abrahams" <abrahams@motu.com>
Date: 1999/02/10 Raw View
Edward Diener wrote in message <36C0CE15.63898A2D@abraxis.com>...
>
>I would like to see the implementation of the closure template and of the
bind
>function. It's a cop out to me saying that you leave this as an exercise to
the
>reader. If you do have a workable implementation using templates let's see
it.
>I'm assuming the bind function is completely templatized also.
Yes.
Unfortunately, I don't have time to write this out just now and test it to
prove that I haven't made any serious gaffes. I wouldn't do it this way
anyway, because I find the mix-in class approach much simpler and cleaner.
Still, I do think I know how the template approach could work. Let's leave
this as a gadfly-of-the-week problem (with apologies to Herb Sutter) for the
time being and see if anyone can come up with an answer.
Cheers,
Dave
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: Francis Glassborow <francis@robinton.demon.co.uk>
Date: 1999/02/07 Raw View
In article <918204313.111294@saturn.riv.be>, Chris Minnoy
<cminnoy@starlab.net> writes
>I think you all make it to complicated. Closures are a very simple
>concept, so keep it a simple concept. Don't try to do things nobody
>understands anymore.
They maybe, but I am completely confused. I get the impression that at
least two different things are referred to as closures. Could you
provide a simple but complete explanation, not just for me but for all
those lurkers who are as confused as I am.
Francis Glassborow Chair of Association of C & C++ Users
64 Southfield Rd
Oxford OX4 1PA +44(0)1865 246490
All opinions are mine and do not represent those of any organisation
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: Edward Diener <eddielee@abraxis.com>
Date: 1999/02/07 Raw View
Here is a simple explanation for closures in Borland's C++ Builder:
A closure is a pointer to a class member function of a given prototype to
which any object's member function of that given prototype can be assigned. A
closure an C++ Builder has the __closure keyword prior to the pointer
notation, and obeys all the C++ scoping rules. Example:
class A
{
public:
void VoidFunction();
int IntFunction(int i);
};
class B
{
public:
void AnotherVoidFunction();
int AnotherIntFunction(int i);
};
void ( __closure * VoidClosurePrototype )();
int ( __closure * IntClosurePrototype)(int i);
A a;
B b;
VoidClosurePrototype = a.VoidFunction; // OK
VoidClosurePrototype = b.AnotherVoidFunction; // OK
IntClosurePrototype = a.IntFunction; // OK
IntClosurePrototype = b.AnotherIntFunction; // OK
VoidClosurePrototype = a.IntFunction; // Error
IntClosurePrototype = b.AnotherVoidFunction; // Error
Closures themselves can be class member data also just like any other pointer
and this is where their power shows itself in C++ Builder for implementing
events. An event is added to a class by specifying a __closure for that event
type. Then any class member function in any other class that has the
prototype of the __closure can be assigned to that __closure. The event is
fired from within the originating class by checking if the __closure is 0
and, if it is not, calling the event handler through the __closure. This is
just one highly effective use of the __closure concept. I don't care if C++
Builder __closures are called by any other name if it is implemented in the
next C++ standard; I just think it is a great idea and can see no reason why
it should not eventually be implemented.
Francis Glassborow wrote:
> They maybe, but I am completely confused. I get the impression that at
> least two different things are referred to as closures. Could you
> provide a simple but complete explanation, not just for me but for all
> those lurkers who are as confused as I am.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: "Chris Minnoy" <cminnoy@starlab.net>
Date: 1999/02/08 Raw View
>I'm reasonably certain that there are, in fact, two completely
>different sorts of things referred to as closures. Let's see if 1) I
>ever really knew enough about LISP to notice, and 2) assuming I did,
>whether I remember enough to make any sense.
Many things can be called closure, because it just is way of
saying that you have two things sitting close to each other.
What those things are is never clear from the name only.
>A function that makes such a reference to a surrounding
>variable is called a closure.
Can be that they call it so in LISP, but I use Scheme a lot, which
is a derivate from LISP, and they never use the name closure.
Scheme uses exactly the same environment system as LISP.
It's not the environment system that we want in C++, no, C++ is
has static scoping, and we certainly do not want to introduce
dynamic scoping in any way (that would be efficient, and
efficiency is a high priority).
What we understand under a closure is a small package
(call it a structure if you like) that has only two things. Those two
things are both pointers, one pointer to an object, and one pointer
to a member function of that object. Nothing more, nothing less.
That closure-(pointer) can, if not assigned anything, be zero (NULL)
just like a normal pointer. If you are still not sure about what closures
are, pleace look at C++Builder or Delphi, both use this system, which
is very powerful for eventhandling.
The main advantage of closures is that syntacticaly they are very
clean. You could emulate a closure in C++ right now, just be putting
an object pointer and a member-func-pointer into a struct, but that wouldn't
be that clean. You could forget to assign one of them, or try to assign
wrong
elements to those pointers. This is not possible in C++Builder, because
the assignment syntax of closures is different. (see explanation Edward
Diener).
>My initial stab at things would probably be to declare a base class
>functor object. When I wanted to use a "closure"-like device, I'd use
>a derivative of this functor instead of a member function.
Don't try to invent the wheel once again, Borland did a good job, so
use their system. It's much cleaner and more powerfull than any
system you could come up with.
The only realy improvement I can think of is having a third kind of pointer,
a combination of a normal pointer and a closure. One that can point
to a free function or a memberfunction+object. This alows the greatest
flexibility for callbackfunctions. The third kind of pointer however doesn't
recuire new keyword or C++ language change (in the contrast to a closure).
It's just a simple template class than has inside it a union of a pointer
and a
closure. Thats' it. Fast, memory efficient, clean and not confusing.
That's how a language should be.
Chris
cminnoy@starlab.net
http://www.starlab.net
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: Petter Urkedal <petter@matfys.lth.se>
Date: 1999/02/08 Raw View
Christopher Eltschka wrote:
>
> I would say they may contain local variables, since those can
> just be members of the surrounding class. In some way, the embedded
> class could be seen as an encapsulation of a part of the surrounding
> class, instead of being a full fledged class on it's own. It exists
> just as part of it's outer class (or, more generally, of it's outer
> context). This is why I would prohibit sizeof on it - the members
> need not be a compact block in the surrounding context. Even
> extending in derived classes is possible this way.
Thanks, then I see your idea of an `embedded class'. So, I shall
not use the same term for my own solution then, but rather call
it a `strong local class' (or `local class' for short) as opposed to
the present `weak local class'.
Your solution is then just sufficient to support properties. So, I
approve of it. However, I disagree with the term `class' here.
Since an embedded `class' cannot be instantiated independently
of its surrounding class, it is not a class to me. Rather, it
provides an alternative interface to an object, namely a different
namespace,
struct graphic {
namespace thickness {
int n;
graphic::thickness& operator=()(int i) {
n = i;
redisplay();
return this->thickness;
}
// etc.
}
private:
void redisplay();
}
That is, the `.thickness' or `->thickness' acts on an object to
cast it into the same object, but with a different interface.
Further, the types `graphic::thickness' and `graphic' are isomorphic
and share the same memory layout, but provide different interfaces.
So, if you'd excuse me I'll use the term `local namespace' (or
maybe `embedded namespace' is better?)
> Petter Urkedal wrote:
> >
> > Christopher Eltschka wrote:
> > >
> > or simply `using class graphic'. OK, `embedded' may be better. But
> > if we have several levels of classes, a using directive may be more
> > expressive:
> >
> > struct A {
> > struct B {
> > struct C { using class A; }
> > };
> > };
> >
> Nut an embedded class relative to A doesn't make sense if the
> directly enclosing class isn't embedded in A as well.
True for local namespaces, but not for strong local classes.
> However, the using syntax has the advantage of reusing existing
> identifiers. Probably it's hte better syntax for this reason.
> OTOH, embedded makes more clear what really happens.
Yes, I think using-directives may be confusing in this case, and
the real confusion comes from the lack of an `embedded' keyword
to warn about the `class' keyword which does not declare a class.
> > Christopher Eltschka wrote:
> > > BTW, I like the "embedded class" idea because of the
> > > wider generality (see the function example above).
> >
> > than properties?
>
> Exactly.
>
> > In which case I still agree.
>
And I may point out that strong local classes have an even wider
generality. But that's another discussion then.
> > So is it useful? At least there is no reason not to provide
> > construction
>
> IMHO there is - see below.
Again, true for local namespaces but not for strong local
classes.
> It seems we have quite different vies aboud embedded classes.
> In my view, it's an encapsulation of a _part_ of something,
> and therefore conceptionally very different from just an object
> contining a reference to something, even if it can be
> implemented as such (think of inheritance, which also is
> implemented by containing, but is fundamentally different).
I agree that a local namespace is _part_ of the surrounding
class. On the other hand, a strong local class is simply
_in the context of_ the surrounding class.
> It doesn't make sense to define a part of something outside
> this "something". You cannot get a hole without the matter
> surrounding it.
But don't think that applies to the term `in the context of'.
-petter.
--
[- http://matfys.lth.se/~petter/ -]
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: Francis Glassborow <francis@robinton.demon.co.uk>
Date: 1999/02/08 Raw View
In article <918467238.229397@saturn.riv.be>, Chris Minnoy
<cminnoy@starlab.net> writes
>
>Many things can be called closure, because it just is way of
>saying that you have two things sitting close to each other.
>What those things are is never clear from the name only.
What has 'closeness' got to do with it? The property of being 'close'
and the action of 'closing' are effectively unrelated. A closure is what
you get when you close something. I am now more than ever convinced
that we have serious terminilogical confusion here.
>The main advantage of closures is that syntacticaly they are very
>clean. You could emulate a closure in C++ right now, just be putting
>an object pointer and a member-func-pointer into a struct, but that wouldn't
>be that clean. You could forget to assign one of them, or try to assign
>wrong
>elements to those pointers. This is not possible in C++Builder, because
>the assignment syntax of closures is different. (see explanation Edward
>Diener).
First, in the part I snipped you suggested we refer to
C++Builder/Delphi. That is not an option available for many (probably
the substantial majority of readers of this NG)
Second, what is wrong with enhancing your struct to provide correct
assignment etc.?
>
>>My initial stab at things would probably be to declare a base class
>>functor object. When I wanted to use a "closure"-like device, I'd use
>>a derivative of this functor instead of a member function.
>
>
>Don't try to invent the wheel once again, Borland did a good job, so
>use their system. It's much cleaner and more powerfull than any
>system you could come up with.
But their solution is inherently non-portable (and probably their
intellectual property)
>
>The only realy improvement I can think of is having a third kind of pointer,
>a combination of a normal pointer and a closure. One that can point
>to a free function or a memberfunction+object. This alows the greatest
>flexibility for callbackfunctions. The third kind of pointer however doesn't
>recuire new keyword or C++ language change (in the contrast to a closure).
>It's just a simple template class than has inside it a union of a pointer
>and a
>closure. Thats' it. Fast, memory efficient, clean and not confusing.
>That's how a language should be.
Unions are fraught with danger, particularly ones such as you suggest.
Francis Glassborow Chair of Association of C & C++ Users
64 Southfield Rd
Oxford OX4 1PA +44(0)1865 246490
All opinions are mine and do not represent those of any organisation
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: Petter Urkedal <petter@matfys.lth.se>
Date: 1999/02/08 Raw View
Chris Minnoy wrote:
>
> I think you all make it to complicated. Closures are a very simple
> concept, so keep it a simple concept. Don't try to do things nobody
> understands anymore.
Sorry, my message was about local classes, and thus relating to
properties rather than closures. IMO, closures is present, or at least
implementable in C++. It is simply a functional with a virtual
`operator()' encapsulated in some class to support assignment of
functionals and function pointers.
But, it seem that lambda expressions sometimes goes along with
closures. To large extent lambda expression can also be implemented
through expression templates. (See my web page.)
> I think you shouldn't reinvent the wheel. The creators of C++Builder
> already have experience in implementing closures, in using
> closures and they know what are the pro's and the contra's.
> And I think there implementation of the concept is far better
> and more understandable than yours.
Again, my message was not about closures. Neither was my message
meant to explain local classes, but rather to show that it can be
implemented in the general case and that such a prototype
implementation implies a well defined semantic. My message may
lack in pedagogical quality, but what about the concept of
local classes by itself?
-petter.
--
[- http://matfys.lth.se/~petter/ -]
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: "Dave Abrahams" <abrahams@motu.com>
Date: 1999/02/08 Raw View
Chris Minnoy wrote in message <918467238.229397@saturn.riv.be>...
>Don't try to invent the wheel once again, Borland did a good job, so
>use their system. It's much cleaner and more powerfull than any
>system you could come up with.
I would tend to dispute the claim that it's 'cleaner' than using the
built-in facilities of C++. Could you explain the advantage in power given
by not having to use a common mixin class with a pointer-to-function?
Personally, I get power from features which are designed to protect me.
These features allow me to code freely with less worry about stupid errors.
It sounds as though it would be easy to mistakenly invoke a function with
the correct signature but the wrong purpose using closures.
If you absolutely insist on avoiding the common base class, there are ways
to do it using templates.
-Dave
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: jcoffin@taeus.com (Jerry Coffin)
Date: 1999/02/08 Raw View
In article <pw5fQdBcnDv2EwPv@robinton.demon.co.uk>,
francis@robinton.demon.co.uk says...
[ talking about closures ]
> They maybe, but I am completely confused. I get the impression that at
> least two different things are referred to as closures. Could you
> provide a simple but complete explanation, not just for me but for all
> those lurkers who are as confused as I am.
I'm reasonably certain that there are, in fact, two completely
different sorts of things referred to as closures. Let's see if 1) I
ever really knew enough about LISP to notice, and 2) assuming I did,
whether I remember enough to make any sense.
The first version originated years ago, I believe in LISP. In LISP
(or almost any functional language) it's possible to create a function
and, for example, use it as the return value from another function.
If the function being returned refers to a variable in the scope of
the function that created it (dynamically scoped originally, lexically
scoped more recently), LISP (and others like it, of course) maintain
the variable (or variables) in a usable state even though you've
returned from the function in which they're declared, because the
scope is still in use, even though execution is no longer within that
scope. A function that makes such a reference to a surrounding
variable is called a closure.
If anybody cares, the name originated from the way it was originally
implemented -- IIRC, it referred to the fact that upon leaving the
scope, the scope was simply "closed" rather than completely destroyed
as it usually would be. Only later, when the function using the scope
went out of use, was the scope destroyed. My immediate guess is that
it would be difficult (at best) to implement this without a garbage
collector.
This ALL runs pretty much contrary to anything in C++ at all: it
simply doesn't allow you to create functions at run-time to start
with, so there's nothing to govern how they'd work if you could. The
closest you can come is creating an object at run-time, and returning
it. The object can, of course, have variables of its own, creating a
scope in which a member function executes. If the class of which you
return an instance overloads the function-call operator, you might be
able to use this to simulate something vaguely similar to a closure,
though my memory of using them isn't clear enough that I feel safe
saying with any certainty.
The second version is an invention of Inprise/Borland. This is
similar to a pointer to a member function. However, as long as the
member function is of the correct type, it can refer to a member of
ANY class, rather than being required to refer only to members of a
specific class (or derivatives thereof, of course) as C++ requires.
In any case, I think that more or less covers what's being discussed.
I'll leave it to others to say whether it would be desirable to
include either or both in a future version of the C++ standard -- I'm
honestly not sure. I can see some point from the Borland users
advocating that it be included, but I think it'd take some work with
an implementation of it, as well as an exploration of alternatives
before it'd be clear what was desirable.
My initial stab at things would probably be to declare a base class
functor object. When I wanted to use a "closure"-like device, I'd use
a derivative of this functor instead of a member function. Since a
member object doesn't require a different sort of pointer than a non-
member object, a pointer to the base class could invoke the function
call operator in any derived object, regardless of what it happened to
be a member of (or even if it wasn't a member of anything.)
Assuming this worked, the obvious question would be whether enough
people would use this often enough to justify adding keywords and what
looks like it might well be a fairly complex implementation, strictly
to support writing something as a function instead of a functor.
If deriving all "closures" from a single base class was particularly
onerous, it seems like you could probably do the same basic thing with
a template instead, though in this case I'm not sure it accomplishes
enough to care much about.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: enrightm@acm.org (Mike Enright)
Date: 1999/02/08 Raw View
Edward Diener <eddielee@abraxis.com> wrote:
><...>
>void ( __closure * VoidClosurePrototype )();
>int ( __closure * IntClosurePrototype)(int i);
>
>A a;
>B b;
>
>VoidClosurePrototype = a.VoidFunction; // OK
>VoidClosurePrototype = b.AnotherVoidFunction; // OK
>IntClosurePrototype = a.IntFunction; // OK
>IntClosurePrototype = b.AnotherIntFunction; // OK
>
>VoidClosurePrototype = a.IntFunction; // Error
>IntClosurePrototype = b.AnotherVoidFunction; // Error
>
It sounds like closures are similar to mem_fun_t's except you can
specify the arguments the member function must receive. At the moment
one scheme comes to mind for emulating them:
class ClosureEmulatingMouseUpNoticer
{
public:
struct MouseUp
{
void Click(int x, int y) = 0;
};
template <class T>
struct TMouseUp : public MouseUp
{
TMouseUp(T* obj, void (T::* fn)(int x, int y)
: obj(obj), fn(fn)
{
}
void Click(int x,int y)
{
(obj->fn)(x, y);
}
T* obj;
void (T::*fn)(int,int);
};
template <class T>
void SetMouseUpClosure(T* obj, void (T::* fn)(int x, int y))
{
mouseUp = new TMouseUp(obj, fn);
}
private:
void MouseUpReaction(int x, int y)
{
if (mouseUp)
mouseUp->Click(x,y)
}
MouseUp* mouseUp;
};
Generics are cool!
--
Mike Enright
enrightm@acm.org (Email replies cheerfully ignored, use the news group)
http://www.users.cts.com/sd/m/menright/
Cardiff-by-the-Sea, California, USA
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: Edward Diener <eddielee@abraxis.com>
Date: 1999/02/09 Raw View
Dave Abrahams wrote:
> Chris Minnoy wrote in message <918467238.229397@saturn.riv.be>...
> >Don't try to invent the wheel once again, Borland did a good job, so
> >use their system. It's much cleaner and more powerfull than any
> >system you could come up with.
>
> I would tend to dispute the claim that it's 'cleaner' than using the
> built-in facilities of C++. Could you explain the advantage in power given
> by not having to use a common mixin class with a pointer-to-function?
It's cleaner because you wouldn't have to create innumerable mix-in classes and
connect it to both the closure class and the closure user for every instance
that you want to emulate closures. Closures would be a simple language addition
that would allow the programmer to specify a closure within a given scope and
then allow any user of the closure to hook to it.
> Personally, I get power from features which are designed to protect me.
> These features allow me to code freely with less worry about stupid errors.
> It sounds as though it would be easy to mistakenly invoke a function with
> the correct signature but the wrong purpose using closures.
C and C++ were created with the philosophy that the programmer should know what
he or she is doing. There are languages that do a much better job at protecting
the programmer from his or her errors at the cost of power and flexibility. Not
desiring a powerful, flexible feature because it might allow the programmer to
make an error seems to me a poor reason for a feature to be disallowed in C++.
> If you absolutely insist on avoiding the common base class, there are ways
> to do it using templates.
Perhaps there are, but could it be a simple addition to the language might
still be much better and easier to use. Sometimes it is better to add a new
feature to a language, if that feature has a clean implementation, than to come
up with an alternative way that a language already supports for that feature
that is much harder to implement and use. I think that if you honestly look at
the way that closures have been implemented by Borland in C++ Builder ( see my
explanation in another post ), it would be hard for you to say that it is not
an elegant compiler extension to the language, even if you don't support its
inclusion in a future version of C++.
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: Christopher Eltschka <celtschk@physik.tu-muenchen.de>
Date: 1999/02/09 Raw View
Petter Urkedal wrote:
>
> Christopher Eltschka wrote:
> >
> > I would say they may contain local variables, since those can
> > just be members of the surrounding class. In some way, the embedded
> > class could be seen as an encapsulation of a part of the surrounding
> > class, instead of being a full fledged class on it's own. It exists
> > just as part of it's outer class (or, more generally, of it's outer
> > context). This is why I would prohibit sizeof on it - the members
> > need not be a compact block in the surrounding context. Even
> > extending in derived classes is possible this way.
>
> Thanks, then I see your idea of an `embedded class'. So, I shall
> not use the same term for my own solution then, but rather call
> it a `strong local class' (or `local class' for short) as opposed to
> the present `weak local class'.
>
> Your solution is then just sufficient to support properties. So, I
> approve of it. However, I disagree with the term `class' here.
> Since an embedded `class' cannot be instantiated independently
> of its surrounding class, it is not a class to me. Rather, it
> provides an alternative interface to an object, namely a different
> namespace,
>
> struct graphic {
> namespace thickness {
> int n;
> graphic::thickness& operator=()(int i) {
> n = i;
> redisplay();
> return this->thickness;
> }
> // etc.
> }
> private:
> void redisplay();
> }
>
> That is, the `.thickness' or `->thickness' acts on an object to
> cast it into the same object, but with a different interface.
> Further, the types `graphic::thickness' and `graphic' are isomorphic
> and share the same memory layout, but provide different interfaces.
>
> So, if you'd excuse me I'll use the term `local namespace' (or
> maybe `embedded namespace' is better?)
This doen't get it as well. You cannot
- make pointers/references to namespaces
- derive namespaces from "real" classes
(very important for function-embedded classes/whatever!)
- define operators on namespaces
The idea is something which looks like a class from outside
(including their own scope), however it's just part of it's
scope from inside (whatever that scope may be).
Indeed, it looks like a class in most situations; especially
from outside the scope, the only way to distinguish it
from a normal class with inaccessible constructors and
destructor is that you cannot take its size (except of
course by reading the error message text, which will
probably be different for normal and embedded class).
Maybe just "embedding" is a good choice. I'll use that term in
the following. I strongly oppose to "local namespace".
>
> > Petter Urkedal wrote:
> > >
> > > Christopher Eltschka wrote:
> > > >
> > > or simply `using class graphic'. OK, `embedded' may be better. But
> > > if we have several levels of classes, a using directive may be more
> > > expressive:
> > >
> > > struct A {
> > > struct B {
> > > struct C { using class A; }
> > > };
> > > };
> > >
> > Nut an embedded class relative to A doesn't make sense if the
> > directly enclosing class isn't embedded in A as well.
>
> True for local namespaces, but not for strong local classes.
But your strong local classes can be implemented with
today's C++ with only a little extra work. Moreover, due
to their definition, the compiler-implementation cannot be
more optimized than a good compiler can do with the
hand-made solution.
[...]
> > > Christopher Eltschka wrote:
> > > > BTW, I like the "embedded class" idea because of the
> > > > wider generality (see the function example above).
> > >
> > > than properties?
> >
> > Exactly.
> >
> > > In which case I still agree.
> >
> And I may point out that strong local classes have an even wider
> generality. But that's another discussion then.
I don't see any application of strong local classes that cannot
be achieved with normal classes containing references. Indeed,
the changes to programs using references are minimal. Moreover,
the very definition doesn't allow the compiler to optimize more
than a good compiler will do today with the hand-made solution.
I *do* see applications of embeddings which are not
possible today, or only with large overhead.
[...]
> > It seems we have quite different vies aboud embedded classes.
> > In my view, it's an encapsulation of a _part_ of something,
> > and therefore conceptionally very different from just an object
> > contining a reference to something, even if it can be
> > implemented as such (think of inheritance, which also is
> > implemented by containing, but is fundamentally different).
>
> I agree that a local namespace is _part_ of the surrounding
> class. On the other hand, a strong local class is simply
> _in the context of_ the surrounding class.
An embedding is part of the surrounding _scope_, which
may be a class, a partial class, a function or even
a namespace.
Could you please give an (implementation-independant,
conceptional) definition of "in the context of", as used here?
[...]
Ok, we have therefore two different concepts, which both
are intended to solve the prorperty problem in a more
general form (i.e. being useful in more situations than
a simple property extension).
To sum up, as I understand both concepts now:
Your "strong local classes"
- are full classes, which carry around a "context" which refers to
a particular object (I'm still not understanding fully what
you understand by this "context" conceptionally; I think I _do_
understand the mechanisms which result, however)
- will be implemented with a hidden pointer or reference to
a given object, which is initialized by prefixing the type
with the object (which can be omitted if the object is local
to the class of its context).
- Have access to the "context object" by using this hidden pointer
internally as delegation pointer (that is, if they don't contain
a given object/function themselves, it is looked up in the
context object)
My "embeddings"
- are part of the enclosing scope (which will generally be
either a class, an embedding or a function); especially
they are not really an entity by themselves, although they
act as if they were
- act mostly like complete classes inside their enclosing scope;
if the enclosing scope is a class, another class deriving from
it can add new members/member functions as well as override virtual
member functions, affecting objects of the embedding
defined in the base class. As a consequence, sizeof is not
allowed for embeddings.
- act similary outside their enclosing scope, but objects
may not be defined here. However, pointers/references to
them or to base classes of them (which can be full classes)
can be used as usual.
I see the following advantages of embeddings over strong local
classes:
- Since they are embedded into a certain scope, they need just
one this pointer, namely pointing to that scope (if the scope
is a function, it's a pointer into the local stack frame;
for global/namespace scope, no this is required).
Strong local classes OTOH have to deal with at least two
pointers: Their own this pointer and the hidden context pointer
(possibly it should be explicit, too, to allow context->foo()
as opposed to this->foo())
- Since they don't have a sizeof and objects cannot be created
outside the enclosing scope, they can be extended in derived
classes, causing an effect similar to "virtual data members".
Strong local classes OTOH are classes on their own right,
and therefore local objects cannot be extended in any way,
just as with other classes
- Since embeddings just refer to their scope, whatever
that may be, they can also be used to access the local
context of a function outside of that function, by deriving
an embedding in function scope from a normal class
in another scope, and handing out a base class pointer.
I don't see a way to accomplish this with strong local classes.
- Embeddings allow you to do things which cannot be done
easily with current C++. Strong local classes OTOH can be
implemented by a simple procedure:
* add a context pointer or reference explicitly
* for each access to the context class, write context->
(or context.) explicitly
* initialize the context pointer from each constructor
* Replace obj.Type(xx) by Type(obj, xx)
- Embeddings address two topics which come up in the
newsgroups from time to time:
* efficient properties
* passing the local context of functions to other
functions or objects
Strong local classes seem only to address properties, and
as far as I can see, for this they can not be better than
current hand-made solutions, since they still demand
a pointer or reference to their context, which can AFAICS
be optimized away only in the same situations as they can
be currently. While one can assume that compiler
writers would take more efford to optimize this, if it
were an explicit language feature, I think it would be
as well, if it were a common idiom.
The extra optimisation the compiler can do for the
embedded class results from the fact that the compiler
always knows that it is really operating on the enclosing
scope, and therefore can avoid the reference, as well as
dereferencing that. That is, with embedding, an access
to an enclosing class member is just this->member, while
for strong local classes it is this->context->member.
For out-of-line functions, this cannot be optimized,
since the compiler must expect the function to be called
on objects which are not members of their context.
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: "Dave Abrahams" <abrahams@motu.com>
Date: 1999/02/09 Raw View
Edward Diener wrote in message <36BF74DD.FE050CBB@abraxis.com>...
>
>Dave Abrahams wrote:
>
>> Chris Minnoy wrote in message <918467238.229397@saturn.riv.be>...
>> >Don't try to invent the wheel once again, Borland did a good job, so
>> >use their system. It's much cleaner and more powerfull than any
>> >system you could come up with.
>>
>> I would tend to dispute the claim that it's 'cleaner' than using the
>> built-in facilities of C++. Could you explain the advantage in power
given
>> by not having to use a common mixin class with a pointer-to-function?
>
>It's cleaner because you wouldn't have to create innumerable mix-in classes
and
>connect it to both the closure class and the closure user for every
instance
>that you want to emulate closures. Closures would be a simple language
addition
>that would allow the programmer to specify a closure within a given scope
and
>then allow any user of the closure to hook to it.
To carry this argument to an extreme, you could advocate the inclusion of a
lisp interpreter in C++ because it would be simpler than trying to emulate
lisp in C++. You've made a circular argument for Borland closures in terms
of Borland closures. My question is, "what would you want Borland closures
for that you couldn't do just as well with mix-in classes?"
>> Personally, I get power from features which are designed to protect me.
>> These features allow me to code freely with less worry about stupid
errors.
>> It sounds as though it would be easy to mistakenly invoke a function with
>> the correct signature but the wrong purpose using closures.
>
>C and C++ were created with the philosophy that the programmer should know
what
>he or she is doing. There are languages that do a much better job at
protecting
>the programmer from his or her errors at the cost of power and flexibility.
Not
>desiring a powerful, flexible feature because it might allow the programmer
to
>make an error seems to me a poor reason for a feature to be disallowed in
C++.
I don't desire it because:
a. I haven't heard a compelling argument for why it is neccessary
b. It sounds like it might be an undue restriction on implementors
c. You can do the same job without any special language support (even
without any mix-in classes).
>> If you absolutely insist on avoiding the common base class, there are
ways
>> to do it using templates.
>
>Perhaps there are, but could it be a simple addition to the language might
>still be much better and easier to use. Sometimes it is better to add a new
>feature to a language, if that feature has a clean implementation, than to
come
>up with an alternative way that a language already supports for that
feature
>that is much harder to implement and use.
Sometimes, if enough people really need the feature, that's true. In any
case, I think you're overrating the complexity of the alternative. If you
can come up with a way that is already supported by the language and isn't
much more complex, you're almost always better off avoiding a language
extension.
>I think that if you honestly look at
>the way that closures have been implemented by Borland in C++ Builder ( see
my
>explanation in another post ), it would be hard for you to say that it is
not
>an elegant compiler extension to the language, even if you don't support
its
>inclusion in a future version of C++.
Sorry, I don't think it is so elegant. You can do the exact same job with
templates.
Here's your example rewritten using only built-in language support. I'm
leaving the implementation of the classes and functions used to implement
closures as an exercise for the reader. It doesn't look so bad to me...
closure<void (*)()> VoidClosurePrototype;
closure<int (*)(int)> IntClosurePrototype;
A a;
B b;
VoidClosurePrototype.bind(a, A::VoidFunction); //
OK
VoidClosurePrototype.bind(b, B::AnotherVoidFunction); // OK
IntClosurePrototype.bind(a, A::IntFunction);
// OK
IntClosurePrototype.bind(b, B::AnotherIntFunction); // OK
VoidClosurePrototype.bind(a,A::IntFunction); //
Error
IntClosurePrototype.bind( b, B::AnotherVoidFunction); // Error
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: hursh <hursh@sparc.isl.net>
Date: 1999/02/03 Raw View
Jean-Louis Leroy wrote:
> In article <36A4007F.9916759C@abraxis.com>, Edward Diener wrote:
>
> However, I don't see how you can provide a full-fledged implementation in C++
> without contradicting some fundemental design choices of C++. This is because
> closures can refer to variables pertaining to the scope in which the closure
> was created, and thus make those variables outlive the scope that created them.
>
Instead of trying to fit closures of this sort into C++, why not try something
more in tune with C++'s static scoping. For me the main features of a closure is
that you get an anonymous function, and that you can place a quick one or two line
function 'literal' where it is being 'assigned' so to speak. Normally these one
or two line function are just a quick bit of interface 'glue' and don't represent
enough of a concept to have a meaningful name.
That said, I think it would be nice enough if C++ allowed 'anonymous function
literals' and let the compiler assign some unique id as a name for its uses.
set_callback( // set_callback requires a void(*)(int)
lambda<void> (int severity) {
if( severity > 5)
cout << "Oh God, it's happening again!" << endl;
}
);
The next issue might be the fact that this solution does not allow the closure
to see variables from the lexical scope in which it was declared. A cop out could
be to say that the language is not dynamically scoped, and let the issue die at
that. However, if we want to make life more difficult with compiler guys, at
compile time we could have the closure generate a function object that has the
function literal as the operator()() function, and has data members that are
copies of the lexical variables use by the closure at the time function literal
declared. So in other words:
foo(string onDuty){
int local_var
//...
set_callback(
lambda<void> (int severity) {
if( (severity > local_var) && PANIC) // PANIC is global
sendMail(onDuty, "Oh God! It's happening again!\n");
}
);
//...
}
could generate an anonymous object like:
class ???{
onDuty; // assigned value of foo's onDuty var at the time set_callback was
called
local_var; // copy of local_var's value too
operator()(int severity){
if( (severity > local_var) && PANIC) // PANIC is still global
sendMail(onDuty, "Oh God! It's happening again!\n");
}
};
Granted, there are probably problems with this as stated. One problem would
be that the closure could use a lexical pointer that pointed a lexical variable.
That could be left as undefined behavior though, once again using our cop out
"This is not a dynamically scoped language, so you can't do dynamically scope
tricks". There are probably other points to be ironed out, but I think a feature
like this would be able to cover most of the features folks would like from a
closure without requiring too much more in the way of compiler tricks.
Regards,
Dan Hursh
-----------== Posted via Newsfeeds.Com, Uncensored Usenet News ==----------
http://www.newsfeeds.com/ The Largest Usenet Servers in the World!
-----------== Over 66,000 Groups, Plus a Dedicated Binaries Server ==----------
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: Petter Urkedal <petter@matfys.lth.se>
Date: 1999/02/04 Raw View
Christopher Eltschka wrote:
>
> If I understand you correctly, you want anonymous structs/classes
> to get access to the members of the surrounding class.
Actually, the point was to allow contextual access for _any_ local
class. No special rule. Though, I see it could be misunderstood.
> And more problematic. It would either make current programs
> illegal (since objects of local classes may currently be
> defined outside of objects of the outer class, so there's
> no context which could be accessed), or make the rules more
> confusing (if restricted to unnamed structs where the previous
> problem doesn't arise, you get different special rules for
> different unnamed types).
The scope of a member function defined outside the class definition
includes the class members, so by the same token, the scope of a
member function of a local class includes the class and its context
classes, when (as specified below) the class en embedded.
> > This can be implemented in a rigid way if we follow the present C++
> > practice in situations of the kind. Namely, properties can
> > be implemented through proxy objects,
> >
> > struct graphic {
> > struct thickness_proxy {
> > proxy(graphic& g_) : g(g_) {}
> > void operator=(int t) { g.the_thickness=t; g.redisplay(); }
> > // ...
> > };
> > thickness_proxy& thickness() { return *this; }
> >
> > private:
> > int the_thickness;
> > void redisplay();
> > };
>
> This doesn't give the wanted syntax - additional () -, and in
> addition can cause overhead (the reference in thickness_proxy).
> It for sure _will_ get overhead, if you try to get the syntax
> right by making the thickness_proxy a reference.
I know. But it is still practice, AFAIK. Though full optimization
of the above proxy is realistic, not all compilers will do that.
> > Generalizing this scheme, the basic transformation of a class A written
> > with true local classes to present C++ is,
> >
> > * If a local class L (or it's local classes) does not access any
> > members
> > in A, no change is made.
>
> If this is a proposed change to the language, it doesn't work.
> The compiler cannot decide if there's a member access, unless
> it knows all the definitions of the member functions. But
> those may be in a different translation unit - they may even
> be in a dynamic library, and therefore this fact may change
> without recompiling all code using this class.
> The only way to make that difference at all is to tell the
> compiler explicitly (f.ex. with "embedded").
The using directive could also be useful here:
struct graphic {
struct local {
using the_thickness;
// ...
};
private:
int the_thickness;
}
or simply `using class graphic'. OK, `embedded' may be better. But
if we have several levels of classes, a using directive may be more
expressive:
struct A {
struct B {
struct C { using class A; }
};
};
> > * Otherwise, insert a declaration `A &__context;' into the private
> > data-
> > section of L and transform each constructor
>
> Unnecessary overhead. Moreover, if you are willing to accept
> this overhead (and some extra typing), you can implement
> properties today, with normal classes.
I'm not. The scheme was meant to show how the semantics of embedded
classes can be well defined, and that there _is_ a way to implement
it in the general case. However, once the feature is in the language,
compilers can optimize, and I'd expect that in many situations,
especially for inlined member functions of the local class.
* if the local class is empty, it can be considered to be a class
derived privately from its context class, and an object of the
class to be a static_cast reference to it. Cf.
class local : private context { };
context c; static_cast<local&>(c)->memfun();
which, of course is ill formed, but still works on many platforms.
(I guess this is the non-standard optimization for proxy objects
you mentioned?)
* if the local class is private, and it is not present (neither as
sub objects) in the interfacing members of the surrounding class,
the __context reference can be elided.
Point is, it is possible with today's standard to emulate embedded
classes, but I would not expect such coding practice to be optimized.
On the other hand, true language support defines a framework around
which compiler writers can provide optimizations.
> BTW, I like the "embedded class" idea because of the
> wider generality (see the function example above).
than properties? In which case I still agree. So, the question is
whether embedded classes may contain local variables (and whether
they are declared with a new keyword or with the `using' directive.)
> > `L(arglist) : init body'
> >
> > into
> >
> > `L(A& __c, arglist) : __context(__c), init body'
>
> Which cannot work if you are not in a member function of A.
> Therefore you have to restrict usage of the local class to
> inside A.
>
> >
> > * Whenever a member x of A is accessed in L, without an object given
> > (as in `a.x'), it is replaced by `__context.x'.
> >
> > * When an object of the class is constructed within the class A,
> > the proper `*this' argument is known, and can be inserted into the
> > argument list of the ctor. From outside A, we may say
> >
> > A a;
> > a.C c(arglist);
> >
> > where `a.L l(arglist);' --> `L l(a, arglist)' iff `L' is a local
> > class in the class of `a'.
>
> I don't see any usage of this.
You mean why it is useful? Or what it is supposed to mean? The type
of a local class is normally given as `A::L'. However, since
construction from outside `A', requires an object `A a' of the class,
we substituted `A::' with `a.'. Thus the construction looks like
`a.L l' or `new a.L'.
Interestingly `a.L' may be considered to be the true type of the local
class. It could be compared to
template<A& context> struct L { /* ... */ };
where `L<a>' is a unique type for each `a'. Of course, this us useless
in implementing properties, since `a' must have external linkage.
So is it useful? At least there is no reason not to provide
construction
of an embedded class from outside, since such a construction can already
be done though a member function of the context class. A possible use:
struct styles_and_preferences {
class diaglog_box;
class button;
class whatever;
void three_D_look(bool);
void border_thickness(int);
void look_like(int this_and, int that);
private:
// ...
};
class dialog_box {
using class styles_and_preferences;
// ...
};
Now, the context may be initialized and passed around the program.
Assuming `styles_and_preferences s;', a new button is constructed
as `s.button b;'. Notice that the using-declaration need not be
given before the local classes are defined.
> IMHO, embedded classes should be considered part of the surrounding
> class (if they are embedded in a class, not in a function). That is,
> the *this pointer of the embedded class is just the *this pointer of
> the surrounding class, and the virtual functions of the embedded
> class are put into the vtbl of the surrounding class. This allows
> overriding them on derived classes, even though there's an object
> of the class declared - giving some sort of member variable
> inheritance. To allow to add more members, one would not demand
> that embedded classes are continuous (and therefore one should
> disallow sizeof on embedded classes; sizeof wouldn't make sense
> with function-embedded classes as well, since the local
> variables of the function are not technically part of the class,
> although they are logically).
>
> Indeed, I now think embedded classes are the best solution
> for properties, as well as for passing the local context of
> functions to other functions.
Author: "Chris Minnoy" <cminnoy@starlab.net>
Date: 1999/02/05 Raw View
I think you all make it to complicated. Closures are a very simple
concept, so keep it a simple concept. Don't try to do things nobody
understands anymore.
On page 7 of Bjarne's book I find:
Simplicity was an important design criterion...
Eistein said: keep it simple, as simple as possible, but no simpler.
I think you shouldn't reinvent the wheel. The creators of C++Builder
already have experience in implementing closures, in using
closures and they know what are the pro's and the contra's.
And I think there implementation of the concept is far better
and more understandable than yours.
Chris
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: Christopher Eltschka <celtschk@physik.tu-muenchen.de>
Date: 1999/02/05 Raw View
Petter Urkedal wrote:
>
> Christopher Eltschka wrote:
> >
> > If I understand you correctly, you want anonymous structs/classes
> > to get access to the members of the surrounding class.
>
> Actually, the point was to allow contextual access for _any_ local
> class. No special rule. Though, I see it could be misunderstood.
>
> > And more problematic. It would either make current programs
> > illegal (since objects of local classes may currently be
> > defined outside of objects of the outer class, so there's
> > no context which could be accessed), or make the rules more
> > confusing (if restricted to unnamed structs where the previous
> > problem doesn't arise, you get different special rules for
> > different unnamed types).
>
> The scope of a member function defined outside the class definition
> includes the class members, so by the same token, the scope of a
> member function of a local class includes the class and its context
> classes, when (as specified below) the class en embedded.
>
I didn't speak of the _class_ definition outside of the outer class,
but about *object* definitions of that class, f.ex.:
class A
{
public:
class B
{
...
};
...
};
A::B x; // Problem: No surrounding A object here.
> > > This can be implemented in a rigid way if we follow the present C++
> > > practice in situations of the kind. Namely, properties can
> > > be implemented through proxy objects,
> > >
> > > struct graphic {
> > > struct thickness_proxy {
> > > proxy(graphic& g_) : g(g_) {}
> > > void operator=(int t) { g.the_thickness=t; g.redisplay(); }
> > > // ...
> > > };
> > > thickness_proxy& thickness() { return *this; }
> > >
> > > private:
> > > int the_thickness;
> > > void redisplay();
> > > };
> >
> > This doesn't give the wanted syntax - additional () -, and in
> > addition can cause overhead (the reference in thickness_proxy).
> > It for sure _will_ get overhead, if you try to get the syntax
> > right by making the thickness_proxy a reference.
>
> I know. But it is still practice, AFAIK. Though full optimization
> of the above proxy is realistic, not all compilers will do that.
>
If the function is used as above, the compiler can do it.
But, as I said, the syntax is wrong. Doing it with correct
syntax, the compiler cannot optimize the reference away
(since it is a class member, and the compiler doesn't know
if the object might be instantiated from elsewhere, nor
if the reference will ever be initialized with another
object than the surrounding one.
> > > Generalizing this scheme, the basic transformation of a class A written
> > > with true local classes to present C++ is,
> > >
> > > * If a local class L (or it's local classes) does not access any
> > > members
> > > in A, no change is made.
> >
> > If this is a proposed change to the language, it doesn't work.
> > The compiler cannot decide if there's a member access, unless
> > it knows all the definitions of the member functions. But
> > those may be in a different translation unit - they may even
> > be in a dynamic library, and therefore this fact may change
> > without recompiling all code using this class.
> > The only way to make that difference at all is to tell the
> > compiler explicitly (f.ex. with "embedded").
>
> The using directive could also be useful here:
>
> struct graphic {
> struct local {
> using the_thickness;
> // ...
> };
> private:
> int the_thickness;
> }
>
> or simply `using class graphic'. OK, `embedded' may be better. But
> if we have several levels of classes, a using directive may be more
> expressive:
>
> struct A {
> struct B {
> struct C { using class A; }
> };
> };
>
Nut an embedded class relative to A doesn't make sense if the
directly enclosing class isn't embedded in A as well.
However, the using syntax has the advantage of reusing existing
identifiers. Probably it's hte better syntax for this reason.
OTOH, embedded makes more clear what really happens.
[...]
> > BTW, I like the "embedded class" idea because of the
> > wider generality (see the function example above).
>
> than properties?
Exactly.
> In which case I still agree. So, the question is
> whether embedded classes may contain local variables (and whether
> they are declared with a new keyword or with the `using' directive.)
I would say they may contain local variables, since those can
just be members of the surrounding class. In some way, the embedded
class could be seen as an encapsulation of a part of the surrounding
class, instead of being a full fledged class on it's own. It exists
just as part of it's outer class (or, more generally, of it's outer
context). This is why I would prohibit sizeof on it - the members
need not be a compact block in the surrounding context. Even
extending in derived classes is possible this way.
>
> > > `L(arglist) : init body'
> > >
> > > into
> > >
> > > `L(A& __c, arglist) : __context(__c), init body'
> >
> > Which cannot work if you are not in a member function of A.
> > Therefore you have to restrict usage of the local class to
> > inside A.
> >
> > >
> > > * Whenever a member x of A is accessed in L, without an object given
> > > (as in `a.x'), it is replaced by `__context.x'.
> > >
> > > * When an object of the class is constructed within the class A,
> > > the proper `*this' argument is known, and can be inserted into the
> > > argument list of the ctor. From outside A, we may say
> > >
> > > A a;
> > > a.C c(arglist);
> > >
> > > where `a.L l(arglist);' --> `L l(a, arglist)' iff `L' is a local
> > > class in the class of `a'.
> >
> > I don't see any usage of this.
>
> You mean why it is useful? Or what it is supposed to mean? The type
> of a local class is normally given as `A::L'. However, since
> construction from outside `A', requires an object `A a' of the class,
> we substituted `A::' with `a.'. Thus the construction looks like
> `a.L l' or `new a.L'.
>
> Interestingly `a.L' may be considered to be the true type of the local
> class. It could be compared to
>
> template<A& context> struct L { /* ... */ };
>
> where `L<a>' is a unique type for each `a'. Of course, this us useless
> in implementing properties, since `a' must have external linkage.
>
> So is it useful? At least there is no reason not to provide
> construction
IMHO there is - see below.
> of an embedded class from outside, since such a construction can already
> be done though a member function of the context class. A possible use:
>
> struct styles_and_preferences {
> class diaglog_box;
> class button;
> class whatever;
>
> void three_D_look(bool);
> void border_thickness(int);
> void look_like(int this_and, int that);
> private:
> // ...
> };
>
> class dialog_box {
> using class styles_and_preferences;
> // ...
> };
This is *not* an object independant of styles_and_preferences;
it's just a plain out-of-line definition of the class itself.
Indeed, you haven't declared any object of type dialog_box,
not even inside styles_and_preferences.
>
> Now, the context may be initialized and passed around the program.
> Assuming `styles_and_preferences s;', a new button is constructed
> as `s.button b;'. Notice that the using-declaration need not be
> given before the local classes are defined.
But as soon as you "rip" the object of the class out of the
surrounding object (so it's not embedded any more), you loose
the special optimisation - from then, it's just another normal
class containing a reference to the outer class. Moreover, if
you extend it to embedded classes to functions (which is the
main advantage over properties).
It seems we have quite different vies aboud embedded classes.
In my view, it's an encapsulation of a _part_ of something,
and therefore conceptionally very different from just an object
contining a reference to something, even if it can be
implemented as such (think of inheritance, which also is
implemented by containing, but is fundamentally different).
It doesn't make sense to define a part of something outside
this "something". You cannot get a hole without the matter
surrounding it.
>
> > IMHO, embedded classes should be considered part of the surrounding
> > class (if they are embedded in a class, not in a function). That is,
> > the *this pointer of the embedded class is just the *this pointer of
> > the surrounding class, and the virtual functions of the embedded
> > class are put into the vtbl of the surrounding class. This allows
> > overriding them on derived classes, even though there's an object
> > of the class declared - giving some sort of member variable
> > inheritance. To allow to add more members, one would not demand
> > that embedded classes are continuous (and therefore one should
> > disallow sizeof on embedded classes; sizeof wouldn't make sense
> > with function-embedded classes as well, since the local
> > variables of the function are not technically part of the class,
> > although they are logically).
> >
> > Indeed, I now think embedded classes are the best solution
> > for properties, as well as for passing the local context of
> > functions to other functions.
>
> From an implementation-and-optimization point of view, this solution
> is easier. However, the more general solution would be _conceptually_
> simpler, since the only new rules are that contextual variables may
> be accessed after a using directive, and that upon construction from
> outside the surrounding class a contextual object must be provided.
My solution IMHO has some conceptual advanntages:
- It's easily used on _any_ context (esp. in functions).
- It allows to replace data members of an embedded class with data
members of the according derinived embedded class in the derived
outer class (this is *not* possible with normal classes, because
they _must_ have a standalone representation.
- They denote the concept of encapsulation a part of a whole,
unlike normal classes, which are a whole per se. That is, objects
of an embedded class are really part of the surrounding class,
with all the advantages following from that.
- OTOH, they are an entity you can point to/reference to, and
therefore they allow other objects to get a "view" of the
surrounding context. Especially they can have base classes
with virtual functions, allowing them to provide access to
objects of the surrounding which are not really part of a class.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: Edward Diener <eddielee@abraxis.com>
Date: 1999/02/02 Raw View
Christopher Eltschka wrote:
> I think there are two different concepts here with the same name:
>
> A "closure" as in functional languages is in some way similar to
> local functions+pointers to them: You can access the surrounding
> context of the closure, without being really there. That is, you
> can in a way "reenter" the context through a call. (I hope my
> explanation is correct)
>
> The "BC++B closure" - from what you've written - seems to be more
> like a "pre-bound pointer to member" (please correct me, if I'm
> wrong).
> That is, you can define s.th. like
>
> typedef void (*closure fp)(int); // or whatever the syntax is
>
> class A
> {
> void foo(int);
> void bar(int);
> };
>
> class B
> {
> voif baz(int);
> };
>
> A a1, a2;
> B b;
>
> void foo()
> {
> fp fp1=closure(a1, A::foo); // point to foo of a1
> fp fp2=closure(a1, A::bar); // point to bar of a1
> fp fp3=closure(a2, A::foo); // point to foo of a2
> fp fp4=closure(b, B::baz); // point to baz of b
>
> fp1(5); // a1.foo(5)
> fp2(5); // a1.bar(5)
> fp3(5); // a2.foo(5)
> fp4(5); // b.baz(5)
>
> fp1=fp4;
> fp1(5); // b.baz(5)
> }
>
> If so, it would probably be a good idea to rename the
> "BC++B closure" to a different name for this discussion,
> so no confusion arises.
I agree but I was using the name "closure" because BCB actually extends C++ with a __closure
keyword. BTW your illustration, while correct in concept for BC++B closure, is even simpler in
BCB:
typedef void (*__closure fp)(int);
fp fp1 = a1.foo;
fp fp2 = a1.bar;
etc. etc.
and of course this can be done with any closure at runtime, and you can assign the closure to
0 or to any appropriate function. The power this entails in BCB is to be able to use BCB
closures as event handler pointers and to call event handlers within any class through them.
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: Petter Urkedal <petter@matfys.lth.se>
Date: 1999/02/03 Raw View
Christopher Eltschka wrote:
>
> IMHO if properties are implemented in C++, they should offer
> about the same flexibility as full classes; that is, operator
> overloading etc, just that it doesn't have it's own members,
> but only accesses the surrounding class (i.e. its *this is the
> surrounding class' *this, and they are considered part of
> the surrounding type). If the property wants to appear to
> have members, they must be implemented via properties themselves,
> too.
>
> A possible syntax:
>
> class Graphic
> {
[...]
> property thickness // allows much more operations
> {
> void operator=(int t) { the_thickness=t; redisplay(); }
> operator int() { return the_thickness; }
> void operator++() { ++the_thickness; redisplay(); }
> void operator--() { --the_thickness; redisplay(); }
> ...
> }
> ...
> private: // the properties access this data!
> Color the_color;
> thickness the_thickness;
> void redisplay();
> ...
> };
I agree that this is definitely more the C++ way of doing things.
But why not use the present syntax:
struct Graphic {
struct {
void operator=(int t) { the_thickness=t; redisplay(); }
operator int() ...
} thickness;
...
};
Properties seems like an ad-hoc solution to a very specific problem.
IMO, to allow local classes to access the members of its context class
is more useful (and more aesthetic).
This can be implemented in a rigid way if we follow the present C++
practice in situations of the kind. Namely, properties can
be implemented through proxy objects,
struct graphic {
struct thickness_proxy {
proxy(graphic& g_) : g(g_) {}
void operator=(int t) { g.the_thickness=t; g.redisplay(); }
// ...
};
thickness_proxy& thickness() { return *this; }
private:
int the_thickness;
void redisplay();
};
Generalizing this scheme, the basic transformation of a class A written
with true local classes to present C++ is,
* If a local class L (or it's local classes) does not access any
members
in A, no change is made.
* Otherwise, insert a declaration `A &__context;' into the private
data-
section of L and transform each constructor
`L(arglist) : init body'
into
`L(A& __c, arglist) : __context(__c), init body'
* Whenever a member x of A is accessed in L, without an object given
(as in `a.x'), it is replaced by `__context.x'.
* When an object of the class is constructed within the class A,
the proper `*this' argument is known, and can be inserted into the
argument list of the ctor. From outside A, we may say
A a;
a.C c(arglist);
where `a.L l(arglist);' --> `L l(a, arglist)' iff `L' is a local
class in the class of `a'.
With multiple levels of local classes, each class will simply contain
a reference to its context, and accessing a member `x' several
levels up will look like `__context.__context ... __context.x'.
Note that this scheme does not interfere with efficiency in cases where
local classes are not used. Neither does it interfere with the C++
memory model (or the lack of such), and yes, it does not prohibit
spooky invalid references, just like present C++.
-pu.
--
[- http://matfys.lth.se/~petter/ -]
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: Christopher Eltschka <celtschk@physik.tu-muenchen.de>
Date: 1999/02/03 Raw View
Petter Urkedal wrote:
>
> Christopher Eltschka wrote:
> >
> > IMHO if properties are implemented in C++, they should offer
> > about the same flexibility as full classes; that is, operator
> > overloading etc, just that it doesn't have it's own members,
> > but only accesses the surrounding class (i.e. its *this is the
> > surrounding class' *this, and they are considered part of
> > the surrounding type). If the property wants to appear to
> > have members, they must be implemented via properties themselves,
> > too.
> >
> > A possible syntax:
> >
> > class Graphic
> > {
> [...]
> > property thickness // allows much more operations
> > {
> > void operator=(int t) { the_thickness=t; redisplay(); }
> > operator int() { return the_thickness; }
> > void operator++() { ++the_thickness; redisplay(); }
> > void operator--() { --the_thickness; redisplay(); }
> > ...
> > }
> > ...
> > private: // the properties access this data!
> > Color the_color;
> > thickness the_thickness;
> > void redisplay();
> > ...
> > };
>
> I agree that this is definitely more the C++ way of doing things.
> But why not use the present syntax:
>
> struct Graphic {
> struct {
> void operator=(int t) { the_thickness=t; redisplay(); }
> operator int() ...
> } thickness;
> ...
> };
If I understand you correctly, you want anonymous structs/classes
to get access to the members of the surrounding class.
This could be a solution; however it would need a special rule
for such unnamed local classes. Especially, it could not be
extended to named local classes, since no one prohibits me
to write
struct Graphic
{
struct X { ... };
...
};
Graphic::X x;
We already have a special rule for unnamed unions, but
it's quite different. And a _different_ special rule
for unnamed structs would be confusing.
However, one could extend the scheme by making an "embedded"
specifier, which makes the class objects "embedded" in their
scope. Objects of embedded classes then could only be declared
in their own scope, but could access the members of the scope.
This scope would not have to be a class, but could also
be a function, solving another common problem:
struct X
{
virtual void operator()()=0;
}
void twice(X& x) { x(); x(); }
void foo()
{
int i=3;
embedded struct Incrementer:
public X
{
void operator()(int& a) { a*=i; ++i; } // embedded class can access
its scope
} inc;
twice(inc); // increments local i twice
}
>
> Properties seems like an ad-hoc solution to a very specific problem.
> IMO, to allow local classes to access the members of its context class
> is more useful (and more aesthetic).
And more problematic. It would either make current programs
illegal (since objects of local classes may currently be
defined outside of objects of the outer class, so there's
no context which could be accessed), or make the rules more
confusing (if restricted to unnamed structs where the previous
problem doesn't arise, you get different special rules for
different unnamed types).
>
> This can be implemented in a rigid way if we follow the present C++
> practice in situations of the kind. Namely, properties can
> be implemented through proxy objects,
>
> struct graphic {
> struct thickness_proxy {
> proxy(graphic& g_) : g(g_) {}
> void operator=(int t) { g.the_thickness=t; g.redisplay(); }
> // ...
> };
> thickness_proxy& thickness() { return *this; }
>
> private:
> int the_thickness;
> void redisplay();
> };
This doesn't give the wanted syntax - additional () -, and in
addition can cause overhead (the reference in thickness_proxy).
It for sure _will_ get overhead, if you try to get the syntax
right by making the thickness_proxy a reference.
>
> Generalizing this scheme, the basic transformation of a class A written
> with true local classes to present C++ is,
>
> * If a local class L (or it's local classes) does not access any
> members
> in A, no change is made.
If this is a proposed change to the language, it doesn't work.
The compiler cannot decide if there's a member access, unless
it knows all the definitions of the member functions. But
those may be in a different translation unit - they may even
be in a dynamic library, and therefore this fact may change
without recompiling all code using this class.
The only way to make that difference at all is to tell the
compiler explicitly (f.ex. with "embedded").
>
> * Otherwise, insert a declaration `A &__context;' into the private
> data-
> section of L and transform each constructor
Unnecessary overhead. Moreover, if you are willing to accept
this overhead (and some extra typing), you can implement
properties today, with normal classes.
The main point is to avoid the overhead (which cannot be done
in a conforming program, but is easy with the compiler's
knowledge - on many compilers it's even possible with a
non-standard use of offsetof on non-PODs from user code).
BTW, I like the "embedded class" idea because of the
wider generality (see the function example above).
>
> `L(arglist) : init body'
>
> into
>
> `L(A& __c, arglist) : __context(__c), init body'
Which cannot work if you are not in a member function of A.
Therefore you have to restrict usage of the local class to
inside A.
>
> * Whenever a member x of A is accessed in L, without an object given
> (as in `a.x'), it is replaced by `__context.x'.
>
> * When an object of the class is constructed within the class A,
> the proper `*this' argument is known, and can be inserted into the
> argument list of the ctor. From outside A, we may say
>
> A a;
> a.C c(arglist);
>
> where `a.L l(arglist);' --> `L l(a, arglist)' iff `L' is a local
> class in the class of `a'.
I don't see any usage of this.
>
> With multiple levels of local classes, each class will simply contain
> a reference to its context, and accessing a member `x' several
> levels up will look like `__context.__context ... __context.x'.
With the restriction of objects to the surrounding class, this
is not needed. This is the main reason of properties: Avoiding
this overhead. Otherwise, it would not be difficult
>
> Note that this scheme does not interfere with efficiency in cases where
> local classes are not used. Neither does it interfere with the C++
> memory model (or the lack of such), and yes, it does not prohibit
> spooky invalid references, just like present C++.
IMHO, embedded classes should be considered part of the surrounding
class (if they are embedded in a class, not in a function). That is,
the *this pointer of the embedded class is just the *this pointer of
the surrounding class, and the virtual functions of the embedded
class are put into the vtbl of the surrounding class. This allows
overriding them on derived classes, even though there's an object
of the class declared - giving some sort of member variable
inheritance. To allow to add more members, one would not demand
that embedded classes are continuous (and therefore one should
disallow sizeof on embedded classes; sizeof wouldn't make sense
with function-embedded classes as well, since the local
variables of the function are not technically part of the class,
although they are logically).
Indeed, I now think embedded classes are the best solution
for properties, as well as for passing the local context of
functions to other functions.
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: Bjarke Dahl Ebert <bde@cryptomathic.dk>
Date: 1999/01/27 Raw View
Chris Minnoy wrote:
> I totaly agree. For the moment I'm using this class as to enable properties
> in C++. However, it would be best not to use a class for representing a
> property. I prefer to have a built in keyword in the language itself,
> just like it's the case in Delphi (and probably C++Builder, but I'm not
> sure about that). Having a class act like a property introduces some
> overhead, which is ofcourse not what we want. This overhead come
> from the fact that the property-object has to keep track of the pointers
> to the get/set-functions and to the main object that owns the property.
> If 'property' whould be a compiler thing, the compiler could and would
> optimize it.
After all, it is all just a matter of syntax, i.e. writing obj.prop=a instead of
obj.setprop(a). Making a new keyword just to obtain what some might find a more
attractive syntax, is IMHO a bit overkill. The keyword also gives some extra
compiler complexity, and (cf. earlier in this thread) some semantics questions
can be raised.
For instance, your "getter" function in the property class template returns a
reference to const Type. So what if I do "Type &myRef = (obj.myprop);"? The
"obj.myprop" expression will call the getter function, which returns an *lvalue*
of type Type, so I can initialize my reference to it - so the code is
wellformed. Voila, I have a reference 'myRef' to an object that might or might
not alias the underlying object in the property. So, I can read from it whenever
I like, bypassing the getter function of the property.
I guess there could be other problems like this, so I vote for sticking to the
old-fashioned access member functions.
There's also a (small) problem with using a conversion function to read the
property: User conversions are not "transitive", so a property of type Type1
can't be used in a context where a type Type2 is expected (even if there's a
user conversion from Type1 to Type2), because already one conversion is needed
from "property<...>" to "const Type1&".
Regards,
Bjarke Dahl Ebert,
Systems Engineer, Cryptomathic A/S
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: Jean-Louis Leroy <jll@skynet.be>
Date: 1999/01/27 Raw View
In article <36A4007F.9916759C@abraxis.com>, Edward Diener wrote:
> I think closures are an idea that really should be considered for the next
> version of C++
I find closures *extremely* useful, they occupy top position in my wishlist for
an ideal language.
However, I don't see how you can provide a full-fledged implementation in C++
without contradicting some fundemental design choices of C++. This is because
closures can refer to variables pertaining to the scope in which the closure
was created, and thus make those variables outlive the scope that created them.
Consider this simple Perl example:
sub make_closure
{
my ($tag) = @_; # read argument list
return sub { print $tag, "\n" } # 1 - create it
}
my $c1 = make_closure('homer');
my $c2 = make_closure('marge');
&$c1(); # 2 - call it; prints 'homer'
&$c2(); # prints 'marge'
(1) creates a closure by means of the 'sub { ... }', aka the anonymous
subroutine composer. Note that the closure refers to the lexical variable $tag.
When (2) is reached, $tag is still around, and dinstinct from the $tag that was
used in the second call to make_closure().
Supporting closures in C++ would have important consequences WRT speed,
compatibility with other languages (specifically with C), compatibility with
existing language features and idioms (e.g. dtors executed in reverse ctor
order for auto variables)...
OTOH many usages of closures can be obtained via function objects, at the cost
of verbosity and some discomfort (client code needs to explicitly collaborate
in the management of the lifetime of the 'enclosed' states).
IOW, this proposal may well fall in the same category as, say, templatized
typedefs: 'it would be nice to have it, but you can get the same effect with
the existing language anyway, so it's not worth making C++ even more
complicated, remember the Vasa ;-), etc...'.
Jean-Louis Leroy
http://ourworld.compuserve.com/homepages/jl_leroy
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: Scott Johnson <sj_nospam@nospam.aracnet.com>
Date: 1999/01/28 Raw View
Jean-Louis Leroy wrote:
>
> In article <36A4007F.9916759C@abraxis.com>, Edward Diener wrote:
>
> > I think closures are an idea that really should be considered for the next
> > version of C++
>
> I find closures *extremely* useful, they occupy top position in my
> wishlist for an ideal language.
> However, I don't see how you can provide a full-fledged implementation > in C++ without contradicting some fundemental design choices of C++.
> This is because closures can refer to variables pertaining to the
> scope in which the closure was created, and thus make those variables > outlive the scope that created them.
Creating closures with data located on someone else's stack is evil,
obviously--that doesn't make them useful.
Likewise, this function is also evil, for the same reason:
char *evil_function (void){
char [100];
return char;
}
How does perl handle this? Does it make a copy of $tag within the
closure?
> Consider this simple Perl example:
>
> sub make_closure
> {
> my ($tag) = @_; # read argument list
> return sub { print $tag, "\n" } # 1 - create it
> }
>
> my $c1 = make_closure('homer');
> my $c2 = make_closure('marge');
>
> &$c1(); # 2 - call it; prints 'homer'
> &$c2(); # prints 'marge'
>
> (1) creates a closure by means of the 'sub { ... }', aka the anonymous
> subroutine composer. Note that the closure refers to the lexical
> variable $tag. When (2) is reached, $tag is still around, and > dinstinct from the $tag that was
> used in the second call to make_closure().
In C++:
// This class would be part of a standard library, for anyone to
// derive from
class void_closure {
public:
void_closure(void) {}
virtual void operator () (void) = 0;
virtual ~void_closure (void) = 0;
};
// Here is our closure making function
void_closure &make_closure (const char *str) throw (bad_alloc){
class my_closure : public void_closure {
public:
my_closure (const char *arg) {
str_ = new[strlen(str)+1] char();
strcpy (str_, arg);
}
void operator () (void) {cout << str_;}
~my_closure(void) {delete [] str_;}
private:
char *str_;
}
return *(new my_closure(str));
}
Of course, many OTHER differences between C++ and perl arise.
C++ does not have garbage collection, so any code that calls
make_closure has to eventually delete the returned object. Also,
C++ does not support Java-style inner classes, or the Java construct
of declaring a class in a new statement, so the closure has
to be given a name (though we could just as well call it class01,
as the name won't be visible outside the scope of my_closure). Also,
C++ is a strongly typed language, so we have to indicate what sort of
thing we are returning.
BUT--the above code (assuming it is correct, I probably made a boo-boo
there somewhere) does the same thing as your perl example.
> Supporting closures in C++ would have important consequences WRT
> speed, compatibility with other languages (specifically with C),
Howso? C will have to support them, too...the only thing that C knows
how to call are static (meaning not associated with an object instance,
and not "not in the global namespace") functions and function pointers.
It would be NICE if C had closures, though...
> compatibility with existing language features and idioms (e.g. dtors
> executed in reverse ctor order for auto variables)...
ANY time you point at something on a stack from something not on a
stack, you are asking for trouble. This is, in a large part, why
languages such as Java disallow automatic objects.
> OTOH many usages of closures can be obtained via function objects, at > the cost of verbosity and some discomfort (client code needs to
> explicitly collaborate in the management of the lifetime of the > 'enclosed' states).
That's a resource management issue, not a issue with closures per se.
Perl has garbage collection (of some sort). It deallocates resources
for you when you are done with them; whether they be closures or
strings or whatever.
C++ does not.
The two issues are largely orthogonal.
> IOW, this proposal may well fall in the same category as, say,
> templatized typedefs: 'it would be nice to have it, but you can get
> the same effect with the existing language anyway, so it's not worth
> making C++ even more complicated, remember the Vasa ;-), etc...'.
As I indicated earlier in the thread, I tend to prefer interface
objects to closures (or to functors). You can do a lot more with
them--at any rate, if you create an interface object with a single
method called "doit()", essetially you got yourself a closure. The
only difference is the syntax-- closure.doit() rather than closure().
Not a big difference.
Java gets along fine without closures (Microsoft-bastardized versions
of Java excluded, of course). Sun has a white paper up on their site
explaining why closures (or bound method references) weren't put into
Java--they were considered. (The paper was written in reaction to
MS's addition of closures to their flavor of Java...but it still is
a good read.)
Now if inner classes (or something similar) were added to C++...I'd
be happy. ;)
Scott
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: Jean-Louis Leroy <jll@skynet.be>
Date: 1999/01/28 Raw View
In article <36AFE7B2.2B90@nospam.aracnet.com>, Scott Johnson wrote:
Firstly, in order to make the discussion simpler, I suggest the following=
syntax for=20
closures:
lambda<return-type>(arg-list) block-statement
=20
is of type 'return_type (*)(arg-list)' (or perhaps a function object that=
has an operator=20
() with that signature and ret type); you call it like an ordinary functi=
on.
> Creating closures with data located on someone else's stack is evil,
> obviously--that doesn't make them useful.
It's evil in C++ because it leads to trouble. And the problem is not rela=
ted to closures=20
or 'pointing to someone else's stack', as your example below demonstrates.
This is a very useful behavior sometimes (in languages that properly supp=
ort closures=20
that is). For example, to implement generators (I've read this was the or=
iginal intent)=20
or callbacks:
void register_callback(void (*)());
class Foo
{
static void f();
void g();
void h()
{
register_callback(f); // Ok now
register_callback(g); // wrong - what will g's 'this' be??
register_callback(lambda<void>() { this->g(); } );
}
};
No need for bound pointers anymore! This technique is used extensively in=
Perl/Tk, where=20
you can attach callbacks to GUI objects. The callbacks are CODEREFs (Perl=
's pointer to=20
function). Most of the time you need to call some method on some object t=
hough. No=20
problem, use a closure et voila!
> Likewise, this function is also evil, for the same reason:
> =20
> char *evil_function (void){
> =20
> char [100];
> return char;
> }
I assume you mean:
...
char x[100];
return x;
> How does perl handle this? Does it make a copy of $tag within the
> closure?
$tag will be destroyed whenever it's not referenced anymore (barring the =
presence of=20
cycles, since Perl uses reference counting). Just like in Lisp, Smalltalk=
, Beta...
> In C++:
> [snipped code]
Sure, as I said, you can obtain most effects of closures in C++ at the co=
st of extra=20
work, typically greasing your hands in memory management <g>
> C++ does not support Java-style inner classes
What do you mean by inner class? C++ has nested classes, and, with some r=
estrictions,=20
local classes. Does Java have full-fledged inner classes =E0 la Beta? I.e=
., can the inner=20
class refer to the enclosing *object*'s state?
> > Supporting closures in C++ would have important consequences WRT=20
> > speed, compatibility with other languages (specifically with C),
> Howso?
=20
Remember, for me (and probably for most people who have significantly use=
d closures) it's=20
essential that closures can refer to whatever variable available at closu=
re creation time=20
- including lexicals. Otherwise 'closure' is but a misnomer.
Supporting closures in C++ would mean doing away with true stack-based ob=
jects, and=20
taking the Smalltalk approach where *everything* is a reference, includin=
g integers, and=20
has a lifetime independant of its lexical scope. IOW we would go from:
+---------+ +-------+
| Context |1<>-->| Value |
+---------+ +-------+
(the '<>' denotes aggregation)
to:
+---------+ +-------+
| Context |*--->*| Value |
+---------+ +-------+
>> OTOH many usages of closures can be obtained via function objects, at
>> the cost of verbosity and some discomfort (client code needs to=20
>> explicitly collaborate in the management of the lifetime of the
>> 'enclosed' states).
> That's a resource management issue, not a issue with closures per se.
Author: Edward Diener <eddielee@abraxis.com>
Date: 1999/01/28 Raw View
Jean-Louis Leroy wrote:
> In article <36A4007F.9916759C@abraxis.com>, Edward Diener wrote:
>
> > I think closures are an idea that really should be considered for the next
> > version of C++
>
> OTOH many usages of closures can be obtained via function objects, at the cost
> of verbosity and some discomfort (client code needs to explicitly collaborate
> in the management of the lifetime of the 'enclosed' states).
I do not see how function objects relate to closures as implemented by Inprise in
C++ Builder. A closure is just a special pointer to a function type that can be
made to point to any function of any object that matches the function type. Just
like any other pointer, if the value pointed to is out of scope, the pointer can
point to anything and cause problems. A closure is not a function or function
object in and of itself. Having to create function objects rather than normal class
functions to make some imitation of closures work under C++ certainly seems to me a
much bigger headache than simply supporting some notation for closure in a future
version of C++ . Sometimes a reason for adding a new feature is that while the
feature may be mimicked by a very complicated notation and implementation in the
current version of a language, there would be no necessity for this if the feature
were simply added as a usable concept. I find no reason why a closure, which mimics
the idea of a global function pointer but in respect to any function of any object
that matches the type, could not be added to the C++ language. I do not even care
if this new feature is called "closure" or any other thing as long as the idea of
the functionality that Inprise has implemented in their present form of closure
become part of C++ in the future. Probably anybody who has used C++ Builder is
struck by the elegance of the solution, using closures, which Inprise has developed
in this respect.
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: "Chris Minnoy" <cminnoy@starlab.net>
Date: 1999/01/29 Raw View
>After all, it is all just a matter of syntax, i.e. writing obj.prop=a
instead of
>obj.setprop(a). Making a new keyword just to obtain what some might find a
more
>attractive syntax, is IMHO a bit overkill. The keyword also gives some
extra
>compiler complexity, and (cf. earlier in this thread) some semantics
questions
>can be raised.
What compiler complexity? Properties? If that would be through, how could it be
that many other languages support it. No, it's as simple as it can be, and
there is no run-time overhead involved with it.
>For instance, your "getter" function in the property class template returns a
>reference to const Type. So what if I do "Type &myRef = (obj.myprop);"? The
>"obj.myprop" expression will call the getter function, which returns an *lvalue*
>of type Type, so I can initialize my reference to it - so the code is
>wellformed.
You mean illformed?
Maybe you didn't read my statement correct, I definitly said that there are
almost no
restricions on the return signature of the get/set functions. If you do not
want to pass
a reference, fine by me and the compiler. It is your responsibility.
The one thing that the compiler could request you to do is:
- to make get functions that have no parameter, and do return something,
whatever that may be
- to make set functions that do except one parameter, and can optionaly
return something, but are not oblight to.
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: Jean-Louis Leroy <jll@skynet.be>
Date: 1999/01/29 Raw View
In article <36AF97A5.DACCDEF@abraxis.com>, Edward Diener wrote:
> A closure is just a special pointer to a function type that can be
> made to point to any function of any object that matches the function type. Just
> like any other pointer, if the value pointed to is out of scope, the pointer can
> point to anything and cause problems.
Fair enough, after all C++ doesn't prohibit things like:
int& f() { int x = 666; return x; }
However, there seems to be two very different situations, depending on whether
the closure is allowed to escape from the function that created it.
1) If a function returns a closure (to be called at a later time), it's hard to see
any sensible use of the semantics you propose, except in degenerate cases (i.e. the
closure doesn't refer to local variables).
2) If OTOH a function creates a closure and just passes it down to another function
call, you're safe. However, compare these two snippets (I've omitted const, access
specifiers etc):
// sans closures, just C++
class Node { ... };
class Visitor
{
virtual void process(Node*) = 0;
};
class Graph
{
void traverse(Visitor& v)
{
for (Node* p = ...; ...; ...)
v.process(p);
}
};
class GraphPrinter : Graph::Visitor
{
GraphPrinter(ostream& os) : os(os) { }
virtual void process(Node* p) { ... }
ostream& os;
};
ostream& operator <<(ostream& os, Graph& g)
{
GraphPrinter v(os);
g.traverse(v);
return os;
}
// using a closure, 'improved' C++
class Graph
{
void traverse(void (*process)(Node*))
{
for (Node* p = ...; ...; ...)
process(p);
}
};
ostream& operator <<(ostream& os, Graph& g)
{
g.traverse( lambda<void>(Node* p) { ... } );
}
Both approaches are functionally equivalent. Does the 'closure' version look nicer?
Certainly. A *lot* nicer? I'm not that convinced.
IOW, closures of that sort are either very unsafe and barely useful; or provide
limited benefits.
If we're not willing to bite the bullet and implement true, safe closures, perhaps it
would be more productive to lift the restrictions on virtual functions in local
classes...
Hmm. The closure version *does* look nice though...
Certainly the committee received similar proposals. Perhaps a member of the committee
could jump in and share bits of history??
Jean-Louis Leroy
http://ourworld.compuserve.com/homepages/jl_leroy/
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: "Chris Minnoy" <cminnoy@starlab.net>
Date: 1999/01/29 Raw View
> Sometimes a reason for adding a new feature is that while the
>feature may be mimicked by a very complicated notation and implementation in the
>current version of a language, there would be no necessity for this if the feature
>were simply added as a usable concept. I find no reason why a closure, which mimics
>the idea of a global function pointer but in respect to any function of any object
>that matches the type, could not be added to the C++ language.
I agree. But you do not even have to stick to closures. Like you said, they can
point to any memberfunction of an object that matches a certain signature and
also remembers the object in respect.
There is however no reason why you couldn't expand this idea a little further.
Suppose you create a kind of pointer that unifies a closure and a normal
pointer. This way you could assign a free function or a memberfunction+obj
to it,
as long as they follow the signature. This would increase the expressive
power of C++ a great deal.
Chris
cminnoy@starlab.net
http://www.starlab.org
Be one with the (c++) force...
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: Francis Glassborow <francis@robinton.demon.co.uk>
Date: 1999/01/29 Raw View
In article <VA.000001db.002a8e4c@enterprise>, Jean-Louis Leroy
<jll@skynet.be> writes
>Fair enough, after all C++ doesn't prohibit things like:
>
> int& f() { int x = 666; return x; }
It rather depends on what you mean by prohibit. It does not require the
compiler to tell you that your code has the potential for reformatting
your hard-drive, nor is it prohibited from generating code that does
just that:)
Francis Glassborow Chair of Association of C & C++ Users
64 Southfield Rd
Oxford OX4 1PA +44(0)1865 246490
All opinions are mine and do not represent those of any organisation
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: Edward Diener <eddielee@abraxis.com>
Date: 1999/01/30 Raw View
Chris Minnoy wrote:
> > Sometimes a reason for adding a new feature is that while the
> >feature may be mimicked by a very complicated notation and implementation in the
> >current version of a language, there would be no necessity for this if the feature
> >were simply added as a usable concept. I find no reason why a closure, which mimics
> >the idea of a global function pointer but in respect to any function of any object
> >that matches the type, could not be added to the C++ language.
>
> I agree. But you do not even have to stick to closures. Like you said, they can
> point to any memberfunction of an object that matches a certain signature and
> also remembers the object in respect.
> There is however no reason why you couldn't expand this idea a little further.
> Suppose you create a kind of pointer that unifies a closure and a normal
> pointer. This way you could assign a free function or a memberfunction+obj
> to it,
> as long as they follow the signature. This would increase the expressive
> power of C++ a great deal.
I don't mind this idea either for maximum flexibility but I will settle for less, the
original idea of closures from C++ Builder and as I described. But you are right that
such a pointer should be doable also. I believe that in C++ Builder Inprise implements a
closure as an eight byte item of data, where the first 4 bytes contain the 'this'
pointer of the class and the last 4 bytes contain the actual class member function
pointer, whereas in your extended idea, setting the first 4 bytes to 0 while using the
last 4 bytes as a pointer to a global function, should do the trick in the case of a
closure that points to a global function. But these are implementation details and your
idea has great merit also.
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: Edward Diener <eddielee@abraxis.com>
Date: 1999/01/30 Raw View
Jean-Louis Leroy wrote:
> In article <36AF97A5.DACCDEF@abraxis.com>, Edward Diener wrote:
>
> > A closure is just a special pointer to a function type that can be
> > made to point to any function of any object that matches the function type. Just
> > like any other pointer, if the value pointed to is out of scope, the pointer can
> > point to anything and cause problems.
>
> However, there seems to be two very different situations, depending on whether
> the closure is allowed to escape from the function that created it.
>
> 1) If a function returns a closure (to be called at a later time), it's hard to see
> any sensible use of the semantics you propose, except in degenerate cases (i.e. the
> closure doesn't refer to local variables).
>
> 2) If OTOH a function creates a closure and just passes it down to another function
> call, you're safe. However, compare these two snippets (I've omitted const, access
> specifiers etc):
I am much more interested in closures as data members of classes, or even global data
members, than as variables being passed between functions. This is where closures would
show their power. As data members of classes, closures would act as event pointers that
could be hooked to an event handler in any other class. This is how they are used in C++
Builder. A class that can generate events defines a closure for each event, makes that
closure public and initialies it to 0. A member function of that closure type can be
specified in any other class to handle the event and all that it does is assign its
handler to the closure. Voila ! It handles the event from outside the class that
generates it. Actually in C++ Builder this is even easier since the closure is declared
as a __property and can be put in a __published section of the class so that it shows up
visually and an event handler in another class can be assigned to it visually ( which
generates internal code in the header and source file of the class of the event handler
). But this refinement to closures is neither here nor there since without C++ Builder's
__property and __published extensions, they are still insanely useful in the same way.
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: Christopher Eltschka <celtschk@physik.tu-muenchen.de>
Date: 1999/02/01 Raw View
Edward Diener wrote:
>
> Jean-Louis Leroy wrote:
>
> > In article <36AF97A5.DACCDEF@abraxis.com>, Edward Diener wrote:
> >
> > > A closure is just a special pointer to a function type that can be
> > > made to point to any function of any object that matches the function type. Just
> > > like any other pointer, if the value pointed to is out of scope, the pointer can
> > > point to anything and cause problems.
> >
> > However, there seems to be two very different situations, depending on whether
> > the closure is allowed to escape from the function that created it.
> >
> > 1) If a function returns a closure (to be called at a later time), it's hard to see
> > any sensible use of the semantics you propose, except in degenerate cases (i.e. the
> > closure doesn't refer to local variables).
> >
> > 2) If OTOH a function creates a closure and just passes it down to another function
> > call, you're safe. However, compare these two snippets (I've omitted const, access
> > specifiers etc):
>
> I am much more interested in closures as data members of classes, or even global data
> members, than as variables being passed between functions. This is where closures would
> show their power. As data members of classes, closures would act as event pointers that
> could be hooked to an event handler in any other class. This is how they are used in C++
> Builder. A class that can generate events defines a closure for each event, makes that
> closure public and initialies it to 0. A member function of that closure type can be
> specified in any other class to handle the event and all that it does is assign its
> handler to the closure. Voila ! It handles the event from outside the class that
[...]
I think there are two different concepts here with the same name:
A "closure" as in functional languages is in some way similar to
local functions+pointers to them: You can access the surrounding
context of the closure, without being really there. That is, you
can in a way "reenter" the context through a call. (I hope my
explanation is correct)
The "BC++B closure" - from what you've written - seems to be more
like a "pre-bound pointer to member" (please correct me, if I'm
wrong).
That is, you can define s.th. like
typedef void (*closure fp)(int); // or whatever the syntax is
class A
{
void foo(int);
void bar(int);
};
class B
{
voif baz(int);
};
A a1, a2;
B b;
void foo()
{
fp fp1=closure(a1, A::foo); // point to foo of a1
fp fp2=closure(a1, A::bar); // point to bar of a1
fp fp3=closure(a2, A::foo); // point to foo of a2
fp fp4=closure(b, B::baz); // point to baz of b
fp1(5); // a1.foo(5)
fp2(5); // a1.bar(5)
fp3(5); // a2.foo(5)
fp4(5); // b.baz(5)
fp1=fp4;
fp1(5); // b.baz(5)
}
If so, it would probably be a good idea to rename the
"BC++B closure" to a different name for this discussion,
so no confusion arises.
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: Christopher Eltschka <celtschk@physik.tu-muenchen.de>
Date: 1999/02/01 Raw View
Chris Minnoy wrote:
[...]
> Maybe you didn't read my statement correct, I definitly said that there are
> almost no
> restricions on the return signature of the get/set functions. If you do not
> want to pass
> a reference, fine by me and the compiler. It is your responsibility.
> The one thing that the compiler could request you to do is:
> - to make get functions that have no parameter, and do return something,
> whatever that may be
> - to make set functions that do except one parameter, and can optionaly
> return something, but are not oblight to.
IMHO if properties are implemented in C++, they should offer
about the same flexibility as full classes; that is, operator
overloading etc, just that it doesn't have it's own members,
but only accesses the surrounding class (i.e. its *this is the
surrounding class' *this, and they are considered part of
the surrounding type). If the property wants to appear to
have members, they must be implemented via properties themselves,
too.
A possible syntax:
class Graphic
{
public:
class Color { ... }; // normal class; represents color
property color
{
void operator=(Color c) { the_color=c; redisplay(); }
operator Color() { return the_color; }
property red // sub-property; readonly, calculated
{
operator int() { return the_color.extract_red(); }
}
property green
{
operator int() { return the_color.extract_green(); }
}
property blue
{
operator int() { return the_color.extract_blue(); }
}
}
property thickness // allows much more operations
{
void operator=(int t) { the_thickness=t; redisplay(); }
operator int() { return the_thickness; }
void operator++() { ++the_thickness; redisplay(); }
void operator--() { --the_thickness; redisplay(); }
...
}
...
private: // the properties access this data!
Color the_color;
thickness the_thickness;
void redisplay();
...
};
Example usage:
void foo(Graphic& g)
{
if (g.color == Graphic::Color("green"))
{
g.color = Color("yellow"); // automatically redisplays
}
if (g.color.red > 0)
{
++g.thickness; // automatically redisplays
// --g.color.red; error: no operator-- for this property
}
}
Now, there are of course some improvement possible, and some
thinks to think about (f.ex. the syntax to define property
functions out of line). Also there might be an improved syntax
possible. However, I think this is the minimum flexibility
which should be provided.
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: "Chris Minnoy" <cminnoy@starlab.net>
Date: 1999/01/26 Raw View
I totaly agree. For the moment I'm using this class as to enable properties
in C++. However, it would be best not to use a class for representing a
property. I prefer to have a built in keyword in the language itself,
just like it's the case in Delphi (and probably C++Builder, but I'm not
sure about that). Having a class act like a property introduces some
overhead, which is ofcourse not what we want. This overhead come
from the fact that the property-object has to keep track of the pointers
to the get/set-functions and to the main object that owns the property.
If 'property' whould be a compiler thing, the compiler could and would
optimize it.
This is the code I use:
template <class Class, class Type>
class property
{
typedef const Type& (Class::*TGetFunc)() const;
typedef const Type& (Class::*TSetFunc)( const Type& );
public:
property( const Class*, TGetFunc, TSetFunc );
const Type& operator=( const Type& );
operator const Type() const throw();
private:
const Class* object;
TGetFunc getfunc;
TSetFunc setfunc;
};
template <class Class, class Type> inline
property<Class,Type>::property( const Class* t, TGetFunc gf, TSetFunc sf )
: object( t ), getfunc( gf ), setfunc( sf )
{
}
template <class Class, class Type> inline
const Type& property<Class,Type>::operator=( const Type& r )
{
return( (object->*setfunc)( r ) );
}
template <class Class, class Type> inline
property<Class,Type>::operator const Type() const
{
return( (object->*getfunc)() );
}
You can use it like this:
class Test
{
int fValue;
const int& get() const { return fValue; }
const int& set( const int& value_i ) { return fValue = value_i; }
public:
Test() : fValue( 0 ), value( this, &Test::get, &Test::set ) {}
property<Test,int> value;
};
int main() { Test t; t.value = 5; std::cout << t.value; }
You can even make the get/set routines virtual, so you can define extra
functionality in derived classes, for example adding extra security checks.
This implies however that you may not forget to call the previous get/set
routine out of the baseclass, because that's the only one that can access
the
data.
If you make get/set virtual you can do another trick. See this:
class Derived : public Test
{
int fValue;
const int& get() const { return fValue; }
const int& set( const int& value_i ) { return fValue = value_i; }
};
Now all usage of the property will lead to a different datamember, which in
fact
doesn't have to be of the same type as the one defined in return and
parameter
list of get/set (however, you must avoid tempory objects).
I propose the following notation in the C++ language for properties:
property<getfunction,setfunction> name;
and for read-only properties:
property<getfunction> name;
A usage whould look like this:
class Test
{
int fValue;
const int get() const { return fValue; }
const int set( const int value_i ) { return fValue = value_i;}
public:
property<get,set> value;
};
Now you do not need to pass the this pointer, nor do you need to specify the
class
where the get/set function is part of.
I do not wanna imply any retrictions on the function defintions, in the
sence that
the return value do not have to be const, nor references or that a function
needs
to be virtual or not. Also specifying the get function as const should be
optional.
The only thing the compiler needs to do is convert a notation like:
Test t;
t.value = 5;
std::cout << t.value;
to
t.set( 5 );
std::cout << t.get();
and ignore the access-rights of the member functions.
Like Edward Diener already stated:
It should not be allowed to take an address of a property.
Chris Minnoy
cminnoy@starlab.net
http://www.starlab.org
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: Edward Diener <eddielee@abraxis.com>
Date: 1999/01/25 Raw View
Bjarke Dahl Ebert wrote:
> Chris Minnoy wrote:
>
> > Is there ever been a discussion about closures and properties,
> > like those found in CPPBuidler and Delphi???
> > Personly I think they are great, maybe we should think of
> > providing them also in the Standard.
>
> I think a problem with Borland's properties is the lack of accurate semantics
> specification (at least, I have never found it). Borland is just saying: when
> you read it, we use the 'get' member function, when you assign to it, we use the
> 'set' member function. So what do we mean by "read" and "write"??
You have made a really good point. Although I use C++ Builder I have never
investigated the possibility of doing anything other than either assigning a value
to a property ( setting a property ) or getting the value of a property.
> E.g., what effect does the "property" nature have on properties of classes with
> assignment operator, conversion functions; what if the class defines operator
> '+=', is that operator used on the object returned from the 'get' member
> function? Or is "a.prop1 += b.prop2;" implicitly converted to
> "a.setprop1(a.getprop1() + b.getprop2();"? How do properties relate to user
> defined conversion and overload resolution? And is it legal to take the address
> of a property? Or initialize a (parameter) reference to refer to it? (I think it
> should not be). Should we have a type "pointer to property" and "reference to
> property"? Is there such a type as "cv-qualified property"?
I don't believe that you can or should be allowed to create a pointer or reference
to a property or to take its address. If properties were to be part of the C++
standard I would suggest to keep it as simple as possible. A programmer should be
allowed to assign a value to a property via the "property = value" expression,
where, of course, "value" can be an arbitrarily complex expression that can be
converted to the property type. A programmer should be allowed to use the value of a
property in an arbitrarily complex expression. I would support the other assignment
operations, such as '+=' since they can easily be translated into your basic '='
assignment.
> Maybe it is possible to sort all this kind of questions out, but in my opinion
> Borland has not done a very good job on this.
Agreed, but then most BCB3 programmers will not be doing anything but the basics
with properties.
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: Bjarke Dahl Ebert <bde@cryptomathic.dk>
Date: 1999/01/24 Raw View
Chris Minnoy wrote:
> Is there ever been a discussion about closures and properties,
> like those found in CPPBuidler and Delphi???
> Personly I think they are great, maybe we should think of
> providing them also in the Standard.
I think a problem with Borland's properties is the lack of accurate semantics
specification (at least, I have never found it). Borland is just saying: when
you read it, we use the 'get' member function, when you assign to it, we use the
'set' member function. So what do we mean by "read" and "write"??
E.g., what effect does the "property" nature have on properties of classes with
assignment operator, conversion functions; what if the class defines operator
'+=', is that operator used on the object returned from the 'get' member
function? Or is "a.prop1 += b.prop2;" implicitly converted to
"a.setprop1(a.getprop1() + b.getprop2();"? How do properties relate to user
defined conversion and overload resolution? And is it legal to take the address
of a property? Or initialize a (parameter) reference to refer to it? (I think it
should not be). Should we have a type "pointer to property" and "reference to
property"? Is there such a type as "cv-qualified property"?
Maybe it is possible to sort all this kind of questions out, but in my opinion
Borland has not done a very good job on this.
Regards,
Bjarke Ebert
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: Edward Diener <eddielee@abraxis.com>
Date: 1999/01/20 Raw View
I think they are pretty good too. While properties can be done via get_ and
set_ methods, they do provide for great ease of use, with the compiler
generating the necessary get_ and set_ methods for the programmer.
I think closures are an idea that really should be considered for the next
version of C++ and I would be glad to work with anyone who would like to
present it to the C++ committee in 4 years time or so. While I have seen a
number of examples of implementing closures via templates, I feel that a
natural closure type, which is essentially a pointer to a member of any class
with a particular function signature, would be a great boon to C++. After all
we have the concept of a generic pointer to a global function with a
particular function signature, so why not allow a generic pointer to a class
member function along the same lines. It obviously makes event driven
programmer in C++, ala C++ Builder, stupendously easy to implement and would
make C++ more flexible in the bargain, without giving up any power.
Chris Minnoy wrote:
> Is there ever been a discussion about closures and properties,
> like those found in CPPBuidler and Delphi???
> Personly I think they are great, maybe we should think of
> providing them also in the Standard.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: Scott Johnson <sj_nospam@nospam.aracnet.com>
Date: 1999/01/18 Raw View
Barry Margolin wrote:
>
> In article <916413391.369525@saturn.riv.be>,
> Chris Minnoy <cminnoy@starlab.net> wrote:
> >Is there ever been a discussion about closures and properties,
> >like those found in CPPBuidler and Delphi???
>
> Yes, there has. A year or so ago. Check DejaNews.
CBuilder/Delphi style properties (which, near as I can tell,
are essentially public data members that you can attach semantics
to accesses of) can be handled well enough with getXXX() and setXXX()
methods. About the only advantage a "property" has over providing
accessor functions is the ability to quickly and easily
include it in arithmetic expressions--
foo.pressure += bar.pressure;
is a lot easier to write than
foo.setPressure (foo.getPressure() + bar.getPressure());
Of course, in a multithreaded environment, there may be synchronization
issues impossible to specify with the former syntax; with the latter
syntax, one can control explicitly the order and synchronziation of
access. Java has a robust component model using only accessor
(get and set) methods; no reason this can't apply to C++ as well.
As far as closures; howazabout the following? (This one doesn't
deal with exceptions, but it works nonetheless...):
template <typename return_type>
class closure_zero_args {
public:
virtual return_type &operator () (void) = 0;
};
template <typename return_type, typename arg_0_type>
class closure_one_Arg {
public:
virtual return_type &operator () (arg_o_type &) = 0;
};
And so on... (would variable template arguments be a Reasonable Thing
for the future; they would make closures a bit easier...C9X seems
to be adding varargs to macros....)
You simply derive from the appropriate base class, as follows:
class button_event; // event structure for a button action
class component; // Base class for our windowing system
class button_handler : public closure_one_arg <void, foo_event> {
// Still abstract; this class just de-templates the thing.
};
// adapters are convenient beasties in Java;
class button_adapter : public button_handler {
void operator () (button_event &) {} // do nothing
};
// For our example, we use a single-target (no multicast) model
class button : public component {
private:
button_handler *my_button_handler;
public:
void set_button_handler (button_handler *);
button_handler *get_button_handler (void);
void action (button_event &event)) {
if (my_button_handler)
(*my_button_handler)(event);
}
};
class my_window : public component {
public:
class my_button_handler : public button_handler {
private:
my_window &target_;
public:
my_button_handler (my_window &target) :
target_ (target) {}
void operator () (button_event &event) {
target_.handle_button_event(event);
}
};
void handle_button_event (button_event &event);
my_window (button &b) {
// do something
h = new my_button_handler(*this);
b.set_button_handler (h);
}
~my_window (void) {
b.set_button_handler (0);
delete h;
}
private:
my_button_handler *h;
};
I typed the above info from scratch, so don't flame me for any typos
or errors, please... :) But it shows how to create closures in C++.
Of course, more useful (IMHO) than closures are raw interface classes,
which is what Java uses. A single interface object can have multiple
methods, whereas a closure is really little more than a sophisticated
fuction pointer; that has only one method. These are doable in C++
as well.
The only real gripe, and it is minor, is that C++ doesn't support
inner classes (which provide a form of "inheritance by reference",
allowing an implementation of the interface to directly manipulate
a target's members.) The same functionality can be had in C++, but
with a bit more typing. (You have to explicitly make the "handler"
class a friend if you want it to manipulate private members; you have to
explicitly put a reference to the target object in the handler,
and you have to give it a name so it may have a constructor that does
this for you; and you have to explicitly CALL the correct constructor.)
Scott
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: AllanW@my-dejanews.com
Date: 1999/01/19 Raw View
In article <916413391.369525@saturn.riv.be>,
"Chris Minnoy" <cminnoy@starlab.net> wrote:
> Is there ever been a discussion about closures and properties,
> like those found in CPPBuidler and Delphi???
> Personly I think they are great, maybe we should think of
> providing them also in the Standard.
If you want C++ to adopt a feature already found in another language,
you would do well to explain what that feature is, and how and why it
is used. Suggestions of the "make-C++-more-like-Language-X" variety
always meet a lot of resistance, but when the suggestion isn't even
explained, it's very likely to be ignored by many people.
For all I know, closures are such a good idea that it's worth the
fight. But not from the explanation above.
----
AllanW@my-dejanews.com is a "Spam Magnet" -- never read.
Please reply in USENET only, sorry.
-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/ Search, Read, Discuss, or Start Your Own
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: "Chris Minnoy" <cminnoy@starlab.net>
Date: 1999/01/19 Raw View
I created some code that can handle closures very easily, without
writing to much huss and fuss.
Here you can see an example of how I use my code:
#include <windows.h>
#include <iostream>
#include <kt_functional.h>
using namespace Tiger;
//==========================================================================
====
//
// class TWindow
//
//==========================================================================
====
class TWindow
{
typedef TWindow Self;
private:
int a_value;
protected:
// by making these virtual you can add checks in derived classes
virtual const int& GetValue() const throw() { return a_value; }
virtual const int& SetValue( const int& i ) { return a_value = i; }
public:
struct TClickStruct
{
int x, y;
enum { LEFT, MIDDLE, RIGHT } button;
};
property<Self,int> Value;
binary_call<Self&,const TClickStruct&,const bool> OnClick;
TWindow() : Value( this, &Self::GetValue, &Self::SetValue ), OnClick() { }
void Click() // Just a test case
{
TClickStruct s = { 80, 100, TClickStruct::LEFT }; // just as an example
if ( OnClick.IsAssigned() )
OnClick( *this, s ); // the returnvalue could be used as check to see
// if the event has been handled well
}
};
//==========================================================================
====
//
// event handlers
//
//==========================================================================
====
const bool my_eventhandler( TWindow& w, const TWindow::TClickStruct& s )
{
// do something
std::cout << "event handler: function" << std::endl;
std::cout << s.x << ' ' << s.y << std::endl;
return( true );
}
class TMyClass
{
private:
int x;
int y;
public:
virtual const bool MyEventHandler(TWindow& w, const TWindow::TClickStruct&
s)
{
x = s.x;
y = s.y;
std::cout << "event handler: class" << std::endl;
std::cout << x << ' ' << y << std::endl;
return( true );
}
};
//==========================================================================
====
//
// WinMain
//
//==========================================================================
====
int PASCAL WinMain
( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int
nShowCmd )
{
TWindow w;
w.Value = 5;
std::cout << w.Value << std::endl;
w.Click(); // does nothing, no event handler assigned
w.OnClick = std::ptr_fun( my_eventhandler );
w.Click(); // works now with function
TMyClass t;
w.OnClick = closure( t, &TMyClass::MyEventHandler );
w.Click(); // works now with member function
return( 0 );
}
A binary_call is a class I made that can handle functions AND closures.
A closure is made by using the template function closure that accepts
a reference to an object and a member function.
I hope you can clearly see that this even small example is very powerful
AND adaptable. It's clear there has to be a functionality similar added
to the standard.
Sorry that I can not give you the code of my other classes yet, maybe in
the future I will give them free.
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: "Chris Minnoy" <cminnoy@starlab.net>
Date: 1999/01/17 Raw View
Is there ever been a discussion about closures and properties,
like those found in CPPBuidler and Delphi???
Personly I think they are great, maybe we should think of
providing them also in the Standard.
Chris Minnoy
cminnoy@starlab.net
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: Barry Margolin <barmar@bbnplanet.com>
Date: 1999/01/18 Raw View
In article <916413391.369525@saturn.riv.be>,
Chris Minnoy <cminnoy@starlab.net> wrote:
>Is there ever been a discussion about closures and properties,
>like those found in CPPBuidler and Delphi???
Yes, there has. A year or so ago. Check DejaNews.
--
Barry Margolin, barmar@bbnplanet.com
GTE Internetworking, Powered by BBN, Burlington, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.
Don't bother cc'ing followups to me.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]