Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to reduce template arguments?

Tags:

c++

templates

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>();
}
like image 295
Alexey Malistov Avatar asked Nov 19 '09 10:11

Alexey Malistov


3 Answers

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) );
like image 183
UncleBens Avatar answered Sep 21 '22 07:09

UncleBens


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.

like image 38
sellibitze Avatar answered Sep 21 '22 07:09

sellibitze


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.

like image 40
Matthieu M. Avatar answered Sep 21 '22 07:09

Matthieu M.