c++ template challenge

Started by
13 comments, last by Codeka 14 years, 6 months ago
Using the same technique as c++'s iostreams you can do things like this: obj << 1 << 2 << 3; To effectively call a function 3 times with each value. obj.Exec(1); obj.Exec(2); obj.Exec(3); This is easy! Can anyone devise a way to transform this: obj << 1 << 2 << 3; To this: obj.Exec(1,2); obj.Exec(3); ie. Pairing every two arguments into a single call. The only solution I can begin to think of would depend on destructors of temporaries, which would mess up the ordering. Anyone have a solution?
Advertisement
struct Pair {  Pair(int a, int b) : a(a), b(b) {}  int a, b;};Foo & operator<<(Foo &f, const Pair & p) {  foo.op(p.a, p.b);  return f;}Foo foo;foo << Pair(1,2) << Pair(2,3);
Thanks for the reply, but the point was for the template to do the pairing for the user. Anything's possible if you can change the caller, but is it possible keeping the same syntax:
obj << 1 << 2 << 3;
Well, you could do something like this (not compiled or tested):
struct Foo {    Foo() : cachedValue(0), valueCached(false) {}    void operator()(int value) {        if (valueCached) {            do_stuff(cachedValue, value);            valueCached = false;        } else {            cachedValue = value;            valueCached = true;        }    }    void do_stuff(int value1, int value2) { /*...*/ }    int cachedValue;    bool valueCached;};Foo& operator<<(Foo& foo, int value) {    foo(value);    return f;}
This could probably be made a bit more elegant by using (e.g.) boost::optional.

Note that the cached value will 'roll over', so if you write (e.g.):
Foo foo;foo << 1 << 2 << 3;foo << 4 << 5 << 6;
The pairs that are processed will be (1,2), (3,4), and (5,6).

Just out of curiosity, what's the actual problem that you're trying to solve?
The cached value idea is closer to what I want, but I didn't mention that the parameters aren't always integers.

The purpose of this was to improve performance.
Suppose you store some data with this overloaded operator, calling some function Store(). Each call to Store() has overhead for the function call, and for ensuring space requirements. So Store(x,y) is less than 75% of the cost of Store(x)+Store(y).

I could cache a void*/byte count, but that's going to fail with local variables if I don't clear the 'cache' prior to losing scope, ie:
fn(obj)
{
if (1)
{
int v=5;
obj << v;
}
}

It's likely there's not a great solution to this, and the performance gain probably isn't worth worrying about. But I thought there might be an elegant solution I was missing.
Hi there,

If performance is your concern and you have to keep the syntax then how about using a macro to break the line up into more efficient code without the users knowledge.

-JonRambo
Just out of curiosity: why would you want to do that? I think "a << 1 << 2 << 3 << 4" implicitly building pairs is confusing. I dislike using operators to such an extent, because the operator wasn't meant for that, it's syntactically confusing.
I agree if the point was to make pairs, it wouldn't be intuitive. But the point was, to make, say 20 function calls under-the-hood instead of 40 effectively doing the same work but with less overhead.
During the construction of the temporaries, you could build up a boost::mpl sequence of the types involved. When the first temporary is destroyed, process all of the "arguments", looking for pairs or N-tuples as required.

I don't know whether it's really worth the effort, though...
Hello,

What you want to do is impossible. The best, closest thing you can do to intercept all the arguments at once and write less code is something like this:

foo _(1,2,3) or foo << _(1,2,3).
First case is a macro doing the same thing as
the second case which is a template function with variable argument number.
from that on you can return a template class to temporarily hold the arguments.


the operator << is a template itself taking that class. It should work but you have to write a hell lot of code and it doesn't worth it.
Edit:
There is another way: but it would requare to place your statement betwin {}
ex: { foo<<1<<2<<3 } this will cause the destruction of the object returned by the first operator << to be called. that object upon calling << can store the argument and return himself. When the destructor is called the operation can be performed on all the arguments.


Raxvan.

This topic is closed to new replies.

Advertisement