gpt4 book ai didi

c++ - FastDelegate 的 Variadic 版本和额外的值拷贝

转载 作者:太空狗 更新时间:2023-10-29 23:08:31 25 4
gpt4 key购买 nike

我正在移植 FastDelegate使用可变参数模板到 C++0x。

    #include "FastDelegate.h"

template<class R=fastdelegate::detail::DefaultVoid, class ...P>
class fast_delegate_base {
private:
typedef typename fastdelegate::detail::DefaultVoidToVoid<R>::type desired_ret_t;
typedef desired_ret_t (*static_func_ptr)(P...);
typedef R (*unvoid_static_func_ptr)(P...);
typedef R (fastdelegate::detail::GenericClass::*generic_mem_fn)(P...);
typedef fastdelegate::detail::ClosurePtr<generic_mem_fn, static_func_ptr, unvoid_static_func_ptr> closure_t;
closure_t closure_;
public:
// Typedefs to aid generic programming
typedef fast_delegate_base type;

// Construction and comparison functions
fast_delegate_base() { clear(); }

fast_delegate_base(const fast_delegate_base &x)
{
closure_.CopyFrom(this, x.closure_);
}

void operator = (const fast_delegate_base &x)
{
closure_.CopyFrom(this, x.closure_);
}
bool operator ==(const fast_delegate_base &x) const
{
return closure_.IsEqual(x.closure_);
}
bool operator !=(const fast_delegate_base &x) const
{
return !closure_.IsEqual(x.closure_);
}
bool operator <(const fast_delegate_base &x) const
{
return closure_.IsLess(x.closure_);
}
bool operator >(const fast_delegate_base &x) const
{
return x.closure_.IsLess(closure_);
}

// Binding to non-const member functions
template<class X, class Y>
fast_delegate_base(Y *pthis, desired_ret_t (X::* function_to_bind)(P...) )
{
closure_.bindmemfunc(fastdelegate::detail::implicit_cast<X*>(pthis), function_to_bind);
}

template<class X, class Y>
inline void bind(Y *pthis, desired_ret_t (X::* function_to_bind)(P...))
{
closure_.bindmemfunc(fastdelegate::detail::implicit_cast<X*>(pthis), function_to_bind);
}

// Binding to const member functions.
template<class X, class Y>
fast_delegate_base(const Y *pthis, desired_ret_t (X::* function_to_bind)(P...) const)
{
closure_.bindconstmemfunc(fastdelegate::detail::implicit_cast<const X*>(pthis), function_to_bind);
}

template<class X, class Y>
inline void bind(const Y *pthis, desired_ret_t (X::* function_to_bind)(P...) const)
{
closure_.bindconstmemfunc(fastdelegate::detail::implicit_cast<const X *>(pthis), function_to_bind);
}

// Static functions. We convert them into a member function call.
// This constructor also provides implicit conversion
fast_delegate_base(desired_ret_t (*function_to_bind)(P...) )
{
bind(function_to_bind);
}

// for efficiency, prevent creation of a temporary
void operator = (desired_ret_t (*function_to_bind)(P...) )
{
bind(function_to_bind);
}

inline void bind(desired_ret_t (*function_to_bind)(P...))
{
closure_.bindstaticfunc(this, &fast_delegate_base::invoke_static_func, function_to_bind);
}

// Invoke the delegate
template<typename ...A>
R operator()(A&&... args) const
{
return (closure_.GetClosureThis()->*(closure_.GetClosureMemPtr()))(std::forward<A>(args)...);
}
// Implicit conversion to "bool" using the safe_bool idiom

private:
typedef struct safe_bool_struct
{
int a_data_pointer_to_this_is_0_on_buggy_compilers;
static_func_ptr m_nonzero;
} useless_typedef;
typedef static_func_ptr safe_bool_struct::*unspecified_bool_type;
public:
operator unspecified_bool_type() const { return empty()? 0: &safe_bool_struct::m_nonzero; }
// necessary to allow ==0 to work despite the safe_bool idiom
inline bool operator==(static_func_ptr funcptr) { return closure_.IsEqualToStaticFuncPtr(funcptr); }
inline bool operator!=(static_func_ptr funcptr) { return !closure_.IsEqualToStaticFuncPtr(funcptr); }
// Is it bound to anything?
inline bool operator ! () const { return !closure_; }
inline bool empty() const { return !closure_; }
void clear() { closure_.clear();}
// Conversion to and from the DelegateMemento storage class
const fastdelegate::DelegateMemento & GetMemento() { return closure_; }
void SetMemento(const fastdelegate::DelegateMemento &any) { closure_.CopyFrom(this, any); }

private:
// Invoker for static functions
R invoke_static_func(P... args) const
{
return (*(closure_.GetStaticFunction()))(args...);
}
};

// fast_delegate<> is similar to std::function, but it has comparison operators.
template<typename _Signature>
class fast_delegate;

template<typename R, typename ...P>
class fast_delegate<R(P...)> : public fast_delegate_base<R, P...>
{
public:
typedef fast_delegate_base<R, P...> BaseType;

fast_delegate() : BaseType() { }

template<class X, class Y>
fast_delegate(Y * pthis, R (X::* function_to_bind)(P...))
: BaseType(pthis, function_to_bind)
{ }

template<class X, class Y>
fast_delegate(const Y *pthis, R (X::* function_to_bind)(P...) const)
: BaseType(pthis, function_to_bind)
{ }

fast_delegate(R (*function_to_bind)(P...))
: BaseType(function_to_bind)
{ }

void operator = (const BaseType &x)
{
*static_cast<BaseType*>(this) = x;
}
};

但是,我的实现的一个局限性是,当使用非成员函数时,如果该函数按值接受参数,则会为每个参数复制一个额外的值。我假设这发生在 fast_delegate_base::operator()()fast_delegate_base::invoke_static_func() 之间。

我试图让 fast_delegate_base::invoke_static_func() 接受右值参数,但失败了。

例如:

class C1
{
public:
C1() { printf("C1()\n"); }
~C1() { printf("~C1()\n"); }
C1(const C1&)
{
printf("C1(const C1&)\n");
}

int test(int t) const
{
printf("C1::test(%d)\n", t);
return 1;
}
};

int test(C1 c)
{
c.test(1234);
return 1;
}

// ...

C1 c1;
fast_delegate<int(C1)> t1(test);
t1(c1);

这段代码的结果是:

C1()
C1(const C1&)
C1(const C1&)
C1::test(1234)
~C1()
~C1()
~C1()

您有什么想法可以避免这种额外的值(value)复制吗?

最佳答案

在我看来,这个拷贝是类设计中固有的,特别是 invoke_static_func 的存在。

据我所知,这是将静态函数和成员函数规范化为成员函数的代理,因此它们的每次分派(dispatch)都可以作为成员函数调用来完成。唯一的区别是该成员是 fast_delegate_base 实例,而不是目标函数所属的任何类的实例。

因此在调用静态函数时有一个额外的调用框架,要摆脱这个额外的拷贝,您需要使额外的调用框架 (invoke_static_func) 通过引用获取其参数(忽略现在,如果参数类型不是值,则此操作的后果)。

不幸的是,invoke_static_func 需要通过一个函数指针来调用,该函数指针具有包含值类型的参数列表,因此 operator() 被迫进行复制以便调用函数指针(即调用 invoke_static_func)。让 invoke_static_func 通过引用获取参数没有帮助,因为它仍然必须通过没有引用参数类型的函数指针调用。

并且 invoke_static_func 无法避免制作拷贝以调用 test(C1),这只是一个简单的按值调用 - 因此您需要两个拷贝才能使此设计生效。


从不同的角度来解释它,从纯 C 的角度来看:

Operator() 需要调用函数func (this_ptr, arg_1, arg_2, arg_3)。目标函数期望这些参数位于特定的寄存器或特定的堆栈位置,具体取决于它们在参数列表中的位置和大小。

但是静态函数没有神奇的第一个“this”参数,它的签名只是func(arg_1, arg_2, arg_3)。所以它期望所有其他参数与相应的成员函数位于不同的寄存器和/或堆栈位置。因此,您需要该拷贝将参数移动到正确的寄存器/堆栈位置,以符合静态函数的调用约定。

这基本上意味着您无法通过这种设计避免静态函数的第二个拷贝。


但是...您可以通过一些狡猾的模板元编程来改进这一点,以在 invoke_static_func 的实现中将 std::move 应用于值类型参数,从而减少对拷贝的调用开销和一个 Action ,几乎和一个拷贝一样好。

如果我认为这是否可能(以及如果可能的话如何),我会更新此答案。


编辑

像这样的东西应该可以解决问题:

template <bool IsClass, class U>
struct move_if_class
{
template <typename T>
T&& operator()(const T& t) { return std::move(const_cast<T&>(t)); }
};

template <class T>
struct move_if_class<false,T>
{
T&& operator()(typename std::remove_reference<T>::type& t) { return std::forward<T>(t); }
T&& operator()(typename std::remove_reference<T>::type&& t) { return std::forward<T>(t); }
};

R invoke_static_func(P... args) const
{
return (*(closure_.GetStaticFunction()))(move_if_class<std::is_class<P>::value,P>()(args)...);
}

在添加一个移动 c'tor 之后:

C1()
C1(const C1&)
C1(C1&&)
C1::test(1234)
~C1()
~C1()
~C1()

关于c++ - FastDelegate 的 Variadic 版本和额外的值拷贝,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9235847/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com