简体   繁体   English

为什么在Boost或Standard中没有任何“变体”?

[英]Why no “variant” any in boost or Standard?

One advantage of any over variant is, that one does not need to specify all types, that it may contain. 的一个优点any超过variant是,一个并不需要指定所有类型,它可能含有。 I've noticed, that as the number of types a variant may contain grows, people tend to switch to any at some point, because they simply don't keep track of all the types anymore. 我注意到,随着variant可能包含的类型数量的增加,人们倾向于在某个时候切换到any ,因为他们根本不再跟踪所有类型。 I think a hybrid between any and variant is possible. 我认为anyvariant之间的混合都是可能的。 One could store the " placeholder " (via placement new ) of any in aligned_storage , with the size calculated in a constexpr function or template metafunction, from a sample of the largest types, that may end up being stored. 一个可以存储“ placeholder ”(通过放置new )的anyaligned_storage ,与在所计算的大小constexpr功能或模板元函数,从最大的类型的一个样本,其可以最终被存储。 The user, on the other hand, would not need to specify all the types, that an any might contain. 另一方面,用户不需要指定any可能包含的所有类型。 The any could also throw at any time, if the user would try to store something larger than the aligned_storage in there. 如果用户尝试在其中存储大于aligned_storage ,则any可能抛出。

Does such a " variant_any " class exist? 是否存在这样的“ variant_any ”类? Is there some inherent problem with the idea? 这个想法有一些内在的问题吗?

Here is a basic some . 这是基本的some

The T copy/assign/move/etc can be implemented in terms of emplace . T复制/分配/移动/等可以根据emplace来实现。 SFINAE using can_store<T> can ensure that only types the some can actually store are assignable to it, avoiding needless exceptions. SFINAE使用can_store<T>可以确保只有some类型可以实际存储的类型才可以分配给它,避免不必要的异常。

Currently, moving from some destroys its contents instead of just moving from it. 当前,从some移动会破坏其内容,而不仅仅是移动它。 And a some can be empty (they are "nulllable"). 并且some可以为空(它们是“不可占用的”)。

load_from is a 'can-fail' copy constructor from another some -- it returns false on failure. load_from是从另一个“能不能倒”的拷贝构造函数some -它返回false失败。 I could add a 'cannot-fail' from a smaller some (even a copy/assignment operator) to complete it. 我可以从一小some (甚至是复制/分配运算符)中添加一个“无法失败”来完成它。

some_meta is a manual virtual function table. some_meta是一个手动虚拟功能表。 One exists per type T you store in a some of any size. 每种类型存在一个T你在商店里some任意大小的。 It stores the type-erased operations on the type T that some wants to use (in this case, copy move and destroy), plus some data about the type (size, alignment and type identity). 它在some要使用的类型T上存储了类型擦除的操作(在这种情况下,是复制移动和销毁),以及一些有关该类型的数据(大小,对齐和类型标识)。 It could be augmented with additional operations like comparison and serialization. 可以通过其他操作(例如比较和序列化)来增强它。 For binary operations, logic to handle "no matching type" has to be considered. 对于二进制操作,必须考虑处理“无匹配类型”的逻辑。 For stuff like serialization, I'd have it call the free function serialize and deserialize on the T . 对于诸如序列化之类的东西,我会让它在T上调用自由函数serializedeserialize serialize In both cases, we impose additional requirements on what some can store (you can, with a bit of work, handle "maybe serialize", but that gets messy). 在这两种情况下,我们对some存储内容都施加了附加要求(您可以花some工作来处理“也许序列化”,但这很麻烦)。

You could even imagine a system where you can store a set of operations to perform on the data (binary and unary) and pass said operations bundled in types passed to some. 您甚至可以想象一个系统,在其中可以存储一组对数据(二进制和一元数据)执行的操作,然后将捆绑在一起的所述操作传递给某些类型。 At this point, we are approaching boost 's type erasure library, however. 此时,我们正在接近boost的类型擦除库。

namespace details {
template<std::size_t Size, std::size_t Align=0>
struct storage_helper {
  using type = std::aligned_storage_t<Size, Align>;
  enum { alignment = alignof(type), size = Size };
};
template<std::size_t Size>
struct storage_helper<Size, 0> {
  using type = std::aligned_storage_t<Size>;
  enum { alignment = alignof(type), size = Size };
};
template<std::size_t size, std::size_t align>
using storage_helper_t = typename storage_helper<size,align>::type;

template<class T>using type=T;
struct some_meta {
  type<void(void*)>* destroy;
  type<void(void* dest, void const* src)>* copy;
  type<void(void* dest, void* src)>* move;
  std::type_index type;
  size_t size;
  size_t align;
  template<class T> static some_meta const* get() {
    static const some_meta retval( create<T>() );
    return &retval;
  };
  private:
  template<class T> static some_meta create() {
    return {
        [](void* p){ ((T*)p)->~T(); },
        [](void* out, void const* in){ new(out)T(*(T*)in); },
        [](void* dest, void* src) { new(dest)T(std::move(*(T*)src)); },
        typeid(T),
        sizeof(T),
        alignof(T)
    };
  }
};
}

template<class>struct emplace_as{};

template< std::size_t size, std::size_t Align=0 >
struct some {
  enum { align = details::storage_helper<size, Align>::alignment };
  using data_type = details::storage_helper_t<size, Align>;

  template<size_t, size_t> friend struct some;
  template<class T> struct can_store :
    std::integral_constant< bool, ((align%alignof(T))==0) && sizeof(T) <= size) >
  {};

  template<size_t x, size_t a>
  static bool can_fit( some<x,a> const& o ) {
    if (x<=size && ((align%some<x,a>::align)==0)) return true; // should cause optimizations
    if (!o.meta) return true;
    if (o.meta->size > size) return false;
    if (o.meta->align > align) return false;
    return true;
  }
private:
  data_type data;
  details::some_meta const* meta = nullptr;
public:
  // true iif we are (exactly) a T
  template<class T>
  bool is() const {
      return meta && (meta->type == typeid(T));
  }

  explicit operator bool()const { return meta!=nullptr; }

  template<class T>
  T* unsafe_get() { return reinterpret_cast<T*>(&data); }

  template<class T>
  T* get() { if (is<T>()) return unsafe_get<T>(); else return nullptr; }

  void clear() { if (meta) meta->destroy(&data); meta = nullptr; }

  template<class T, class... Args>
  std::enable_if_t< can_store<T>{} >
  emplace(Args&&...args) {
    clear();

    new(&data) T(std::forward<Args>(args)...);
    meta = details::some_meta::get<T>();
  }
  some()=default;
  some(some const& o) {
    *this = o;
  }
  some(some const&&o):some(o){}
  some(some&o):some(const_cast<some const&>(o)){}
  some(some&& o) {
    *this = std::move(o);
  }

  some& operator=(some const&o) {
    if (this == &o) return *this;
    clear();
    if (o.meta) {
      o.meta->copy( &data, &o.data );
      meta=o.meta;
    }
    return *this;
  }        
  some& operator=(some &&o) {
    if (this == &o) return *this;
    clear();
    if (o.meta) {
      o.meta->move( &data, &o.data );
      meta=o.meta;
      o.clear();
    }
    return *this;
  }
  some& operator=(some const&&o) { return *this=o; }
  some& operator=(some &o) { return *this=const_cast<some const&>(o); }

  // from non-some:
  template<class T,class=std::enable_if_t<can_store<std::decay_t<T>>{}>>
  some(T&& t){
    emplace<std::decay_t<T>>(std::forward<T>(t));
  }
  template<class T, class...Args,class=std::enable_if_t<can_store<T>{}>>
  some( emplace_as<T>, Args&&...args ){
    emplace<T>(std::forward<Args>(args)...);
  }
  template<class T,class=std::enable_if_t<can_store<std::decay_t<T>>{}>>
  some& operator=(T&&t){
    emplace<std::decay_t<T>>(std::forward<T>(t));
    return *this;
  }

  template<size_t x, size_t a>
  bool load_from( some<x,a> const& o ) {
    if ((void*)&o==this) return true;
    if (!can_fit(o)) return false;
    clear();
    if (o.meta) {
      o.meta->copy( &data, &o.data );
      meta=o.meta;
    }
    return true;
  }
  template<size_t x, size_t a>
  bool load_from( some<x,a> && o ) {
    if ((void*)&o==this) return true;
    if (!can_fit(o)) return false;
    clear();
    if (o.meta) {
      o.meta->move( &data, &o.data );
      meta=o.meta;
      o.clear();
    }
    return true;
  }
  ~some() { clear(); }
};

template<class T, class...Ts>
using some_that_fits = some< (std::max)({sizeof(T),sizeof(Ts)...}), (std::max)({alignof(T),alignof(Ts)...}) >;

the meta object is a manually implemented virtual function table, basically. meta对象基本上是一个手动实现的虚拟功能表。 It reduces the memory overhead of a given some to one pointer (above its storage buffer). 它减少了给定内存开销some到一个指针(高于其贮存缓冲液)。

live example 现场例子

As demonstrated above, it is quite viable. 如上所述,这是非常可行的。

Note that create returns a pointer to the same meta for the same type T , even if called more than once. 请注意,即使多次调用, create也会为相同类型T返回指向相同meta的指针。

I have exercised about half the code paths in my test above. 在上面的测试中,我已经练习了大约一半的代码路径。 The others probably have bugs. 其他人可能有错误。

some_that_fits lets you pass a set of types, and it returns a some type that fits those types. some_that_fits允许您传递一组类型,并返回适合这些类型的some类型。

No exceptions, other than those generated by the operations on the stored types by said stored types, are thrown. 除了由所述存储类型对存储类型的操作所生成的异常以外,不会引发任何异常。 When possible, I test at compile time to ensure types fit. 如果可能,我会在编译时进行测试以确保类型适合。

I could possibly add support for greater alignment, small storage types by starting them at an offset into my data? 我可以通过在数据偏移量处启动小存储类型来增加对更大对齐的支持?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM