C++11/14:如果函数存在,则对其进行包装
C++11/14: Wrap a function if it exists
我想写一个wrapper
类(非常像一个代理),它聚合一个对象,并向它转发成员函数调用。这在使用可变模板和decltype
的C++11/14中是微不足道的。我的问题是,有些成员函数被包装的对象可能支持,也可能不支持。
我想出了一个似乎有效的解决方案,然而,它看起来非常笨拙,我正在寻求简化。特别是,我担心这在编译时可能会非常昂贵(有许多函数需要封装)。这种笨拙来自于需要指定函数的返回类型,而不需要对要阻塞的内容进行decltype。
有人会有更好的主意吗?
下面的代码也可以实时使用。
#include <iostream>
#include <utility>
/// Compute the result type of a member function call, or void if invalid.
#define RESULT_OF(Name)
template <typename T>
class result_impl_ ## Name
{
public:
/* Type made public to please Clang 3.7. */
template <typename C, typename... Args>
static auto Type(void*)
-> decltype(std::declval<C>().Name(std::declval<Args>()...));
template <typename, typename...>
static void Type(...);
template <typename... Args>
using type = decltype(Type<T, Args...>(0));
};
template <typename T, typename... Args>
using maybe_result_of_ ## Name
= typename result_impl_ ## Name<T>::template type<Args...>
/// Forward to function Name, if is exists.
#define FORWARD(Name)
template <typename... Args>
auto Name(Args&&... args)
-> maybe_result_of_ ## Name<Base, Args...>
{
return base.Name(std::forward<Args>(args)...);
}
#define DEFINE(Name)
RESULT_OF(Name);
FORWARD(Name)
template <typename Base>
struct wrapper
{
Base base;
DEFINE(foo);
DEFINE(bar);
};
#define PING()
std::cerr << __PRETTY_FUNCTION__ << 'n'
struct foo_no_bar
{
void foo(int) const { PING(); }
int foo(double) const { PING(); return 1; }
int foo(double, double) const { PING(); return 1; }
};
struct foo_and_bar
{
void foo() const { PING(); }
void bar() { PING(); }
};
int main()
{
wrapper<foo_and_bar> f;
f.foo();
f.bar();
wrapper<foo_no_bar> b;
b.foo(1);
b.foo(1.0);
b.foo(1.0, 2.0);
}
所以我把你在宏中所做的一堆工作拿了出来。
can_apply_t
采用一个template<class...>class
和一组类型,如果这些类型可以合法地应用于模板,则为true。
template<class...>struct voider { using type=void; };
template<class...Ts>using void_t=typename voider<Ts...>::type;
template<class...>struct types{ using type=types; };
namespace details {
template<template<class...>class Z, class types, class=void>
struct can_apply : std::false_type {};
template<template<class...>class Z, class...Ts>
struct can_apply<Z, types<Ts...>, void_t< Z<Ts...> > >:
std::true_type
{};
}
template<template<class...>class Z, class...Ts>
using can_apply_t = details::can_apply<Z, types<Ts...>>;
然后我们来替换您的宏。我将方法名称与对象名称解耦,并通过几个步骤来实现。对于每个方法,这些步骤可以在类的外部完成:
#define CALL_METHOD_RESULT(Name, Method)
template<class Sig, class=void>
struct Name {};
template<class C, class...Ts>
struct Name< C(Ts...), void_t<
decltype( std::declval<C>().Method(std::declval<Ts>()...) )
>> {
using type = decltype( std::declval<C>().Method(std::declval<Ts>()...) );
};
template<class Sig>
using Name ## _t = typename Name< Sig >::type
上面定义了Name_t
,这是一个特性类,它接受Object(Args...)
,并在SFINAE友好上下文中告诉Object.Method(Args...)
的返回类型。
接下来,我们使用这个和上面的can_apply_t
:构建一个can_call_Method
帮助模板
#define CAN_CALL_METHOD( Method )
CALL_METHOD_RESULT( call_ ## Method, Method );
template<class Sig>
using can_call_ ## Method = can_apply_t< call_ ## Method ## _t, Sig >
接下来,为Name
生成一对过载的FORWARD
宏,其中一个调用target.Method(Args...)
,其中Target target
,而另一个仅在第一个不是时才被考虑,并且明确地=delete
调用以生成可能更好的错误消息。
/// Forward to function Name, if is exists.
#define FORWARD(Name, Method, Target, target)
template <class... Args>
auto Name(Args&&... args)
-> call_ ## Method ## _t<Target(Args...)>
{
return target.Method(std::forward<Args>(args)...);
}
template <class...Args>
std::enable_if_t<!can_call_ ## Method <Target(Args...)>{}>
Name ( Args&&...) = delete
现在,我们在一个类似的DEFINE宏中总结以上内容。在这里,我们将所有内容重新组合为一个名称:
#define DEFINE(Name)
CAN_CALL_METHOD(Name);
FORWARD(Name, Name, Base, base)
实例
如果您愿意,我们还可以让=delete
方法做其他事情,比如什么都不做。
然而,请注意,如果我们省略整个=delete
方法,我们实际上可以在两个子对象之间进行受控调度(即使具有优先级!)
完成这一切的一个更简单的方法当然是
Base* operator->(){ return &base; }
Base const* operator->()const{ return &base; }
使您可以将CCD_ 17作为CCD_。它确实暴露了关于Base
的所有。
- C++模板来检查友元函数的存在
- 如何在c++17中制作一个模板包装器/装饰器
- 既然存在危险,为什么项目要使用-I include开关
- 我们可以访问一个不存在的联盟的成员吗
- std::vector的包装器,使数组的结构看起来像结构的数组
- C++:对不存在的命名空间使用命名空间指令
- 如何在c++迭代器类型中包装std::chrono
- 是否可以用"iostream"包装现有的TCP/OOpenSSL会话
- C++quit()函数中可能存在作用域问题
- C++擦除(如果存在)
- 用pybind11包装C++抽象类时出错
- g++ 说函数不存在,即使包含正确的标头
- 这个极客对极客的trie实现是否存在内存泄漏问题
- 有了gcc,是否可以链接库,但前提是它存在
- 为左值和右值的包装器实现C++范围
- C++LinkedList问题.数据类型之间存在冲突?没有匹配的构造函数
- C结构,其指针将被包装在unique_ptr中
- C++11/14:如果函数存在,则对其进行包装
- 如何在预先存在的3个标准压缩行/列数组上包装Eigen::SparseMatrix
- 为已经存在的注册表类使用包装类是一种好方法吗?