Topic: Parameter validation (was: Suggestion: "typeof" keyword)


Author: sbnaran@uiuc.edu (Siemel B. Naran)
Date: 1999/08/26
Raw View
On 25 Aug 99 19:29:32 GMT, Christopher Eltschka
>"Siemel B. Naran" wrote:

>What about:
>
>II find(II begin, II end,
>        typeof(void(++II()),void(II()++),*II) const& val);

Good idea.  Although I don't like the idea of using typeof to document
required traits.


>> The direct approach might be better
>>    template<class II, class T>
>>    II find(II begin, II end, const T& val) [ ++II ] [ II++ ] [ *II ] ;
>> This says that we require operator++(II) or II::operator++(), etc.
>
>In that case, a better place for the restrictions would probably
>be the template<...> part:
>
>template<class II [++II(), II()++], class T [ T()==*II() ]>
> II find(II begin, II end, T const& val);

My idea was for the compiler to generate these required traits
automatically when it compiles template code.  I really don't want users
to mess around with documenting 16 or so required functions and typedefs.

Say you have template.hh and template.cc.  Now you compile the template
file template.cc.  The result is a template file template.oo that holds
the template code in pre-compiled template form.  BTW, this is one
reason why the typename keyword is necessary.

Now you have a file myfile.cc that includes template.hh.  The compiler
automatically looks at template.oo if it is up to date.  The compiler
quickly learns if you are missing any traits.

Disdadvantage with this approach: you can't compile .cc files in
isolation.



>I could even imagine those restrictions to be used in
>template function argument deduction:

Overloading by traits?  Its too subtle, kind of like overloading by return
value.


>template<class Iter [ lvalue<Iter>()++, ++_lvalue<Iter>() ],
>         class T    [ *rvalue<Iter>() == rvalue<T>() ]>
>Iter find(Iter begin, Iter end, T const& t);

Yikes!

--
----------------------------------
Siemel B. Naran (sbnaran@uiuc.edu)
----------------------------------
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: Christopher Eltschka <celtschk@physik.tu-muenchen.de>
Date: 1999/08/25
Raw View
"Siemel B. Naran" wrote:
>
> On 23 Aug 99 20:32:16 GMT, blargg <postmast.root.admi.gov@iname.com> wrote:
> >In article <7pk70i$6as$1@nnrp1.deja.com>, niklasb@my-deja.com wrote:
>
> >    template<class II>
> >    II find(II begin, II end, const typeof(*II())& val );
>
> >> This allows for better parameter validation, and
> >> better diagnostics, because the compiler has more
> >> information about the parameter types.
>
> >That's interesting, but considering the things it wouldn't check, I don't
> >like this inconsistency. For example, find probably needs operator ++
> >defined, but that wouldn't be expressed in the interface.
>
> How about this
>    template<class II>
>    II find(II begin, II end, const typeof(*((++II())++))& val );
> This says that II must have an accessible
>    default constructor
>    pre-increment
>    post-increment
>    operator*
>
> Here's another idea
>    template<class II>
>    II find(II begin, II end, const typeof(++II,II++,*II)& val );
> IOW, typeof(A,B,C) is the same as typeof(C).  But then again, there
> might be a A::operator,(B).

What about:

II find(II begin, II end,
        typeof(void(++II()),void(II()++),*II) const& val);

Since you can't overload operator, with a first argument
type of void, the built-in operator, will always be used.
However, if used with pointers, this may fail because of the
rvalueness if II().

>
> The direct approach might be better
>    template<class II, class T>
>    II find(II begin, II end, const T& val) [ ++II ] [ II++ ] [ *II ] ;
> This says that we require operator++(II) or II::operator++(), etc.

In that case, a better place for the restrictions would probably
be the template<...> part:

template<class II [++II(), II()++], class T [ T()==*II() ]>
 II find(II begin, II end, T const& val);

I could even imagine those restrictions to be used in
template function argument deduction:

// Use the following if == is defined
template<class Iter [++Iter(), Iter()++],
         class T [ *Iter() == T() ]>
 Iter find(Iter begin, Iter end, T const& t)
{
  while (begin!=end && !(*begin == t));
  return begin;
}

// Use the following if < is defined, but == is not
template<class Iter [++Iter(), Iter++()],
         class T [ *Iter() < T() ] ![ *Iter() == T() ]>
 Iter find(Iter begin, Iter end, T const& t)
{
  while (begin!=end && (*begin<t || *begin>t));
  return begin;
}

struct A
{
  int i;
  bool operator==(A const&);
};

struct B
{
  int j;
};

bool operator<(B const&, B const&);

struct C
{
  int k;
  bool operator<(C const&);
  bool operator==(C const&);
};

A a { 1 };
B b { 2 };
C c { 3 };

int main()
{
  std::vector<A> vA;
  std::vector<B> vB;
  std::vector<C> vC;

  find(vA.begin(), vA.end(), a); // only first version fits
  find(vB.begin(), vB.end(), b); // only second version fits
  finc(vC.begin(), vC.end(), c); // only first version fits
}

For C, the second version doesn't fit since the definition
excludes classes with operator== defined. Otherwise, the
third call to find would have been ambiguous.
Another possibility would have been to provide a third version
for classes having both operators defined, and relying on partial
ordering of templates.

As with typeof, instead of using just the typename to denote
an object of a given type, something else should IMHO be used.

Also, a point neglected up to now in the typeof discussion
is distinction of lvalue and rvalue. Should the typename
stand for an lvalue of for an rvalue?

With the function approach, this is simple:

template<class T> T& lvalue();
template<class T> T rvalue();

template<class Iter [ lvalue<Iter>()++, ++_lvalue<Iter>() ],
         class T    [ *rvalue<Iter>() == rvalue<T>() ]>
 Iter find(Iter begin, Iter end, T const& t);

If you don't like the syntax, a macro helps:

#define lvalue(T) (lvalue<T>())
#define rvalue(T) (rvalue<T>())

template<class Iter [ lvalue(Iter)++, ++lvalue(Iter) ],
         class T    [ *rvalue(Iter) == rvalue(T) ]>
 Iter find(Iter begin, Iter end, T const& t);

A single obj template would also work, by explicitly
specifying the "&":

template<class T> T obj();

template<class Iter [ obj<Iter&>()++, ++obj<Iter&>() ], // lvalues
         class T    [ *obj<Iter>() == obj<T>() ]>       // rvalues
 ...

Again, a macro could help simplifying syntax:

template<class T> T obj();
#define obj(T) (obj<T>());

template<class Iter [ obj(Iter&)++, ++obj(Iter&) ],
         class T    [ *obj(Iter) == obj(T) ]>
 ...

The "pseudo-cast" notation would also allow this distinction:
(T)0 is an rvalue, (T&)0 is an lvalue.

However the "typename stands for object of type" approach would
have a problem in this respect, since you preclude complex
"type expressions" like T* or T&.
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]