These template things are good...right?

  • Thread starter Steven T. Hatton
  • Start date
E

Evan

[snip]
IMO, boost::lambda is a path to becoming an academic niche language.

I'm in academia, so that's okay. ;-)

On a more serious note, why? Most compliers now are good enough to
support it. Is the implementation poor? Is it slow or bloaty or
something? It seems like it would be useful for a fair number of times;
I don't see why you wouldn't want to use it.

Evan
 
S

Steven T. Hatton

Evan said:
[snip]
IMO, boost::lambda is a path to becoming an academic niche language.

I'm in academia, so that's okay. ;-)

On a more serious note, why? Most compliers now are good enough to
support it. Is the implementation poor? Is it slow or bloaty or
something? It seems like it would be useful for a fair number of times;
I don't see why you wouldn't want to use it.

It's the same sentiment that struck me when I looked at the example on page
100 of TC++SL today:
pos = find_if(coll.begin(), coll.end(),
compose_f_gx_hx(logical_or<bool>(),
bind2nd(equal_to<int>(),25),
bind2nd(equal_to<int>(),35)));

I looked at that and thought to myself: 'didn't we already have a
programming language?'

If you want an esoteric programming language try this:

\[Rho] = 10;
\[Theta] = \[Pi]/5;

basis[\[Theta]_, \[Rho]_] :=
Arrow @@ Displacement[#, \[Rho], o] & /@ {\[Theta], \[Theta] + \[Pi]/2}

\[Epsilon][\[Theta]_, \[CurlyEpsilon]_, \[Gamma]_] /;
Mod[\[Theta], \[Gamma]] == 0. := \[CurlyEpsilon]
\[Epsilon][\[Theta]_, \[CurlyEpsilon]_, \[Gamma]_] /;
Mod[\[Theta], \[Gamma]] != 0. := 0

tac[\[Theta]_, \[Rho]_, \[Gamma]_] /; Mod[\[Theta], \[Gamma]] == 0. :=

Text[\[Pi]/2 - \[Theta]
, \[Rho] UR[\[Theta]]
, -1.5UR[\[Theta]]
, TextStyle -> {FontSize -> 16, FontWeight -> "Bold"}
]

tac[\[Theta]_, \[Rho]_, \[Gamma]_] /; Mod[\[Theta], \[Gamma]] != 0. := {}
 
K

kwikius

Steven said:
Nothing wrong with creating a new class to perform an operation which can
easily be done in one line of code, and not even eliminating the original
line of code? There are times when it makes sense to use a visitor pattern
with function objects, but there are also many times when it serves no
other purpose than the complicate the code.

Yeah!

Hows about this. Grabs fixed size arrays and turns them into containers
and does compile time loop unrolling ;-)

(Uses later version of my Quan lib which I havent got round to updating
on sourceforge for a while... whatever)

Can also work on arbitrary transform matrices of course, but lets keep
it simple...

#include <quan/two_d/out/vect.hpp>
#include <quan/out/angle.hpp>
#include <quan_matters/src/constant.cpp>
#include <quan_matters/src/angle.cpp>
#include <quan/out/length.hpp>
#include <algorithm>
#include <vector>
#include <boost/static_assert.hpp>
#include <iterator>

// class to adapt a static array ( ar[N])
// for use as a container
// inpired by a recent post on comp.lang.c++.moderated
// who I will be happy to acknowledge
// if I can find it
template <typename T,int N>
class array_wrapper{
T * data_ ;
public:
const static std::size_t size = N;
array_wrapper(T (&a) [N]): data_(a){}
typedef T * iterator;
typedef T const * const_iterator;
iterator begin() {return data_;}
const_iterator begin()const {return data_;}
iterator end() {return data_ + N;}
const_iterator end()const {return data_ + N;}
template<int N1>
T& at(){ return data_[N1];}
T const & at()const{ return data_[N1];}
};

// compile time loop unrolling impl
template <int N>
struct for_x_in_wrapper;

template <>
struct for_x_in_wrapper<0>{
template <typename ArrayWrapper, typename F>
void operator()(ArrayWrapper& a, F const & f)const
{
f(a.at<0>());
}
};

template <int N>
struct for_x_in_wrapper{
template<typename ArrayWrapper, typename F>
void operator()(ArrayWrapper& a, F const & f)const
{
BOOST_STATIC_ASSERT(( ArrayWrapper::size >= N ) );
for_x_in_wrapper<N-1> prev;
prev(a,f);
f(a.at<N>());
}
};

template <typename T,int N,typename F>
void for_each(array_wrapper<T,N> & ar,F const & f)
{
for_x_in_wrapper<N-1> ff;
ff(ar,f);
}

template <typename T,int N,typename F>
void for_each(T (&ar)[N],F const & f)
{
array_wrapper<T,N> arw(ar);
for_each(arw,f);
}

// functor to rotate a vector
// calls sin, cos once irrespective of
// number of vertices
struct rotate{

rotate(double const & angle)
: cos_theta (std::cos(angle)), sin_theta(std::sin(angle)){}
double cos_theta, sin_theta;
template <typename Vect>
Vect & operator()(Vect & in)const
{
in = in * cos_theta + perp_vector(in) * sin_theta;
return in;
}
};

// functor to translate a vector
template <typename T>
struct translate{

translate(quan::two_d::vect<T> const & trans_in)
: trans(trans_in){}
quan::two_d::vect<T> trans;
template <typename Vect>
Vect & operator()(Vect & in)const
{
return in += trans;
}
};

// combine above
template <typename T>
struct point_rotate{

translate<T> to_origin;
rotate do_rotate;
translate<T> from_origin;
point_rotate(quan::two_d::vect<T> const & translation,double angle)
:
to_origin(-translation),do_rotate(angle),from_origin(translation){}

template <typename Vect>
void operator()(Vect & in)const
{
to_origin(in);
do_rotate(in);
from_origin(in);
}
};

// rotate_by_point adaptor for static arrays
template <typename T, int N, typename Point,typename Angle>
void rotate_by_point(T (&ar)[N], Point const & point, Angle const &
angle)
{
array_wrapper<T,N> arw(ar);
for_each(arw,point_rotate<typename Point::value_type>(point,angle));
}

struct output{
std::eek:stream & os;
output(std::eek:stream & os_in):eek:s(os_in){}
template <typename T>
void operator()(T const & t)const
{
os << t <<'\n';
}
};

int main()
{
std::cout.setf(std::ios_base::fixed,std::ios_base::floatfield);
std::cout.precision(2);
typedef double d;
std::cout << "using 2d vects of doubles\n";
quan::angle::rad angle = quan::angle::pi/2;
using quan::two_d::vect;

vect<d> box[] = {
vect<d>(0,0),
vect<d>(1,0),
vect<d>(1,1),
vect<d>(0,1)
};

for_each(box,output(std::cout));
vect<d> point(2,0);
std::cout << "rotation angle = " << quan::angle::deg(angle) <<'\n';
std::cout << "rotation point = " << point <<'\n';
rotate_by_point(box,point,angle);
for_each(box,output(std::cout));

// works for any type of points...
std::cout << "\n Now using 2d vects of length::mm\n";
typedef quan::length::mm mm;
vect<mm> box_mm[] = {
vect<mm>(mm(0),mm(0)),
vect<mm>(mm(1),mm(0)),
vect<mm>(mm(1),mm(1)),
vect<mm>(mm(0),mm(1))
};

for_each(box_mm,output(std::cout));
vect<mm> point_mm(mm(1),mm(1));
std::cout << "rotation angle = " << quan::angle::deg(angle) <<'\n';
std::cout << "rotation point = " << point_mm <<'\n';
rotate_by_point(box_mm,point_mm,quan::angle::pi/2);
for_each(box_mm,output(std::cout));

return 0;
}

output:

using 2d vects of doubles
[0.00, 0.00]
[1.00, 0.00]
[1.00, 1.00]
[0.00, 1.00]
rotation angle = 90.00 deg
rotation point = [2.00, 0.00]
[2.00, -2.00]
[2.00, -1.00]
[1.00, -1.00]
[1.00, -2.00]

Now using 2d vects of length::mm
[0.00 mm, 0.00 mm]
[1.00 mm, 0.00 mm]
[1.00 mm, 1.00 mm]
[0.00 mm, 1.00 mm]
rotation angle = 90.00 deg
rotation point = [1.00 mm, 1.00 mm]
[2.00 mm, 0.00 mm]
[2.00 mm, 1.00 mm]
[1.00 mm, 1.00 mm]
[1.00 mm, 0.00 mm]

regards
Andy Little
 
N

Noah Roberts

Evan said:
[snip]
IMO, boost::lambda is a path to becoming an academic niche language.

I'm in academia, so that's okay. ;-)

On a more serious note, why? Most compliers now are good enough to
support it. Is the implementation poor? Is it slow or bloaty or
something? It seems like it would be useful for a fair number of times;
I don't see why you wouldn't want to use it.

I've considered completely ignoring its existance. Two
problems...first it can be quite abused so that your C++ code looks
more like lisp. I don't dig that too much but it is remenicient of
Java's ability to create classes on the fly inside of function calls.
Second, it has some technical problems that make it much less pleasant
to use than boost::bind.

It can be useful though to create very short statements inside of an
algorithm call without the need to create a function, functor, or
implement the algorithm by hand...and algorithms go beyond debating
whether to use for_each or a for loop.
 
S

Steven T. Hatton

kwikius said:
Yeah!

Hows about this. Grabs fixed size arrays and turns them into containers
and does compile time loop unrolling ;-)

My compiler will do that for me without templates.
(Uses later version of my Quan lib which I havent got round to updating
on sourceforge for a while... whatever)

Can also work on arbitrary transform matrices of course, but lets keep
it simple...

[snip]

I'll have to give that further thought. I have to wonder how much of that
is dependent on the fact that the code is template code. I hope this is
the right version. I haven't looked at this in quite some time:

template <typename T>
struct Inverse_T {
Inverse_T(const T& t_=0):_t(&t_) {}
operator T() const { return *_t; };
const T* _t;
};

template <typename T, size_t S>
inline boost::array<boost::array<T, S>, S>
operator*( const boost::array<boost::array<T, S>, S>& lhs,
const boost::array<boost::array<T, S>, S>& rhs ) {

boost::array<boost::array<T, S>, S> ret;
boost::array<boost::array<Inverse_T<T>, S>, S> inv;

for(size_t i = 0; i < lhs.size(); i++) {
for(size_t j = 0; j < lhs.size(); j++) {
inv[j] = Inverse_T<T>(rhs[j]);
}
}

for(size_t i = 0; i < lhs.size(); i++) {
for(size_t j = 0; j < lhs.size(); j++) {
ret[j]=0;
for(size_t ii=0; ii < lhs.size(); ii++) {
ret[j] += lhs[ii] * inv[j][ii];
}
}
}
return ret;
}
 
K

kwikius

Steven said:
My compiler will do that for me without templates.

Sure, but it won't do a worse job with more compile time info and will
often do a better one.

I'll have to give that further thought. I have to wonder how much of that
is dependent on the fact that the code is template code. I hope this is
the right version. I haven't looked at this in quite some time:

<..>

You'll probably exceed some compiler limit if you tried to unroll all
that of course, but all is not lost, just do it for the inner loop(s).
Not sure if that will work or not.. whatever Here is stuff I was
working on recently:

http://tinyurl.com/v9qwm


These matrices are typed ie tuples, which means that you can put
arbitrary compile time 'static' values particularly for zeros and ones,
which optimises away many calcs to no-ops. This can result in a
reduction of 1/5 to 1/10 of the calcs on runtime values. There is a
lot of potential there to rock 3D games etc, but I don't have time to
work on it properly ..

Needs a compiler like VC8.0 or gcc4.0 . VC7.1 runs out of steam on this
stuff.

regards
Andy Little
 
S

Steven T. Hatton

Noah said:
[snip]
As you said, there is boost::lambda

IMO, boost::lambda is a path to becoming an academic niche language.

I'm in academia, so that's okay. ;-)

On a more serious note, why? Most compliers now are good enough to
support it. Is the implementation poor? Is it slow or bloaty or
something? It seems like it would be useful for a fair number of times;
I don't see why you wouldn't want to use it.

I've considered completely ignoring its existance. Two
problems...first it can be quite abused so that your C++ code looks
more like lisp. I don't dig that too much but it is remenicient of
Java's ability to create classes on the fly inside of function calls.

That, IMO, is actually superior to boost::lambda. At least the code is, for
the most part, "normal".
Second, it has some technical problems that make it much less pleasant
to use than boost::bind.

It can be useful though to create very short statements inside of an
algorithm call without the need to create a function, functor, or
implement the algorithm by hand...and algorithms go beyond debating
whether to use for_each or a for loop.

I'm trying to figure out if it would be possible to create a generic
non-template abstract Function class that can be used as a base class for
local functors which could be passed to STL algorithms with a static_cast,
and still get the behavior of the derived functor class. I haven't tried
writing out the code. It's just an idea I've been meaning to try.
 
P

peter koch

Steven T. Hatton skrev:
Evan said:
[snip]
As you said, there is boost::lambda

IMO, boost::lambda is a path to becoming an academic niche language.

I'm in academia, so that's okay. ;-)

On a more serious note, why? Most compliers now are good enough to
support it. Is the implementation poor? Is it slow or bloaty or
something? It seems like it would be useful for a fair number of times;
I don't see why you wouldn't want to use it.

It's the same sentiment that struck me when I looked at the example on page
100 of TC++SL today:
pos = find_if(coll.begin(), coll.end(),
compose_f_gx_hx(logical_or<bool>(),
bind2nd(equal_to<int>(),25),
bind2nd(equal_to<int>(),35)));

But boost lambda was made exactly to avoid the code above. You simply
write:

pos = find_if(coll.begin(), coll.end(),_1 == 25 or _1 == 35);

So your argumentation makes no sense at all.

/Peter
[snip]
 
S

Steven T. Hatton

kwikius said:
Sure, but it won't do a worse job with more compile time info and will
often do a better one.



<..>

You'll probably exceed some compiler limit if you tried to unroll all
that of course, but all is not lost, just do it for the inner loop(s).
Not sure if that will work or not.. whatever Here is stuff I was
working on recently:

http://tinyurl.com/v9qwm


These matrices are typed ie tuples, which means that you can put
arbitrary compile time 'static' values particularly for zeros and ones,
which optimises away many calcs to no-ops. This can result in a
reduction of 1/5 to 1/10 of the calcs on runtime values. There is a
lot of potential there to rock 3D games etc, but I don't have time to
work on it properly ..

Needs a compiler like VC8.0 or gcc4.0 . VC7.1 runs out of steam on this
stuff.

regards
Andy Little

I need to find time to review all this. What the example I posted is
supposed to do is multiply two arbitrarily sized conferment tensors. I
don't guarantee that it works correctly, though I believe it does. I just
posted it to show that I have done a bit with templates, and understand
that they can be useful.

I am also aware that they are not the only viable approach to creating
highly adaptive libraries. There's nothing intrinsic to C++ which prevents
the use of an object-based "core" library similar to what Java uses.
AAMOF, C++ may even have some significant advantages when it comes to
creating and using such a library.

I understand that there are trade-offs in taking such an approach. I can't
say that I clearly understand what those trade-offs are. I wonder if
anybody does. Unfortunately the question often devolves to little more
than zealotry.
 
K

kwikius

Steven said:
I understand that there are trade-offs in taking such an approach. I can't
say that I clearly understand what those trade-offs are. I wonder if
anybody does. Unfortunately the question often devolves to little more
than zealotry.

I think the choice is simplified by asking the question:

Is such and such information available (and useable) at compile time?

If it is try to do that computation at compile time as possible, which
means generally using templates. That is my approach.

IOW templates represent polymorphism, but at compile time, while
virtual functions and so on represent polymorphism at runtime. Given
the choice I generally go for the compile time polymorphism option. The
only potential trade off may be code duplication, however once you get
to a stage where eveything is inlined and optimised away ( which
templates help with as its basically compiler friendly info) even this
is arguable.

regards
Andy Little
 
S

Steven T. Hatton

kwikius said:
I think the choice is simplified by asking the question:

Is such and such information available (and useable) at compile time?

If it is try to do that computation at compile time as possible, which
means generally using templates. That is my approach.

There's also a question of whether saving a few clock cycles is worth a
couple hours of programming. Sometimes it is, sometimes it isn't. Of
course there's noting to say templates aren't the easier solution in some
cases.
IOW templates represent polymorphism, but at compile time, while
virtual functions and so on represent polymorphism at runtime. Given
the choice I generally go for the compile time polymorphism option. The
only potential trade off may be code duplication, however once you get
to a stage where eveything is inlined and optimised away ( which
templates help with as its basically compiler friendly info) even this
is arguable.

For me the trade-off is usually in flexibility. Scene graphs and windowing
hierarchies are the examples which come to mind. In both situations it's
convenient to stick similar things together in arbitrary arrangements which
may be determined at run time. I know flow control via RTTI is typically
frowned upon by people whose opinions I hold in high regard. Nonetheless,
it is the essence of how many scene graph technologies are implemented.
IRIS Inventor is the example used in GoF for a visitor pattern implemented
in C++. I believe that is from the same SGI that brought us the STL.
 
K

kwikius

Steven said:
For me the trade-off is usually in flexibility. Scene graphs and windowing
hierarchies are the examples which come to mind.

One doesnt preclude the other of course::

struct node{virtual~node();};

struct branch : virtual node {};

template <typename Data>
struct leaf : virtual node{};

regards
Andy Little
 
C

Chris Theis

Steven T. Hatton said:
Victor said:
Steven said:
[.. gripe, gripe ..]
Will a programmer who knows how to use templates very well be
significantly more productive than one who sticks to the traditional
OOP aspects of C++?

In what team, doing what? IOW, yes, if using templates is what is
required from that programmer. No, if using templates is not in
his/her everyday activity.

However, consider what you yourself wrote: while reading the book on
templates you have discovered how much you didn't know of the "core"
C++ language. It is conceivable that the "one who sticks to the
traditional OOP aspects of C++" is actually not as knowledgeable
where the "core" C++ is concerned than one thinks one is.

Can you learn to read a human language and claim proficiency in it
without, say, being able to talk? There were people who were very
versed in a foreign language without knowing how the words they read
or write are pronounced. Are they missing much? I don't know. How
do you actually measure that? What I can say is that their knowledge
is incomplete and very artificial. They are definitely missing some
jokes based on how words are pronounced... Of course, if they stick
to the traditional scientific language, they can even be productive
in an environment where they don't need to talk or understand speech
or understand jokes based on word play.

Does that answer your concerns?

V
Not really. But that's probably because my concerns are such that there
really is no answer than "there's only one way to find out". I guess I'm
feeling discouraged by the number of idiosyncracies I have learned lurk
under the guise of templates. The way I see things right now, template
programming is kind of like programming the compiler to write code rather
than simply using the compiler to compile the code we write. It requires
a
fairly good understanding of the compilation stages in order to do
anything
beyond the basics. The knowledge and understanding will likely be useful
regardless of whether templates themselves prove useful to me.

I do have to ask myself, however, what do I get out of creating templates
which adapt to the type I instantiate them with in order to create
functions or classes? How much /reuse/ am I really likely to get out of a
template? One exercise I undertook several months ago was to create my
own
vector math library using templates. You helped me work out some of the
trickier parts, you may recall. So now I have a vector math library which
is some ways is better than any others I have seen. I can instantiate the
templates with float or double. For what I'm doing, I almost always use
float because its faster, leaner, and sufficiently precise.

How much reuse you're gonna get out of using templates cannot be answered in
such a general way. They will certainly not be the solution to all your
problems and sometimes they might be overused. You will find a large group
of people programming in C++ without really having a clue about templates.
They will also manage to do their task in one way or another, but that is
exactly the point. Templates allow you to do things that are not as easily
(or sometimes not at all) accomplished as doing them without the use of
templates.

In the case of your vector math lib it is arguable whether it was worth the
effort and that really depends. Still, you now have a tool at your hand
which can switch from float to double precision in no time and one day you
might need that precision.
But let's forget for a moment the obvious applications of templates in the
implementation of collections. There are things like concept checking
(probably quite important and useful for library designers), several design
patterns and also meta-template optimized structures that would not work or
only with limitations, without templates. Yet, I do agree that these topics
are quite advanced and require a sound knowledge of how a C++ compiler
works.

Thus, one should first acquire a profound knowledge of the "core" of the
language before considering to venture off into the area of advanced
template programming. But you can somehow compare it to a "real human"
language - it's one thing to read & write English at a level where you can
communicate with others and find your way around. However, reading &
understanding good literature will increase your appreciation of the subtle
nuances of the language and certainly improve your proficiency in the long
run.

Cheers
Chris
 
S

Steven T. Hatton

kwikius said:
One doesnt preclude the other of course::

struct node{virtual~node();};

struct branch : virtual node {};

template <typename Data>
struct leaf : virtual node{};

regards
Andy Little
I have to refine my thinking regarding programming with RTTI. I didn't
realize there was a significant difference between using virtual functions
and using dynamic casts. According to this document, dynamic_cast<> is
very expensive ~5to10 times more expensive than virtual function calls.

http://www.research.att.com/~bs/performanceTR.pdf

That makes me wonder why I see as many dynamic_cast<>s as I do in some code.
What I posted earlier was making the incorrect assumption that the two
methods incurred comparable overhead.
 
E

Evan

Steven said:
There's also a question of whether saving a few clock cycles is worth a
couple hours of programming. Sometimes it is, sometimes it isn't. Of
course there's noting to say templates aren't the easier solution in some
cases.

There's also a question of whether the template approach will let you
detect errors at compile time. One of my favorite TMP examples (and one
of the more complicated examples that I can still understand) is the
one from the book I have on it and the Boost docs where they create a
set of templates that do dimensional analysis at compile time to make
sure that units are done correctly.

It's a rather more complicated approach than doing the same thing
"traditionally", but even if the performance were the same it's
probably worth it because it lets you catch a certain class of errors
during compilation rather than at runtime.

(The utility of the error messages your compiler will give you if you
do make a mistake is another matter...)

Evan
 
K

kwikius

Steven said:
I have to refine my thinking regarding programming with RTTI. I didn't
realize there was a significant difference between using virtual functions
and using dynamic casts. According to this document, dynamic_cast<> is
very expensive ~5to10 times more expensive than virtual function calls.

http://www.research.att.com/~bs/performanceTR.pdf

That makes me wonder why I see as many dynamic_cast<>s as I do in some code.
What I posted earlier was making the incorrect assumption that the two
methods incurred comparable overhead.

Wll you can also use the curiously recurring template pattern. Examples
are Microsoft ATL and WTL on sourceforge.

You can of course combine the whole shebang. templates, virtual
functions and multiple inheritance and CRTP. In the above you might
use a virtual is_branch and is_leaf function so you dont need the
dynamic cast unless it will succeed. And of course in the above you can
have nodes that are both branches and leaves ;-)

regards
Andy Little
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,813
Messages
2,569,696
Members
45,488
Latest member
MohammedHa

Latest Threads

Top