 |
C++Talk.NET C++ language newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
James Kanze Guest
|
Posted: Fri Apr 06, 2007 3:59 pm Post subject: Incomplete type in template parameter, complete type as argu |
|
|
I've got a somewhat strange situtation, and while I have a
solution, I'm curious about the legality of my first attempted
solution, purely templates. (G++ doesn't compile it, but I
can't decide whether the problem is in my code, or a bug in
g++). Basically, the code is:
template< typename T, size_t N >
T*
begin( T (&array)[ N ] )
{
return array ;
}
template< typename T, size_t N >
T*
end( T (&array)[ N ] )
{
return array + N ;
}
struct Match
{
explicit Match( char const* key )
: myKey( key )
{
}
bool operator()( char const* lhs ) const
{
return strcmp( lhs, myKey ) == 0 ;
}
char const* myKey ;
} ;
template< char const* (&keys)[] >
bool
accept(
char const* key )
{
return std::find_if( begin( keys ), end( keys ),
Match( key ) )
!= end( keys ) ;
}
char const* tab1[] =
{
"Lu", "Ll", "Lt", "Lm", "Lo", "Nd", "Nl", "No"
} ;
char const* tab2[] =
{
"Lu", "Ll", "Lt", "Lm", "Lo"
} ;
struct Table
{
int id ;
bool (* fnc)( char const* key ) ;
} ;
Table const tbl[] =
{
{ 1, &accept< tab1 > },
{ 2, &accept< tab2 > },
} ;
G++ complains because, if I interpret the error messages
correctly, it still consideres the type of keys as incomplete in
the instantiations of "accept" (which causes problems when it
attempts to instantiate "begin" and "end"), although the
instantiation arguments have complete types. (The code works if
the template parameters of "accept" are "template< size_t N,
char const* (&keys)[ N ] >", and I explicitly pass the length of
the array.) On one hand, I can see its point, but on the
other... if it really wants to complain, it shouldn't allow me
to instantiate the template with a complete type either:-). It
is the nature of incomplete types to be completed.
FWIW: I can't find anything in the standard concerning the
handling of incomplete types in template non-type parameters.
G++ also complains about:
struct T ;
template< T const* p >
bool
f()
{
return p->ok() ;
}
struct T
{
bool ok() const { return true ; }
} ;
T anA ;
bool (*pf)() = &f< &anA > ;
Which really seems like it should be legal.
--
James Kanze (Gabi Software) email: james.kanze (AT) gmail (DOT) com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
---
[ 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.comeaucomputing.com/csc/faq.html ] |
|
| Back to top |
|
 |
Greg Herlihy Guest
|
Posted: Sun Apr 08, 2007 6:27 am Post subject: Re: Incomplete type in template parameter, complete type as |
|
|
On 4/6/07 8:59 AM, in article
1175859305.420565.301320 (AT) y80g2000hsf (DOT) googlegroups.com, "James Kanze"
<james.kanze (AT) gmail (DOT) com> wrote:
| Quote: | I've got a somewhat strange situtation, and while I have a
solution, I'm curious about the legality of my first attempted
solution, purely templates. (G++ doesn't compile it, but I
can't decide whether the problem is in my code, or a bug in
g++). Basically, the code is:
.
template< char const* (&keys)[]
bool
accept(
char const* key )
{
return std::find_if( begin( keys ), end( keys ),
Match( key ) )
!= end( keys ) ;
}
|
As noted, the template function keys(), specifies an incomplete type (an
unbouned array type) as its nontype template parameter.
| Quote: | char const* tab1[] > {
"Lu", "Ll", "Lt", "Lm", "Lo", "Nd", "Nl", "No"
} ;
char const* tab2[] > {
"Lu", "Ll", "Lt", "Lm", "Lo"
} ;
struct Table
{
int id ;
bool (* fnc)( char const* key ) ;
} ;
Table const tbl[] > {
{ 1, &accept< tab1 > },
{ 2, &accept< tab2 > },
} ;
|
The tbl array attempts to instantiate the accept() function template with
two bounded arrays types (meaning that in each instance the nontype argument
is a complete type). The attempt to instantiate accept() fails (and fails
correctly) because the nontype argument used to instantiate accept() in both
cases is not the same type as the type specified in accept's function
template declaration. In other words, an incomplete array type is a
different type than a complete array type:
"The declared type of an array object might be an array of unknown size and
therefore be incomplete at one point in a translation unit and complete
later on; the array types at those two points (³array of unknown bound of
T² and ³array of N[T]²) are different types. The type of a pointer to array
of unknown size, or of a type defined by a typedef declaration to be an
array of unknown size, cannot be completed." [§3.9/7]
So in order to instantiate accept() with tab1 and tab2, it is necessary to
ensure that their types are complete only after accept() has been
instantiated:
extern char const* tab1[];
extern char const* tab2[];
struct Table
{
int id ;
bool (* fnc)( char const* key ) ;
} ;
Table const tbl[] {
{ 1, &accept< tab1 > }, // OK, tab1 has incomplete type
{ 2, &accept< tab2 > },
} ;
char const* tab1[]={"Lu","Ll", "Lt","Lm","Lo","Nd","Nl","No"};
char const* tab2[] ={"Lu", "Ll", "Lt", "Lm", "Lo" } ;
Unfortunately, the program still fails to compile because accept() uses its
own nontype template argument ("keys" which is an incomplete type) as a
function parameter in calls to the function templates begin() and end() -
both of which use the function parameter's type to deduce a complete array
type - a deduction which naturally fails when the parameter's type is
certain to be incomplete.
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.comeaucomputing.com/csc/faq.html ] |
|
| Back to top |
|
 |
James Kanze Guest
|
Posted: Sun Apr 08, 2007 8:17 pm Post subject: Re: Incomplete type in template parameter, complete type as |
|
|
On Apr 8, 3:27 am, gre...@pacbell.net (Greg Herlihy) wrote:
| Quote: | On 4/6/07 8:59 AM, in article
1175859305.420565.301...@y80g2000hsf.googlegroups.com, "James Kanze"
james.ka...@gmail.com> wrote:
I've got a somewhat strange situtation, and while I have a
solution, I'm curious about the legality of my first attempted
solution, purely templates. (G++ doesn't compile it, but I
can't decide whether the problem is in my code, or a bug in
g++). Basically, the code is:
template< char const* (&keys)[]
bool
accept(
char const* key )
{
return std::find_if( begin( keys ), end( keys ),
Match( key ) )
!= end( keys ) ;
}
As noted, the template function keys(), specifies an incomplete type (an
unbouned array type) as its nontype template parameter.
|
Yes. That is, of course, the crux of my question.
| Quote: | char const* tab1[] =
{
"Lu", "Ll", "Lt", "Lm", "Lo", "Nd", "Nl", "No"
} ;
char const* tab2[] =
{
"Lu", "Ll", "Lt", "Lm", "Lo"
} ;
struct Table
{
int id ;
bool (* fnc)( char const* key ) ;
} ;
Table const tbl[] =
{
{ 1, &accept< tab1 > },
{ 2, &accept< tab2 > },
} ;
The tbl array attempts to instantiate the accept() function template with
two bounded arrays types (meaning that in each instance the nontype argument
is a complete type). The attempt to instantiate accept() fails (and fails
correctly) because the nontype argument used to instantiate accept() in both
cases is not the same type as the type specified in accept's function
template declaration.
|
The instantiation doesn't fail, and if I modify the code to use
a sentinal value, rather that "begin()" and "end()", it works
perfectly. char const* [N] (with N a compile time constant) is
"compatible" with char const* [].
There's not the slightest doubt that the template function
should be instantiated. The question centers around the notion
of when the incompleteness is resolved: in the definition of the
template, or when the template is instantiated. And it is
linked to a dependant function call---if I call a dependent
function in a template in which the parameter type is
incomplete, but the instantiation type is complete, does the
dependent fonction see the complete type, or only the
incomplete. (In the case of g++, it only sees the incomplete
type, but the question seems awkward enough that I want an
answer based on the standard.)
| Quote: | In other words, an incomplete array type is a
different type than a complete array type:
|
Different, but compatible.
| Quote: | "The declared type of an array object might be an array of unknown size and
therefore be incomplete at one point in a translation unit and complete
later on;
|
That is exactly the phrase on which my problem hinges. The type
is incomplete when the template is defined, but it is complete
when the template is instantiated.
| Quote: | the array types at those two points (³array of unknown bound of
T² and ³array of N[T]²) are different types. The type of a pointer to array
of unknown size, or of a type defined by a typedef declaration to be an
array of unknown size, cannot be completed." [§3.9/7]
|
That is, perhaps, significant. Although it doesn't actually
mention reference to array, it doesn't take much imagination to
think it this is also meant.
| Quote: | So in order to instantiate accept() with tab1 and tab2, it is necessary to
ensure that their types are complete only after accept() has been
instantiated:
|
Which is totally wrong, since the types are compatible.
[..]
| Quote: | Unfortunately, the program still fails to compile because accept() uses its
own nontype template argument ("keys" which is an incomplete type) as a
function parameter in calls to the function templates begin() and end() -
both of which use the function parameter's type to deduce a complete array
type - a deduction which naturally fails when the parameter's type is
certain to be incomplete.
|
The problem is that accept uses its non-type template argument
in a context where a complete type is required, yes. There's no
problem instantiating accept. The problem involves whether
instantiating it with a complete type results in a complete
type.
I'd also be curious if there was a clever work-around which
worked. A priori, the compiler has all of the knowledge which
it needs, but I can't figure out a way of making it use it.
--
James Kanze (Gabi Software) email: james.kanze (AT) gmail (DOT) com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
---
[ 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.comeaucomputing.com/csc/faq.html ] |
|
| Back to top |
|
 |
Greg Herlihy Guest
|
Posted: Mon Apr 09, 2007 6:35 am Post subject: Re: Incomplete type in template parameter, complete type as |
|
|
On 4/8/07 1:17 PM, in article
1176052237.259633.230510 (AT) o5g2000hsb (DOT) googlegroups.com, "James Kanze"
<james.kanze (AT) gmail (DOT) com> wrote:
| Quote: | On Apr 8, 3:27 am, gre...@pacbell.net (Greg Herlihy) wrote:
On 4/6/07 8:59 AM, in article
1175859305.420565.301...@y80g2000hsf.googlegroups.com, "James Kanze"
james.ka...@gmail.com> wrote:
The tbl array attempts to instantiate the accept() function template with
two bounded arrays types (meaning that in each instance the nontype argument
is a complete type). The attempt to instantiate accept() fails (and fails
correctly) because the nontype argument used to instantiate accept() in both
cases is not the same type as the type specified in accept's function
template declaration.
The instantiation doesn't fail, and if I modify the code to use
a sentinal value, rather that "begin()" and "end()", it works
perfectly. char const* [N] (with N a compile time constant) is
"compatible" with char const* [].
|
No, an unbounded array type is not at all "compatible" with a bounded array
type - they are distinct and completely different types - as the following
program demonstrates (confirmed with gcc, Comeau and EDG):
template <int N, int (&a)[N]>
void f() {}
extern int s[];
int main()
{
f<1, s>(); // error: no matching function for call to 'f'
}
int s[] = { 1 };
Moving the definition of "s" before main() does compile successfully (again,
on all three C++ compilers tested), because moving the definition actually
changes the type of "s". Unlike forwardly-declared types (which have a
consistent type before and after the type is completed), the type of an
array object actually changes once the array's type is completely defined.
That is the significant point that you do not seem to have realized just
yet.
Furthermore, there is no such thing as "compatible" types when it comes to
templates. Granted, a limited number of conversions is allowed for certain
non-type arguments - but none are allowed for reference non-type arguments.
Therefore there is no conversion between an incomplete array type and any
bounded array type. Besides, if complete and incomplete array types were
compatible, then the following overloads of f() should be ambiguous:
template <int (&a)[]>
void f() {}
template <int (&a)[1]>
void f() {}
But they are not ambiguous in the least.
| Quote: | There's not the slightest doubt that the template function
should be instantiated.
|
The EDG, gcc, and Comeau C++ compilers show not the slightest doubt that a
complete array type cannot be passed as a template nontype argument where an
incomplete array type has been declared. In other words, as long as the
program tries to instantiate accept() with a array whose type is complete,
the program will not compile successfully.
| Quote: | The question centers around the notion
of when the incompleteness is resolved: in the definition of the
template, or when the template is instantiated. And it is
linked to a dependant function call---if I call a dependent
function in a template in which the parameter type is
incomplete, but the instantiation type is complete, does the
dependent fonction see the complete type, or only the
incomplete. (In the case of g++, it only sees the incomplete
type, but the question seems awkward enough that I want an
answer based on the standard.)
|
The answer is that the type of the argument is determined at the point that
the template instantiation appears in the source file - and that is the
reason why moving the definition of the array after that point works - while
having the definition appear prior to that point - is certain to fail.
| Quote: | In other words, an incomplete array type is a
different type than a complete array type:
Different, but compatible.
|
Either the types are the same or they are different. Since they are
different in this case - the types are incompatible.
| Quote: | "The declared type of an array object might be an array of unknown size and
therefore be incomplete at one point in a translation unit and complete
later on;
That is exactly the phrase on which my problem hinges. The type
is incomplete when the template is defined, but it is complete
when the template is instantiated.
|
The accept() function template was defined with an incomplete array type and
therefore it can only be instantiated with an array whose type is
incomplete. Accept() may not be instantiated with any array whose type is
complete - because that array object has the wrong type to match the
declaration.
| Quote: | the array types at those two points (³array of unknown bound of
T² and ³array of N[T]²) are different types. The type of a pointer to array
of unknown size, or of a type defined by a typedef declaration to be an
array of unknown size, cannot be completed." [§3.9/7]
That is, perhaps, significant. Although it doesn't actually
mention reference to array, it doesn't take much imagination to
think it this is also meant.
So in order to instantiate accept() with tab1 and tab2, it is necessary to
ensure that their types are complete only after accept() has been
instantiated:
Which is totally wrong, since the types are compatible.
|
The program above proves the contrary - otherwise how could moving the
definition of the array object make any difference at all? And once again,
there is no such thing as "compatible" types.
| Quote: | [..]
Unfortunately, the program still fails to compile because accept() uses its
own nontype template argument ("keys" which is an incomplete type) as a
function parameter in calls to the function templates begin() and end() -
both of which use the function parameter's type to deduce a complete array
type - a deduction which naturally fails when the parameter's type is
certain to be incomplete.
The problem is that accept uses its non-type template argument
in a context where a complete type is required, yes. There's no
problem instantiating accept. The problem involves whether
instantiating it with a complete type results in a complete
type.
|
Any attempt to pass a complete array type where an incomplete non-type
parameter is declared will not compile. And if you can produce such a
program that does compile successfully, then by all means post it.
| Quote: | I'd also be curious if there was a clever work-around which
worked. A priori, the compiler has all of the knowledge which
it needs, but I can't figure out a way of making it use it.
|
The issues are exactly as I have described them: first, passing an array
with a complete type where an array with an incomplete type is required (but
fixing that problem leads to the opposite situation) passing an array with
an incomplete array type where an array with a complete type is needed.
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.comeaucomputing.com/csc/faq.html ] |
|
| Back to top |
|
 |
Carl Barron Guest
|
Posted: Mon Apr 09, 2007 8:01 pm Post subject: Re: Incomplete type in template parameter, complete type as |
|
|
James Kanze <james.kanze (AT) gmail (DOT) com> wrote:
| Quote: |
I'd also be curious if there was a clever work-around which
worked. A priori, the compiler has all of the knowledge which
it needs, but I can't figure out a way of making it use it.
|
how about a set of structs of the same form with the same static
members. Then instead of begin(),end(),and accept depending on a non
typed data they depend on one of these structs of static members.
struct table_a
{
static char **data;
static const int size;
};
struct table_b
{
static char const **data;
static const int size;
};
template <class T>
char const **begin() {return T::data;}
template <class T>
char const **end() {return T::data + T::size;}
template <class T>
bool accept(char const *key)
{
return std::find(begin<T>(),end<T>(),key) != end<T>();
}
struct Table
{
int id;
bool (*fnc)(char const *)
};
Table const tbl[] = ...;
---
[ 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.comeaucomputing.com/csc/faq.html ] |
|
| Back to top |
|
 |
Greg Herlihy Guest
|
Posted: Fri Apr 13, 2007 4:42 am Post subject: Re: Incomplete type in template parameter, complete type as |
|
|
On 4/11/07 9:01 AM, in article
1176302440.110184.69550 (AT) d57g2000hsg (DOT) googlegroups.com, "James Kanze"
<james.kanze (AT) gmail (DOT) com> wrote:
| Quote: | On Apr 9, 3:35 am, gre...@pacbell.net (Greg Herlihy) wrote:
No, an unbounded array type is not at all "compatible" with a
bounded array type
You mean we've broken something that fundamental? It certainly
is in C, and IIRC, something like the following worked in
CFront:
void f( int (&array)[] ) ;
void g()
{
int a[ 3 ] ;
f( a ) ;
}
There's no reason why it shouldn't work, and it seems a bit
broken if it doesn't. (Of the two compilers I have access to,
one accepts it, the other doesn't.)
- they are distinct and completely different types - as the following
program demonstrates (confirmed with gcc, Comeau and EDG):
...
That I can understand. You can't use an incomplete type where a
complete type is required. The reverse should work.
|
No, switching the bounded and unbounded array types around - also fails -
and with the same error message as before:
template <int (&a)[]>
void f() {}
int s[] = { 1 };
int main()
{
f<s>(); // error: no matching function for call to 'f'
}
| Quote: | Moving the definition of "s" before main() does compile successfully (again,
on all three C++ compilers tested), because moving the definition actually
changes the type of "s". Unlike forwardly-declared types (which have a
consistent type before and after the type is completed), the type of an
array object actually changes once the array's type is completely defined.
That is the significant point that you do not seem to have realized just
yet.
I realize it, that's why I spoke of "compatible" types, and not
the same type. (And I know, that actual concept of compatible
types is from the C standard, not from C++.)
|
C++ has not retained C's notion of "compatible types" - at least not as far
as templates are concerned.
| Quote: | Furthermore, there is no such thing as "compatible" types when it comes to
templates.
That is the crux of my question. If g++ had refused to
instantiation the template in my example, I would put it down to
that. What it did, however, was generate error messages from
the instantiation.
|
No, the compiler is not instantiating the function template accept() (and
the error that the gcc reports in the original program - is not an
instantiation failure.) Instead, the compiler has simply detected (during
the course of resolving the accept() function call) that the definition of
the accept() template function is ill-formed.
According to §14.6/7, the compiler may issue a diagnostic message for an
ill-formed (even if never instantiated) template definition. (In fact, the
point at which a compiler detects and reports this kind of error is left up
to the compiler as a "qualify of implementation" issue). As an example:
void Begin( int (&p)[1])
{
}
extern int s[];
template < int N>
void f()
{
Begin(s); // Error: invalid initialization of reference
}
int s[] = { 1 };
int main()
{
}
In the above program, the gcc compiler reports an error in the f() function
template - even though the function template f() in which the error is found
- is never instantiated at all. The issue in the original program is
comparable: gcc found an error in accept() - even though the accept()
function template was not instantiated.
So the conclusion here is simply that bounded and unbounded array types are
not at all alike in C++. So C++ programs that deal with those two types -
must accommodate the difference.
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.comeaucomputing.com/csc/faq.html ] |
|
| Back to top |
|
 |
Vidar Hasfjord Guest
|
Posted: Mon Apr 16, 2007 9:06 am Post subject: Re: Incomplete type in template parameter, complete type as |
|
|
On Apr 11, 5:01 pm, "James Kanze" <james.ka...@gmail.com> wrote:
| Quote: | Even in C++, the notion of compatible type exists. Whether it applies
with regards to incomplete and complete array types, I'm not sure, and
whether it applies in this case, even less, but the concept is there
(if only for reasons of C compatibility, in certain cases).
|
In my view the "complete or incomplete type" question is a red herring
in this case. Answering a simpler question; "On which parameters is
the template parameterized?"; seems to give a simple answer that
confirms the compiler error. (By the way, VC++ 8 complains with error
C2265: "'abstract declarator' : reference to a zero-sized array is
illegal" on the template declaration itself.)
Here is your template again:
template <char const* (&keys) []>
bool accept (char const* key) {
return std::find_if (begin (keys), end (keys), Match (key))
!= end (keys);
}
This template is simply not parameterized on array size. There is no
rule that says it is implicitly parameterized on size just because the
size is omitted. Hence, this template is only parameterized on
references to a single array type; an array of unspecified size, which
by standard rules decays to a pointer. If you want to parameterize the
template on the size of the array (i.e. a range of array types; since
arrays of different sizes are of different types), you have to say so
explicitly:
template <size_t N, char const* (&keys) [N]>
bool accept (char const* key)
{
return std::find_if (begin (keys), end (keys), Match (key))
!= end (keys);
}
| Quote: | It may be an error in g++ (the fact that g++
does refuse to instantiate in simpler cases tends to confirm this),
but the template was definitly being instantiated.
|
Unlike Visual C++, g++ may allow declaration and instantiation of the
template. I don't know which compiler displays the correct behaviour,
but what's beyond doubt AFAIAC is that all instantiations are using a
single array type, an array-of-unspecified-size type (char const* []).
This type doesn't carry size information, hence the error in the
instantiation (since the implementation depends on that size info).
| Quote: | Accept() may not be instantiated with any array whose type is
complete - because that array object has the wrong type to match the
declaration.
In which case, I'd call that a defect in the standard.
|
Not by my interpretation. There is a mismatch of types like Greg
Herlihy says. The exact type of the array argument passed to the
template cannot be captured because the template is simply not
parameterized on array type (the size in this case).
| Quote: | The compiler has the information it needs at the point of
instantiation. The problem is how to tease them out of it.
|
True. It has all the information, but the template as you've declared
it doesn't allow for parameterization on that info (i.e. on the size).
Hence it decays to the parameter type you've specified, an array-of-
unspecified-size type.
Using my declaration above you can implement your table as follows:
Table const tbl [] =
{
{1, &accept <N_ELEMENTS (tab1), tab1>},
{2, &accept <N_ELEMENTS (tab2), tab2>},
};
This is a little more to write but otherwise as you intended.
If that is too verbose, you can use functors, provided the rest of
your program can be adapted, of course. A functor allows the array
type, the size in this case, to be captured through a templated
constructor:
struct Accept
{
char const* const* keys;
const size_t n;
template <size_t N> Accept (char const* (&k) [N])
: keys (k), n (N) {}
bool operator () (char const* key) {
return std::find_if (keys, keys + n, Match (key))
!= keys + n;
}
};
Table const tbl [] =
{
{1, Accept (tab1)},
{2, Accept (tab2)},
};
Regards,
Vidar Hasfjord
---
[ 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.comeaucomputing.com/csc/faq.html ] |
|
| Back to top |
|
 |
James Kanze Guest
|
Posted: Mon Apr 16, 2007 2:42 pm Post subject: Re: Incomplete type in template parameter, complete type as |
|
|
On Apr 16, 11:06 am, "Vidar Hasfjord" <vattilah-gro...@yahoo.co.uk>
wrote:
| Quote: | On Apr 11, 5:01 pm, "James Kanze" <james.ka...@gmail.com> wrote:
Even in C++, the notion of compatible type exists. Whether it applies
with regards to incomplete and complete array types, I'm not sure, and
whether it applies in this case, even less, but the concept is there
(if only for reasons of C compatibility, in certain cases).
In my view the "complete or incomplete type" question is a red herring
in this case. Answering a simpler question; "On which parameters is
the template parameterized?"; seems to give a simple answer that
confirms the compiler error.
|
[...]
| Quote: | Here is your template again:
template <char const* (&keys) []
bool accept (char const* key) {
return std::find_if (begin (keys), end (keys), Match (key))
!= end (keys);
}
This template is simply not parameterized on array size.
|
True. As I said, the problem is that the compiler has the
necessary information, but there's no way to tease it out of it.
[...]
| Quote: | Accept() may not be instantiated with any array whose type is
complete - because that array object has the wrong type to match the
declaration.
In which case, I'd call that a defect in the standard.
Not by my interpretation. There is a mismatch of types like Greg
Herlihy says. The exact type of the array argument passed to the
template cannot be captured because the template is simply not
parameterized on array type (the size in this case).
|
There are two aspects. As you've pointed out, the template
isn't parameterized on size, only on the "name" of the array.
It seems a little frustrating, but I can see the logic behind
this. On the other hand, I think that not being able to use an
array of known size anywhere an array of unknown size is
required *is* a defect. It doesn't make sense.
| Quote: | The compiler has the information it needs at the point of
instantiation. The problem is how to tease them out of it.
True. It has all the information, but the template as you've declared
it doesn't allow for parameterization on that info (i.e. on the size).
Hence it decays to the parameter type you've specified, an array-of-
unspecified-size type.
Using my declaration above you can implement your table as follows:
Table const tbl [] =
{
{1, &accept <N_ELEMENTS (tab1), tab1>},
{2, &accept <N_ELEMENTS (tab2), tab2>},
};
This is a little more to write but otherwise as you intended.
|
That is one solution; I'd thought of that. The other solution
would be to modify the code to use a sentinel (so that the
template function doesn't need the length), forward declare the
arrays, with unknown size, instantiate the templates, and only
then define the arrays.
(In practice, the solution was to go back to the classical
solution, using inheritance instead of templates. My playing
around with templates here was only an experiment, to see if it
would make sense.)
| Quote: | If that is too verbose, you can use functors, provided the rest of
your program can be adapted, of course. A functor allows the array
type, the size in this case, to be captured through a templated
constructor:
struct Accept
{
char const* const* keys;
const size_t n;
template <size_t N> Accept (char const* (&k) [N])
: keys (k), n (N) {}
bool operator () (char const* key) {
return std::find_if (keys, keys + n, Match (key))
!= keys + n;
}
};
Table const tbl [] =
{
{1, Accept (tab1)},
{2, Accept (tab2)},
};
|
I thought of that. I just bothers me that the initialization
ceases to be static (not that it matters, in this case---the
functions using the table are never called from the constructor
of a static object).
--
James Kanze (GABI Software) email:james.kanze (AT) gmail (DOT) com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
---
[ 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.comeaucomputing.com/csc/faq.html ] |
|
| Back to top |
|
 |
Guest
|
Posted: Tue Apr 17, 2007 5:05 pm Post subject: Re: Incomplete type in template parameter, complete type as |
|
|
On 16 Apr., 16:42, "James Kanze" <james.ka...@gmail.com> wrote:
| Quote: | (In practice, the solution was to go back to the classical
solution, using inheritance instead of templates. My playing
around with templates here was only an experiment, to see if it
would make sense.)
|
It makes sense. Only the rules are counter-intuitive in ways that
are no longer to be kept in mind.
What strikes me most is that gcc and KAI C++ _used_ to have intuitive
behaviour and template things worked smoothly a few years ago.
Now after some standard's clarification I find myself back in template
hell
shaking head all the time.
Correct me if I am wrong:
I am not quite sure, but I think this discussion is somehow related to
a passage in
http://www.cs.colorado.edu/~siek/pubs/pubs/2006/siek06_sem_cpp.pdf
<cite>
The semantics of member function instantiation is a point of some
controversy.
Section [14.6.4.1 p1] of the Standard says that the point of
instantiation
for a member function immediately follows the enclosing declaration
that triggered
the instantiation (with a caveat for dependent uses within templates).
The
problem with this rule is that uses of a member function may legally
precede its
definition and the definition is needed to generate the instantiation.
(A use of a
member function must only come after the declaration of the member
function,
which is in the template specialization.) In general, the C++ Standard
is formulated
to allow for compilation in a single pass, whereas the current rules
for
member instantiation would require two passes. Also, there is a
disconnect between
the Standard and the current implementations. The Edison Design Group
and GNU compilers both delay the instantiation of member functions to
the end
of the program (or translation unit). We discussed this issue on C++
committee2
and the opinion was that this is a defect in the C++ Standard and that
instantiation
of member functions should be allowed to occur at the end of the
program.
Therefore, C++.T places instantiations for member functions at the end
of the
program.
</cite>
Best Regards,
Markus
---
[ 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.comeaucomputing.com/csc/faq.html ] |
|
| Back to top |
|
 |
James Kanze Guest
|
Posted: Wed Apr 18, 2007 2:30 pm Post subject: Re: Incomplete type in template parameter, complete type as |
|
|
On Apr 17, 7:05 pm, numerical.simulat...@web.de wrote:
| Quote: | On 16 Apr., 16:42, "James Kanze" <james.ka...@gmail.com> wrote:
What strikes me most is that gcc and KAI C++ _used_ to have intuitive
behaviour and template things worked smoothly a few years ago.
|
You mean before two phased look-up?
| Quote: | Now after some standard's clarification I find myself back in
template hell shaking head all the time.
Correct me if I am wrong:
I am not quite sure, but I think this discussion is somehow related to
a passage inhttp://www.cs.colorado.edu/~siek/pubs/pubs/2006/siek06_sem_cpp.pdf
cite
The semantics of member function instantiation is a point of
some controversy. Section [14.6.4.1 p1] of the Standard says
that the point of instantiation for a member function
immediately follows the enclosing declaration that triggered
the instantiation (with a caveat for dependent uses within
templates). The problem with this rule is that uses of a
member function may legally precede its definition and the
definition is needed to generate the instantiation. (A use of
a member function must only come after the declaration of the
member function, which is in the template specialization.) In
general, the C++ Standard is formulated to allow for
compilation in a single pass, whereas the current rules for
member instantiation would require two passes. Also, there is
a disconnect between the Standard and the current
implementations. The Edison Design Group and GNU compilers
both delay the instantiation of member functions to the end of
the program (or translation unit). We discussed this issue on
C++ committee2 and the opinion was that this is a defect in
the C++ Standard and that instantiation of member functions
should be allowed to occur at the end of the program.
Therefore, C++.T places instantiations for member functions at
the end of the program.
/cite
|
It don't think this is the problem. The above passage speaks
about the point of instantiation. What seems to cause the most
problems, however, is the fact that compilers now parse a
significant part of the template when it is defined, before any
instantiation, and at the point where it is defined. Back in
the good old days(TM), templates acted in many ways like macros;
until they were instantiated, they were just text. And the
instantiation substituted the name of the actual type for T, or
whatever, and compiled that. (Of course, they were never quite
this simple. But compared to today, it often seemed like it.)
It's a very intuitive model. Also a possibly error prone one; a
template could pick up all sorts of junk its author never
considered. So we get the present rules, where a template can
only pick up all sorts of junk for a dependent name (and
presumably, that's what the author wanted).
The goal, in itself, is laudable. Whether it is worth the
price, or whether the best solution was chosen, is an open
question, I think.
--
James Kanze (GABI Software) email:james.kanze (AT) gmail (DOT) com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
---
[ 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.comeaucomputing.com/csc/faq.html ] |
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|