Topic: Ideas for N1782
Author: gcc@integrable-solutions.net (Gabriel Dos Reis)
Date: Mon, 29 Aug 2005 15:57:50 GMT Raw View
"Daphne Pfister" <lanatha@gmail.com> writes:
| > > | concept Derived_distance_type<typename T> {
| > > | extern T v1; // 1
| > > | extern T v2;
| > > | using T::difference_type = decltype(v2-v1); // 2
| > > | };
|
| Douglas Gregor wrote:
| > Does Derived_distance_type<MyIter> work? Yes, it does, since, MyIter
| > has an operator-; it happens to return an int.
|
| First I want to note the above pseudo code that I wrote is not
| 'correct' according to n1782, at least as I understand it (hence the
| //2 footnotes). I took a few liberties.
I understood all of that -- and in fact, I implicitly took them for
suggestions of alternate syntax, as I was under the impression that you
wanted to discuss the "meat". That is very much appreciated, for it is not
an easy exercise -- the slope to unwarranted and unfounded
qualifications being (too) easy.
| Assuming that it was, this
| should fail to match since T has a difference_type and it's not a int.
| The same would be true of Default_distance_type, hence the
| static_assert Has_difference_type<MyIter>; would not be ill-formed and
| MyIter::difference_type should be a long. (How ever what if MyIter
| didn't not have difference_type defined?, I would assume/hope that the
| first match in a disjunction would be the one that took priority.)
That point is an engineering decision where there are more than one
sensible choice. The exact direction we'll take is still under active
discussion among the authors of the proposal :-)
| However, my reading of n1782 only seems to support the using T::..... =
| ... in static_asserts.
In fact, "defaults" in concept definitions are not outlawed. But,
again we wanted to attack the core issues that looked serious to us in
the main papers -- which were already too long for our taste.
Thanks for bringing it up.
|
| Trying here to write this 100% correctly based on my understanding of
| n1782. Hopefully someone more familiar with n1782 will correct any of
| my mistakes.
|
| First the explicit case should be straight forward as is.
|
| concept Explicit_difference_type<typename T> {
| typename T::difference_type;
| };
I would add
template<typename T>
static_assert !Explicit_difference_type<T>;
| Next we need a case that provides a difference type if there is a
| operator- and the Explicit_difference_type does not apply.
|
| concept Derived_difference_type_check<typename T>
| where !Explicit_difference_type<T> {
I would not have used the where-clause there.
| T v1;
| T v2;
| v2 - v1;
| };
[...]
| I'm finding all sorts of meta-concept play to be very interesting. It
| seems almost all the meta-template stuff can be done also with
| concepts.
It was a deliberate choice that major "template-based" techniques be
supported or improved by concepts :-)
---
[ 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: "Daphne Pfister" <lanatha@gmail.com>
Date: 21 Aug 2005 06:10:02 GMT Raw View
> > | concept Derived_distance_type<typename T> {
> > | extern T v1; // 1
> > | extern T v2;
> > | using T::difference_type = decltype(v2-v1); // 2
> > | };
Douglas Gregor wrote:
> Does Derived_distance_type<MyIter> work? Yes, it does, since, MyIter
> has an operator-; it happens to return an int.
First I want to note the above pseudo code that I wrote is not
'correct' according to n1782, at least as I understand it (hence the
//2 footnotes). I took a few liberties. Assuming that it was, this
should fail to match since T has a difference_type and it's not a int.
The same would be true of Default_distance_type, hence the
static_assert Has_difference_type<MyIter>; would not be ill-formed and
MyIter::difference_type should be a long. (How ever what if MyIter
didn't not have difference_type defined?, I would assume/hope that the
first match in a disjunction would be the one that took priority.)
However, my reading of n1782 only seems to support the using T::..... =
... in static_asserts.
Trying here to write this 100% correctly based on my understanding of
n1782. Hopefully someone more familiar with n1782 will correct any of
my mistakes.
First the explicit case should be straight forward as is.
concept Explicit_difference_type<typename T> {
typename T::difference_type;
};
Next we need a case that provides a difference type if there is a
operator- and the Explicit_difference_type does not apply.
concept Derived_difference_type_check<typename T>
where !Explicit_difference_type<T> {
T v1;
T v2;
v2 - v1;
};
concept Derived_difference_type {
typename T::difference_type;
};
static_assert template<Derived_difference_type_check T>
Derived_difference_type<T> {
using T::difference_type = decltype(v2-v1);
};
Now to support input/output iterators where difference_type really
doesn't make sense we would like to use ptrdiff_t.
concept Default_difference_type_check<typename T>
where !Explicit_difference_type<T>
&& !Derived_difference_type_check<T> {
};
// We could reuse Derived_difference_type here if we wish....
concept Default_difference_type {
typename T::difference_type;
};
static_assert template<Default_difference_type_check T>
Default_difference_type<T> {
using T::difference_type = ptrdiff_t;
};
But this makes one more assumption, that is the type provided by the
static asserts are not considered for Derived_difference_type_check or
Default_difference_type_check. Also it seems to me to be needless
verbose. Hopefully there is a better way to write this. If not, I
would like to see something like the following line allowed in a
concept (without requiring static_assert)
using T::name = type;
to do the following, if T already has a type "name", it should be the
same as "type" otherwise the concept doesn't match. If T does not
provide a type "name" it should become mapped to "type" just as if the
statement had occurred in static cast.
I'm finding all sorts of meta-concept play to be very interesting. It
seems almost all the meta-template stuff can be done also with
concepts.
concept unfactorable<int N, int D=N/2>
where D*int(N/D) != N
&& unfactorable<N,D-1> {
};
static_assert template<int N> unfactorable<N,1>;
concept prime<int N>
where N => 2
&& unfactorable<N> {
};
I need to reread n1758 and n1799 at some point. It's just at my first
reading n1782, it seemed closer a the syntax I was playing with.
---
[ 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: "Douglas Gregor" <doug.gregor@gmail.com>
Date: Mon, 22 Aug 2005 10:03:41 CST Raw View
Gabriel Dos Reis wrote:
> "Daphne Pfister" <lanatha@gmail.com> writes:
>
> | Gabriel Dos Reis wrote:
> | > [ I'm not going to debate whether "Same_type" should be built-in or
> | > not, I consider that to be details; so I would focuse on your
> | > suggestion of try/else ]
> | I only mentioned Same_type because I saw a potentially cleaner way to
> | write the non-built-in version.
>
> Thanks for clarifying. The reason why I consider it a "detail" is
> with respect to other items on the TODO list, like addressing
> implementation issues, having people agree on a common set of rules
> for overload resolution, clarifying the document and working on the
> specification etc. It was not meant to diminish your remark.
Whether Same_type is built-in or not *is* an implementation issue. For
a where-clause requirement like "Same_type<T, U>" to contribute to the
type-checking of the body of a function template, "Same_type" needs to
be built-in. We'll have a paper in the upcoming committee mailing
discussing some of the implementation challenges of concepts, and one
of the most challenging aspects is getting same-type constraints to
work. Clever definitions of the non-builtin Same_type concept only work
when we're checking "Same_type<T, U>" as a predicate (the "user side"
of concepts), not when we're type-checking the body of a template (the
"library author side" of concepts). The "library author side" is by far
the more complicated--and, arguably, more important--piece of the
puzzle, because that's what will making writing correct templates
easier and more enjoyable.
Doug
---
[ 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: "Douglas Gregor" <doug.gregor@gmail.com>
Date: Mon, 22 Aug 2005 10:03:01 CST Raw View
Daphne Pfister wrote:
> > > | concept Derived_distance_type<typename T> {
> > > | extern T v1; // 1
> > > | extern T v2;
> > > | using T::difference_type = decltype(v2-v1); // 2
> > > | };
>
> Douglas Gregor wrote:
> > Does Derived_distance_type<MyIter> work? Yes, it does, since, MyIter
> > has an operator-; it happens to return an int.
>
> First I want to note the above pseudo code that I wrote is not
> 'correct' according to n1782, at least as I understand it (hence the
> //2 footnotes). I took a few liberties.
Okay.
> Assuming that it was, this
> should fail to match since T has a difference_type and it's not a int.
Okay.
> | concept Default_distance_type<typename T> {
> | using T::difference_type = ptrdiff_t; // 2
> | };
> The same would be true of Default_distance_type,
. because T::difference_type is already "int", which is not the same
as ptrdiff_t. That might not actually be true, since ptrdiff_t is a
typedef of a signed integral type... which may, on some platforms, be
an int. So I think that static_assert Default_difference_type<MyIter>;
is well-formed on some platforms and ill-formed on other platforms,
depending on the definition of ptrdiff_t.
> hence the
> static_assert Has_difference_type<MyIter>; would not be ill-formed and
> MyIter::difference_type should be a long. (How ever what if MyIter
> didn't not have difference_type defined?, I would assume/hope that the
> first match in a disjunction would be the one that took priority.)
Right, so if MyIter::difference_type was not defined, it would get
"long" (from Derived_distance_type) and "ptrdiff_t" (from
Default_distance_type). I'm a little worried about the rule that the
first match is the one that takes priority, for two reasons. First, it
is an arbitrary rule (why should the first match be any better than the
others?). Second, the ordering may become a little unclear when there
are lots of disjunctions and conjunctions around, because--as Gaby
mentioned--the requirements need to be converted into disjunctive
normal form to be type-checked.
> However, my reading of n1782 only seems to support the using T::..... =
> ... in static_asserts.
I agree.
> Trying here to write this 100% correctly based on my understanding of
> n1782. Hopefully someone more familiar with n1782 will correct any of
> my mistakes.
[snip example]
You've managed to use concepts to develop a metaprogram :)
> But this makes one more assumption, that is the type provided by the
> static asserts are not considered for Derived_difference_type_check or
> Default_difference_type_check.
I consider that a reasonable assumption. I don't believe that
static_asserts are intended to change the underlying types (e.g., the
MyIter type) by introducing new member types.
> Also it seems to me to be needless verbose.
Agreed.
> Hopefully there is a better way to write this. If not, I
> would like to see something like the following line allowed in a
> concept (without requiring static_assert)
>
> using T::name = type;
>
> to do the following, if T already has a type "name", it should be the
> same as "type" otherwise the concept doesn't match. If T does not
> provide a type "name" it should become mapped to "type" just as if the
> statement had occurred in static cast.
N1758 has a feature very similar to this. In N1758, concepts may have
"associated types" that can have defaults. An associated type lives in
the concept itself, not in a type like "Iter" or "T". So, for instance,
HasDifferenceType may be written as:
template<typename Iter>
concept HasDifferenceType
{
typename difference_type = Iter::difference_type;
};
You can get to the difference_type of MyIter with
HasDifferenceType<MyIter>::difference_type. However, if
HasDifferenceType<Iter> is in scope, difference_type is available
without the extra qualification:
template<typename Iter> where { HasDifferenceType<Iter> }
difference_type distance(Iter first, Iter last);
N1758's equivalent to the static_assert, called a "model", may provide
a typedef for "difference_type" or, alternatively, the default of
"Iter::difference_type" will be used. For instance, this model defines
the difference_type to be int:
model HasDifferenceType<MyIter>
{
typedef int difference_type;
};
Alternatively, one could write a model that picks up "long" from
MyIter::difference_type by omitting the typedef:
model HasDifferenceType<MyIter> { };
Note that the defaults for associated types do not have the all of the
behavior you requested. Specifically, the default of
"Iter::difference_type" in the concept does not need to match the
typedef in the model. It acts like a default template argument or
default function argument in C++: if no argument is supplied, it will
be used; if an argument is supplied, the default is ignored. We chose
this behavior both for consistency with default template arguments and
because it fits best with the ways we've needed defaults in our uses of
concepts.
Doug
---
[ 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: gcc@integrable-solutions.net (Gabriel Dos Reis)
Date: Thu, 11 Aug 2005 05:26:02 GMT Raw View
[ I'm not going to debate whether "Same_type" should be built-in or
not, I consider that to be details; so I would focuse on your
suggestion of try/else ]
"Daphne Pfister" <lanatha@gmail.com> writes:
| I could see to approaches to N1782 for something similar using try {
| .. } else try { .... } else try { .... } where the concept would
| match if any of bodies matched, and allowing the using syntax outside
| of just static_assert. To simplified compiler support after any of else
| try (should this be "catch") bodies match, the original try must also
| match.
|
| concept Trivial_iterator<Arrow Iter>
| where Copyable <Iter> {
| try {
| typename Iter::value_type;
| } else try {
| extern Iter some_iter;
| using Iter::value_type =
| strip_const_and_reference<decltype(*some_iter)>::result_type;
| }
| try {
| typename Iter::difference_type;
| } else try {
| extern Iter some_iter;
| extern Iter some_iter2;
| using Iter::difference_type =
| strip_const_and_reference<decltype(some_iter2-some_iter)>::result_type;
| } else try {
| using Iter::difference_type = ptrdiff_t;
| }
| };
These alternatives look disjunctions to me. I'm wondering why the
use of the "||" operator would not handle it conveniently.
---
[ 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: "Daphne Pfister" <lanatha@gmail.com>
Date: 12 Aug 2005 14:50:05 GMT Raw View
Gabriel Dos Reis wrote:
> [ I'm not going to debate whether "Same_type" should be built-in or
> not, I consider that to be details; so I would focuse on your
> suggestion of try/else ]
I only mentioned Same_type because I saw a potentially cleaner way to
write the non-built-in version.
> "Daphne Pfister" <lanatha@gmail.com> writes:
>
> | I could see to approaches to N1782 for something similar using try {
> | .. } else try { .... } else try { .... } where the concept would
> | match if any of bodies matched, and allowing the using syntax outside
> | of just static_assert. To simplified compiler support after any of else
> | try (should this be "catch") bodies match, the original try must also
> | match.
> |
> These alternatives look disjunctions to me. I'm wondering why the
> use of the "||" operator would not handle it conveniently.
Your right. This would mean that Jeremy's concern (if I understand it
correctly), could be handled with N1782 as is. Though from my
understanding of N1782, using "||" would require the alternatives to
each be there own concept.
I guess this would look something like:
concept Explicit_difference_type<typename T> {
typename T::difference_type;
};
concept Derived_distance_type<typename T> {
extern T v1; // 1
extern T v2;
using T::difference_type = decltype(v2-v1); // 2
};
concept Default_distance_type<typename T> {
using T::difference_type = ptrdiff_t; // 2
};
concept Has_difference_type<typename T>
where Explicit_difference_type<T>
|| Derived_difference_type<T>
|| Default_difference_type<T> {
typename T::difference_type; // 3
};
1 - Syntax suggested to deal with 6.7 bullet 1
2 - This should actually use static_assert and typedef here according
to N1782. Allowing using directly inside concepts seems like a nice
shortcut, otherwise these two concepts would be.
concept Derived_distance_type<typename T> {
typename T::difference_type;
};
static_assert template<typename T> Derived_distance_type<T> {
extern T v1;
extern T v2;
using T::difference_type = decltype(v2-v1);
};
concept Default_distance_type<typename T> {
typename T::difference_type;
};
static_assert template<typename T> Default_distance_type<T> {
using T::difference_type = ptrdiff_t;
};
3 - Is this required? The general question is when concepts are listed
in a disjunction are they usable beyound just requiring the concepts to
match.
For example:
concept A<typename T> {
extern T const& value;
foo1(value);
foo2(value);
};
concept B<typename T> {
extern T const& value;
foo2(value);
foo3(value);
};
concept C<A && B T> {};
concept D<A || B T> {};
template <C T> void bar(T const& t)
{
foo1(t); // We should be able to call this
foo2(t); // and this
foo3(t); // and this
}
// But what about
template <D T> void bar(T const& t)
{
foo2(t); // Is this allowed?
foo1(t); // Surely this isn't
foo3(t); // Likewise this shouldn't be.
}
---
[ 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: gcc@integrable-solutions.net (Gabriel Dos Reis)
Date: Sat, 13 Aug 2005 23:19:22 GMT Raw View
"Daphne Pfister" <lanatha@gmail.com> writes:
| Gabriel Dos Reis wrote:
| > [ I'm not going to debate whether "Same_type" should be built-in or
| > not, I consider that to be details; so I would focuse on your
| > suggestion of try/else ]
| I only mentioned Same_type because I saw a potentially cleaner way to
| write the non-built-in version.
Thanks for clarifying. The reason why I consider it a "detail" is
with respect to other items on the TODO list, like addressing
implementation issues, having people agree on a common set of rules
for overload resolution, clarifying the document and working on the
specification etc. It was not meant to diminish your remark.
| > "Daphne Pfister" <lanatha@gmail.com> writes:
| >
| > | I could see to approaches to N1782 for something similar using try {
| > | .. } else try { .... } else try { .... } where the concept would
| > | match if any of bodies matched, and allowing the using syntax outside
| > | of just static_assert. To simplified compiler support after any of else
| > | try (should this be "catch") bodies match, the original try must also
| > | match.
| > |
|
| > These alternatives look disjunctions to me. I'm wondering why the
| > use of the "||" operator would not handle it conveniently.
| Your right. This would mean that Jeremy's concern (if I understand it
| correctly), could be handled with N1782 as is. Though from my
| understanding of N1782, using "||" would require the alternatives to
| each be there own concept.
|
| I guess this would look something like:
| concept Explicit_difference_type<typename T> {
| typename T::difference_type;
| };
|
| concept Derived_distance_type<typename T> {
| extern T v1; // 1
| extern T v2;
| using T::difference_type = decltype(v2-v1); // 2
| };
|
| concept Default_distance_type<typename T> {
| using T::difference_type = ptrdiff_t; // 2
| };
|
| concept Has_difference_type<typename T>
| where Explicit_difference_type<T>
| || Derived_difference_type<T>
| || Default_difference_type<T> {
| typename T::difference_type; // 3
| };
[...]
| 3 - Is this required? The general question is when concepts are listed
| in a disjunction are they usable beyound just requiring the concepts to
| match.
When a concept is in disjunctive (normal) form and used to type a
template definition, the semantics are that the template definition
shall conceptcheck with respect to type assumptions contained in each
sub-concept listed in the disjunction. At use site, exactly one of
them shall provide basis for bindings -- but at the definition site,
each of them shall lead to successful check.
| For example:
| concept A<typename T> {
| extern T const& value;
| foo1(value);
| foo2(value);
| };
|
| concept B<typename T> {
| extern T const& value;
| foo2(value);
| foo3(value);
| };
|
| concept C<A && B T> {};
|
| concept D<A || B T> {};
|
| template <C T> void bar(T const& t)
| {
| foo1(t); // We should be able to call this
| foo2(t); // and this
| foo3(t); // and this
| }
|
| // But what about
| template <D T> void bar(T const& t)
| {
| foo2(t); // Is this allowed?
Yes, because each of A and B makes the call concept-check.
| foo1(t); // Surely this isn't
| foo3(t); // Likewise this shouldn't be.
right on both counts.
---
[ 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: "Douglas Gregor" <doug.gregor@gmail.com>
Date: 18 Aug 2005 05:20:13 GMT Raw View
Gabriel Dos Reis wrote:
> | > "Daphne Pfister" <lanatha@gmail.com> writes:
> | 3 - Is this required? The general question is when concepts are listed
> | in a disjunction are they usable beyound just requiring the concepts to
> | match.
>
> When a concept is in disjunctive (normal) form and used to type a
> template definition, the semantics are that the template definition
> shall conceptcheck with respect to type assumptions contained in each
> sub-concept listed in the disjunction. At use site, exactly one of
> them shall provide basis for bindings -- but at the definition site,
> each of them shall lead to successful check.
Hmmm, these rules seem to have some unintended consequences. On the
definition side, it means that a single function template "foo" that
has a disjunctive requirement (A<T> | B<T> | C<T>) has to be
type-checked three times, separately. Moreover, the code generated may
be very different for each of the three versions, because names will
bind differently. In essence, "foo" is really three function templates
under the same name. Although this might be annoying to implement, I
believe it is possible, so let's talk about the use site a little more.
You mention that "exactly one of them shall provide [a] basis for
bindings". Does that mean that satisfying more than one of the
requirements in the disjunction creates a program that is ill-formed?
Let's take an example type "MyIter" and see how we can make it match
all three of the "distance type" concepts:
class MyIter
{
public:
typedef long difference_type;
};
int operator-(const MyIter&, const MyIter&);
> | I guess this would look something like:
> | concept Explicit_difference_type<typename T> {
> | typename T::difference_type;
> | };
Does Explicit_difference_type<MyIter> work? Yes, it does, since MyIter
has a nested difference_type (which happens to be long).
> | concept Derived_distance_type<typename T> {
> | extern T v1; // 1
> | extern T v2;
> | using T::difference_type = decltype(v2-v1); // 2
> | };
Does Derived_distance_type<MyIter> work? Yes, it does, since, MyIter
has an operator-; it happens to return an int.
> | concept Default_distance_type<typename T> {
> | using T::difference_type = ptrdiff_t; // 2
> | };
Does Default_distance_type<MyIter> work? I think it does, but I'm not
sure I understand the Texas proposal well enough. If it works, I expect
that "MyIter::difference_type" is defined as ptrdiff_t inside a
template where "Default_distance_type<MyIter>" is present, even though
it conflicts with the definition of MyIter::difference_type as "long".
> | concept Has_difference_type<typename T>
> | where Explicit_difference_type<T>
> | || Derived_difference_type<T>
> | || Default_difference_type<T> {
> | typename T::difference_type; // 3
> | };
Is the following ill-formed? If not, what is T::difference_type for
Has_difference_type<MyIter>?
static_assert Has_difference_type<MyIter>;
If it is ill-formed, we have a problem: the user dutifully placed a
"difference_type" in her type, but gets an error due to the
disjunction.
If it is well-formed, shall T::difference_type be "int", "long", or
"ptrdiff_t?"
Doug
---
[ 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: "Jeremy" <jeremy.siek@gmail.com>
Date: Tue, 26 Jul 2005 11:18:00 CST Raw View
Hi Daphne,
Daphne Pfister wrote:
> I had played with the idea of interfaces with a C++ front end a while,
> which was very similar to the concept design suggested in N1782. I did
> have a couple different ideas that I wanted to suggest.
>
> 6.7 Usage patterns, bullet 1.
>
> T x;
>
> Instead of using "T x;", to introduce a variable X of type T with out
> requiring initialization how about using "extern T x;" here.
>
> 7.4 The "same type problem" offers the following as a way to write
> same_type:
> template<class T, class U> concept Same_type {
> T t;
> U u;
> T* r1 = &u;
> U* r2 = &t;
> };
> This is another way to write same type:
> template <class T, class U> concept same_type;
> static_assert template <class T, class U> !same_type<T,U>;
> static_assert template <class T> same_type<T,T>;
Same-type constraints really need to be a built-in feature of the
language and cannot be encoded as a concept or a set of
static assertions. The reason for this is that there are two
parts to concept checking, but people tend to focus on the
first and forget about the second.
1) when a template is instantiated, check that the type requirements
are satisfied by the type arguments.
2) type check the template definition independently of any
instantiation, but instead with respect to its type requirements.
To illustrate how this relates to the same-type issue,
consider the std::includes function:
template<typename InputIter1, typename InputIter2>
bool includes(InputIter1 first1, InputIter1 last1,
InputIter2 first2, InputIter2 last2)
{
...
if (*first2 < *first1)
...
}
and the type requirements from
http://www.sgi.com/tech/stl/includes.html
* The value_type of InputIter1 and InputIter2 are required to be the
same type.
* The value_type is a model of LessThanComparable.
When the body of includes is type-checked, the compiler
must know that *first1 and *first2 have the same-type.
It is difficult for me to see how either the same-type concept from
n1782
or the static assertions you suggest would inform the
compiler that *first1 and *first2 have the same type.
> One of the reasons I thought the idea of concepts interfaces is
> interesting is because of code space optimizations that are possible.
> With the right compiler support function templates (and member
> functions of template classes) that use concepts can be generated as a
> single function that passes a hidden trait class(es) that explains how
> to map the features required to by the used concepts to it's arguments.
> This should be mostly a quality of implementation, and it would be nice
> if the design of concepts permitted this optimization even if it wasn't
> required.
This may or may not be similar to what we've been thinking for our
proposal n1758 or the design of G, the language of my Ph.D. thesis,
described
in
http://www.osl.iu.edu/publications/Year/2005.complete.php#siek05:_g_stl
and
http://www.osl.iu.edu/publications/Year/2005.complete.php#siek05:_fg_pldi
Could you perhaps write a little bit of C++ code so that I can get a
better
idea of what you're suggesting?
Cheers,
Jeremy
---
[ 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: "Daphne Pfister" <lanatha@gmail.com>
Date: Tue, 26 Jul 2005 14:49:05 CST Raw View
Jeremy wrote:
> template<typename InputIter1, typename InputIter2>
> bool includes(InputIter1 first1, InputIter1 last1,
> InputIter2 first2, InputIter2 last2)
> {
> ...
> if (*first2 < *first1)
> ...
>
> }
>
> and the type requirements from
>
> http://www.sgi.com/tech/stl/includes.html
>
> * The value_type of InputIter1 and InputIter2 are required to be the same type.
> * The value_type is a model of LessThanComparable.
I knew I forgot one of the differences I had played with.
template <Input_iterator InputIter1, Input_iterator InputIter2>
where Same_type<InputIter1::value_type,InputIter2::value_type>
&& Less_comparable<InputIter1::value_type,InputIter2::value_type>
bool includes(InputIter1 first1, InputIter1 last2,
InputIter2 first2, InputIter2 last2)
{
.
}
Yes, but then what if InputIter1 doesn't define value_type? while n1782
provides a solution that works for T*, it would be nice to have a
general solution.
The method I had looked at was something along the lines of (mixing my
syntax with n1782)
template <typename T>
interface base_iterator<T> : copy_constructable<T> ...
{
.
switch (enum value_type_source) {
case (value_type_internel) {
typename T::value_type;
}
case (value_type_derived) {
extern T some_iter;
using T::value_type =
strip_const_and_reference<decltype(*some_iter)>::result_type;
}
}
.
}
1 - With a little more expressiveness, you could use remove cv stuff
here.
In this model, the switch defined a enum for the interface, the
interface would match if any of the cast bodies matched, the value
interface_name::enum_name would be the case label of the first match in
case anyone needed that information. I think that is overkill now.
I could see to approaches to N1782 for something similar using try {
.. } else try { .... } else try { .... } where the concept would
match if any of bodies matched, and allowing the using syntax outside
of just static_assert. To simplified compiler support after any of else
try (should this be "catch") bodies match, the original try must also
match.
concept Trivial_iterator<Arrow Iter>
where Copyable <Iter> {
try {
typename Iter::value_type;
} else try {
extern Iter some_iter;
using Iter::value_type =
strip_const_and_reference<decltype(*some_iter)>::result_type;
}
try {
typename Iter::difference_type;
} else try {
extern Iter some_iter;
extern Iter some_iter2;
using Iter::difference_type =
strip_const_and_reference<decltype(some_iter2-some_iter)>::result_type;
} else try {
using Iter::difference_type = ptrdiff_t;
}
};
Jeremy wrote:
> Could you perhaps write a little bit of C++ code so that I can get a better
> idea of what you're suggesting?
Sure.
In file: some_concept.H
-------------------------
concept my_concept<typename T> { // 1
extern T const value;
value.something(); //2
};
concept my_concept2<typename T>{ // 3
extern T const value;
value.something_else();
};
In file: some_function.H
-------------------------
template <my_concept T>
void do_something_else(T const&) {};
template <my_concept2 T>
void do_something_else(T const&) {
T.something_else();
};
template <my_concept T>
void do_something(T const& arg) {
T.something();
do_something_else(T); // 4
} // 5
template <my_concept T>
void do_something_maybe(T const& arg) { }
In file: some_user.H
-------------------------
class foo1 {
..
void something() const;
..
}
class foo2 {
..
void something() const;
..
}
In file: some_user.C
-------------------------
#include "some_concept.H"
#include "some_function.H"
#include "some_user.H"
..
foo1 foo1_value;
foo2 foo2_value;
.
do_something_maybe(foo1_value); // 6
do_something_maybe(foo2_value); // 7
do_something(foo1_value); // 8
do_something(foo2_value); // 9
.
In this case I could see a smart compiler (this is something that would
take a lot of compiler work and probably not something for immediately
after concepts became available as part of the standard, my first
attempt at coding this generated too much compile time/memory overhead
to be practical) doing something like:
1: Create a concept record type my_concept
2: Add T const::* sometype() field to record type my_concept
3: Create a concept record type my_concept2 (it will get a T const::*
sometype_else()) field.
4: Add optional field isa my_concept2 to record type my_concept
5: Code for this function could be considered to be
void do_something_impl(T const& arg, my_concept_record const& arg_rec)
{
T.(arg_rec.something)();
if (arg_rec.my_concept2_record) {
do_something_else_impl2(arg,*arg_rec.my_concept2_record);
} else {
do_something_else_impl(arg,arg_rec);
}
}
6: Note that type foo1 needs to be considered for my_concept, test
concept and create concept_record. Because of 4 also consider
my_concept2 here, even though do_something_maybe itself does not need
that concept. Note foo1 is not a my_concept2.
7: Same as above, but this time foo2 is a my_concept2, so not that for
my_concept record also.
8: foo1 has already been proven to be a my_concept, call
do_something_impl(foo1_value,foo1_my_concept_record);
9: foo1 has already been proven to be a my_concept, call
do_something_impl(foo2_value,foo2_my_concept_record);
Notice that 8 and 9 share the same function code at this point. This is
the space optimization I was talking about. Also with a little more (as
in a lot) work this can be made to work across compilation units.
---
[ 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: "Daphne Pfister" <lanatha@gmail.com>
Date: Mon, 25 Jul 2005 16:51:07 CST Raw View
I had played with the idea of interfaces with a C++ front end a while,
which was very similar to the concept design suggested in N1782. I did
have a couple different ideas that I wanted to suggest.
6.7 Usage patterns, bullet 1.
T x;
Instead of using "T x;", to introduce a variable X of type T with out
requiring initialization how about using "extern T x;" here.
7.4 The "same type problem" offers the following as a way to write
same_type:
template<class T, class U> concept Same_type {
T t;
U u;
T* r1 = &u;
U* r2 = &t;
};
This is another way to write same type:
template <class T, class U> concept same_type;
static_assert template <class T, class U> !same_type<T,U>;
static_assert template <class T> same_type<T,T>;
One of the reasons I thought the idea of concepts interfaces is
interesting is because of code space optimizations that are possible.
With the right compiler support function templates (and member
functions of template classes) that use concepts can be generated as a
single function that passes a hidden trait class(es) that explains how
to map the features required to by the used concepts to it's arguments.
This should be mostly a quality of implementation, and it would be nice
if the design of concepts permitted this optimization even if it wasn't
required.
---
[ 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 ]