模拟 std::function 功能
Emulate std::function functionality
我正在尝试模拟下面的 std::function
功能,但 运行 出现以下错误
class Player
{
public:
void move_to(Point location);
};
std::function<void(Player&, Point)> fun = &Player::move_to;
Player hero;
fun(hero, point{ 2, 4 });
fp.cc:32:33: error: variable ‘fun<void(Player&, Point)> f’ has initializer but incomplete type
32 | fun<void(Player &, Point p)> f = &Player::move_to;
#include <iostream>
using namespace std;
template <typename T>
struct fun;
template <typename Ret, typename T, typename ...Args>
struct fun <Ret(*)(T&, Args...)>{
char *data;
using fptr = Ret(T::*)(Args...);
fun(fptr p) : data(p) {}
Ret operator()(T &t, Args... args) {
if (std::is_same_v<Ret, void>) {
(t.*((fptr)data))(args...);
}
return (t.*((fptr)data))(args...);
}
};
struct Point {
int x;
int y;
};
struct Player {
void move_to(Point p) {
cout << __PRETTY_FUNCTION__ << endl;
}
};
int main() {
fun<void(Player &, Point p)> f = &Player::move_to;
Point p{1,2};
Player pl;
// f(pl, p);
}
您的代码很接近,但有几个小错误:
您只为 Ret(*)(T&, Args...)
提供了 fun
的定义,但 fun<void(Player &, Point p)>
与此模板不匹配。所以我删除了 (*)
,这在你的专业化中并不是真正需要的。
函数指针不可转换 to/from char *
,所以我将 data
的类型更改为 fptr
因此它与参数匹配类型。
#include <iostream>
using namespace std;
template <typename T>
struct fun;
template <typename Ret, typename T, typename ...Args>
struct fun <Ret(T&, Args...)>{
using fptr = Ret(T::*)(Args...);
fptr data;
fun(fptr p) : data(p) {}
Ret operator()(T &t, Args... args) {
if (std::is_same_v<Ret, void>) {
(t.*((fptr)data))(args...);
}
return (t.*((fptr)data))(args...);
}
};
struct Point {
int x;
int y;
};
struct Player {
void move_to(Point p) {
cout << __PRETTY_FUNCTION__ << endl;
}
};
int main() {
fun<void(Player &, Point p)> f = &Player::move_to;
Point p{1,2};
Player pl;
f(pl, p);
}
struct unused_t {};
// can store any of a function pointer, member function pointer, or other
// pointer. It is a bit annoying, and not complete even.
union state_t {
void(unused_t::*pm)();
void(*pf)();
void*pv;
state_t():pv(nullptr){}
template<class R0, class...A0s>
state_t(R0(*f)(A0s...)):pf(reinterpret_cast<void(*)()>(f)) {}
template<class X, class R0, class...A0s>
state_t(R0(X::*f)(A0s...)):pm(reinterpret_cast<void(unused_t::*)()>(f)) {}
template<class X, class R0, class...A0s>
state_t(R0(X::*f)(A0s...) const):pm(reinterpret_cast<void(unused_t::*)()>(f)) {}
// add in & and && overloads of member function pointers here
template<class T>
state_t(T* p):pv((void*)p) {}
};
template<class Sig>
struct fun;
template <class R, class...As>
struct fun <R(As...)>{
using pf_t = R(*)(state_t, As&&...);
state_t state;
pf_t pf = nullptr;
fun() = default;
explicit operator bool() const { return pf; }
template<class R0, class...A0s>
fun( R0(*f)(A0s...) ):
state(f),
pf([](state_t state, As&&...as)->R{
auto f = (R0(*)(A0s...))(state.pf);
return std::invoke( f, std::forward<As>(as)... );
})
{}
template<class T, class R0, class...A0s>
fun( R0(T::*f)(A0s...) ):
state(f),
pf([](state_t state, As&&...as)->R{
auto f = (R0(T::*)(A0s...))(state.pm);
return std::invoke( f, std::forward<As>(as)... );
})
{}
template<class T, class R0, class...A0s>
fun( R0(T::*f)(A0s...) const ):
state(f),
pf([](state_t state, As&&...as)->R{
auto f = (R0(T::*)(A0s...) const)(state.pm);
return std::invoke( f, std::forward<As>(as)... );
})
{}
R operator()(As... args) const {
return pf( state, std::forward<As>(args)... );
}
};
这确实使用了 std::invoke
.
测试代码:
struct bob {
int foo() const { return 3; };
};
int main() {
fun<int(bob&)> f = &bob::foo;
bob b;
std::cout << f(b);
}
这是一个 c++20 版本:
struct unused_t {};
// can store any of a function pointer, member function pointer, or other
// pointer. It is a bit annoying, and not complete even.
union state_t {
void(unused_t::*pm)();
void(*pf)();
void*pv;
state_t():pv(nullptr){}
template<class R0, class...A0s>
state_t(R0(*f)(A0s...)):pf(reinterpret_cast<void(*)()>(f)) {}
template<class X, class R0, class...A0s>
state_t(R0(X::*f)(A0s...)):pm(reinterpret_cast<void(unused_t::*)()>(f)) {}
template<class X, class R0, class...A0s>
state_t(R0(X::*f)(A0s...) const):pm(reinterpret_cast<void(unused_t::*)()>(f)) {}
// add in & and && overloads of member function pointers here
template<class T>struct tag_t {};
template<class T>using type_t = T;
template<class R, class...As>
type_t<R(*)(As...)> extract( tag_t<R(*)(As...)>) const {
return (R(*)(As...))pf;
}
template<class T, class R, class...As>
type_t<R(T::*)(As...)> extract( tag_t<R(T::*)(As...)>) const {
return (R(T::*)(As...))pm;
}
template<class T, class R, class...As>
type_t<R(T::*)(As...) const> extract( tag_t<R(T::*)(As...) const>) const {
return (R(T::*)(As...) const)pm;
}
template<class T>
type_t<T*> extract( tag_t<T*>) const {
return (T*)pv;
}
template<class T>
state_t(T* p):pv((void*)p) {}
};
template<class Sig>
struct fun;
template <class R, class...As>
struct fun <R(As...)>{
using pf_t = R(*)(state_t, As&&...);
state_t state;
pf_t pf = nullptr;
fun() = default;
explicit operator bool() const { return pf; }
template<std::invocable<As...> F>
requires std::is_convertible_v< std::invoke_result_t<F, As...>, R >
fun( F f ):
state(std::forward<F>(f)),
pf([](state_t state, As&&...as)->R{
auto f = state.extract(state_t::tag_t<F>{});
return std::invoke( f, std::forward<As>(as)... );
})
{}
R operator()(As... args) const {
return pf( state, std::forward<As>(args)... );
}
};
烦人的地方是成员函数的指针,指针或函数指针以统一的方式存储。 C++ 标准规定不能保证这 3 种类型的指针兼容。
所以 state_t
类型可以在联合中存储 3 个中的任何一个,但是有一堆样板来统一对待它们。
我正在尝试模拟下面的 std::function
功能,但 运行 出现以下错误
class Player
{
public:
void move_to(Point location);
};
std::function<void(Player&, Point)> fun = &Player::move_to;
Player hero;
fun(hero, point{ 2, 4 });
fp.cc:32:33: error: variable ‘fun<void(Player&, Point)> f’ has initializer but incomplete type
32 | fun<void(Player &, Point p)> f = &Player::move_to;
#include <iostream>
using namespace std;
template <typename T>
struct fun;
template <typename Ret, typename T, typename ...Args>
struct fun <Ret(*)(T&, Args...)>{
char *data;
using fptr = Ret(T::*)(Args...);
fun(fptr p) : data(p) {}
Ret operator()(T &t, Args... args) {
if (std::is_same_v<Ret, void>) {
(t.*((fptr)data))(args...);
}
return (t.*((fptr)data))(args...);
}
};
struct Point {
int x;
int y;
};
struct Player {
void move_to(Point p) {
cout << __PRETTY_FUNCTION__ << endl;
}
};
int main() {
fun<void(Player &, Point p)> f = &Player::move_to;
Point p{1,2};
Player pl;
// f(pl, p);
}
您的代码很接近,但有几个小错误:
您只为
Ret(*)(T&, Args...)
提供了fun
的定义,但fun<void(Player &, Point p)>
与此模板不匹配。所以我删除了(*)
,这在你的专业化中并不是真正需要的。函数指针不可转换 to/from
char *
,所以我将data
的类型更改为fptr
因此它与参数匹配类型。
#include <iostream>
using namespace std;
template <typename T>
struct fun;
template <typename Ret, typename T, typename ...Args>
struct fun <Ret(T&, Args...)>{
using fptr = Ret(T::*)(Args...);
fptr data;
fun(fptr p) : data(p) {}
Ret operator()(T &t, Args... args) {
if (std::is_same_v<Ret, void>) {
(t.*((fptr)data))(args...);
}
return (t.*((fptr)data))(args...);
}
};
struct Point {
int x;
int y;
};
struct Player {
void move_to(Point p) {
cout << __PRETTY_FUNCTION__ << endl;
}
};
int main() {
fun<void(Player &, Point p)> f = &Player::move_to;
Point p{1,2};
Player pl;
f(pl, p);
}
struct unused_t {};
// can store any of a function pointer, member function pointer, or other
// pointer. It is a bit annoying, and not complete even.
union state_t {
void(unused_t::*pm)();
void(*pf)();
void*pv;
state_t():pv(nullptr){}
template<class R0, class...A0s>
state_t(R0(*f)(A0s...)):pf(reinterpret_cast<void(*)()>(f)) {}
template<class X, class R0, class...A0s>
state_t(R0(X::*f)(A0s...)):pm(reinterpret_cast<void(unused_t::*)()>(f)) {}
template<class X, class R0, class...A0s>
state_t(R0(X::*f)(A0s...) const):pm(reinterpret_cast<void(unused_t::*)()>(f)) {}
// add in & and && overloads of member function pointers here
template<class T>
state_t(T* p):pv((void*)p) {}
};
template<class Sig>
struct fun;
template <class R, class...As>
struct fun <R(As...)>{
using pf_t = R(*)(state_t, As&&...);
state_t state;
pf_t pf = nullptr;
fun() = default;
explicit operator bool() const { return pf; }
template<class R0, class...A0s>
fun( R0(*f)(A0s...) ):
state(f),
pf([](state_t state, As&&...as)->R{
auto f = (R0(*)(A0s...))(state.pf);
return std::invoke( f, std::forward<As>(as)... );
})
{}
template<class T, class R0, class...A0s>
fun( R0(T::*f)(A0s...) ):
state(f),
pf([](state_t state, As&&...as)->R{
auto f = (R0(T::*)(A0s...))(state.pm);
return std::invoke( f, std::forward<As>(as)... );
})
{}
template<class T, class R0, class...A0s>
fun( R0(T::*f)(A0s...) const ):
state(f),
pf([](state_t state, As&&...as)->R{
auto f = (R0(T::*)(A0s...) const)(state.pm);
return std::invoke( f, std::forward<As>(as)... );
})
{}
R operator()(As... args) const {
return pf( state, std::forward<As>(args)... );
}
};
这确实使用了 std::invoke
.
测试代码:
struct bob {
int foo() const { return 3; };
};
int main() {
fun<int(bob&)> f = &bob::foo;
bob b;
std::cout << f(b);
}
这是一个 c++20 版本:
struct unused_t {};
// can store any of a function pointer, member function pointer, or other
// pointer. It is a bit annoying, and not complete even.
union state_t {
void(unused_t::*pm)();
void(*pf)();
void*pv;
state_t():pv(nullptr){}
template<class R0, class...A0s>
state_t(R0(*f)(A0s...)):pf(reinterpret_cast<void(*)()>(f)) {}
template<class X, class R0, class...A0s>
state_t(R0(X::*f)(A0s...)):pm(reinterpret_cast<void(unused_t::*)()>(f)) {}
template<class X, class R0, class...A0s>
state_t(R0(X::*f)(A0s...) const):pm(reinterpret_cast<void(unused_t::*)()>(f)) {}
// add in & and && overloads of member function pointers here
template<class T>struct tag_t {};
template<class T>using type_t = T;
template<class R, class...As>
type_t<R(*)(As...)> extract( tag_t<R(*)(As...)>) const {
return (R(*)(As...))pf;
}
template<class T, class R, class...As>
type_t<R(T::*)(As...)> extract( tag_t<R(T::*)(As...)>) const {
return (R(T::*)(As...))pm;
}
template<class T, class R, class...As>
type_t<R(T::*)(As...) const> extract( tag_t<R(T::*)(As...) const>) const {
return (R(T::*)(As...) const)pm;
}
template<class T>
type_t<T*> extract( tag_t<T*>) const {
return (T*)pv;
}
template<class T>
state_t(T* p):pv((void*)p) {}
};
template<class Sig>
struct fun;
template <class R, class...As>
struct fun <R(As...)>{
using pf_t = R(*)(state_t, As&&...);
state_t state;
pf_t pf = nullptr;
fun() = default;
explicit operator bool() const { return pf; }
template<std::invocable<As...> F>
requires std::is_convertible_v< std::invoke_result_t<F, As...>, R >
fun( F f ):
state(std::forward<F>(f)),
pf([](state_t state, As&&...as)->R{
auto f = state.extract(state_t::tag_t<F>{});
return std::invoke( f, std::forward<As>(as)... );
})
{}
R operator()(As... args) const {
return pf( state, std::forward<As>(args)... );
}
};
烦人的地方是成员函数的指针,指针或函数指针以统一的方式存储。 C++ 标准规定不能保证这 3 种类型的指针兼容。
所以 state_t
类型可以在联合中存储 3 个中的任何一个,但是有一堆样板来统一对待它们。