Am I reinventing the wheel with this trivial method call forwarding class?
Asked Answered
E

2

5

I just found myself creating a class

template <typename T> struct invoker {
  void operator()(T& it) const {it();}
};

so I could pass an invoker<foo> to something (which isn't under my control) which wants to call invoker<foo>::operator()(foo&) on it repeatedly with different foo instances, to get it to forward those calls to the foo's foo::operator()() method.

I know it's only a few lines, but this seems like the sort of thing which is probably already provided for by STL's functional, or boost::bind somehow. Except I can't see the trick, if there is one. (I'm sure I'm not the first person to use something very like this; does it have a name ?)

Europa answered 15/5, 2012 at 23:41 Comment(8)
i think c++11 has something for this tooAffidavit
For now I'm on g++ 4.4.5; I don't think g++ got lambdas until 4.5. Curious to see a lambda solution if one is posted though; I can live with my invoker class until a g++ version upgrade.Europa
Why wouldn't you call foo::operator() directly? I mean, the class which calls invoker::operator() has to do something like some_invoker(some_foo);, while you could just call it like this: some_foo();. You're adding an unnecessary layer of indirection.Larios
What you're doing is probably the best solution. I don't see bind or lambdas making this any better.Thorvaldsen
I have no idea why the hell you would possibly use this class instead of simply using T as the function type.Dismuke
@DeadMG: (and others) No, that doesn't work: the API of the thing I'm dealing with (TBB's parallel_for_each, to be specific) only lets me pass in one invoker and then calls it in a loop with the foos as successive arguments. That's also why I noted changing that code isn't an option; I could certainly imagine changing it/creating a variant to eliminate the need for invoker if it was.Europa
Oh, wait, never mind, I misread the content of the class. You're talking about iterating over, say, a std::vector<std::function<void()>> and callin them all, right?Dismuke
Bigger example of where/why you need something like this at https://mcmap.net/q/569686/-simplest-tbb-example, in the second example using tbb::parallel_for_each.Europa
K
5

Yeah, you're reinventing the wheel. std::mem_fun_ref does what you want.

std::vector<foo> foos;

...

std::for_each(foos.begin(), foos.end(), std::mem_fun_ref(&foo::operator()));

Alternatively:

std::vector<foo*> foos;

...

std::for_each(foos.begin(), foos.end(), std::mem_fun(&foo::operator()));

Not having to mess with whether your param is ptr or not is one great benefit of boost::mem_fn.

Anything much more complex than that though and you begin running into trouble with the C++03 binders and need something more expressive like boost.bind.

Kersey answered 16/5, 2012 at 0:19 Comment(1)
Just wanted to point out std::mem_fun and std::mem_fun_ref are deprecated for std::mem_fn in C++11.Farthingale
L
6

Well, you can use std::bind, probably boost::bind as well to achieve the same behaviour:

#include <string>
#include <iostream>
#include <functional>

struct foo {
    void operator()() {
        std::cout << "Hallo, im at addr: " << std::hex << this << std::endl;
    }
};

int main() {
    std::function<void(foo*)> fun = std::bind(&foo::operator(), std::placeholders::_1);
    foo f1, f2;
    fun(&f1);
    fun(&f2);
}

Outputs:

Hallo, im at addr: 0xbffc396a
Hallo, im at addr: 0xbffc3969

If you use a template class for the argument type, you can have the same behvaiour without reinventing the wheel.

Edit: as Crazy Eddie pointed out, you can just use boost::mem_fn or std::mem_fn:

std::function<void(foo*)> fun = std::mem_fn(&foo::operator());

Instead of bind.

Larios answered 15/5, 2012 at 23:53 Comment(4)
You actually don't need bind here and it can be worthwhile to omit it. Bind has to go through an argument conversion system to merge the two argument lists and invoke the target callable. boost::mem_fn will do the trick and actually bind() for member pointers uses it.Kersey
@CrazyEddie nice one, I have used mem_fn some time ago, but totally forgot about it. Editted my answer :DLarios
OK this is exactly the sort of thing I was looking for thanks; it's obvious when you see it, bit I'd somehow convinced myself (too late here) that because the foo method I was interested in had no arguments, there was no role for a _1 placeholder... so wrong.Europa
I was refering to the boost one, but the standard one works too...just hardly as nicely.Kersey
K
5

Yeah, you're reinventing the wheel. std::mem_fun_ref does what you want.

std::vector<foo> foos;

...

std::for_each(foos.begin(), foos.end(), std::mem_fun_ref(&foo::operator()));

Alternatively:

std::vector<foo*> foos;

...

std::for_each(foos.begin(), foos.end(), std::mem_fun(&foo::operator()));

Not having to mess with whether your param is ptr or not is one great benefit of boost::mem_fn.

Anything much more complex than that though and you begin running into trouble with the C++03 binders and need something more expressive like boost.bind.

Kersey answered 16/5, 2012 at 0:19 Comment(1)
Just wanted to point out std::mem_fun and std::mem_fun_ref are deprecated for std::mem_fn in C++11.Farthingale

© 2022 - 2024 — McMap. All rights reserved.