From: si-units-list@si-units (si-units-digest)
To: si-units-digest@si-units
Subject: si-units-digest V1 #11
Reply-To: si-units-list@si-units
Sender: si-units-list@si-units
Errors-To: si-units-list@si-units
Precedence: bulk
si-units-digest Tuesday, October 29 2002 Volume 01 : Number 011
In this issue:
SI Units Project: SI Units: More about containers with diffent types
Re: SI Units Project: HELLO AGAIN SI UNITS
SI Units Project: Declaring the return type for a template operator
----------------------------------------------------------------------
Date: Fri, 27 Sep 2002 22:28:01 -0700
From: Pete and Lana Klier
Subject: SI Units Project: SI Units: More about containers with diffent types
As it seems virtually impossible to automatically instantiate a template for
T op U and get the correct result type, perhaps the following compromise
can be used (this works): Create an operation that has 3 types as templates
T, U, and V where V is the result of T op U for the operation in question.
With a little bit of manual instantiation (or a better compiler) it may be
possible
to create the type system that is relatively complete. Hopefully, if
installed
in our system, cast-operators and the like would prevent the need for
instantiating
a quadratic number of types.
I am using "complex" and "normalized complex" as the demonstration contained
types, and for purposes of illustration, "vector" as the container. I
will wait
for someone who can do better! Otherwise, I propose installing something like
this into the SI type system and playing with the additional opportunities
that arise
from cast operators that I cannot do with the system-given "vector" (actually
it
would be nice to derive a class from "vector" and see what we can do with
cast operators in a system like this).
Please let me know if this strawman is an acceptable approach to pursue, or
if we can do better. "vector" and "complex" are from the standard libraries.
(scroll past ncmplx.h to get to the next one)
- ---------- ncmplx.h ------------------------------------------------------
#ifndef __NCMPLX__
#define __NCMPLX__
#include
#include
template class ncmplx : public complex
{
private:
// preconditions: re and im MUST be have a sum-squares of 1
ncmplx(const T &re, const T &im) : complex(re,im)
{
}
ncmplx(const complex &rhs) : complex(rhs) {}
public:
class normalize {};
ncmplx(const ncmplx &rhs) : complex(rhs) {}
// operator + is inherited
ncmplx(const complex &c,normalize n) : complex(c)
{
(*this) /= sqrt(real()*real() + imag()*imag());
}
ncmplx operator * (const ncmplx &rhs) const
{
return (const ncmplx &) (complex(*this) * rhs);
}
ncmplx operator ~ ( void ) const
{
return ncmplx(real(), - imag());
}
ncmplx operator / (const ncmplx &rhs) const
{
return (*this) * ~rhs;
}
template ncmplx cexp (const angle_t &shift)
{
return ncmplx(cos(shift),sin(shift));
}
template ncmplx operator << (const angle_t &shift)
{
return (*this) * cexp(shift);
}
};
template ncmplx operator /
(const complex &lhs, ncmplx::normalize N)
{
return ncmplx(lhs,N);
}
#endif
- --------------------------typeof3.cc--------------------
/* -*- compile-command: "g++ -o typeof3 typeof3.cc" -*- */
#include
#include
#include
#include "ncmplx.h"
typedef complex cmplx;
typedef ncmplx norml;
namespace binary_templates
{
template vector AddVecs (const vector
&t_,const vector__ &u_)
{
vector result;
for(int i = 0; i < t_.size(); i++)
result.push_back(t_[i] + u_[i]);
return result;
}
template vector MulVecs (const vector
&t_,const vector____ &u_)
{
vector result;
for(int i = 0; i < t_.size(); i++)
result.push_back(t_[i] * u_[i]);
return result;
}
template vector DivVecs (const vector
&t_,const vector____ &u_)
{
vector result;
for(int i = 0; i < t_.size(); i++)
{
const V temp = (t_[i] / u_[i]);
result.push_back(temp);
}
return result;
}
}
// In real life, cast operators and ctors (members) would help promote
// things like vector to vector automatically
// or it would be relatively automatic
vector operator + (const vector &lhs,const vector &rhs)
{
return binary_templates::AddVecs (lhs,rhs);
}
vector operator + (const vector &lhs,const vector &rhs)
{
return binary_templates::AddVecs (lhs,rhs);
}
vector operator * (const vector &lhs,const vector &rhs)
{
return binary_templates::MulVecs(lhs,rhs);
}
vector operator / (const vector &lhs, const vector &rhs)
{
return binary_templates::DivVecs(lhs,rhs);
}
vector operator / (const vector &lhs,const vector &rhs)
{
return binary_templates::DivVecs(lhs,rhs);
}
int main (int argc, char **argv)
{
vector vcd;
vector vnd;
cmplx j(0,-1);
for(int i = 0; i < 10;i++) vcd.push_back(j*i);
for(int i = 0; i < 10;i++)
vnd.push_back(norml(j*(i+1)+5,norml::normalize()));
vector result;
vector vndsquares;
result = vcd + vnd;
vndsquares = vnd * vnd;
for(int i = 0; i < 10; i++)
{
cout << vcd[i] << " + " << vnd[i] << " ==> " << result[i] << "\n";
cout << vnd[i] << "^2" << " ==> " << vndsquares[i] << "\n";
}
return 0;
}
- ---------------------------------------------------------
Watts, Simon (UK) wrote:
> > 1. Smooth interoperation of SI units types with different underlying
> > numeric types, for example:
> > Resistance R_C = ...;
> > Resistance R_r = ...;
> > Resistance xyz = R_r + R_c; // note the ordering
>
> There is a discussion of this in Todd Veldhuizen's paper "Techniques for
> Scientific C++" [http://osl.iu.edu/~tveldhui/papers/techniques/]
> (sec. 1.8.2). Although this requires some infrastructure to be set up
> and maintained for various types.
>
> Thinking also about the matrix example: [M,N]*[N,P] -> [M,P], perhaps
> this is another reason to phrase our operators as expression templates?
> Such that the evaluation of "R_r + R_c" only takes place on conversion
> to type Resistance, when the desired return type is known.
>
> This has the added advantage of (possibly) supporting datatype whose
> operations are themselves defined in terms of expression templates.
>
> > 2. Good printing of the types, up to and including an algorithm (or
>
> I have had thoughts on this, an outline of which is in the "TODO" file
> in my archive. Started to re-work the class library according to that
> outline, but not had time to progress it.
>
> I am assuming that we are using stream manipulators, and have a catalogue
> mapping names (long and short, narrow and wide forms) to dimension traits.
> The job is then to break down the dimensionality of the quanitity being
> printed to give meaningful unit names.
>
> I did write a bit more (inc code re-working) a while ago, but did not
> finish it. I will look it up and post it when I get a chance.
>
> There are lots of options in any algorithm to determine printable units
> though.
>
> > 3. Good compile-time error messages, such as "can't add ohms
> > per meter squared to volts"
>
> V hard to achive in a generic fashion. Unless we wrote a perl script to
> post-process compiler output? (cf. similar tools for STL). This would
> be very dependant on the compiler and the state of the library.
>
> > 4. Usability testing, make sure everyone is happy with the results.
>
> SAW.
------------------------------
Date: Thu, 10 Oct 2002 19:44:57 -0700
From: Pete and Lana Klier
Subject: Re: SI Units Project: HELLO AGAIN SI UNITS
Busy as ever, in my "spare time" I have been playing with a few variations.
If anyone has any brilliant ideas, please post them. I am working this,
though,
but things change too fast for me to write anything up yet.
Watts, Simon (UK) wrote:
> > 1. Smooth interoperation of SI units types with different underlying
> > numeric types, for example:
> > Resistance R_C = ...;
> > Resistance R_r = ...;
> > Resistance xyz = R_r + R_c; // note the ordering
>
> There is a discussion of this in Todd Veldhuizen's paper "Techniques for
> Scientific C++" [http://osl.iu.edu/~tveldhui/papers/techniques/]
> (sec. 1.8.2). Although this requires some infrastructure to be set up
> and maintained for various types.
>
> Thinking also about the matrix example: [M,N]*[N,P] -> [M,P], perhaps
> this is another reason to phrase our operators as expression templates?
> Such that the evaluation of "R_r + R_c" only takes place on conversion
> to type Resistance, when the desired return type is known.
>
> This has the added advantage of (possibly) supporting datatype whose
> operations are themselves defined in terms of expression templates.
>
> > 2. Good printing of the types, up to and including an algorithm (or
>
> I have had thoughts on this, an outline of which is in the "TODO" file
> in my archive. Started to re-work the class library according to that
> outline, but not had time to progress it.
>
> I am assuming that we are using stream manipulators, and have a catalogue
> mapping names (long and short, narrow and wide forms) to dimension traits.
> The job is then to break down the dimensionality of the quanitity being
> printed to give meaningful unit names.
>
> I did write a bit more (inc code re-working) a while ago, but did not
> finish it. I will look it up and post it when I get a chance.
>
> There are lots of options in any algorithm to determine printable units
> though.
>
> > 3. Good compile-time error messages, such as "can't add ohms
> > per meter squared to volts"
>
> V hard to achive in a generic fashion. Unless we wrote a perl script to
> post-process compiler output? (cf. similar tools for STL). This would
> be very dependant on the compiler and the state of the library.
>
> > 4. Usability testing, make sure everyone is happy with the results.
>
> SAW.
------------------------------
Date: Mon, 28 Oct 2002 20:49:06 -0800
From: Pete and Lana Klier
Subject: SI Units Project: Declaring the return type for a template operator
After much weeping and gnashing of teeth, I stumbled across something
that might work for us.
Please let me know if it's worth pursuing and installing in the SI units
system.
The problem is to declare the return type for a container +
container____ as container
wherever T + U yields V.
Here's the top level of one solution (using vector for the container
class) :
- --------------vecops.h---------------------------------
#ifndef __VECOPS__
#define __VECOPS__
#include
#include "binoptypes.h"
template vector::Type> operator +
(const vector &lhs,const vector____ &rhs)
{
vector::Type> Result;
for(int i = 0; i < lhs.size();i++) Result.push_back(lhs[i] + rhs[i]);
return Result;
}
template vector::Type> operator *
(const vector &lhs,const vector____ &rhs)
{
vector::Type> Result;
for(int i = 0; i < lhs.size();i++) Result.push_back(lhs[i] * rhs[i]);
return Result;
}
#endif
- ------------------------------------------------
the key here is the template classses SumType and ProdType
which have an internal
"Type" field.
How do you implement such a thing for disparate types? The key is
partial template instantiation
and metaprogramming.
On my compiler I had to settle for this, which can get fairly verbose as
you need to echo each
pair of types that you will be using, but using variations or a better
compiler can improve on this.
The key thing to note is that although the implementation of
SumType may vary, its
interface will not. On some compilers it may be possible to use the
"typeof" macro.
- ---------------binoptypes.h------------------------------------------
#ifndef __BINOPTYPES__
#define __BINOPTYPES__
#include "../ncmplx.h"
typedef complex dcmplx;
typedef ncmplx ndcmplx;
template class SumType { public: typedef void Type;
};
template class ProdType{ public: typedef void Type;
};
template<> class SumType { public: typedef dcmplx
Type; };
// The following specializations should not be necessary
template<> class SumType { public: typedef dcmplx
Type; };
template<> class SumType { public: typedef dcmplx
Type; };
template<> class SumType { public: typedef dcmplx
Type; };
template<> class ProdType { public: typedef dcmplx
Type; };
// The following specializations should not be necessary
template<> class ProdType { public: typedef dcmplx
Type; };
template<> class ProdType { public: typedef dcmplx
Type; };
// The actual specialization of interest
template<> class ProdType { public: typedef ndcmplx
Type; };
#endif
- ----------------------------------
where ncmplx is a derived class of cmplx
if I were willing to make the "root" declaration anything but "void" and
assume, by default,
that T op U returns a T, for example, then only the exceptions to the
general rule would
be necessary. Furthermore, a smarter compiler would be able to figure
out that it should
instantiate SumType every time it sees a one of the
specializations such as
SumType.
It is probably possible to improve the implementation in other ways, and
the implementations
are likely to be compiler-dependent. For instance, someone might figure
out a way to put
template classes such as "complex" into the partial instantiations
for all T -- I have a feeling
that this is easy if one is willing to play with it a little bit.
All that being said, this is probably the only way to do it.
Please let me know if this is an acceptable starting point for further
work on SI units, or
if there are any better ideas! Thanks,
Pete
- ------------------------typeof7.c++-----------------------------------------
// -*- compile-command: "g++ -o typeof7 -I.. typeof7.cc" -*-
#include "binoptypes.h"
#include "vecops.h"
#include
#include
#include
int main(int argc, char **argv)
{
SumType::Type *foo = 0; // interpret as void *
assert(typeid(foo) == typeid(void *));
SumType::Type C0(4.5,7.2); // interpret as dcmplx
SumType::Type C01(6.7,8.9); // interpret as dcmplx as
well
assert(typeid(C0) == typeid(dcmplx));
ProdType::Type C1(6.3,10.0); // dcmplx
ProdType::Type C2(0.5,7.2); // dcmplx
ProdType::Type C3(0.5,7.2); // dcmplx
ProdType::Type N0 = C01 / ndcmplx::normalize(); //
ndcmplx
assert(typeid(C1) == typeid(dcmplx));
assert(typeid(C2) == typeid(dcmplx));
assert(typeid(C3) == typeid(dcmplx));
assert(typeid(N0) == typeid(ndcmplx));
vector VN0;
vector VN1;
vector VN01 = VN0 * VN1;
vector VC0;
// vector BAD = VC0 * VN0; // will not compile
vector VC1 = VC0 * VN0;
vector VC2 = (VC0 + VN0) * VN1;
vector VN02 = VN0 * VN1 * VN01;
assert(typeid(VN0) == typeid(VN0 * VN1));
assert(typeid(VC1) == typeid(VC0 * VN0));
assert(typeid(vector) == typeid((VC0 + VN0) * VN1));
cout << "typeof7 -- OK\n";
}
------------------------------
End of si-units-digest V1 #11
*****************************
__