For a class template with function template argument, I hope to make it a member of my class with one of my class's function as its template argument, or use a lambda to call a member function. Here is rough example of what I want to do but I cannot get it working.
#include <iostream>
template<std::string (*fun)()>
struct wrapper{
void operator()() {std::cout<<"wrapped "<<fun()<<std::endl;}
};
std::string foo() {
return "foo";
}
struct bar{
// static function works:
static std::string str1() { return "bar1";}
wrapper<str1> wrapped_bar1;
// Non-static member function does not work:
// std::string str2() { return "bar2";}
// wrapper<&bar::str2> wrapped_bar2;
// Lambda with *this does not work either:
// static std::string str3(std::string input) { return input "bar3";}
// wrapper<[this](){return this->str3("bar")}> wrapped_bar3;
};
int main() {
wrapper<foo> wrapped;
wrapped();
bar some_bar;
some_bar.wrapped_bar1();
}
CodePudding user response:
The type of &bar::str2
is std::string (bar::*)()
and not std::string (*)()
. There are ways to achieve the desired effect as shown below. In the below shown example, i have made wrapper
to be an ordinary class-type instead of a class template. Moreover, the overloaded opeartor()
is templated and is itself overloaded with a nontemplated version.
struct wrapper{ //wrapper is not a class template anymore
void operator()(std::string (*fun)()) {std::cout<<"wrapped "<<fun()<<std::endl;}
template<typename T>
void operator()(std::string (T::*fun)(), T obj)
{
std::cout<<"wrapped "<<(obj.*fun)()<<std::endl;
}
};
std::string foo() {
return "foo";
}
struct bar{
// static function works:
static std::string str1() { return "bar1";}
wrapper wrapped_bar1;
std::string str2() { return "bar2";}
};
int main() {
wrapper wrapped;
wrapped(foo);
bar some_bar;
some_bar.wrapped_bar1(&bar::str1);
//------------------------------------vvvvvvvv---->pass the object also on which non-static member function should be called
some_bar.wrapped_bar1(&bar::str2, some_bar);
}
The output of the above modified program is:
wrapped foo
wrapped bar1
wrapped bar2
CodePudding user response:
There's no way to make something of the form wrapper<&bar::str2> wrapped_bar2;
just work as-is.
Consider:
struct bar {
std::string str2() { return "bar2";}
wrapper<&bar::str2> wrapped_bar2;
};
bar b1;
bar b2;
Now b1.wrapped_bar2
needs to have stored the pointer &b1
, and b2.wrapped_bar2
needs to have stored the pointer &b2
. This (this
) pointer is the instance needed to call an instance method.
It clearly can't be part of the static type (like a free function pointer, or the member function pointer itself), because b1
and b2
have the same type but different addresses. That means you need at least to write a constructor similar to
bar::bar() : wrapped_bar2{this} {}
Now, if we're having to do this, the two wrapper
instantiations can't be used interchangeably, and we might just as well write a dedicated wrapper type for member functions:
template<typename Class, std::string (Class::*fun)()>
struct mem_wrapper{
Class *instance_;
explicit mem_wrapper(Class *that) : instance_(that) {}
void operator()() {std::cout<<"wrapped "<< (instance_->*fun)() <<std::endl;}
};
If you want a single top-level type that can deal with both free functions and member functions, you need some way to hide these implementation differences (type erasure). Honestly, std::function
already does all this for you, and it's not obvious there's a lot of benefit to reinventing this.