Topic: Defect Report: Recursive typedef - unclear


Author: Roshan Naik <naikrosh@gmail.com>
Date: Fri, 21 Oct 2005 17:36:55 +0000 (UTC)
Raw View
[Note: forwarded to C++ Committee. -sdc]

This typedef is clearly valid:

   typedef int (*bar) (int);   // bar is a (ptr to a) function type

But it is unclear if this is valid:
   typedef int (*foo) ( foo );     // foo is a function type that takes
another  foo as argument

C++ , generally speaking allows allows referring to incomplete types if you
are not instantiating an object of the incomplete type at that point.

The request is to be specific in the standard about this issue. It is nice
to allow such recursive type referencing.
Just like inside a class declaration we can refer (by pointer or reference)
to an object of its own type, we should be able
to do the same with function types. eg:
class A {
  A* ref1;
  A& ref2;
};

Similarly we should be able to specify recursive return types ... That is ..

typedef foo (*foo) (void);      //   returns another function of its own
type
typedef int (*bar) ( bar );       //   takes its own type as argument
typedef baz (*baz) ( baz )     //  baz takes a baz as argument and produces
another baz

Example usages:

int  takeABar1 ( bar arg1 ) {
   return 1;
}

int takeABar2 ( bar arg1 ) {
   return 2;
}

int takeABar3 ( bar arg1 ) {
    return arg1( takeABar1 )  ;
}

cout << takeABar3 ( takeABar2 );


- Roshan Naik


[ 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: hyrosen@mail.com (Hyman Rosen)
Date: Sat, 22 Oct 2005 13:50:37 GMT
Raw View
Roshan Naik wrote:
> [Note: forwarded to C++ Committee. -sdc]
> This typedef is clearly valid:
>    typedef int (*bar) (int);   // bar is a (ptr to a) function type
>
> But it is unclear if this is valid:
>    typedef int (*foo) ( foo );     // foo is a function type that takes
> another  foo as argument
>
> C++ , generally speaking allows allows referring to incomplete types if you
> are not instantiating an object of the incomplete type at that point.
>
> The request is to be specific in the standard about this issue. It is nice
> to allow such recursive type referencing.

The standard is already clear and specific about this.
In 3.3.1/1 we read
     The point of declaration for a name is immediately
     after its complete declarator
so you cannot use foo to name a parameter type as you are
doing because that is before the end of the declarator which
declares foo. But you can do this:
     typedef int foo;
     namespace { typedef int (*foo)(foo); }
and then foo becomes a pointer to a function which takes a
single integer parameter and returns an integer.

---
[ 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: AlbertoBarbati@libero.it (Alberto Ganesh Barbati)
Date: Sat, 22 Oct 2005 18:34:58 GMT
Raw View
Roshan Naik wrote:
>
> This typedef is clearly valid:
>
>    typedef int (*bar) (int);   // bar is a (ptr to a) function type
>
> But it is unclear if this is valid:
>    typedef int (*foo) ( foo );     // foo is a function type that takes
> another  foo as argument

It's not at all unclear: it's invalid because of 3.3.1/1: "The point of
declaration for a name is immediately after its complete declarator
[...]". So the name "foo" can't be used twice in the declarator that
defines "foo" itself.

>
> Example usages:
>
> int  takeABar1 ( bar arg1 ) {
>    return 1;
> }
>
> int takeABar2 ( bar arg1 ) {
>    return 2;
> }
>
> int takeABar3 ( bar arg1 ) {
>     return arg1( takeABar1 )  ;
> }
>
> cout << takeABar3 ( takeABar2 );
>

These are quite shallow examples... Can you show us a real use-case
where this feature would be useful?

Ganesh

---
[ 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: johnchx2@yahoo.com
Date: Sat, 22 Oct 2005 13:34:50 CST
Raw View
Roshan Naik wrote:
> [Note: forwarded to C++ Committee. -sdc]
>
> This typedef is clearly valid:
>
>    typedef int (*bar) (int);   // bar is a (ptr to a) function type
>
> But it is unclear if this is valid:
>    typedef int (*foo) ( foo );     // foo is a function type that takes
> another  foo as argument
>
> C++ , generally speaking allows allows referring to incomplete types if you
> are not instantiating an object of the incomplete type at that point.
>
> The request is to be specific in the standard about this issue. It is nice
> to allow such recursive type referencing.

Recursion is nifty, but we do try to avoid *infinite* recursion.  If
this were allowed, for what type would foo be an alias?  Can you write
it down without circumlocutions like "takes itself"?

---
[ 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: naikrosh@gmail.com ("Roshan Naik")
Date: Sun, 23 Oct 2005 04:50:07 GMT
Raw View
"Hyman Rosen" <hyrosen@mail.com> wrote in message
news:200510211839.j9LIdKuC018739@horus.isnic.is...
> The standard is already clear and specific about this.
> In 3.3.1/1 we read
>     The point of declaration for a name is immediately
>     after its complete declarator
> so you cannot use foo to name a parameter type as you are
> doing because that is before the end of the declarator which
> declares foo. But you can do this:
>     typedef int foo;
>     namespace { typedef int (*foo)(foo); }
> and then foo becomes a pointer to a function which takes a
> single integer parameter and returns an integer.


Your (and Alberto's) reference to the standard  is necesary but not
sufficient. All it says is what is point of declaration. Not where a name
can be used. Consdier this

class A {
  A* ptrA;       // at this point declaration of A is not complete
};   // at this point A  is declared...fully

Using names referring to incomplete types (whenever possible) are allowed in
C++. I dont see any distinction specified in the standard (as far as usage
of names is concerned) between instroducing a name as part of a typedef
versus class declaration.

-Roshan

---
[ 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: naikrosh@gmail.com ("Roshan Naik")
Date: Sun, 23 Oct 2005 04:50:32 GMT
Raw View
"Alberto Ganesh Barbati" <AlbertoBarbati@libero.it> wrote in message
news:w3f6f.25323$Pe2.486574@twister2.libero.it...
> It's not at all unclear: it's invalid because of 3.3.1/1: "The point of
> declaration for a name is immediately after its complete declarator
> [...]".
> So the name "foo" can't be used twice in the declarator that
> defines "foo" itself.

You are mixing up two concepts.
There is a difference between "what is a point of declaration for a name"
and "when can a name be used".
You quotation of the standard answers the former question. I ask the very
specific form of the latter.
See my response to Hyman's similar comment.

>> Example usages:
>>
>> int  takeABar1 ( bar arg1 ) {
>>    return 1;
>> }
>>
>> int takeABar2 ( bar arg1 ) {
>>    return 2;
>> }
>>
>> int takeABar3 ( bar arg1 ) {
>>     return arg1( takeABar1 )  ;
>> }
>>
>> cout << takeABar3 ( takeABar2 );
>>
>
> These are quite shallow examples... Can you show us a real use-case
> where this feature would be useful?
>


Yes they are... but new uses will be found when people begin to explore the
usage of it. Who would have ever thought
template specializations will open up a whole field of metaprogramming when
it was originally conceived ?
There needs to be a good reason to disallow it. I would have to provide way
too much context to
describe the problem space where I am finding it very useful which is why I
kept the above examples simple but
illustrative of the desired effect. It was not to build a case for its
usefulness importance in day to day applications.
If we can do such recursive composition with classes why not with functions
too ?

If you search for "recrusive typedef" in the comp.lang.c newsgroup you will
find another instance where someone
gives an example where he found it useful... but had to settle for a
workaround instead of the natural syntax.

If I can think of some simpler applications i will post it.
-Roshan

---
[ 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: hyrosen@mail.com (Hyman Rosen)
Date: Sun, 23 Oct 2005 06:54:01 GMT
Raw View
Roshan Naik wrote:
> You are mixing up two concepts.
> There is a difference between "what is a point of declaration for a name"
> and "when can a name be used".

3.3.1/2 is essentially your example, and proves my point.
There is no ambiguity in the standard, nor is there the
possibility of declaring such recursive function types in
C++.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: "Greg Herlihy" <greghe@pacbell.net>
Date: Sun, 23 Oct 2005 12:25:55 CST
Raw View
"Roshan Naik" wrote:
> "Hyman Rosen" <hyrosen@mail.com> wrote in message
> news:200510211839.j9LIdKuC018739@horus.isnic.is...
> > The standard is already clear and specific about this.
> > In 3.3.1/1 we read
> >     The point of declaration for a name is immediately
> >     after its complete declarator
> > so you cannot use foo to name a parameter type as you are
> > doing because that is before the end of the declarator which
> > declares foo. But you can do this:
> >     typedef int foo;
> >     namespace { typedef int (*foo)(foo); }
> > and then foo becomes a pointer to a function which takes a
> > single integer parameter and returns an integer.
>
>
> Your (and Alberto's) reference to the standard  is necesary but not
> sufficient. All it says is what is point of declaration. Not where a name
> can be used. Consdier this
>
> class A {
>   A* ptrA;       // at this point declaration of A is not complete
> };   // at this point A  is declared...fully
>
> Using names referring to incomplete types (whenever possible) are allowed in
> C++. I dont see any distinction specified in the standard (as far as usage
> of names is concerned) between instroducing a name as part of a typedef
> versus class declaration.
>
> -Roshan

A class-name is inserted into the present scope (and within the class
definition) as soon as its declaration is seen (9.2). Therefore, and in
contrast to a typedef name within its definition, the name of a class
can appear as a class name (but not as a complete type) within its own
definition.

Since a class name can appear inside its own definition, it seems that
the language already accomodates a self-referential function call
declaration of one kind:

    struct FunctionObject
    {
         int operator()(const FunctionObject* f) const;
    };

The above declaration declares a struct whose function call operator
accepts a pointer to itself or another FunctionObject just like itself.

Greg

---
[ 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: naikrosh@gmail.com ("Roshan Naik")
Date: Sun, 23 Oct 2005 17:26:10 GMT
Raw View
"Hyman Rosen" <hyrosen@mail.com> wrote in message
news:b6G6f.1788$dW6.1046@trndny09...
> Roshan Naik wrote:
>> You are mixing up two concepts.
>> There is a difference between "what is a point of declaration for a name"
>> and "when can a name be used".
>
> 3.3.1/2 is essentially your example, and proves my point.
> There is no ambiguity in the standard, nor is there the
> possibility of declaring such recursive function types in
> C++.

Only if you insist! However I fail to see any relevance.
In my copy of the (1998 edition) standard 3.3.1/2 is ......

      [Note: a nonlocal name remains visible up to the point of declaration
of the local name that hides it.
      [Example:
       const int i = 2;
       { int i[i]; }


3.3.1 section talks about what "point-of-declration" is for class, class
members,  friend,  .....etc
Anyway what we need is not a definition of what the point-of-declaration
is...but at what point can the
name be used. That is described in  3.3/1

"
3.3 Declarative regions and scopes [basic.scope]
------------------------------------------------------
Every name is introduced in some portion of program text called a
declarative region, which is the largest
part of the program in which that name is valid, that is, in which that name
may be used as an unqualified
name to refer to the same entity.
"

Given your insistence on "Point-of-Declaration", you seem to be under the
impression that a name cannot be
used until it has been already its point of declaration has been crossed
.. which is not true.
I can use data members in member functions that are defined (lexically)
before I define data members.


- Roshan


---
[ 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: AlbertoBarbati@libero.it (Alberto Ganesh Barbati)
Date: Sun, 23 Oct 2005 17:27:09 GMT
Raw View
Roshan Naik wrote:
> "Hyman Rosen" <hyrosen@mail.com> wrote in message
> news:200510211839.j9LIdKuC018739@horus.isnic.is...
>
>>The standard is already clear and specific about this.
>>In 3.3.1/1 we read
>>    The point of declaration for a name is immediately
>>    after its complete declarator
>>so you cannot use foo to name a parameter type as you are
>>doing because that is before the end of the declarator which
>>declares foo. But you can do this:
>>    typedef int foo;
>>    namespace { typedef int (*foo)(foo); }
>>and then foo becomes a pointer to a function which takes a
>>single integer parameter and returns an integer.
>
> Your (and Alberto's) reference to the standard  is necesary but not
> sufficient. All it says is what is point of declaration. Not where a name
> can be used. Consdier this

A name cannot be used until it has been declared. That's mainly the
definition of "declaration". It seems that you making confusion between
the concepts of "declaration" and "definition". See below.

> class A {
>   A* ptrA;       // at this point declaration of A is not complete
> };   // at this point A  is declared...fully

Incorrect. It seems that in my quotation of 3.3.1/1 I snipped an
important part which you didn't read. Here is the complete paragraph
(with emphasys added): "The point of declaration for a name is
immediately after its complete declarator (clause 8) and before its
initializer (if any), *except as noted below*."

The words "except as noted below" introduce a list of exceptions to the
rule: the point of declaration of class names is one such exception,
described in 3.3.1/5 (also referred to in 3.1/5). So in your example:

class A /* point of declaration of A, A is an incomplete type */
{
  A* ptrA; // the name A can be used because it has been declared
           // and taking pointers to incomplete types is allowed
} /* point of definition of A, A is now a complete type */ ;

Please notice that there is no concept of "incomplete declaration" or
"full declaration". There is only "declaration" and "definition" (3.1).
An incomplete class type is a class type that has been declared but not
defined (3.9/6).

> Using names referring to incomplete types (whenever possible) are allowed in
> C++. I dont see any distinction specified in the standard (as far as usage
> of names is concerned) between instroducing a name as part of a typedef
> versus class declaration.

I just showed you there's indeed a big difference.

Ganesh

---
[ 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: AlbertoBarbati@libero.it (Alberto Ganesh Barbati)
Date: Sun, 23 Oct 2005 20:54:15 GMT
Raw View
Roshan Naik wrote:
> 3.3 Declarative regions and scopes [basic.scope]
> ------------------------------------------------------
> Every name is introduced in some portion of program text called a=20
> declarative region, which is the largest
> part of the program in which that name is valid, that is, in which that=
 name=20
> may be used as an unqualified
> name to refer to the same entity.
>=20
> Given your insistence on "Point-of-Declaration", you seem to be under t=
he=20
> impression that a name cannot be
> used until it has been already its point of declaration has been crosse=
d=20
> .. which is not true.

Well... let's be precise and see what the standard says about it:

3.3.2/1: (names at local scope) Its potential scope begins at its point
of declaration (3.3.1) and ends at the end of its declarative region.

3.3.5/1: (names at namespace scope) Its potential scope includes its
namespace from the name=92s point of declaration (3.3.1) onwards;

3.3.6/1: (names at class scope) The potential scope of a name declared
in a class consists not only of the declarative region following the
name=92s declarator, but also of all function bodies, default arguments,
and constructor ctor initializers in that class (including such things
in nested classes).

Ah! Indeed, there is a case where a name can be used in a place that
occurs (lexically) before its point of declaration. Fact is that such
exception does not apply to the case of typedef declarations, because
the declaration cannot be at the same time both at class scope and
inside a function body.

> I can use data members in member functions that are defined (lexically)=
=20
> before I define data members.
>=20

Yes, that's exactly the exception described in 3.3.6/1.

Ganesh

---
[ 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: naikrosh@gmail.com ("Roshan Naik")
Date: Sun, 23 Oct 2005 20:54:41 GMT
Raw View
"Alberto Ganesh Barbati" <AlbertoBarbati@libero.it> wrote
>
> A name cannot be used until it has been declared.

That is not true as a general statement. This is perhaps why we
misunderstand each other. "Point-of-declaration" and "scope of declaration"
are two different concepts and are defined in the standard.
 You are saying "the scope of a declaration begins only after its
point-of-declaration".  Here is what the standard says:

"3.3/3  The names declared by a declaration are introduced into the scope in
which the declaration occurs"

For illustration see this example:

class A {
    int getJ () {
       return j;  // j is not declared yet, but we are in j's scope of
declaration.
  }
  int j;     // point-of-declration for j
};

This is allowed by "class scope" rules in 3.3.6.
To determine where a name is valid, we need to determine its scope as per
the rules in the 3.3.2 to 3.3.7.
The only scope rules that comes somewhat close to my defect report is
3.3.3/1 which i dont understand, to
be honest.  What , i think, is needed is an additional section under 3.3
that titled  "typedef scope".


"Alberto Ganesh Barbati" <AlbertoBarbati@libero.it> wrote
>> class A {
>>   A* ptrA;       // at this point declaration of A is not complete
>> };   // at this point A  is declared...fully
>
> Incorrect. It seems that in my quotation of 3.3.1/1 I snipped an
> important part which you didn't read. Here is the complete paragraph
> (with emphasys added): "The point of declaration for a name is
> immediately after its complete declarator (clause 8) and before its
> initializer (if any), *except as noted below*."
>
> The words "except as noted below" introduce a list of exceptions to the
> rule: the point of declaration of class names is one such exception,
> described in 3.3.1/5 (also referred to in 3.1/5). So in your example:
>
> class A /* point of declaration of A, A is an incomplete type */
> {
>  A* ptrA; // the name A can be used because it has been declared
>           // and taking pointers to incomplete types is allowed
> } /* point of definition of A, A is now a complete type */ ;
>
> Please notice that there is no concept of "incomplete declaration" or
> "full declaration". There is only "declaration" and "definition" (3.1).
> An incomplete class type is a class type that has been declared but not
> defined (3.9/6).

My bad, I was using the term "incomplete declaration" to mean that its
definition is incomplete at a given point.
A definition can indeed be incomplete. Like inside the class the definition
of the class itself is incomplete.
Yes, the comments i specified inside the class were incorrect about the
point-of-declaration for a class.

Anyway, I have been trying to stress the point that....what the standard
calls "scope" of a name (where a name's usage is valid)
does not necessarily begin after the point of declaration. The "exceptions"
you have noted above state in the standard
are excpetions to the definition of point-of-declaration. Not exceptions to
the "scope".

-Roshan

---
[ 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: hyrosen@mail.com (Hyman Rosen)
Date: Sun, 23 Oct 2005 20:54:52 GMT
Raw View
Roshan Naik wrote:
> However I fail to see any relevance.=20
>       [Note: a nonlocal name remains visible up to the point of declara=
tion=20
> of the local name that hides it.
>       [Example:
>        const int i =3D 2;
>        { int i[i]; }

And this declares the new i as an array of two integers,
by virtue of the old i. This is exactly the same as your
example, only you are using the old foo to declare a
parameter type instead of an array size. If you fail to
see the relevance of this, so be it, but I trust that it
is not lost on anyone else.

> Given your insistence on "Point-of-Declaration", you seem to be under t=
he=20
> impression that a name cannot be used until it has been already its poi=
nt
 > of declaration has been crossed ... which is not true.

3.3.2/1 says
     A name declared in a block (6.3) is local to that block.
     Its potential scope begins at its point of declaration
     (3.3.1) and ends at the end of its declarative region.

3.3.5/1 says
     A namespace member name has namespace scope. Its potential
     scope includes its namespace from the name=92s point of
     declaration (3.3.1) onwards;

3.3.6/1/1 says
     The potential scope of a name declared in a class consists
     not only of the declarative region following the name=92s
     declarator, but also of all function bodies, default arguments,
     and constructor ctor-initializers in that class (including such
     things in nested classes).

Where can you locate your foo declaration such that it won't be
subject to one of these three clauses?

---
[ 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: naikrosh@gmail.com ("Roshan Naik")
Date: Mon, 24 Oct 2005 15:22:29 GMT
Raw View
> Where can you locate your foo declaration such that it won't be
> subject to one of these three clauses?

Inside a class body ...to take advantage of 3.3.6/1  ?
So if I place a typedef within a class ..that should be ok... isnt it ?

-Roshan

---
[ 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: "William M. (Mike) Miller" <wmm@edg.com>
Date: 25 Oct 2005 22:50:06 GMT
Raw View
Roshan Naik wrote:

> [Note: forwarded to C++ Committee. -sdc]
>
>
> This typedef is clearly valid:
>
>
>    typedef int (*bar) (int);   // bar is a (ptr to a) function type
>
>
> But it is unclear if this is valid:
>    typedef int (*foo) ( foo );     // foo is a function type that takes
> another  foo as argument

As has been noted by many other posters, this is not a defect in
the C++ Standard; the text is clear that such recursive typedefs
are not permitted because the typedef-name is not in scope until
its full declarator has been seen and thus cannot be used in its
own declarator.  I will not be adding this report as an issue to
the core language issues list.

--
William M. (Mike) Miller | Edison Design Group, Inc.
wmm@edg.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                       ]