重新绑定 std::function 的一个参数
rebind one parameter of std::function
在我的 C++ 项目中,我决定尝试新的 ++ 功能。其中一个功能是将函数与 std::function
via std::bind
.
绑定
现在我来到一个用例,我必须重新绑定 std::function
。
考虑以下简化代码:
class Test
{
public:
void first()
{
second(bind(&Test::third, this));
}
void second(function<void()> fun)
{
Test *other = new Test();
fun->rebindLastParameter(other); // How can I do this?
fun();
}
void third()
{
// This is called in context of "other"
}
}
如何做 fun->rebindLastParameter(other);
部分(将 this
指针替换为 other
)?
(编辑)上下文:
在我的应用程序中有几个 class 继承自名为 BaseModel
的 class。这些 classes 是从自制的描述语言自动编译而来的。下面的 class 表示一个简单的 "asteroid",它由另外两个 "asteroids":
#pragma once
#include "BaseModel.h"
#include <functional>
using namespace std;
using namespace std::placeholders;
class Test : public BaseModel
{
public:
Test(const OBB& start_obb) : BaseModel(start_obb) {}
void __init() {
scene();
}
void scene() {
combine(bind(&Test::asteroid, this),bind(&Test::asteroid, this));
}
void asteroid() {
translate(random_float(),random_float(),random_float());
sphere(7);
repeat(400,bind(&Test::impact, this));
}
void impact() {
auto p1 = random_surface_point();
select_sphere(p1,random_float() * 2 + 1,1.0);
normalize(p1);
translate_selection(-p1 * random_float() * 0.4);
}
};
问题出在函数 BaseModel::combine
上,它结合了(通过构造立体几何)两个新物体(第 1 个小行星和第 2 个小行星):
void BaseModel::combine(function<void()> left, function<void()> right)
{
BaseModel *leftModel = (BaseModel*) ::operator new (sizeof(BaseModel));
BaseModel *rightModel = (BaseModel*) ::operator new (sizeof(BaseModel));
leftModel->initWithStartOBB(*this->obb);
rightModel->initWithStartOBB(*this->obb);
auto newLeft = bind(left, leftModel); // This does not work
auto newRight = bind(right, rightModel); // This does not work
newLeft();
newRight();
// ... CSG stuff
}
由于leftModel
和rightModel
必须是相同class的新型号,我需要重新绑定我之前的第一个参数给出我自动转译的 class Test::scene
.
也许我走错了路。我希望其他上下文可以解释为什么我 运行 遇到了那个问题。
正如@tobi303 和其他人所指出的,您对 second
参数的函数签名过于受限,因为它采用了空函数。无需重新绑定 - 它不带任何参数。
为了实现您正在尝试做的事情,您需要减少 second
的参数限制。有几种方法可以做到这一点(让它接受一个 function
,或者一个指向成员函数的指针);下面显示了它由一个带有一元参数的函数模板参数化:
#include <functional>
using namespace std;
class Test
{
public:
void first()
{
second([](Test *p){p->third();});
}
template<class Fn>
void second(Fn fn)
{
Test *const other = new Test();
fn(other);
}
void third()
{
// This is called in context of "other"
}
};
int main()
{
Test t;
t.first();
}
个人观点(让我多次投反对票):我认为使用了很多库和技术,大多数 bind
的情况是肯定的,大多数 function
的情况 - 只是在当前的 lambda 之前功能,对它们的需求越来越少。
How can I do the fun->rebindLastParameter(other);
part (replace this
pointer with other
)?
一般情况下你不能。
std::function
依赖于 type-erasure 这意味着存储在 std::function
中的对象的原始类型不是该类型的一部分,并且不容易访问。
如果您确定 fun
肯定存储了 bind(&Test::third, this)
返回的对象,则无需重新绑定参数,只需修改 fun
以包含一个完全不同的函数具有正确参数的对象,即
fun = bind(&Test::third, other);
如果你知道它确实存储了一个由 bind( &Test::???, other)
返回的对象,其中 &Test::???
是任何成员函数 完全 与 [=23= 具有相同的签名] 那么你可以这样做:
using binder_type = decltype(bind(&Test::third, this));
if (auto target = fun.target<binder_type>())
{
// *target is the result of the bind() expression
}
但是仍然没有办法修改 *target
处的对象来替换它持有的 Test*
指针。
如果您知道可以使用的函数集,那么您可以这样做:
using binder_foo_type = decltype(bind(&Test::foo, this));
using binder_bar_type = decltype(bind(&Test::bar, this));
using binder_baz_type = decltype(bind(&Test::baz, this));
if (fun.target<binder_foo_type>())
{
fun = std::bind(&Test::foo, other);
}
else if (fun.target<binder_bar_type>())
{
fun = std::bind(&Test::bar, other);
}
else if (fun.target<binder_baz_type>())
{
fun = std::bind(&Test::baz, other);
}
但这些不是通用解决方案,也不是很容易维护。
std::mem_fn
是您要查找的内容,因为它会为指向成员的指针生成包装对象。
std::function<void(Test*)> f = std::mem_fn(&Test::third);
f(this);
Test *other = new Test();
f(other);
与 std::bind
一起使用 fun 并引用 Test
对象。这样你就可以用一个新的对象来调用它
void second(std::function<void(const Test&)> fun)
{
Test *other = new Test();
fun(*other);
}
或与this
对象
fun(*this);
在我的 C++ 项目中,我决定尝试新的 ++ 功能。其中一个功能是将函数与 std::function
via std::bind
.
现在我来到一个用例,我必须重新绑定 std::function
。
考虑以下简化代码:
class Test
{
public:
void first()
{
second(bind(&Test::third, this));
}
void second(function<void()> fun)
{
Test *other = new Test();
fun->rebindLastParameter(other); // How can I do this?
fun();
}
void third()
{
// This is called in context of "other"
}
}
如何做 fun->rebindLastParameter(other);
部分(将 this
指针替换为 other
)?
(编辑)上下文:
在我的应用程序中有几个 class 继承自名为 BaseModel
的 class。这些 classes 是从自制的描述语言自动编译而来的。下面的 class 表示一个简单的 "asteroid",它由另外两个 "asteroids":
#pragma once
#include "BaseModel.h"
#include <functional>
using namespace std;
using namespace std::placeholders;
class Test : public BaseModel
{
public:
Test(const OBB& start_obb) : BaseModel(start_obb) {}
void __init() {
scene();
}
void scene() {
combine(bind(&Test::asteroid, this),bind(&Test::asteroid, this));
}
void asteroid() {
translate(random_float(),random_float(),random_float());
sphere(7);
repeat(400,bind(&Test::impact, this));
}
void impact() {
auto p1 = random_surface_point();
select_sphere(p1,random_float() * 2 + 1,1.0);
normalize(p1);
translate_selection(-p1 * random_float() * 0.4);
}
};
问题出在函数 BaseModel::combine
上,它结合了(通过构造立体几何)两个新物体(第 1 个小行星和第 2 个小行星):
void BaseModel::combine(function<void()> left, function<void()> right)
{
BaseModel *leftModel = (BaseModel*) ::operator new (sizeof(BaseModel));
BaseModel *rightModel = (BaseModel*) ::operator new (sizeof(BaseModel));
leftModel->initWithStartOBB(*this->obb);
rightModel->initWithStartOBB(*this->obb);
auto newLeft = bind(left, leftModel); // This does not work
auto newRight = bind(right, rightModel); // This does not work
newLeft();
newRight();
// ... CSG stuff
}
由于leftModel
和rightModel
必须是相同class的新型号,我需要重新绑定我之前的第一个参数给出我自动转译的 class Test::scene
.
也许我走错了路。我希望其他上下文可以解释为什么我 运行 遇到了那个问题。
正如@tobi303 和其他人所指出的,您对 second
参数的函数签名过于受限,因为它采用了空函数。无需重新绑定 - 它不带任何参数。
为了实现您正在尝试做的事情,您需要减少 second
的参数限制。有几种方法可以做到这一点(让它接受一个 function
,或者一个指向成员函数的指针);下面显示了它由一个带有一元参数的函数模板参数化:
#include <functional>
using namespace std;
class Test
{
public:
void first()
{
second([](Test *p){p->third();});
}
template<class Fn>
void second(Fn fn)
{
Test *const other = new Test();
fn(other);
}
void third()
{
// This is called in context of "other"
}
};
int main()
{
Test t;
t.first();
}
个人观点(让我多次投反对票):我认为使用了很多库和技术,大多数 bind
的情况是肯定的,大多数 function
的情况 - 只是在当前的 lambda 之前功能,对它们的需求越来越少。
How can I do the
fun->rebindLastParameter(other);
part (replacethis
pointer withother
)?
一般情况下你不能。
std::function
依赖于 type-erasure 这意味着存储在 std::function
中的对象的原始类型不是该类型的一部分,并且不容易访问。
如果您确定 fun
肯定存储了 bind(&Test::third, this)
返回的对象,则无需重新绑定参数,只需修改 fun
以包含一个完全不同的函数具有正确参数的对象,即
fun = bind(&Test::third, other);
如果你知道它确实存储了一个由 bind( &Test::???, other)
返回的对象,其中 &Test::???
是任何成员函数 完全 与 [=23= 具有相同的签名] 那么你可以这样做:
using binder_type = decltype(bind(&Test::third, this));
if (auto target = fun.target<binder_type>())
{
// *target is the result of the bind() expression
}
但是仍然没有办法修改 *target
处的对象来替换它持有的 Test*
指针。
如果您知道可以使用的函数集,那么您可以这样做:
using binder_foo_type = decltype(bind(&Test::foo, this));
using binder_bar_type = decltype(bind(&Test::bar, this));
using binder_baz_type = decltype(bind(&Test::baz, this));
if (fun.target<binder_foo_type>())
{
fun = std::bind(&Test::foo, other);
}
else if (fun.target<binder_bar_type>())
{
fun = std::bind(&Test::bar, other);
}
else if (fun.target<binder_baz_type>())
{
fun = std::bind(&Test::baz, other);
}
但这些不是通用解决方案,也不是很容易维护。
std::mem_fn
是您要查找的内容,因为它会为指向成员的指针生成包装对象。
std::function<void(Test*)> f = std::mem_fn(&Test::third);
f(this);
Test *other = new Test();
f(other);
与 std::bind
一起使用 fun 并引用 Test
对象。这样你就可以用一个新的对象来调用它
void second(std::function<void(const Test&)> fun)
{
Test *other = new Test();
fun(*other);
}
或与this
对象
fun(*this);