tags:

views:

211

answers:

4

Here I have functor of the follow kind:

template<class T, class Foo, T Foo::*p>
struct X {
    void operator()(Foo & f) {
     (f.*p) = 12 * (f.*p); // simple example. could be more complex `operator()`
    }

};

And sample struct:

struct FF
{
    int m;
    int r;
};

I want to use the functor X, but I don't want to explicitly specify template argument as follows:

void testforx()
{
    std::vector<FF> cont(5);
    std::for_each(cont.begin(), cont.end(), X<int, FF, &FF::r>() );  // it work, but I don't want to write `int` and `FF`
    std::for_each(cont.begin(), cont.end(), createx<&FF::r>() );     // how I would like to use it, how to declare `createx` function?
}

Here what I tried with no success:

// that is what I tried, but it could not deduce T and Foo
template<T Foo::*p, class T, class Foo>
X<T, Foo, T Foo::*p> createx()
{
    return X<T, Foo, p>();
}

// this works, but requires to write T and Foo explicitly
template<class T, class Foo, T Foo::*p>
X<T, Foo, T Foo::*p> createx()
{
    return X<T, Foo, p>();
}
+8  A: 

I just wouldn't store the member pointer as a template argument:

template<class T, class Foo>
struct X {
    X(T Foo::*p): p(p) {}
    void operator()(Foo & f) {
        (f.*p) = 12 * (f.*p);   // simple example. could be more complex `operator()`
    }
private:
    T Foo::*p;
};

template <class T, class Foo>
X<T, Foo> MakeX(T Foo::*p)
{
    return p;
}

I don't think it is possible to deduce the types with your approach: you can't use a pointer-to-member passed to a function which is where the type deduction occurs.

Edit: There may be macro-based solutions, though.

For example, you can make a class to create X instances, like this:

template <class T, class Foo>
struct XMaker
{
    template <T Foo::*p>
    X<T, Foo, p> make() { return X<T, Foo, p>(); }
};

Now, you can create a make... function to deduce T and Foo:

template <class T, class Foo>
XMaker<T, Foo> make_x_maker(T Foo::*)
{
    return XMaker<T, Foo>();
}

Which makes it possible to write a macro like:

#define CREATE_X(member) make_x_maker(member).make<member>()

Usage:

std::for_each(cont.begin(), cont.end(), CREATE_X(&FF::r) );
UncleBens
It is time critical code. I don't want to store `p`.
Alexey Malistov
The question was how to write `createx`. I already have working compile-time solution and just want to reduce template arguments.
Alexey Malistov
Now this is almost I need. I would accept this answer but I do not like macros.
Alexey Malistov
I think this thread is similar: http://stackoverflow.com/questions/1447141/c-templates-how-to-use-a-template-parameter-in-another-template-parameter-dec
UncleBens
+1  A: 

I don't think it's possible to reduce the number of template arguments you have to specify if you want an arbitraty member function pointer to be a template argument.

Instead of member function pointers you could use a normal type parameter for a functor that extracts a reference:

template<typename Func>
class X
{
public:
    explicit X(Func f = Func()) : f(f) {}

    template<class K>
    void operator()(K & k) const {
       f(k) = 12 * f(k);
    }
private:
    Func f;
};

Then, you still have the option to use a special functor that directly accesses a certain member (if you think this provides better performance), or use a more general accessor functor that does so with a member function pointer as a member.

sellibitze
It is allowed by Standard to have member function pointer as template argument.
Kirill V. Lyadvinsky
Yeah, I know. The "I don't think it's possible" refers to the title of the question. I didn't say "member function pointers are not allowed as template parameters". Please read answers more carefully before you downvote. I will try to do my best next time to write answers that cannot be misunderstood. Thanks.
sellibitze
This answer has nothing to do with my question.
Alexey Malistov
@sellibitze. Anyway, your code requires a `typename` when a pointer to member function is a constant. You should supply a sample how you suppose to use it.
Kirill V. Lyadvinsky
@Alexey: I'm sorry that you think so. @Kirill: I've trouble relating your last comment to my answer. It seems we have a communication problem.
sellibitze
A: 

The best solution is to use polymorphism. But if you need to stick with templates, a simplified solution is this:

template<class Foo>
struct X 
{
  void operator()(Foo& f) 
  {
    f.set_second(12 * f.get_first());
  }
};

struct FF
{
  int m;
  int r;

  void set_second(int s)
  {
    r = s;
  }
  int get_first() const
  {
    return m;
  }
};

typedef X<FF> FF_X;

void testforx()
{
  std::vector<FF> cont(5);
  std::for_each(cont.begin(), cont.end(), FF_X() );
}

kind of static polymorphism. I don't think this is bad as you already assume that members of class Foo will respond to arithmetic operators.

Vijay Mathew
Alexey Malistov
+1  A: 

I would have one question: do you really need to specify all those arguments ?

struct XR
{
  template <class Foo>
  void operator()(Foo& foo) const { foo.r = 12 * foo.r; }
};

This works, there is no need for an extra make method it just works:

void testforx()
{
  std::vector<FF> cont(5);
  std::for_each(cont.begin(), cont.end(), XR()); 
}

I prefer not to be TOO generic when I create templates.

If you need to have a more complex operator(), you can always do some heavy lifting INSIDE it.

Also, you may consider Boost.Bind, if you really wish to extract pointer functions and references to attributes.

EDIT:

I have an idea, that will be a bit different and does not involve any macro magic, nor even any metaprogramming magic.

Why not simply use a typedef and be done with it ?

Okay, may be not as automated as you wish... but you have only to type this once, after all.

typedef X<int,FF,&FF::m> X_FF_m; // once

std::for_each(cont.begin(), cont.end(), X_FF_m() );

Seems less typing than

std::for_each(cont.begin(), cont.end(), createX<&FF::m>());

repeated over and over.

I barely use naked templates in my code, I prefer to typedef them to improve readability.

Matthieu M.
It is no option. `struct XR` use `.r`-member. If I want use `.m` member do I need to define a new `struct XM`? And what about another members?
Alexey Malistov
One structure per member, nothing fancy, just something that works and is simple. Of course in your example you presented a structure with 2 attributes and I would think it scales quite well to 4 or 5...
Matthieu M.