c++ - 类型不可知的 getter 方法

标签 c++ c++11

我正在为一个系统编写一个客户端,该系统以随机顺序返回自然类型的值(有些可以是 int,有些可以是 float,有些是 string [嗯,几乎是自然的])。问题是,我不知道编译时值的类型。

由于在查询远程系统之前我不知道要返回的值的类型,所以提供统一接口(interface)以允许客户端库的用户提取正确类型的值的最佳方法是什么?

如果查询远程系统一次返回一个字符串,我想要我的 get_value()返回一个字符串。如果是 int,则使其返回 int。或者,如何让客户端库以正确的类型调用 getter?

我想带有类型提示的模板是实现这一目标的好方法?

最佳答案

如果支持类型的列表有限,请检查 boost 或 std 变体。

如果不是有限列表,则 boost 或 std any(或包含 any 的变体)。

您也可以找到其他实现。标准版本在 C++17 中。

变体的简化版本可能会用 100 或两行代码编写。

这是一个粗略的 C++14 变体:

constexpr std::size_t max() { return 0; }
template<class...Ts>
constexpr std::size_t max( std::size_t t0, Ts...ts ) {
    return (t0<max(ts...))?max(ts...):t0;
}
template<class T0, class...Ts>
struct index_of_in;
template<class T0, class...Ts>
struct index_of_in<T0, T0, Ts...>:std::integral_constant<std::size_t, 0> {};
template<class T0, class T1, class...Ts>
struct index_of_in<T0, T1, Ts...>:
    std::integral_constant<std::size_t,
        index_of_in<T0, Ts...>::value+1
    >
{};

struct variant_vtable {
  void(*dtor)(void*) = 0;
  void(*copy)(void*, void const*) = 0;
  void(*move)(void*, void*) = 0;
};
template<class T>
void populate_vtable( variant_vtable* vtable ) {
  vtable->dtor = [](void* ptr){ static_cast<T*>(ptr)->~T(); };
  vtable->copy = [](void* dest, void const* src){
    ::new(dest) T(*static_cast<T const*>(src));
  };
  vtable->move = [](void* dest, void* src){
    ::new(dest) T(std::move(*static_cast<T*>(src)));
  };
}
template<class T>
variant_vtable make_vtable() {
  variant_vtable r;
  populate_vtable<T>(&r);
  return r;
}
template<class T>
variant_vtable const* get_vtable() {
  static const variant_vtable table = make_vtable<T>();
  return &table;
}
template<class T0, class...Ts>
struct my_variant {
  std::size_t index = -1;
  variant_vtable const* vtable = 0;
  static constexpr auto data_size = max(sizeof(T0),sizeof(Ts)...);
  static constexpr auto data_align = max(alignof(T0),alignof(Ts)...);
  template<class T>
  static constexpr std::size_t index_of() {
      return index_of_in<T, T0, Ts...>::value;
  }
  typename std::aligned_storage< data_size, data_align >::type data;
  template<class T>
  T* get() {
    if (index_of<T>() == index)
      return static_cast<T*>((void*)&data);
    else
      return nullptr;
  }
  template<class T>
  T const* get() const {
    return const_cast<my_variant*>(this)->get<T>();
  }
  template<class F, class R>
  using applicator = R(*)(F&&, my_variant*);
  template<class T, class F, class R>
  static applicator<F, R> get_applicator() {
    return [](F&& f, my_variant* ptr)->R {
      return std::forward<F>(f)( *ptr->get<T>() );
    };
  }
  template<class F, class R=typename std::result_of<F(T0&)>::type>
  R visit( F&& f ) & {
    if (index == (std::size_t)-1) throw std::invalid_argument("variant");
    static const applicator<F, R> table[] = {
      get_applicator<T0, F, R>(),
      get_applicator<Ts, F, R>()...
    };
    return table[index]( std::forward<F>(f), this );
  }
  template<class F,
    class R=typename std::result_of<F(T0 const&)>::type
  >
  R visit( F&& f ) const& {
    return const_cast<my_variant*>(this)->visit(
      [&f](auto const& v)->R
      {
        return std::forward<F>(f)(v);
      }
    );
  }
  template<class F,
    class R=typename std::result_of<F(T0&&)>::type
  >
  R visit( F&& f ) && {
    return visit( [&f](auto& v)->R {
      return std::forward<F>(f)(std::move(v));
    } );
  }
  explicit operator bool() const { return vtable; }
  template<class T, class...Args>
  void emplace( Args&&...args ) {
    clear();
    ::new( (void*)&data ) T(std::forward<Args>(args)...);
    index = index_of<T>();
    vtable = get_vtable<T>();
  }
  void clear() {
    if (!vtable) return;
    vtable->dtor( &data );
    index = -1;
    vtable = nullptr;
  }
  ~my_variant() { clear(); }

  my_variant() {}
  void copy_from( my_variant const& o ) {
    if (this == &o) return;
    clear();
    if (!o.vtable) return;
    o.vtable->copy( &data, &o.data );
    vtable = o.vtable;
    index = o.index;
  }
  void move_from( my_variant&& o ) {
    if (this == &o) return;
    clear();
    if (!o.vtable) return;
    o.vtable->move( &data, &o.data );
    vtable = o.vtable;
    index = o.index;
  }
  my_variant( my_variant const& o ) {
    copy_from(o);
  }
  my_variant( my_variant && o ) {
    move_from(std::move(o));
  }
  my_variant& operator=(my_variant const& o) {
    copy_from(o);
    return *this;
  }
  my_variant& operator=(my_variant&& o) {
    move_from(std::move(o));
    return *this;
  }
  template<class T,
    typename std::enable_if<!std::is_same<typename std::decay<T>::type, my_variant>{}, int>::type =0
  >
  my_variant( T&& t ) {
    emplace<typename std::decay<T>::type>(std::forward<T>(t));
  }
};

Live example .

转换为 C++11 将包括一堆用助手替换 lambda。我不喜欢用 C++11 编写,而这个 C++14 主要是对它的机械转换。

它很粗糙,因为 visit除其他原因外,仅采用一种变体并返回 void。

代码几乎完全未经测试,但设计是合理的。

关于c++ - 类型不可知的 getter 方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45419561/

相关文章:

c++ - 我如何找到这段代码的时间复杂度(Big O)?

c++ - RVO 在失败时强制编译错误

c++ - Visual Studio 2012 __cplusplus 和 C++ 11

c++ - 虚拟 final方法是否比根类中的非虚拟方法更好?

c++ - std::function 和错误:没有匹配的函数来调用

c++ - C、C++、python、perl在Web开发中的作用

c++ - 如何在 Xcode 4.6 中使用 googletest 实现 C++ 单元测试?

c++ - 使用 OpenCL 加速 Tesseract

c++ - 在数组中倒数。 [-1]

c++ - 如何在不过于冗长的情况下获得可变参数模板函数的正确解析