Topic: new proposal for Standards Committee of C++, keyword 'interface


Author: Christopher Eltschka <celtschk@web.de>
Date: Sat, 20 Apr 2002 10:09:46 GMT
Raw View
Nicola Musatti <objectway@divalsim.it> writes:

> Both your proposals appear to be related to Gabriel Dos Reis's proposal
> concerning signatures. You can read something about it here:
> http://www.codesourcery.com/publications_folder/generic-programming-in-cxx.pdf

Indeed, thanks for the pointer. Nice to see that someone else also not
only sees a need for such a feature, but even addresses my flexibility
concern by using specification by use syntax instead of specification
by exact function signature.

However, I consider my concepts proposal more general than Gabries Dos
Reis' signature proposal for the following reasons:

1. The signatures proposal only applies to single types, my proposal
   also allows to express relations between types.

   Assume you have the following concept:

   concept<class A, class B>
    AssignableTo(A const& a)
   {
     B b=a;
     b=a;
   }

   With this and the concepts InputIterator and OutputIterator
   you can write e.g. the std::copy template as

   template<class InIter, class OutIter:
            InputIterator<InIter>, OutputIterator<OutIter>,
            convertable<typename InIter::value_type,
                        typename OutIter::value_type> >
    void copy(InIter first, InIter last, OutIter dest);

   and then if you try to do

   std::vector<int> vi;
   std::vector<std::string> vs;

   // fill vi

   std::copy(vi.begin(), vi.end(), std::back_inserter(vs));

   the compiler can catch the violation (int is not assignable to
   std::string) right at the std::copy line. OTOH changing vs to
   std::vector<long> will make the compiler happy, since long
   and int conform to the concept convertable<long,int>.

   The signatures proposal doesn't support this; the only thing
   you could do is

   template<InputIterator InIter, OutputIterator OutIter>
    void copy(InIter first, InIter last, OutIter dest);

   I don't see a way to express the AssignableTo concept for the
   value_types in the signature proposal, nor do I see the possibility
   to easily add that feature.

2. While the text mentions the possibility of explicit conformance
   declaration, it seems to me that the actual signatures proposal
   doesn't support them. My concepts proposal supports both explicit
   and implicit conformance, and it's the concept author's choice
   which one should apply (the default is implicit; it a concept
   should be made explicit, you tell so by putting the keyword
   explicit on the definition). I consider the possibility of
   explicit concepts as important, since some concepts only differ in
   the semantics (which cannot be checked by the computer). For
   example, if you define the concepts Group and AbelianGroup, then
   you may be able to make Group implicit (if you f.ex. demand not
   only a multiplication, but also the members neutral() and
   inverse()), but you can for sure not make the concept AbelianGroup
   implicit, since the only difference to Group is the semantic
   constraint that a*b == b*a.

3. Concept refinement in the signatures proposal is done in a way
   resembling class inheritance. This includes restrictions which
   I carefully avoided in my proposal by using what I called
   constraints clause expansion followed by determining the most
   constrained version.

   One example where inheritance declarations don't work:

   Assume that you have a math library which gives you the concepts
   SemiGroup and Group. Now, for some reason you need the concept
   Monoid. A Monoid is a SemiGroup, and a Group is a Monoid.

   Now if using signatures, the library will look something like this:

   __signature__ SemiGroup { ... }
   __signature__ Group: SemiGroup { ... }

   Those definitions are of course immutable for you. Now how do you
   define the signature Monoid so that you can write

   template<SemiGroup T> void f(T) { ... }
   template<Monoid T> void f(T) { ... }
   template<Group T> void f(T) { ... }

   class Z { ... }; // is a Group, and of course a Monoid,
                    // since every Group is a Monoid
   Z z;

   f(z); // Should of course call Group, and not call Monoid

   Now, with the signature proposal, you couldn't tell the compiler
   that a Group is a Monoid, since you'd have to modify the Group
   definition to do so, and you can't. Of course you will tell the
   compiler that a Monoid is a SemiGroup (so that you can call
   functions for SemiGroups for Monoids). That is, the best you can do
   is writing:

   __signature__ Monoid: SemiGroup { ... }

   If I understand the signature proposal correctly, the compiler will
   see that Z conforms to all three signatures. It will also be able
   to sort out SemiGroup as base signature of both Group and
   Monoid. But it will not see any relation between Group and Monoid,
   so it will end up with an ambiguity between those two versions of
   f.

   If using my concept proposal, SemiGroup and Group will likely be
   implemented as follows:

   // define the two concepts
   concept<typename S> SemiGroup(...) { ... }
   concept<typename G> Group(...) { ... }

   // tell the compiler that a Group is a SemiGroup
   template<typename G: Group<G> > concept SemiGroup<G>; // (1)

   As you see, defining the concepts and defining the relation between
   the concepts are separate actions. This now allows us to seemlessly
   introduce out Monoid in between without changing the above source
   code:

   concept<typename M> Monoid(...)
   {
     concept SemiGroup<M>; // Check that it really is a SemiGroup
     ...                   // further checks
   }

   // Tell the compiler about the relations to Groups and Semigroups

   // A Monoid is a SemiGroup
   template<typename M: Monoid<M> > concept SemiGroup<M>; // (2)

   // A Group is a Monoid
   template<typename G: Group<G> > concept Monoid<G>; // (3)

   Now let us define out three constrained template functions
   using concept syntax:

   template<typename T: SemiGroup<T> > void f(T) { ... } // (a)
   template<typename T: Monoid<T> > void f(T) { ... }    // (b)
   template<typename T: Group<T> > void f(T) { ... }     // (c)

   Now taking the definition of z as above, we look at the resolution
   of f(z):

   First, the compiler finds in the usual way the three function
   templates above, and finds them all suitable (because we postulated
   that Z conforms to all three concepts). Therefore now constraint
   expansion is done on them:

   For SemiGroup, no constrained conformance template (CCT) exists,
   therefore it remains as is.

   For Monoid, there is exactly one matching CCT, namely (2).
   Therefore SemiGroup<M> is added to the list of concepts.

   For Group, all three CCTs match. Two of them (1 and 2) add
   SemiGroup (which of course is added only once), and one (3)
   adds Monoid. Now recursively expanding Monoid would only add
   SemiGroup, which is already there, and SemiGroup doesn't
   expand any further. That is, the expanded constraints clauses
   for the three functions are

   (a) SemiGroup<Z>
   (b) Monoid<Z>, SemiGroup<Z>
   (c) Group<Z>, Monoid<Z>, Semigroup<Z>

   (c) is unambiguously the most restrained function (where
   being more constrained is defined as having a strict superset
   of constraints), therefore (c), the Group version, is selected.

   Note that I effectively added a concept _in between_ two concepts
   of an existing hierarchy, where with inheritance, you only can add
   leaves.


There are also some interesting aspects mentioned in that paper which
don't seem to be included into the concrete signature proposal (and
which my concepts proposal doesn't catch, too). One of them is to
allow also negative constraints. Adding them syntactically would not
be hard with the concepts proposal, just write

template<typename T: !SomeConcept<T> >

Indeed it would even be possible to have a complete logic in the
constraints clause (the commas in my proposal are indeed already and
operators, since all constraints have to be met).

OTOH doing so would interfere with the constraints clause expansion;
I don't know if it could be made playing together.


Another quite interesting aspect is the idea of telling the compiler
the semantic constraints of a concept, so it can use them in
optimization. I think this is a quite useful idea; adding it to my
concepts proposal should IMHO not be harder (but also not be easier)
than adding it to the signature proposal.


After all, I'm glad that the whole topic isn't as dead as it seemed
last year. So chances are well that something like this will appear in
some future version of C++, and chances are well that it's at least as
flexible as Gabriel Dos Reis' version (although I'd like it even more
flexible, as you undoubtedly can see from my posting).

---
[ 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://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: Gabriel Dos Reis <dosreis@cmla.ens-cachan.fr>
Date: Sun, 21 Apr 2002 19:22:08 GMT
Raw View
Christopher Eltschka <celtschk@web.de> writes:

| Nicola Musatti <objectway@divalsim.it> writes:
|
| > Both your proposals appear to be related to Gabriel Dos Reis's proposal
| > concerning signatures. You can read something about it here:
| > http://www.codesourcery.com/publications_folder/generic-programming-in-cxx.pdf
|
| Indeed, thanks for the pointer. Nice to see that someone else also not
| only sees a need for such a feature, but even addresses my flexibility
| concern by using specification by use syntax instead of specification
| by exact function signature.

Right. But there are arguments on both sides as I explained at the
ACCU conference.

| However, I consider my concepts proposal more general than Gabries Dos
| Reis' signature proposal for the following reasons:

I think judging a proposal solely on the ground of a set of slides --
which by definition don't include the spoken part, e.g. the most
important part -- is far too rude :-)

| 1. The signatures proposal only applies to single types, my proposal
|    also allows to express relations between types.

I do consider parameterized signatures, but for the moment I can't
find a way of making them very  general, flexible and still effective
in constraining template-arguments.

[...]

|    the compiler can catch the violation (int is not assignable to
|    std::string) right at the std::copy line.

Exactly, that is one of the objectives of my proposal.

| OTOH changing vs to
|    std::vector<long> will make the compiler happy, since long
|    and int conform to the concept convertable<long,int>.
|
|    The signatures proposal doesn't support this; the only thing
|    you could do is

Please, remember that the reference given above is about a set of
slides for a talk, and there are many things that are not explicitly
written down whereas they are mentioned by voice.  That talk was mainly
introductive to the key ideas of the signatures.  It was simpler to
introduce the key concepts on non-parameterized signatures :-)

|    template<InputIterator InIter, OutputIterator OutIter>
|     void copy(InIter first, InIter last, OutIter dest);
|
|    I don't see a way to express the AssignableTo concept for the
|    value_types in the signature proposal, nor do I see the possibility
|    to easily add that feature.

That is not easily expressible in *non*-parameterized signatures.  But
once one has parameterized signatures, one gains another level of
expressive power.

| 2. While the text mentions the possibility of explicit conformance
|    declaration, it seems to me that the actual signatures proposal
|    doesn't support them.

That is why slides (without the speaker) are not good in conveying
ideas -- they are too sketchy.  Actually, at the ACCU conference, I
did insist on the advantages and disavantges of both forms of
conformance.  Structural conformance makes it possible to "retro-type"
templates.  Because of obvious time constraints, I choosed to expose
the concepts of "structural conformance".  But I clearly stated that
I *don't think* that an eventual final extension should include just one
form of conformance check; I do think a mixin would be much more
useful.  During the discussion that followed the talk, there were
numerous interesting ideas of having both forms of conformance coexist
-- someone related the topic to the issue of having ways for
typedef defining new type by allowing

   typedef DefaultConstructible int my_int;

with my_int now bound to "named conformance check".  There were
equally other interesting suggestions.

[...]

| 3. Concept refinement in the signatures proposal is done in a way
|    resembling class inheritance. This includes restrictions which
|    I carefully avoided in my proposal by using what I called
|    constraints clause expansion followed by determining the most
|    constrained version.

Although I reused inheritance syntax, concept refinement doesn't work
the way class inheritance does.  In particular, given structural
conformance, it is possible for a signature D to refine a signature C
without being explicitly defined by the inheritance syntax.  That
syntax just acts like copy-n-paste as far as structural conformance is
concerned.

|    One example where inheritance declarations don't work:
|
|    Assume that you have a math library which gives you the concepts
|    SemiGroup and Group. Now, for some reason you need the concept
|    Monoid. A Monoid is a SemiGroup, and a Group is a Monoid.
|
|    Now if using signatures, the library will look something like this:
|
|    __signature__ SemiGroup { ... }
|    __signature__ Group: SemiGroup { ... }
|
|    Those definitions are of course immutable for you. Now how do you
|    define the signature Monoid so that you can write
|
|    template<SemiGroup T> void f(T) { ... }
|    template<Monoid T> void f(T) { ... }
|    template<Group T> void f(T) { ... }
|
|    class Z { ... }; // is a Group, and of course a Monoid,
|                     // since every Group is a Monoid
|    Z z;
|
|    f(z); // Should of course call Group, and not call Monoid
|
|    Now, with the signature proposal, you couldn't tell the compiler
|    that a Group is a Monoid, since you'd have to modify the Group
|    definition to do so, and you can't. Of course you will tell the
|    compiler that a Monoid is a SemiGroup (so that you can call
|    functions for SemiGroups for Monoids). That is, the best you can do
|    is writing:
|
|    __signature__ Monoid: SemiGroup { ... }

Unless I misunderstood your example, I think the issue is slightly
different.  When considering the partial ordering, the compiler will
use the signatures of each template parameter in order to partially
order the elements in the overloads set.  In this case, it will see
by structural conformance that a Group is also a monoid.  Therefore
you don't have to explicitly tell it about that fact.
I agree that you have a point when using only named conformance.

|    If I understand the signature proposal correctly, the compiler will
|    see that Z conforms to all three signatures.

Right.

|    It will also be able
|    to sort out SemiGroup as base signature of both Group and
|    Monoid.

Exact.

|    But it will not see any relation between Group and Monoid,

No. It will see, using structural conformance, that Group refines
Monoid.  That is why I think structural conformance can be useful.
Named conformance doesn't cut it.  Signature inheritance doesn't imply
named conformance.

[...]

|    Note that I effectively added a concept _in between_ two concepts
|    of an existing hierarchy, where with inheritance, you only can add
|    leaves.

I think the same is possible with parameterized signatures.

| There are also some interesting aspects mentioned in that paper which

Well, that is not yet a paper; it is just a set of slides :-)  I'm
writing a paper that would expand on the details (not mentioned in the
slides).

| don't seem to be included into the concrete signature proposal (and
| which my concepts proposal doesn't catch, too). One of them is to
| allow also negative constraints. Adding them syntactically would not
| be hard with the concepts proposal, just write
|
| template<typename T: !SomeConcept<T> >

That is interesting -- I had (unofficially) used the same syntax to
denote negative constraints.  There are some projects to extend C++
syntax to allow postfix notation, that may then become "natural".

[...]

| OTOH doing so would interfere with the constraints clause expansion;
| I don't know if it could be made playing together.

I'll have a closer look at your proposal to see how one we can
have the best of both worlds :-)

| Another quite interesting aspect is the idea of telling the compiler
| the semantic constraints of a concept, so it can use them in
| optimization. I think this is a quite useful idea; adding it to my
| concepts proposal should IMHO not be harder (but also not be easier)
| than adding it to the signature proposal.

I can't tell off hand which is easier or harder -- since I haven't
completed the implementation (most of the crux are in the
implementation, at least if one doesn't require major technology
infrastruture)  and haven't studied your proposal in detail.

| After all, I'm glad that the whole topic isn't as dead as it seemed
| last year. So chances are well that something like this will appear in
| some future version of C++, and chances are well that it's at least as
| flexible as Gabriel Dos Reis' version (although I'd like it even more
| flexible, as you undoubtedly can see from my posting).

I appreciated your comments and examples.  Once finished I could send
you a copy of my article for comments if you wanted.

--
Gabriel Dos Reis, dosreis@cmla.ens-cachan.fr

---
[ 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://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: Christopher Eltschka <celtschk@web.de>
Date: Mon, 22 Apr 2002 18:37:57 GMT
Raw View
Gabriel Dos Reis <dosreis@cmla.ens-cachan.fr> writes:

> Christopher Eltschka <celtschk@web.de> writes:
>
> | Nicola Musatti <objectway@divalsim.it> writes:
> |
> | > Both your proposals appear to be related to Gabriel Dos Reis's proposal
> | > concerning signatures. You can read something about it here:
> | > http://www.codesourcery.com/publications_folder/generic-programming-in-cxx.pdf
> |
> | Indeed, thanks for the pointer. Nice to see that someone else also not
> | only sees a need for such a feature, but even addresses my flexibility
> | concern by using specification by use syntax instead of specification
> | by exact function signature.
>
> Right. But there are arguments on both sides as I explained at the
> ACCU conference.

Actually explicit signatures have one very big advantage: They allow
easy relative ordering of concepts/signatures. Just look if the set of
function signatures of the first is a subset of the set of function
signatures of the second (and the same of course for member types
etc.). In a language where the same expression cannot mean so many
different things, probably the explicit function signatures would be
the method of choice.

>
> | However, I consider my concepts proposal more general than Gabries Dos
> | Reis' signature proposal for the following reasons:
>
> I think judging a proposal solely on the ground of a set of slides --
> which by definition don't include the spoken part, e.g. the most
> important part -- is far too rude :-)

Sorry, I didn't mean to be rude.

And by the way, I didn't say "My proposal is more general", but "I
consider my proposal more general". Now considerations may certainly
be wrong.

>
> | 1. The signatures proposal only applies to single types, my proposal
> |    also allows to express relations between types.
>
> I do consider parameterized signatures, but for the moment I can't
> find a way of making them very  general, flexible and still effective
> in constraining template-arguments.

Ok, that should add some flexibility. I must admit I didn't think of
that possibility when writing my answer.

One thing I dislike about it (but that may in practice nly a minor
point) is that this still favours one type in such relations. Concepts
like "is-convertable-to" are not really a property of one type, but a
relation between types. You can satisfy it by adding a conversion
constructor to the destination type, or by adding a conversion
function to the source type. See below for an example.

>
> [...]
>
> |    the compiler can catch the violation (int is not assignable to
> |    std::string) right at the std::copy line.
>
> Exactly, that is one of the objectives of my proposal.
>
> | OTOH changing vs to
> |    std::vector<long> will make the compiler happy, since long
> |    and int conform to the concept convertable<long,int>.
> |
> |    The signatures proposal doesn't support this; the only thing
> |    you could do is
>
> Please, remember that the reference given above is about a set of
> slides for a talk, and there are many things that are not explicitly
> written down whereas they are mentioned by voice.  That talk was mainly
> introductive to the key ideas of the signatures.  It was simpler to
> introduce the key concepts on non-parameterized signatures :-)

Given the fact that with your proposal, you have to give one (and
exactly one) signature for each type (counting typename as general
signature, as indicated in your slides), I think parametrized
signatures would force you to define something like

  OutputIteratorWithValueAssignableFrom<type>

and then write std::copy as

  template<InputIterator InIter,
           OutputIteratorWithValueAssignableFrom<InIter::value_type> OutIter>
   void std::copy(InIter first, InIter last, OutOter dest);

right? This is one point why I prefer to somewhat decouple concepts
from the types themselves.

I admit it's at a first view natural to see concepts as a "typing of
types", and as <3AD1F7F8.19F8DD3A@dollywood.itp.tuwien.ac.at> - from
the previous thread which led me to formulating the concepts proposal
- proves, indeed at one step I took the same view. To cite myself from
the posting mentioned above:

| As I wrote, concepts, as I see them, are basically "Meta-Types":
| Types for types, instead of types for variables. However, since
| concepts don't define the structure of the type, they are more
| flexible; especially thay can be tagged on afterwards.
| The function style definition makes sure that you don't loose
| the advantages of genericity.

Indeed, at that time I even had used the replace-typename-syntax
instead of the explicit-list syntax; quoting from the same posting
again:

| template<ForwardIterator Iterator> void advance(...);
| template<BidirectionalIterator Iterator> void advance(...);
| template<RandomAccessIterator Iterator> void advance(...);

>
> |    template<InputIterator InIter, OutputIterator OutIter>
> |     void copy(InIter first, InIter last, OutIter dest);
> |
> |    I don't see a way to express the AssignableTo concept for the
> |    value_types in the signature proposal, nor do I see the possibility
> |    to easily add that feature.
>
> That is not easily expressible in *non*-parameterized signatures.  But
> once one has parameterized signatures, one gains another level of
> expressive power.

Agreed. As I already said, the idea of parametrized signatures didn't
occur on me. However, the above example doesn't occur to me to be to
be *easy*. The OutputIteratorWithValueAssignableFrom<type> to me looks
more like a workaround.

But it may of course be that I just missed some easy way to express
this constraint.

>
> | 2. While the text mentions the possibility of explicit conformance
> |    declaration, it seems to me that the actual signatures proposal
> |    doesn't support them.
>
> That is why slides (without the speaker) are not good in conveying
> ideas -- they are too sketchy.

Well, I did only have the slides. Now of course I got the speaker as
well ;-)

>  Actually, at the ACCU conference, I
> did insist on the advantages and disavantges of both forms of
> conformance.  Structural conformance makes it possible to "retro-type"
> templates.  Because of obvious time constraints, I choosed to expose
> the concepts of "structural conformance".  But I clearly stated that
> I *don't think* that an eventual final extension should include just one
> form of conformance check; I do think a mixin would be much more
> useful.  During the discussion that followed the talk, there were
> numerous interesting ideas of having both forms of conformance coexist
> -- someone related the topic to the issue of having ways for
> typedef defining new type by allowing
>
>    typedef DefaultConstructible int my_int;
>
> with my_int now bound to "named conformance check".  There were
> equally other interesting suggestions.

That's interesting.

It also gives an interesting question to my proposal:

If I have a typedef like

  typedef foo bar;

and then declare bar to conform to an explicit concept baz, like

  concept<bar> baz;

then should foo conform to baz?

I tend to say yes, because after all the concept is intended to say
something about the semantics of the type; OTOH making it apply only
to bar may give some extra flexibility (and as you certainly found out
by now, I really like flexibility ;-)). Something to think about.
Thanks!

>
> [...]
>
> | 3. Concept refinement in the signatures proposal is done in a way
> |    resembling class inheritance. This includes restrictions which
> |    I carefully avoided in my proposal by using what I called
> |    constraints clause expansion followed by determining the most
> |    constrained version.
>
> Although I reused inheritance syntax, concept refinement doesn't work
> the way class inheritance does.  In particular, given structural
> conformance, it is possible for a signature D to refine a signature C
> without being explicitly defined by the inheritance syntax.  That
> syntax just acts like copy-n-paste as far as structural conformance is
> concerned.

But then I do see a problem (namely the problem because of which I
added the whole constraints clause expansion stuff in my proposal):
Since conformance is done based on an usage-example, the compiler
would have to test if being able to compile one set of code implies
being able to compile another set of code. I don't think this is
possible in general.

Indeed, I expect even my constraints clause expansion stuff will only
work if making some restrictions to the considered constrained
conformance templates (one possible restriction would be to strict
subsets of the current partially-expanded constraint clause, although
I hope it can be made to work with more general constraints).

But maybe I missed something important about your proposal which makes
it possible for the compiler to prove such relationships. In that
case, your proposal would have an advantage over mine in that respect.

>
> |    One example where inheritance declarations don't work:

[...]

> |    __signature__ Monoid: SemiGroup { ... }
>
> Unless I misunderstood your example, I think the issue is slightly
> different.  When considering the partial ordering, the compiler will
> use the signatures of each template parameter in order to partially
> order the elements in the overloads set.  In this case, it will see
> by structural conformance that a Group is also a monoid.  Therefore
> you don't have to explicitly tell it about that fact.

Ok, seems I indeed missed something here... or did you?
See below.

> I agree that you have a point when using only named conformance.

I think using named conformance just for Monoid would be enough.

>
> |    If I understand the signature proposal correctly, the compiler will
> |    see that Z conforms to all three signatures.
>
> Right.
>
> |    It will also be able
> |    to sort out SemiGroup as base signature of both Group and
> |    Monoid.
>
> Exact.
>
> |    But it will not see any relation between Group and Monoid,
>
> No. It will see, using structural conformance, that Group refines
> Monoid.  That is why I think structural conformance can be useful.
> Named conformance doesn't cut it.  Signature inheritance doesn't imply
> named conformance.

Well and here's the point I miss: How can this work in general?

Easy example:

Signature A demands for types T conforming to it that you have an
implicit conversion from Foo to T and can assign T objects including
rvalues to other T objects.

Signature B demands for Types T conforming to it that you can assign
objects of type Foo to T objects.

Now having implicit conversion Foo->T and assignment (rvalue T)->T
implies being able to assign objects of type Foo to type T, therefore
types conforming to A always conform to B, and partial ordering should
prefer A versions over B versions.

But this raises two questions:

a) How far do we want the compiler to go in this analysis?
   Note that A and B may come from different sources and may not be
   mutable by us, and yet we might want to make use of this relation
   between both concepts (say we can use a more efficient algorithm
   if we can make copies of T objects).

b) (A point that admittedly occured to me only while writing this;
   when writing my concepts proposal I had only the above problem (a)
   in mind)
   How far do we want the user to go in this analysis?
   There may be non-obvious implications found by the compiler and
   causing the compiler to chose another function than that expected
   by the user.

Making the relations explicit, while certainly removing flexibility,
allows both the compiler and the user to do less analysis.

But maybe the ideal solution would be a combination of both: Implicit
relative ordering for simple cases (the exact same demands of one
signature/concept occur in the same formulation in the other
signature/concept), and explicit relative ordering for the rest (those
where you need logic instead of just comparing lists to see
conformance, and those defined to need explicit conformance
statements).

[...]

>
> | don't seem to be included into the concrete signature proposal (and
> | which my concepts proposal doesn't catch, too). One of them is to
> | allow also negative constraints. Adding them syntactically would not
> | be hard with the concepts proposal, just write
> |
> | template<typename T: !SomeConcept<T> >
>
> That is interesting -- I had (unofficially) used the same syntax to
> denote negative constraints.  There are some projects to extend C++
> syntax to allow postfix notation, that may then become "natural".

Well, some syntaxes (is there a plural of syntax?) seem just to be
obvious. Maybe it's because of the math notation of sets, like
M = {n \in N: n^2 = k^2 + l^2 for some k,l \in N}

>
> [...]
>
> | OTOH doing so would interfere with the constraints clause expansion;
> | I don't know if it could be made playing together.
>
> I'll have a closer look at your proposal to see how one we can
> have the best of both worlds :-)

That would be great. Please tell me about all the hidden flaws in it!
:-)

>
> | Another quite interesting aspect is the idea of telling the compiler
> | the semantic constraints of a concept, so it can use them in
> | optimization. I think this is a quite useful idea; adding it to my
> | concepts proposal should IMHO not be harder (but also not be easier)
> | than adding it to the signature proposal.
>
> I can't tell off hand which is easier or harder -- since I haven't
> completed the implementation (most of the crux are in the
> implementation, at least if one doesn't require major technology
> infrastruture)  and haven't studied your proposal in detail.

The semantic information is orthogonal to the checked constraints,
therefore there should IMHO be no difference in that part. After
finding out which concepts/signatures apply (which of course is
different in both proposals), the compiler may apply the corresponding
optimizations (which have to be explicitly provided). But maybe I
missed something.

Do you have some concrete ideas for specifying such semantic rules?

Basically I think there are two types of rules: The first type is sort
of a replacement rule, saying one expression, statement or series of
statements can be replaced by another without changing the semantics
of the program (equivalences are a special case of this, where such
replacement rules exist in both directions), and the second type is a
condition which is true after a certain expression, statement or
series of statements. But maybe this one can be considered to be a
special case of the first, where the corresponding expression may be
replaced by the literal true.

One thing which would be worth to be able to specify is that for
iterators, expressions of the form i++ where the value isn't used may
be replaced by ++i. Now it's clear that you have to mention i++ and
++i, but how to tell the compiler the condition that the value is
never used? Maybe one could specify that (void)i++ may be converted to
(void)++i?

Another thing which might sometimes be useful to specify is that
construction and destruction have no side effect, i.e. creating and
then immediatly destroying the object without doing something in
between may be optimized away. This one might be expressed by allowing
{T foo;} to be optimized to {}. However, does this also cover things
like T(); (i.e. creating but not using a temporary) and delete new T?

>
> | After all, I'm glad that the whole topic isn't as dead as it seemed
> | last year. So chances are well that something like this will appear in
> | some future version of C++, and chances are well that it's at least as
> | flexible as Gabriel Dos Reis' version (although I'd like it even more
> | flexible, as you undoubtedly can see from my posting).
>
> I appreciated your comments and examples.  Once finished I could send
> you a copy of my article for comments if you wanted.

That would be great, thanks.

---
[ 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://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: jcp77@mixmail.com (Juan Carlos Pizarro)
Date: Tue, 16 Apr 2002 18:08:58 GMT
Raw View
jcp77@mixmail.com (Juan Carlos Pizarro) wrote in message news:<b9d88bdd.0204100733.50052780@posting.google.com>...
> This is my new proposal for Standards Committee of C++:
> "adding a new keyword 'interface' for the templates"
> (advantage: checking at compilation-time instead execution-time,
> non-virtual methods, efficient, checkeable, ...)

More examples of templates from the most EASY until the most HARD:
----------------------------------------------------------------------------------------------

*** Declarations  of classes and interfaces ***:
---------------------------------------------------------------
1. template <class X> class Y { ... } ;   (equiv. to "template <class
X> Y { ... };")
2. template <interface X> class Y { ... }; /* class Y that uses the
interface X of the template */
3. interface X { ... }; /* declaring the interface X */
4. template <interface W> interface X { ... }; /* declaring the
interface X that uses the interface W of the template */
5. interface X : public interface Y, interface Z { ... }; /* declaring
the interface X hereding from interfaces */
6. class X : public Y, interface Z { ... }; /* class X hereding from
class Y and interface Z to implement the methods */
7. template <interface X X1,interface X X2> class Y { ... }; /* class
Y that uses X1 and X2 of the same interface X of the template */
8. template <interface X, interface Y<X>> class Z : public interface T
{ ... }; /* class Z implementing T but using the interfaces X and
Y(his parameter is X) */
9. template <interface X<int> X1, interface X<float> X2> class Y { ...
}; /* same interface but with distinct parameters */
10. template <interface X<int,int>> interface Y : public interface
Z<float> { ... }; /* hereding from interface Z with parameter float
... */

***Uses of classes and interfaces***: (beginning "I_" are interfaces)
---------------------------------------------------------------------------------------------
1. template <interface I_X> I_X& Y::fun(I_X &x);
2. template <interface I_X<int> I_X1, interface I_X<float> I_X2> void
Z::met(I_X1 &a, I_X2 &b);
3. template <interface I_X, interface I_W<I_X>> I_X& I_Y::proc(I_W
&y,I_X &x);
4. X<int,Y<float>> x;
--------------------------------------------------------------------------------------------------------------------------------
sincerely yours,  Juan Carlos Pizarro Mendez   jcp77@mixmail.com
(note: if the modification is very complicated then don't implement
all, it depends of the time to develop.)

---
[ 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://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: Nicola Musatti <objectway@divalsim.it>
Date: Tue, 16 Apr 2002 18:09:05 GMT
Raw View
Both your proposals appear to be related to Gabriel Dos Reis's proposal
concerning signatures. You can read something about it here:
http://www.codesourcery.com/publications_folder/generic-programming-in-cxx.pdf

Cheers,
Nicola Musatti

---
[ 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://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: Christopher Eltschka <celtschk@web.de>
Date: Sat, 13 Apr 2002 15:22:43 GMT
Raw View
jcp77@mixmail.com (Juan Carlos Pizarro) writes:

> This is my new proposal for Standards Committee of C++:
> "adding a new keyword 'interface' for the templates"
> (advantage: checking at compilation-time instead execution-time,
> non-virtual methods, efficient, checkeable, ...)
> (English isn't my native language (i'm spanish, and i'm sorry of the
> my language's errors))
>
> You can understand viewing my examples (remember, only are examples to
> understand):
>

[... example snipped ...]

>
> --------------------------------------------------------------------------------------------------------
>
>
> Possible solution: add the new keyword 'interface' to the future C++:
>
> /* precondition: be Hashable interface itself , i no need to comment
> this. */
> template <interface Hashable> ADT_Set {
>   private:
>    Hashable *array;
>   public:
>    void contains(Hashable &item) {
>       int hc = item.hashCode();
>       return (array[hc] == item);
>    }
> };

I guess the code above should have followed the definition of Hashable
below?

>
> interface Hashable {
>  public:
>   inline int hashCode();
>   inline bool operator== (Hashable &item);
> };

This shows one weakness of your solution:

The original template would work equally well if operator== were
declared as free function instead as member (and in addition, it would
have worked even with a const-correct operator== <eg>). So if I
defined

class MyHashable
{
  friend operator==(MyHashable const& other);
public:
  int hashCode() const;
  ...
};

then I could have used this class with your original template, but not
with your constrained template.

About one year ago I suggested a more general solution which would
have addressed that problem (see my posting "Feature Request:
Concepts", MsgId <3B094D4D.2BF19F97@dollywood.itp.tuwien.ac.at>).
Unfortunately it didn't generate much interest (there was not a single
follow-up, not even a negative one), which is one reason why I didn't
continue working on that idea soon after.

The idea was to do the same in the language as the standard does in
the library specification: Define the concepts involved by example
code that should compile (checking its semantics is of course not
possible with your code).

Your Hashable interface could be written as concept as follows:

concept<class T> Hashable(T& t1, T& t2)
{
  int i = t1.hash_code();
  bool b = (t1 == t2);
}

Although I'd prefer a const-correct version (which your CowHashable
wouldn't conform to, since it's not const-correct):

concept<class T> Hashable(T const& t1, T const& t2)
{
  int i = t1.hash_code();
  bool b = (t1 == t2);
}

The ADT_Set would then be written as

template<class T: Hashable> class ADT_SET
{
  ...
}

>
> class CowHashable : public Hashable {
>  private:
>   int id;
>  public:
>   inline int hashCode() { return id; }
>   inline bool operator== (Hashable &item) { return (id == item.id); }
> };

And this shows another disadvantage of your approach: You have to
explicitly declare that your class follows this interface, and you
have to do _at it's definition_. This way, if you already have a class
following your interface, you cannot use it as is, but you have to
change it (that is, change it's *definition*) in order to use it.

In this way, AFAICS the only advantage for the template over ordinary
inheritance that remains is avoiding the virtual function call
overhead. But all the flexibility which IMHO makes templates such a
great tool is gone.

My concepts proposal also addressed this point by

 - allowing to define concepts which apply automatically if its
   conditions are met

 - and for defining concepts which don't apply automatically (for
   certain concepts this would just not work well; to take a math
   example: members of a group and members of a commutative group both
   have the same interface [a multiplication; maybe some function to
   get the neutral element], but they are definitively not the same
   concept), allowing to declare that a given class conforms to a
   given concept *without* touching the original class definition (so
   if you happen to have an existing class that conforms to your
   concept - maybe even you defined that concept according to that
   class' interface -, you can then simply declare that class to
   conform to your interface without touching that class).

Making a concept explicit (not automatically applying) is done by
simply adding the keyword explicit to it's definition (like

explicit concept<class T> Hashable(...) { ... }

and declaring a class to conform to a concept is dome by simply writing

concept Hashable<CowHashable>;

[...]

---
[ 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://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: jcp77@mixmail.com (Juan Carlos Pizarro)
Date: Wed, 10 Apr 2002 16:39:17 GMT
Raw View
This is my new proposal for Standards Committee of C++:
"adding a new keyword 'interface' for the templates"
(advantage: checking at compilation-time instead execution-time,
non-virtual methods, efficient, checkeable, ...)
(English isn't my native language (i'm spanish, and i'm sorry of the
my language's errors))

You can understand viewing my examples (remember, only are examples to
understand):

/* It forces me to comment ... */
/* preconditions: these methods should to exist at the class type
Hashable
      - int hashCode();
      - bool operator== (Hashable &) */
template <class Hashable> ADT_Set {
  private:
   Hashable *array;
  public:
   void contains(Hashable &item) {
      int hc = item.hashCode();
      return (array[hc] == item);
   }
};

class CowHashable {
 private:
  int id;
 public:
  inline int hashCode() { return id; }
  inline bool operator== (Hashable &item) { return (id == item.id); }
};

ADT_Set<CowHashable> cows;

/* the big problem: so, i can't to localize the causes of errors at
compile-time
    when i write bad the textcode, and the C++ compiler hasn't the
capacity
   to detect well the warnings or errors. */

--------------------------------------------------------------------------------------------------------


Possible solution: add the new keyword 'interface' to the future C++:

/* precondition: be Hashable interface itself , i no need to comment
this. */
template <interface Hashable> ADT_Set {
  private:
   Hashable *array;
  public:
   void contains(Hashable &item) {
      int hc = item.hashCode();
      return (array[hc] == item);
   }
};

interface Hashable {
 public:
  inline int hashCode();
  inline bool operator== (Hashable &item);
};

class CowHashable : public Hashable {
 private:
  int id;
 public:
  inline int hashCode() { return id; }
  inline bool operator== (Hashable &item) { return (id == item.id); }
};

ADT_Set<CowHashable> cows;

/* advantage: check very well at compilation-time, and better than
    Abstract Class that uses virtual methods. */

---------------------------------------------------------------------------------------------
news measurements of the keyword 'interface':
 (more parameters: Left, Right, ...)
------------------------------------------------------------------------

template <interface Hashable Left,interface Hashable Right>
ADT_together { ... };
interface Hashable { ... };
class CowHashable : public interface Hashable { ... }
class BullHashable : public interface Hashable { ... }
ADT_together<CowHashable,BullHashable> pairs;

------------------------------------------------------------------------

sincerely yours,  Juan Carlos Pizarro Mendez jcp77@mixmail.com

---
[ 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://www.jamesd.demon.co.uk/csc/faq.html                       ]