Topic: Constrained members


Author: SG <s.gesemann@gmail.com>
Date: Fri, 16 Jan 2009 15:45:21 CST
Raw View
On 15 Jan., 23:49, wasti.r...@gmx.net wrote:
> On Jan 14, 5:45 pm, SG <s.gesem...@gmail.com> wrote:
> > On 13 Jan., 05:16, wasti.r...@gmx.net wrote:
>
> > > concept Component<typename C>
> > > {
> > >  typename C::some_type;
> > >  void C::some_function();
> > > }
>
> > As far as I can tell the declaration for an associated type is ill-
> > formed.
>
> It's not an associated type. It's a requirement that a member type
> exists. It turns out there is actually no way of formulating this
> requirement, which is a problem.

What problem? If you try to access a "member type" (typedef or inner
class/struct) of some class that doesn't depend on template parameters
and this "member type"  isn't there the compiler will reject the code
already -- certainly in non-template contexts.

> My concepts aren't just validators
> for templates, they're also interface descriptions for non-template
> contexts. [...] it's a simple documentation tool: if a component
> advertises that it is an InputComponent, then as a user I know that
> every instance of this component will have the stuff that
> InputComponent demands, even in a non-template context.

In a "non-template context" you already know what an object of that
class can and can not do and what kind of inner types (typedefs or
inner class) the class exposes.

> So, if
> InputComponent looks like this:
>
> concept InputComponent<typename C> : Component<C>
> {
>    typename C::input_buffer;
>    ioresult C::read(const input_buffer& target);
> }
>
> then I know I can use this stuff simply because I saw somewhere (the
> documentation or the source) a
> "concept_map<InputComponent<my_component>> {}":

You don't need to see this concept_map definition because you already
know the exact type (my_component) you are dealing with. You should
have the documentation for "my_component" availble. Certainly you know
at least its declaration. Otherwise you wouldn't be able to compile
code that makes use of this class.

> my_component c;
> c.read(my_component::input_buffer(...));
>
> With simple associated types, I have to write
>
> c.read(InputComponent<my_component>::input_buffer(...));
>
> and I'm not even sure if this is valid in a non-template context.

I don't think it is. Besides, I don't understand why you don't want to
write
    c.read(my_component::input_buffer(...));

You're trying really hard to use concepts for something that would be
of no use at all. You should forget about concepts in non-template
functions. Concepts are for constraining template parameters and type-
cheking of templates.

Cheers!
SG


--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: wasti.redl@gmx.net
Date: Sat, 17 Jan 2009 20:49:05 CST
Raw View
On Jan 16, 10:45 pm, SG <s.gesem...@gmail.com> wrote:
> Concepts are for constraining template parameters and type-
> cheking of templates.

In short, I want to use them for interface documentation too,
otherwise I have to duplicate this.

And there's still the syntax mismatch. Why should I write
my_component::input_buffer in a non-template context and
InputComponent<C>::input_buffer in a template context? Why should I
ask of my library users that they remember in which context they are
so that they use the correct syntax? The syntax should be the *same*
in both contexts. Everything else is illogical.

Sebastian


--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: wasti.redl@gmx.net
Date: Mon, 12 Jan 2009 22:16:04 CST
Raw View
Hi,

9.2p19 of N2800 says:

   The member-declaration for a constrained member shall declare a
member function.

What is the rationale for this? I have a situation where I want
components to wrap each other. This looks roughly like this without
concepts:

struct Inner
{
 typedef something some_type;
 void some_function();
};

template <typename Contained>
struct Outer
{
 Inner inner;
 typedef typename Contained::some_type some_type;
 void some_function() { inner.some_function(); }
};

Now the inner component must model a basic concept Component

concept Component<typename C>
{
 typename C::some_type;
 void C::some_function();
}

But it may also model a more refined concept SpecialComponent

concept SpecialComponent<typename C> : Component<C>
{
 typename C::another_type;
 void C::another_function();
}

I want a generic wrapper. I want to make a template base that wrappers
can derive from, which takes case of forwarding. (The concrete wrapper
can override some choices, but shouldn't have to do anything to create
a null-wrapper.)

template <Component Inner>
struct Wrapper
{
 Inner inner;
 typedef Inner::some_type some_type;
 void some_function() { inner.some_function(); }

 // If the inner component is special, forward its members.
 requires SpecialComponent<Inner>
 void another_function() { inner.another_function(); }
 // ... but this doesn't work:
 requires SpecialComponent<Inner>
 typedef Inner::another_type another_type;
};

This is a real detriment to usability, and I don't see a reason for
it. I can understand the reasoning behind not allowing constrained
data members (though that would be cool), but nested types should be
no problem.

--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: SG <s.gesemann@gmail.com>
Date: Wed, 14 Jan 2009 10:45:03 CST
Raw View
On 13 Jan., 05:16, wasti.r...@gmx.net wrote:
> concept Component<typename C>
> {
>  typename C::some_type;
>  void C::some_function();
> }

As far as I can tell the declaration for an associated type is ill-
formed. It should be "typename some_type;" or "typename some_type =
some_default;" instead. Also, it is encouraged to prefer free
functions over member functions so your concept -- with some mapping
-- could apply to built-in types as well.  If you stick with member
functions your concept should look like this:

    concept Component<typename C> {
       typename some_type = C::some_type; // declaration of associated
type + default
       void C::some_function();
    }

(I'm not sure whether a "typename" preceding "C::some_type" is
required.)

> template <Component Inner>
> struct Wrapper
> {
>  Inner inner;
>  typedef Inner::some_type some_type;
>  void some_function() { inner.some_function(); }
>
>  // If the inner component is special, forward its members.
>  requires SpecialComponent<Inner>
>  void another_function() { inner.another_function(); }
>  // ... but this doesn't work:
>  requires SpecialComponent<Inner>
>  typedef Inner::another_type another_type;
> };
>
> This is a real detriment to usability, and I don't see a reason for
> it. I can understand the reasoning behind not allowing constrained
> data members (though that would be cool), but nested types should be
> no problem.

Hmmm... If you need associated types you could make use of concepts.
After all, that's what concepts are for.  Perhaps something like this
solves your problem:

    template<Component C>
    concept_map Component<Wrapper<C>> {}

    template<SpecialComponent S>
    concept_map SpecialComponent<Wrapper<S>> {
       typedef SpecialComponent<S>::another_type another_type;
    }

It would make Wrapper<T> a model of your concept(s) as long as T is a
model of your concept(s).

Cheers!
SG

--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: wasti.redl@gmx.net
Date: Thu, 15 Jan 2009 16:49:42 CST
Raw View
On Jan 14, 5:45 pm, SG <s.gesem...@gmail.com> wrote:
> On 13 Jan., 05:16, wasti.r...@gmx.net wrote:
>
> > concept Component<typename C>
> > {
> >  typename C::some_type;
> >  void C::some_function();
> > }
>
> As far as I can tell the declaration for an associated type is ill-
> formed.

It's not an associated type. It's a requirement that a member type
exists. It turns out there is actually no way of formulating this
requirement, which is a problem. My concepts aren't just validators
for templates, they're also interface descriptions for non-template
contexts.

I should give a bit more context. I'm toying with a library of stream
components. The concept Component describes the basics that all
components must be able to do. Then there are many refined concepts,
such as InputComponent, OutputComponent, Seekable, and so on. These
concepts serve a dual purpose, as I wrote above. On the one hand, you
can demand specific capabilities in a template:

template <InputComponent C> requires Seekable<C>
void doComplicatedInput(C &in);

On the other, it's a simple documentation tool: if a component
advertises that it is an InputComponent, then as a user I know that
every instance of this component will have the stuff that
InputComponent demands, even in a non-template context. So, if
InputComponent looks like this:

concept InputComponent<typename C> : Component<C>
{
   typename C::input_buffer;
   ioresult C::read(const input_buffer& target);
}

then I know I can use this stuff simply because I saw somewhere (the
documentation or the source) a
"concept_map<InputComponent<my_component>> {}":

my_component c;
c.read(my_component::input_buffer(...));

With simple associated types, I have to write

c.read(InputComponent<my_component>::input_buffer(...));

and I'm not even sure if this is valid in a non-template context.
Anyway, I thought concepts should make client code simpler rather than
more complex. In a classical documentation concept specification I'd
have simply demanded that component::input_buffer be valid - but if I
do that now, I have requirements in two places instead of one, and I
really don't want that. I want to be able to use the header file that
defines the concepts as the documentation.

> It should be "typename some_type;" or "typename some_type =
> some_default;" instead. Also, it is encouraged to prefer free
> functions over member functions so your concept -- with some mapping
> -- could apply to built-in types as well.

That makes sense for many concepts, but not for my components.
One, the functionality of components is complex enough that no built-
in type could possibly model them. (Where would a built-in type hold
state?) OK, so actually a primitive could probably be adapted into a
source that emits an infinite sequence of this value, but I would
consider that highly counter-intuitive and not something I want to
encourage.
Two, the other big argument for free functions is that you can adapt
similar, pre-existing components to the concept. (Actually, adapting
built-in types is just a special case of this.) But I don't see this
happening in this case. The requirements of a component have some
subtle aspects regarding exception safety and similar things. If you
want to adapt an existing component, writing a wrapper class isn't
much more work than writing a concept map, and it works outside a
template context.
Three, I want them to be member functions because they *have* to be
member functions, unless you write different member functions and then
adapt to them, which is rather pointless. These functions definitely
need access to internal state. So they're member functions. Now if I
make them free functions in the concept, with defaults to the members,
this results in a bad inconsistency in use in template and non-
template contexts:

Template:
read(c, ...);

Non-template:
c.read(...);
or
InputComponent<decltype(c)>::read(c, ...)

This is not good design.

>
> Hmmm... If you need associated types you could make use of concepts.
> After all, that's what concepts are for.  Perhaps something like this
> solves your problem:
>
>     template<Component C>
>     concept_map Component<Wrapper<C>> {}
>
>     template<SpecialComponent S>
>     concept_map SpecialComponent<Wrapper<S>> {
>        typedef SpecialComponent<S>::another_type another_type;
>     }
>
> It would make Wrapper<T> a model of your concept(s) as long as T is a
> model of your concept(s).
>

That would work if I wanted free associated types. I don't want them,
though.


As a workaround for the constrained member types, I can introduce
those types via mix-in bases, but it's an ugly hack. But now that I've
discovered that I can't actually write a requirement for a nested
type, I'm really unhappy.

Sebastian


--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]