Topic: More flexible templates
Author: clamage@Eng.Sun.COM (Steve Clamage)
Date: 1995/07/06 Raw View
Uwe Tantz <c4037@rphc1.physik.uni-regensburg.de> writes:
>I think there is another approach to this problem.
>1.
>You could solve it if there was something like "method-templates".
>Stroustroup writes there were good reasons not to allow those.
But they are allowed by the draft standard, so long as they
are not virtual. Stroustrup did not want to allow them until
there was enough experience to know whether they are safe.
>2.
>You could solve it if you could write e.g.
>template <class T1, class T2>
>T1 operator+ (const T1 &o1, const T2 &o2)
>{
> T1 result;
> result.data = o1.data + o2.data;
> return result;
>}
>Unfortunately overloaded operators must not be global.
What? A specific, very small set of overloaded operators is
disallowed unless members of a class, but '+' is not among them.
In fact, it is usually recommended to make arithmetic operators global
so as to allow type conversion on the first argument.
--
Steve Clamage, stephen.clamage@eng.sun.com
Author: Uwe Tantz <c4037@rphc1.physik.uni-regensburg.de>
Date: 1995/07/06 Raw View
On 6 Jul 1995, Steve Clamage wrote:
> >2.
> >You could solve it if you could write e.g.
> >template <class T1, class T2>
> >T1 operator+ (const T1 &o1, const T2 &o2)
> >{
> > T1 result;
> > result.data = o1.data + o2.data;
> > return result;
> >}
> >Unfortunately overloaded operators must not be global.
>
> What? A specific, very small set of overloaded operators is
> disallowed unless members of a class, but '+' is not among them.
> In fact, it is usually recommended to make arithmetic operators global
> so as to allow type conversion on the first argument.
^ interresting, I never thought of this, thanks!
Oops, of course you are right. I have written global operators myself not
just once. Templates must have confused my mind.
However you will run into problems as soon as you need an assignment
operator (which is in the above set and you should need one, right?)
Uwe
+---------------------+--------------------------------------------------+
| Uwe-Krischna Tantz | Tel/Fax: +49 941 / 56 31 40 |
| Hackengaesschen 4 | EMail: uwe.tantz@physik.uni-regensburg.de |
| 93047 Regensburg | |
+---------------------+--------------------------------------------------+
Author: Adam Wiewiorka <a.wiewiorka@ic.ac.uk>
Date: 1995/07/04 Raw View
Thank you for your comments although I would like somebody to
clarify a few things.
1)
clamage@Eng.Sun.COM (Steve Clamage) wrote:
>The C++ standard does not discuss compiler directives. It defines
>the syntax and semantics of C++. Compiler directives generally affect
>some system-specific behavior of the compiler, which is outside the
>purview of the standard, or tell the compiler to use some other
>semantics than the standard C++ semantics.
Chapter 16 of the current ANSI Working Paper describes preprocessor
directives and I see no reason why a few C++ specific ones should not be
added. After all, C++ is already much more powerful than C and one can
write C++ programs incomprehensible to C programmers.
2)
>You can use the template class "numeric_limits", predefined on
>all numeric types, to determine in your template whether you
>have a floating or integral type, and generate the appropriate
>code.
This is a very unsatisfactory solution becouse it requires run-time
checking. I do NOT want any run-time binding as it is not required by
the simple & or | operator. Mind you, the ANSI C++ committee saw fit
to include the hidious Run-Time Type Identification keywords like
'reinterpret_cast' and 'typeid'. Surely, it's better to use purely
static linking because the arguments to | are NOT going to change their
type during program execution.
3) In the case of a large container class like the Matrix<T> I used in
my example, the mere fact that you have to use a conversion operator like
'Matrix<T>::Matrix<U>()' means that you create a temporary object and
have to copy each and every element of the matrix. Suppose you have
a 1000x1000 matrix of floats. I hope you can see the problem now.
On the other hand:
clamage@Eng.Sun.COM (Steve Clamage) writes:
>the conversion function can itself be a template function taking
>an extra template parameter. I don't think any compilers support
>this language addition yet.
We can write all arithmetic operator functions as templates within
templates which eliminates the need for the conversion operator.
Adam Wiewiorka
P.S. Does anybody know when or if GNU g++ will support such nested
templates?
Author: clamage@Eng.Sun.COM (Steve Clamage)
Date: 1995/07/04 Raw View
In article 8of@oban.cc.ic.ac.uk, Adam Wiewiorka <a.wiewiorka@ic.ac.uk> writes:
>
>clamage@Eng.Sun.COM (Steve Clamage) wrote:
>
>>The C++ standard does not discuss compiler directives. It defines
>>the syntax and semantics of C++. Compiler directives generally affect
>>some system-specific behavior of the compiler, which is outside the
>>purview of the standard, or tell the compiler to use some other
>>semantics than the standard C++ semantics.
>
>Chapter 16 of the current ANSI Working Paper describes preprocessor
>directives and I see no reason why a few C++ specific ones should not be
>added.
What did you have in mind? Preprocessing takes place before any
parsing of C++ programs. (The preprocessor has its own language,
which is not C or C++, and different rules for what constitutes a token,
for example.)
There is no technical difficuly with adding compiler directives to a
compiler -- every compiler has them. (They might have the form of a
preprocessing directive, but that is an independent issue. Whether the
directive's spelling has a '#' is irrelevent.)
It is a deliberate choice not to address such issues in the standard. For
one thing, the standard would then have to address issues of what is a
compiler, and details of the program generation process.
---
Steve Clamage, stephen.clamage@eng.sun.com
Author: clamage@Eng.Sun.COM (Steve Clamage)
Date: 1995/07/04 Raw View
damian@molly.cs.monash.edu.au (Damian Conway) writes:
>clamage@Eng.Sun.COM (Steve Clamage) writes:
>>You can use the template class "numeric_limits", predefined on
>>all numeric types, to determine in your template whether you
>>have a floating or integral type, and generate the appropriate
>>code. Example:
>> template<class T> Matrix<T> Matrix<T>::operator|(const Matrix & m)
>> {
>> // all the members of numeric_limits are static
>> if( numeric_limits<T>::is_integer() ) {
>> }
>> else {
>> }
>> };
>Um. Forgive me for being dense, but does this actually solve the problem?
> ...
>Now, surely, whether the "if" or the "else" block is destined to be executed
>for a particular instantiation T, both blocks _will_ be generated and if T is
>not integer the line
> newmat[i][j] = (*this)[i][j] | m[i][j];
>is going to generate a compiler error. Isn't it?
Yeah. I skipped a step. You can use an overloaded function call in the
template function so that code gets compiled only for types that
have the desired properties. "Design and Evolution" has examples in
section 15.6.
--
Steve Clamage, stephen.clamage@eng.sun.com
Author: Uwe Tantz <c4037@rphc1.physik.uni-regensburg.de>
Date: 1995/07/05 Raw View
On 3 Jul 1995, Steve Clamage wrote:
> >Matrix<int> a;
> >Matrix<float> b;
> >
> >a = a + b;
> >
> >This will not compile unless you define a conversion operator
> > operator Matrix<int>();
> >
> >If one uses 8 types, 8 explicitly instantiatied operators must be defined,
>
> No, the conversion function can itself be a template function taking
> an extra template parameter. I don't think any compilers support
> this language addition yet.
I think there is another approach to this problem.
1.
You could solve it if there was something like "method-templates".
Stroustroup writes there were good reasons not to allow those.
2.
You could solve it if you could write e.g.
template <class T1, class T2>
T1 operator+ (const T1 &o1, const T2 &o2)
{
T1 result;
result.data = o1.data + o2.data;
return result;
}
Unfortunately overloaded operators must not be global. But as operator
overloading is only "syntactical sugar" (David Kruglinski) you may write
a global template function "Add" which does the same work.
Uwe
+---------------------+--------------------------------------------------+
| Uwe-Krischna Tantz | Tel/Fax: +49 941 / 56 31 40 |
| Hackengaesschen 4 | EMail: uwe.tantz@physik.uni-regensburg.de |
| 93047 Regensburg | |
+---------------------+--------------------------------------------------+
Author: Adam Wiewiorka <a.wiewiorka@ic.ac.uk>
Date: 1995/07/03 Raw View
Dear All,
I have recently written a simple template based matrix library, with
overloaded operators, efficient copying etc, etc. However, the current
C++ standard is still too rigid in such cases:
1) Suppose we have a matrix class
template <class T> class Matrix;
and we want to overload the bitwise operators like &, |, etc.
All is well as long as the template argument is an int or any
type for which the avove operators are defined. If T is a floating
point type the compiler will complain. You have 3 unsatisfactory
solutions: cast everything to 'long int', write a function that
checks for the type at runtime and throws an exception if applied to
'float' or define two matrix classes, one for integral types and
the other for floating poing, which makes the whole idea of templates
completely useless.
Proposed solution:
To define a compiler directive enabling conditional compilation
depending on template arguments. To define another directive which
catches impossible conversions and skips the funcion.
This will help when you want to generate a library and have all silly
errors reported while using it.
2) Let's declare 2 matices and then use them:
Matrix<int> a;
Matrix<float> b;
a = a + b;
This will not compile unless you define a conversion operator
operator Matrix<int>();
If one uses 8 types, 8 explicitly instantiatied operators must be defined,
that generate a temporary object and waste a lot of time and memory doing it.
Another option is to define
template <class T, class U> class Matrix;
and use T and U in the operator+ function. This is OK (although boring)
for binary operators, but fails if we have a function with more than 2
Matrix<T> arguments. If we want to compile a library, it means explicit
instantiation of 64 versions of each operator.
Proposed solution:
To define a 'template cast' which makes the compiler recognise that
'Matrix<int>' and 'Matrix<float>' are very similar types and not the
whole Matrix<float> type but only the <float> is cast to <int>. In the
above example this will mean compiling Matrix<T>::operator+(Matrix<T>&) as
if it was Matrix<int>::operator+(Matrix<float>), and each member of
'Matrix<float>' that depends on the template argument will be cast to 'int'.
I expect somebody to rubbish my ideas and explain why it will never be
possible to implement them.
Adam Wiewiorka
Author: clamage@Eng.Sun.COM (Steve Clamage)
Date: 1995/07/03 Raw View
Adam Wiewiorka <a.wiewiorka@ic.ac.uk> writes:
>1) Suppose we have a matrix class
> template <class T> class Matrix;
>
>and we want to overload the bitwise operators like &, |, etc.
>All is well as long as the template argument is an int or any
>type for which the avove operators are defined. If T is a floating
>point type the compiler will complain.
You can use the template class "numeric_limits", predefined on
all numeric types, to determine in your template whether you
have a floating or integral type, and generate the appropriate
code. Example:
template<class T> Matrix<T> Matrix<T>::operator|(const Matrix & m)
{
// all the members of numeric_limits are static
if( numeric_limits<T>::is_integer() ) {
}
else {
}
};
>Proposed solution:
> To define a compiler directive ...
The C++ standard does not discuss compiler directives. It defines
the syntax and semantics of C++. Compiler directives generally affect
some system-specific behavior of the compiler, which is outside the
purview of the standard, or tell the compiler to use some other
semantics than the standard C++ semantics. That second possibility
has been consistently rejected as an option.
>2) Let's declare 2 matices and then use them:
>
>Matrix<int> a;
>Matrix<float> b;
>
>a = a + b;
>
>This will not compile unless you define a conversion operator
> operator Matrix<int>();
>
>If one uses 8 types, 8 explicitly instantiatied operators must be defined,
No, the conversion function can itself be a template function taking
an extra template parameter. I don't think any compilers support
this language addition yet.
--
Steve Clamage, stephen.clamage@eng.sun.com
Author: damian@molly.cs.monash.edu.au (Damian Conway)
Date: 1995/07/03 Raw View
clamage@Eng.Sun.COM (Steve Clamage) writes:
>You can use the template class "numeric_limits", predefined on
>all numeric types, to determine in your template whether you
>have a floating or integral type, and generate the appropriate
>code. Example:
> template<class T> Matrix<T> Matrix<T>::operator|(const Matrix & m)
> {
> // all the members of numeric_limits are static
> if( numeric_limits<T>::is_integer() ) {
> }
> else {
> }
> };
Um. Forgive me for being dense, but does this actually solve the problem?
Suppose we fill in the if..else code blocks:
template<class T> Matrix<T> Matrix<T>::operator|(const Matrix & m)
{
// all the members of numeric_limits are static
if( numeric_limits<T>::is_integer() ) {
for (i=0;i<rows();i++)
for (int j=0;i<cols();j++)
newmat[i][j] = (*this)[i][j] | m[i][j];
}
else {
for (i=0;i<rows();i++)
for (int j=0;i<cols();j++)
newmat[i][j] = bitor((*this)[i][j],m[i][j]);
}
}
Now, surely, whether the "if" or the "else" block is destined to be executed
for a particular instantiation T, both blocks _will_ be generated and if T is
not integer the line
newmat[i][j] = (*this)[i][j] | m[i][j];
is going to generate a compiler error. Isn't it?
damian
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
who: Damian Conway email: damian@bruce.cs.monash.edu.au
where: Dept. Computer Science phone: +61-3-565-5184
Monash University fax: +61-3-565-5146
Clayton 3168 quote: "A pessimist is never disappointed."
AUSTRALIA
Author: tholaday@jpmorgan.com (Thomas Holaday,COMM)
Date: 1995/07/03 Raw View
(1) conditionals in templates:
See Design and Evolution of C++ and the STL for discussion of how to get the effect
of conditional compilation in template code. See Rogue Wave Math.h++ and Matrix.h++
for examples of how to implement conditionals in template code with a wee preprocessor.
Note also that your compiler should not be complaining about operator&(matrix<float>)
unless (1) it is virtual or (2) you are actually calling it. If you are actually
calling it, then the compiler complaint is (I think) exactly what you want to occur.
(2) Template member functions allow you to write:
template <class T> class Matrix
{
public:
template <class U> Matrix(const Matrix<U> &rhs) { /* assign elements */ }
};
Then you could write:
Matrix<float> mf;
Marrix<bitmap> mb;
Matrix<int> mif(mf); // int = float defined, so okay
Matrix<int> mib(mb); // int = bitmap not defined, so compile-time error
---
~THol() Thomas Holaday
holaday_thomas@jpmorgan.com tlhol@ibm.net
70407.534@compuserve.com