C++11/14:如果函数存在,则对其进行包装

C++11/14: Wrap a function if it exists

本文关键字:包装 存在 如果 函数 C++11      更新时间:2023-10-16

我想写一个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的所有。