2

Short version:

Instead of always having to type:

auto function_pointer = &decorator<int, Foo, void &Foo::bar(int)>

I would like to be able to just write

auto function_pointer = &decorator<void &Foo::bar(int)>

where the int and Foo are automatically extracted from <void &Foo::bar(int)>.


For starters I have:

map<string, Object*> all_object_instances;

class Object {
public:
     Object(const string &name) { all_object_instances[name]=this; }
     virtual ~Object() { all_object_instances.erase(name); }
};

class Foo : public Object {
public:
     Foo(const string &name) : Object(name) {}
     void bar(int);
};

I need a function, that will call Foo::bar(int) with some decoration, so I write:

template <class Arg, class C, void C::*T(Arg)>
void decorator(const string &name, const string &s_arg)
{
    Arg a = my_convert(s_arg);
    C* c = dynamic_cast<C*>(all_object_instances[name]);
    (c->*T)(a);
}

So my main code has to look like this:

   new Foo("MyFoo");
   ....
   auto saved_f = &decorator<int, Foo, void &Foo::bar(int)>;
   ....
   saved_f("MyFoo", "123");
   ....
   delete all_object_instances("MyFoo") // for symmetry

It would be a lot better, if I could just have 1 template argument:

saved_f = &decorator<void &Foo::bar(int)>;

And derive both 'Foo', and 'int' from the argument:

template <TEMPLATE MAGIC>
void decorator(const string &name, const string &s_arg)
{
    typedef ARG_MAGIC ARG;
    typedef CLASS_MAGIC C;

    Arg a = my_convert(s_arg);
    C* c = dynamic_cast<C*>(all_object_instances[name]);
    (c->*T)(a);
}

Does any such thing exist?

Aaron McDaid
  • 26,501
  • 9
  • 66
  • 88
dandax
  • 21
  • 3
  • I've edited the question in an attempt to make it much simpler and clearer? Is my 'Short version' correct? If so, you should consider greatly simplifying (or even deleting!) the remainder of your question as it is mostly irrelevant – Aaron McDaid Nov 01 '16 at 18:25
  • yes that is a great revision. I would like to leave the long version, because sometimes my stab at the problem was wrong to begin with :) Thanks tho – dandax Nov 01 '16 at 18:44
  • You can narrow it down to two template parameters if you just supply the member function pointer type, but as far as I can tell, there's [no way to deduce the type of a function pointer template argument](http://stackoverflow.com/questions/17736337/is-there-a-way-to-deduce-the-value-of-a-function-pointer-template-parameter). Per that answer, though, you *could* make a macro (I know :/) to expand the arguments into the type, pointer pair you want. After that it's only a matter of [finding the return, class, and argument types.](http://coliru.stacked-crooked.com/a/a577f71af2697f16) – jaggedSpire Nov 01 '16 at 19:00
  • There's also [this](http://stackoverflow.com/a/10224611/4892076), if you know a bit about the function pointer's type beforehand. [This](http://stackoverflow.com/a/11746632/4892076) is related, too. – jaggedSpire Nov 01 '16 at 19:01
  • Can you use C++17? I guess you want the member function pointer as template argument instead of as function argument in order to get a "real" function pointer back, i.e. not a closure? – Daniel Jour Nov 01 '16 at 20:00
  • Yes Daniel, I can neither use c++17 nor get a pointer. The whole thing has to be converted to a simple function call with prefixed arguments. So I am "generating" a wrapper function with templates. - If I could change the infrastructure, I would replace function calls with functional objects, and use bind() or other methods to generate and allocate a functor. – dandax Nov 02 '16 at 16:07

2 Answers2

0

Passing the function as an argument will allow it to be invoked. It can be deduced simply through template argument deduction.

In the below example I've just added a bit of genericity, passing the arguments through std::invoke, which can do the automatic pointer-to-member call.

template<class R,class C,class... Args>
auto get_class_type(R(C::*)(Args...))->C;

template<class F>
auto decorator(F f) {
  return [f=move(f)](auto const& name, auto const& arg) {
    using C=decltype(get_class_type(f));
    return std::invoke(move(f),
      dynamic_cast<C*>(all_object_instances[name]),
      my_convert(arg)
    );
  };
}

Now this is reduced to:

auto saved_f = decorator(&Foo::bar);
saved_f("MyFoo", "123");

This requires a C++1y compliant compiler for std::invoke, which can be found in <functional>. If this doesn't compile for you, simply change it to:

auto c = dynamic_cast<C*>(all_object_instances[name]);
return (c->*move(f))(my_convert(arg));

If your goal is to have the template argument be a compile time constant, that is also possible. In this case you will have to use a macro and pass an integral_constant to decorator:

#define decorator(mem_f) decorator_impl( integral_constant<decltype( mem_f ), mem_f>{} )

Then extract the value out of the type:

template<class I>
auto decorator_impl( I ) {
  auto constexpr f = I::value;
  // same code as before...
}
David G
  • 94,763
  • 41
  • 167
  • 253
  • interstging, deciphering this. What's this syntax: template auto get_class_type(R(C::*)(Args...))->C; I only know of template function, class or using (in c++1x) – dandax Nov 02 '16 at 16:16
  • @dandax It's a declaration of a function template. It takes as its argument a pointer to a member function of a class `C`, that takes `Args...` as its parameters and returns the type `R`. I'm simply returning the type of the class `C` (by doing `auto (...) -> C` – David G Nov 02 '16 at 16:22
  • How is that different from: template C get_class_type(R(C::*)(Args...)); – dandax Nov 02 '16 at 16:44
  • @dandax In this case there's absolutely no difference, simply a matter of style. – David G Nov 02 '16 at 16:45
-1

In c++17, you should be able to write:

template <auto m>
void decorator(const string &name, const string &s_arg);

with the desired syntax:

auto saved_f = &decorator<void &Foo::bar(int)>

instead of

template <typename M, M m>
void decorator(const string &name, const string &s_arg);

with syntax

auto saved_f = &decorator<decltype(&Foo::bar), &Foo::bar>;

or your version.

Then you need some function traits to retrieve class and return type, something like:

template <typename> struct method_traits; 

template <typename Ret, typename Class, typename ... Args>
struct method_traits<Ret (Class::*)(Args...)>
{
    using ret_type = Ret;
    using classe_type = Class;
    using args_type = std::tuple<Args...>;
};

And so finally:

template <auto m>
void decorator(const string &name, const string &s_arg)
{
    using C = typename method_traits<decltype(m)>::class_type;
    using Arg = std::tuple_element_t<0, typename method_traits<decltype(m)>::args_type>;

    Arg a = my_convert(s_arg);
    C* c = dynamic_cast<C*>(all_object_instances[name]);
    (c->*T)(a);
}
Jarod42
  • 203,559
  • 14
  • 181
  • 302