tags:

views:

99

answers:

3

Hello,

my question is about how to template'ize the name of a class member that should be used.

Maybe a simplified & pseudo example:

/** 
Does something with a specified member of every element in a List.
*/
template<membername MEMBER> // <-- How to define such thing?
void doSomething(std::vector<MyClass> all){

    for( i=0; i < all.size(); i++)
      all[i].MEMBER++; // e.g.; use all[i].MEMBER in same way

}

and

class MyClass{
public:
    int aaa, bbb, ccc;
}

and the application:

main(){
    vector<MyClass> all = ....

    // applicate doSomething() to all aaa's
    doSomething<aaa>(all);  // or:
    doSomething<MyClass::aaa>(all); // or:
    doSomething<?????>(all);
}

How should the template definition looks like, that I can switch which member variable (aaa, bbb or ccc) of MyClass is accessed/modified in doSomething(.) ?
In my real world task all MEMBER are of same type, as above.

Thanks, Tebas

+6  A: 

Template parameters are restricted to types, integer constants, pointers/references to functions or objects with external linkage and member pointers -- but no identifiers.

But you could use a member pointer as template parameter:

template<int MyClass::* MemPtr>
void doSomething(std::vector<MyClass> & all) {
   for( i=0; i < all.size(); i++)
      (all[i].*MemPtr)++;
}

:

doSomething<&MyClass::aaa>(all);

Note that I changed the doSomething function to take a reference instead of accepting the vector by value.

sellibitze
Thanks. Call 'all' by value was for simplification of the question ... but is call by reference requested by member pointer templates? or can i also use a copy?
Tebas
@Tebas: You can use a copy, but that means the effect of `doSomething` will not be visible outside the function. Call by reference was used to ensure the result would be visible in the caller's vector.
Bart van Ingen Schenau
@Tebas: No, it doesn't make any difference. In either case all[i] will be an lvalue expression of type MyClass.
sellibitze
I suggest you study the *Visitor* design pattern.
Thomas Matthews
Too bad that there can't be a member pointer to a data-member of reference type, and that member pointers can't be used to catch a set of overloaded functions or a function template :( Someone on usenet lately proposed "name" template parameters that would make this possible. I liked the proposal.
Johannes Schaub - litb
A: 

I would use lambdas to solve this problem. Something like this:

#include <vector>     // vector
#include <algorithm>  // for_each
#include <functional> // function

struct MyClass {
   void func1() const { std::cout << __FUNCTION__ << std::endl; }
   void func2() const { std::cout << __FUNCTION__ << std::endl; }
};

void doSomething(std::vector<MyClass> all, std::function<void (MyClass& m)> f)
{
   std::for_each(all.begin(), all.end(), f);
}

int main()
{
   std::vector<MyClass> all;
   all.push_back(MyClass());

    // apply various methods to each MyClass:
   doSomething(all, [](MyClass& m) { m.func1(); });
   doSomething(all, [](MyClass& m) { m.func2(); });
}

Of course in this case the function doSomething is unnecessary. I could just as simply call for_each directly on all.

Daniel Lidström
+3  A: 

sellibitze's solution is fine (though to be honest not very: see my edit), only it limits you to using only members of type int. A more general solution would be this (although the member is NOT a template parameter here)

#include <vector>

struct MyClass
{
   int i;
   char c;
};

template <class T>
void DoSomething(std::vector<MyClass>& all, T MyClass::* MemPtr)
{ 
   for(std::vector<MyClass>::size_type i = 0; i < all.size(); ++i)
      (all[i].*MemPtr)++;
}

int main()
{
   std::vector<MyClass> all;
   DoSomething(all, &MyClass::i);
   DoSomething(all, &MyClass::c);
}

EDIT: Also please note that it is not generally a good idea for a pointer to member to be a template parameter inasmuch as only such pointers that are known compile-time can be passed, that is you can't determine the pointer runtime and then pass it as a template param.

Armen Tsirunyan
`vector::all()` returns `size_t` and not `unsigned` - please use `size_t` for `i`. (Iterators would be even better…)
Steve M
@Steve: I totally agree about the iterators, I just copied the text from op's code. I won't change that. and size() does NOT return size_t, it returns std::vector<MyClass>::size_type. And what is vector::all? :D
Armen Tsirunyan
Oops, you are correct on point 1 and I don't know what I was thinking on point 2. +1 for you. Oddly enough, I *just answered* a question related to vectors and knew to use `size_type` and not `size_t`. Sometimes I am bad.
Steve M