如何围绕任意成员函数编写通用的非成员包装器?
How to write a generic non-member wrapper around arbitrary member functions?
问题
有一个模板可以为任意成员函数生成包装器,允许在对象上调用该函数,例如:
给定 bool std::string::empty()
,要生成的包装器将是 bool wrapper(const std::string& str) { return str.empty(); }
。
包装函数不应是 lambda(以便它可以作为函数指针传递)。
我的方法(对于非常量成员函数)
/* Wrapper (for non-const member functions) */
template<typename T, typename R, typename... ARGS>
struct staticWrapperHelper
{
using fType = R (T::*)(ARGS...);
template<fType FUNCTION>
static R wrapper(T& object, ARGS... args) { return object.*FUNCTION(args...); }
};
template<typename T, typename R, typename... ARGS>
auto staticWrapper(R (T::*memberFunction)(ARGS...))
{
//auto function = [memberFunction] (T& object, ARGS... args) -> R { return (object.*memberFunction)(args...); };
using Helper = staticWrapperHelper<T, R, ARGS...>;
R (*wrapper)(T&, ARGS...) = &Helper::template wrapper<R (T::*)(ARGS...)>;
return wrapper;
}
/* Example */
class A
{
public:
void member() {}
};
int main()
{
A a;
auto f = staticWrapper<A>(&A::member);
f(a);
return 0;
}
我的方法有问题
它不编译。看起来返回的包装器仍然是一个模板:http://coliru.stacked-crooked.com/a/d4c0dd9ab631aa1f
g++ -std=c++14 -O2 -Wall -pedantic -pthread main.cpp && ./a.out
main.cpp: In instantiation of 'auto staticWrapper(R (T::*)(ARGS ...)) [with T = A; R = void; ARGS = {}]':
main.cpp:32:41: required from here
main.cpp:17:9: error: no matches converting function 'wrapper' to type 'void (*)(class A&)'
17 | R (*wrapper)(T&, ARGS...) = &Helper::template wrapper;
| ^~~~~~~
main.cpp:8:14: note: candidate is: 'template static R staticWrapperHelper::wrapper(T&, ARGS ...) [with R (T::* FUNCTION)(ARGS ...) = FUNCTION; T = A; R = void; ARGS = {}]'
8 | static R wrapper(T& object, ARGS... args) { return object.*FUNCTION(args...); }
| ^~~~~~~
有什么想法吗?
关于using fType = R (T::*)(ARGS...);
,类型fType
是指针类型,所以不能作为模板参数,以便memorize/save运行时的实例方法地址以后使用。所以,你应该在staticWrapperHelper
中定义一个成员来保存后面要用到的方法指针:
template<typename T, typename R, typename... ARGS>
struct staticWrapperHelper
{
using fType = R (T::*)(ARGS...);
fType method;
R operator() (T& object, ARGS... args) { return (object.*method)(args...); }
};
另外,我将 wrapper
静态方法更改为实例方法:operator()
成为仿函数对象。
现在,在作为 staticWrapperHelper
的工厂函数工作的 staticWrapper
函数中,我们 return 存储方法指针的 staticWrapperHelper
实例。稍后可以使用此实例对象来调用提供的 T
类型对象的实例方法。
template<typename T, typename R, typename... ARGS>
auto staticWrapper(R (T::*memberFunction)(ARGS...))
{
using Helper = staticWrapperHelper<T, R, ARGS...>;
return Helper{.method = memberFunction};
}
我找到了一个我可以接受的解决方案,方法是将成员函数指针作为模板参数传递,不幸的是,这需要在 C++14 中明确提及类型(辅助宏在这里可以提供帮助)。然后函数指针的签名被辅助函数分解以创建包装器。
#include <iostream>
/* Wrapper (for non-const member functions) */
template<typename PTM, PTM ptm>
struct staticWrapper
{
constexpr static auto make()
{
return breakDownAndWrap(ptm);
}
private:
template<typename C, typename R, typename... ARGS>
constexpr static auto breakDownAndWrap(R (C::*)(ARGS...))
{
R (*wrapper)(C&, ARGS...) = &Wrapper<C, R, ARGS...>::get;
return wrapper;
}
template<typename C, typename R, typename... ARGS>
struct Wrapper
{
constexpr static R get(C& obj, ARGS... args)
{
return (obj.*ptm)(args...);
}
};
};
/* Optional helper macro */
#define AUTO_T(val) decltype(val), val
/* Example */
class A
{
public:
int number;
void member() { std::cout << number << std::endl; }
};
int main()
{
A a {2};
auto f = staticWrapper<decltype(&A::member), &A::member>::make();
auto g = staticWrapper<AUTO_T(&A::member)>::make();
f(a);
g(a);
return 0;
}
问题
有一个模板可以为任意成员函数生成包装器,允许在对象上调用该函数,例如:
给定 bool std::string::empty()
,要生成的包装器将是 bool wrapper(const std::string& str) { return str.empty(); }
。
包装函数不应是 lambda(以便它可以作为函数指针传递)。
我的方法(对于非常量成员函数)
/* Wrapper (for non-const member functions) */
template<typename T, typename R, typename... ARGS>
struct staticWrapperHelper
{
using fType = R (T::*)(ARGS...);
template<fType FUNCTION>
static R wrapper(T& object, ARGS... args) { return object.*FUNCTION(args...); }
};
template<typename T, typename R, typename... ARGS>
auto staticWrapper(R (T::*memberFunction)(ARGS...))
{
//auto function = [memberFunction] (T& object, ARGS... args) -> R { return (object.*memberFunction)(args...); };
using Helper = staticWrapperHelper<T, R, ARGS...>;
R (*wrapper)(T&, ARGS...) = &Helper::template wrapper<R (T::*)(ARGS...)>;
return wrapper;
}
/* Example */
class A
{
public:
void member() {}
};
int main()
{
A a;
auto f = staticWrapper<A>(&A::member);
f(a);
return 0;
}
我的方法有问题
它不编译。看起来返回的包装器仍然是一个模板:http://coliru.stacked-crooked.com/a/d4c0dd9ab631aa1f
g++ -std=c++14 -O2 -Wall -pedantic -pthread main.cpp && ./a.out main.cpp: In instantiation of 'auto staticWrapper(R (T::*)(ARGS ...)) [with T = A; R = void; ARGS = {}]': main.cpp:32:41: required from here main.cpp:17:9: error: no matches converting function 'wrapper' to type 'void (*)(class A&)' 17 | R (*wrapper)(T&, ARGS...) = &Helper::template wrapper; | ^~~~~~~ main.cpp:8:14: note: candidate is: 'template static R staticWrapperHelper::wrapper(T&, ARGS ...) [with R (T::* FUNCTION)(ARGS ...) = FUNCTION; T = A; R = void; ARGS = {}]' 8 | static R wrapper(T& object, ARGS... args) { return object.*FUNCTION(args...); } | ^~~~~~~
有什么想法吗?
关于using fType = R (T::*)(ARGS...);
,类型fType
是指针类型,所以不能作为模板参数,以便memorize/save运行时的实例方法地址以后使用。所以,你应该在staticWrapperHelper
中定义一个成员来保存后面要用到的方法指针:
template<typename T, typename R, typename... ARGS>
struct staticWrapperHelper
{
using fType = R (T::*)(ARGS...);
fType method;
R operator() (T& object, ARGS... args) { return (object.*method)(args...); }
};
另外,我将 wrapper
静态方法更改为实例方法:operator()
成为仿函数对象。
现在,在作为 staticWrapperHelper
的工厂函数工作的 staticWrapper
函数中,我们 return 存储方法指针的 staticWrapperHelper
实例。稍后可以使用此实例对象来调用提供的 T
类型对象的实例方法。
template<typename T, typename R, typename... ARGS>
auto staticWrapper(R (T::*memberFunction)(ARGS...))
{
using Helper = staticWrapperHelper<T, R, ARGS...>;
return Helper{.method = memberFunction};
}
我找到了一个我可以接受的解决方案,方法是将成员函数指针作为模板参数传递,不幸的是,这需要在 C++14 中明确提及类型(辅助宏在这里可以提供帮助)。然后函数指针的签名被辅助函数分解以创建包装器。
#include <iostream>
/* Wrapper (for non-const member functions) */
template<typename PTM, PTM ptm>
struct staticWrapper
{
constexpr static auto make()
{
return breakDownAndWrap(ptm);
}
private:
template<typename C, typename R, typename... ARGS>
constexpr static auto breakDownAndWrap(R (C::*)(ARGS...))
{
R (*wrapper)(C&, ARGS...) = &Wrapper<C, R, ARGS...>::get;
return wrapper;
}
template<typename C, typename R, typename... ARGS>
struct Wrapper
{
constexpr static R get(C& obj, ARGS... args)
{
return (obj.*ptm)(args...);
}
};
};
/* Optional helper macro */
#define AUTO_T(val) decltype(val), val
/* Example */
class A
{
public:
int number;
void member() { std::cout << number << std::endl; }
};
int main()
{
A a {2};
auto f = staticWrapper<decltype(&A::member), &A::member>::make();
auto g = staticWrapper<AUTO_T(&A::member)>::make();
f(a);
g(a);
return 0;
}