|
Ublas : |
Subject: [ublas] Questions on the internal design of Boost.uBLAS
From: Sebastian Gesemann (s.gesemann_at_[hidden])
Date: 2010-10-29 07:00:07
Hello!
I'm currently in the process of building a library on top of uBLAS. I
also started using expression templates for special vector expressions
that carry additional information ("vectorad expression", ad =
automatic differentiation, stores partial derivatives). The code works
so far. But I just noticed that I did do something differently
compared to how expression templates are handled in uBLAS -- hence
this question.
My operator+ which takes two "vectorad expressions" returns an
expression object that stores two pointers to the argument expression
objects. Storing pointers is desirable in case the argument was an
lvalue "vectorad container". This is how expensive copies/temporaries
are avoided after all. But I also store pointers in other cases which
might be a dangerous thing to do (possible life-time issues, dangling
pointers).
After checking the Boost.uBLAS code I noticed that every vector or
matrix expression offers two additional typedefs: closure_type and
const_closure_type. These don't seem to be mentioned anywhere in the
documentation but it seems that they are required. Instead of storing
pointers or references to other expression objects, their respective
closure_type objects are stored. In case of vector containers this
will be a vector_reference. But if the expression object is NOT a
vector container, its closure_type is its self_type. I wonder why this
is. What was the motivation to do that? Was it to minimize dangling
pointers/references? In the light of C++0x people might be tempted to
use auto like this:
auto sum = 3*vector1 + 4*vector2;
where sum will be a vector expression (but not a container) which
indirectly refers to vector1 and vector2 but nothing else. It contains
all non-container expressions. So that's a good thing. No danling
pointers here. But if we use a function that returns a vector
container by value
ublas::vector<double> source();
...
auto sum = 3*source() + 4*vector2;
the expression object will contain a dangling reference because the
closure_type of ublas::vector<..> is a vector_reference and the
returned object will be destroyed after the full expression has been
evaluated. So, this closure_type approach doesn't really solve the
problem, it only minimizes the occurence of dangling
pointers/references. Is that what the authors intended or does it
serve another purpose as well?
I suppose that in C++0x we could use rvalue references to detect the
value category of an expression object. If the expression object is an
lvalue vector container, we could store a pointer and in the other
cases move the expression object into a new expression object. That
should get rid of the danling reference problem in the second example.
Another question is regarding "temporary_type" we can find as nested
typedefs in expression classes. I also don't find anything about this
in the documentation. Is this considered an implementation detail or
part of the vector expression concept? I'm asking because I could use
such a type information to turn an expression object into a container
so that the expression is not evaluated many times. But I would also
like to avoid this in case evaluating the expression is cheap. It
seems all the expression types expose a constant expression named
"complexity". Can somebody explain what this is about? Even if it's an
implementation detail, there might be something to learn about for my
own expression templates.
Thanks in advance!
Sebastian