1

For a given struct:

struct foo
{
    void fooFunc(){}
    int fooVar = 0;
};

I can create a call wapper to the function: std::mem_fn( &foo::fooFunc ) such that I can pass it into another method and call it on an object.
I want to know if there is a similar call wrapper but for member variables.

For example I'm using a pointer to a member variable here and but I'd like to use an call wrapper:

void bar( std::function< void( foo ) > funcPtr, int foo::* varPtr )
{
    foo myFoo;
    funcPtr( myFoo );
    foo.*varPtr = 13;
}
Jonathan Mee
  • 37,899
  • 23
  • 129
  • 288
  • @WilliamAndrewMontgomery I actually already looked through [C++: Pointer to class data member](http://stackoverflow.com/questions/670734/c-pointer-to-class-data-member) there is no STL construct. – Jonathan Mee Jun 04 '14 at 12:24
  • 1
    A member variable pointer is not "C style". – aschepler Jun 04 '14 at 13:16
  • Nothing in your question is from the "[STL](http://stackoverflow.com/tags/stl/info)" – Jonathan Wakely Jun 04 '14 at 13:18
  • What are you trying to accomplish? – rubenvb Jun 04 '14 at 13:18
  • @aschepler that seemed like the clearest way to delineate between an STL pointer wrapper and a simple pointer. If you feel there is a better way to state it, please edit the question. – Jonathan Mee Jun 04 '14 at 13:21
  • 1
    C doesn't support pointers to members, so referring to one as C style definitely isn't the clearest way to say it! The terms you want are "call wrapper" for what `mem_fn` returns, and "pointer-to-member" for `int foo::*` – Jonathan Wakely Jun 04 '14 at 13:31

2 Answers2

5

In addition to member functions, std::mem_fn can wrap data members too, and allow read access to them. So the following works:

void print_fooVar(foo& f, std::function<int(foo)> varAccess)
{
    std::cout << varAccess(f) << std::endl;
}

foo f;
print_fooVar(f, std::mem_fn(&foo::fooVar)); // prints 0

As JonathanWakely mentions in the comments, you can use the (unspecified) type returned by mem_fn itself to set the data member.

foo f;
std::mem_fn(&foo::fooVar)(f) = 13;

Or to transform it into an std::function use

void bar( foo& f, std::function<int&(foo&)> fooVarSet )
{
    fooVarSet(f) = 26;
}

If all you're looking for is a way to generate a callable to set the fooVar data member, and not using std::mem_fn specifically to do this, you can get the job done using a lambda expression as well.

void bar( foo& f, std::function<void(foo)> funcPtr, 
                  std::function<void(foo&, int)> fooVarSet )
{
    funcPtr( f );
    fooVarSet(f, 13);
}

foo f;
bar(f, std::mem_fn(&foo::fooFunc), [](foo& f, int i) {f.fooVar = i;});

Live demo

Praetorian
  • 106,671
  • 19
  • 240
  • 328
  • `mem_fn` on a member variable returns a reference, so you can just to `mem_fn(&foo::fooVar)(f) = 1;` – Jonathan Wakely Jun 04 '14 at 13:17
  • @JonathanWakely It does, and I should've been clearer in the answer. What I don't know how to do is transform whatever type `mem_fn` returns into an `std::function` that can then be used to set the data member. I'll edit the answer. – Praetorian Jun 04 '14 at 13:24
  • Oh, that's easy, just store it in a `std::function` – Jonathan Wakely Jun 04 '14 at 13:25
  • @JonathanWakely I tried `std::function` and it didn't work. So close! Thanks for the pointers. – Praetorian Jun 04 '14 at 13:29
3

N.B. Nothing in your question is from the STL (which is a library from the 1990s), std::function and std::mem_fn are part of the C++ Standard Library, which is not the same thing.

std::mem_fn supports member functions and member variables, so you can just do this to access the member variable and set it:

foo f;
std::function<int&(foo&)> var( std::mem_fn( &foo::fooVar ) );
var(f) = 1;
Jonathan Mee
  • 37,899
  • 23
  • 129
  • 288
Jonathan Wakely
  • 166,810
  • 27
  • 341
  • 521
  • 1
    According to http://en.wikipedia.org/wiki/Standard_Template_Library "functional" is definitely part of the STL. – isarandi Jun 04 '14 at 13:53
  • @isarandi, but `std::function` and `std::mem_fn` are **not**, they are new in C++11 and came from Boost, not from the STL. Just because they're in the same header as something else doesn't make them the same. – Jonathan Wakely Jun 04 '14 at 14:07
  • 1
    But that's just a new version of the STL, right? STL is a part of the C++ Standard Library. And `std::function` is in the STL part of the C++11 Standard Library. At least that's how Wikipedia treats it. Where it came from has no relevance on where it is now. – isarandi Jun 04 '14 at 14:17
  • No, that's not correct, and it's not what Wikipedia says. Parts of the STL were incorporated into the C++ Standard Library (but not all of it) and other things have gone into the C++ Standard Library later. Those other things don't become part of the STL though. `std::function` and `std::mem_fn` are not part of http://www.sgi.com/tech/stl/ for example. – Jonathan Wakely Jun 04 '14 at 15:03
  • @JonathanWakely To get into the bowels of my question, I'm trying to create a templatized setter, but doing this doesn't work: `template< typename T > void bar( std::function< T&( foo& ) > )` when called with something like `bar( std::mem_fn( &foo::fooVar ) )`. Is the problem the function pointer decay perhaps? – Jonathan Mee Jun 04 '14 at 17:53
  • @JonathanMee, your second edit was not necessary, the code was valid anyway (if it didn't compile you need a better compiler) – Jonathan Wakely Jun 04 '14 at 17:53
  • @JonathanWakely Yeah, I'm running VS2012, so I may be bit outta date. In my above comment, I asked about passing the result to a templatized function, maybe the problem is again my compiler? – Jonathan Mee Jun 04 '14 at 17:55
  • 2
    No, the problem is you can't deduce `T` from the argument. See http://stackoverflow.com/a/23900128/981959 which is similar. You can't deduce the `T` in `std::function` from something which is not a `std::function`, because the set of possible conversions that might allow deduction to succeed is infinite, and the compiler can't handle infinity (which is not a problem with your compiler this time, my compiler can't handle infinity either :-) – Jonathan Wakely Jun 04 '14 at 17:57