# Factoring

This topic is 4157 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

I'm writing a small library right now. The objective is to allow the user to manipulate objects which are a function of time transparently. For instance, I would write:
time::value<double> polynomial = 2.0 * time::t * time::t + 3.0 * time::t + 1.0;
time::value<double> linear = 3.0 * polynomial + 2.0;
std::cout << linear(time::now) << std::endl;
This library works based on expression templates. This requires me to define, for every operator, three versions: two with a single time-based member, and one with two time-based members. The code for all of them is extremely similar: the differences have been underlined:
template<typename T,typename LI,typename RI>
struct Sum
{
LI l;
RI r;
Sum(const LI & l, const RI & r) : l(l), r(r) { }
T eval(time t) { return l.eval(t) + r.eval(t); }
};

template<typename RI, typename T>
Wrap< Sum<T,Const<T>,RI>,T>
operator+(const T & left, const Wrap<RI,T> & right)
{ return Sum<T,Const<T>,RI>(left,right.impl); }

template<typename LI, typename T>
Wrap< Sum<T,LI,Const<T> >,T>
operator+(const Wrap<LI,T> & left, const T & right)
{ return Sum<T,LI,Const<T> >(left.impl,right); }

template<typename LI, typename RI, typename T>
Wrap< Sum<LI,RI>,T>
operator+(const Wrap<LI,T> & left, const Wrap<RI,T> & right)
{ return Sum<T,LI,RI>(left.impl,right.impl); }
So, first question: how to reduce the amount of repetitiveness, in order to generate all operators with as little code as possible? Second question: how do I handle operations which have different argument types (such as scalar * vector) ?

##### Share on other sites
I'm no expert on expression templates, so I won't speak directly. But I believe it was Jyk who had created a math library based on template expressions. Hopefully he'll be along, or perhaps you could PM him and ask him to reply.

##### Share on other sites
Maybe this is speaking to my own ignorance, but is there a particular reason you have to take the right side argument by reference? Are you expecting to use complex types with this? Barring that, would it be too cumbersome for the user to create a temporary variable? There are a lot libraries that require this kind of behavior (glLightfv, for instance). The point being that you're already going to have to declare each operator twice (operator+ and then operator+=) so why make it six times? Either there's no reason to have two versions with one variable and one with two variables, or you're stuck with some amount of redundancy.

Even if you boil it down to a very clever common factor, you're still going to have to implement each one's unique portions individually. Is the benefit of having a common factor more than the cost in effort and readability?

Even still, I wouldn't have Sum<type1,type2> along with Sum<type>. It seems inconsistent.

As far as #2, I wouldn't worry about type safety; it will add too much bloat to your function, and templates tend to be bad enough. If the programmer doesn't know better than to try Divide<ComplexNumber,Grapefruit>, then they should be hit over the head with a brick anyways.

##### Share on other sites
This seems like a job for Boost.PP.

I can't see how you could reduce the code required for the specialisations.

##### Share on other sites
Quote:
 Original post by erissianMaybe this is speaking to my own ignorance, but is there a particular reason you have to take the right side argument by reference?

I see no reason not to take it by constant reference...

Quote:
 The point being that you're already going to have to declare each operator twice (operator+ and then operator+=) so why make it six times? Either there's no reason to have two versions with one variable and one with two variables, or you're stuck with some amount of redundancy.

Actually, that would be only three times each (W op T, T op W, W op W) for binary operators without side-effects, which can then be used to implement each side-effect operator in one strike only.

Quote:
 Even if you boil it down to a very clever common factor, you're still going to have to implement each one's unique portions individually. Is the benefit of having a common factor more than the cost in effort and readability?

Ideally, the library would be used, not read. So, common-factor code such as the following would certainly not bother me:
showOperators "timefunc.hpp" {  binary = [     ("*","mul"); ("+","add"); ("/","div"); ("-","sub");    ("<<","shl"); (">>","shr"); ("%","mod") ];  compare = [    ("==","eq"); ("<","lt"); (">","gt"); ("<=","le");     (">=","ge"); ("!=","ne") ];  unary = [     ("!","neg"); ("*","deref") ]}

Quote:
 Even still, I wouldn't have Sum along with Sum. It seems inconsistent.

I would be using Sum<LeftImpl,RightImpl,ReturnType> anyway.

Quote:
 As far as #2, I wouldn't worry about type safety; it will add too much bloat to your function, and templates tend to be bad enough. If the programmer doesn't know better than to try Divide, then they should be hit over the head with a brick anyways.

It's not about type safety. It's about operator*(A,B) returning either type A or type B depending on which of A and B is the scalar or vector type. I have to somehow know what the return type is.

##### Share on other sites
Quote:
 So, first question: how to reduce the amount of repetitiveness, in order to generate all operators with as little code as possible?

I would use a local macro for this, just #define it for the scope of the decelerations taking two parameters and #undef it at the end, you will have to be careful with operator, though since passing commas to macros is at best unwieldy. (On a side note I believe this is the approach taken by boost.lambda)

Quote:
 Second question: how do I handle operations which have different argument types (such as scalar * vector) ?

If its an option then I would use boost::typeof otherwise you could use try using boost::result_of or something similar but it relies on the user following certain conventions.

boost::typeof example:
template<typename L, typename R>Wrap< BOOST_TYPEOF_TPL(*reinterpret_cast<L*>(0) * *reinterpret_cast<R*>(0)), L, R>operator*(L lhs, R rhs){    // ...}

1. 1
2. 2
3. 3
Rutin
15
4. 4
5. 5

• 9
• 9
• 9
• 11
• 11
• ### Forum Statistics

• Total Topics
633679
• Total Posts
3013300
×