여기는 기본 some
입니다.
T
copy/assign/move/etc는 emplace
으로 구현할 수 있습니다. can_store<T>
을 사용하는 SFINAE는 some
이 실제로 저장할 수있는 유형 만 지정할 수 있도록하여 불필요한 예외를 방지합니다.
현재 some
에서 이동하면 콘텐츠가 이동하는 대신 내용이 삭제됩니다. 그리고 some
은 비어있을 수 있습니다 ("nulllable"입니다).
load_from
은 다른 some
의 '실패 할 수있는'복사본 생성자입니다. 실패하면 false
을 반환합니다. 나는 더 작은 some
(복사/할당 연산자조차도)에서 '실패 할 수 없다'를 추가하여 완성 할 수 있습니다.
some_meta
은 수동 가상 함수 테이블입니다. 하나는 크기가 some
인 T
유형에 저장됩니다. some
이 사용하고자하는 유형 T
(이 경우 이동 및 소멸 복사)과 유형 (크기, 정렬 및 유형 ID)에 대한 일부 데이터에 유형 지우기 작업을 저장합니다. 비교 및 직렬화와 같은 추가 작업으로 보강 할 수 있습니다. 2 진 연산의 경우 "일치 유형 없음"을 처리하는 논리를 고려해야합니다. 직렬화 같은 것들에 대해서는 에서 무료 함수 serialize
과 deserialize
을 호출해야합니다. 두 경우 모두 some
에 저장할 수있는 추가 요구 사항을 부과합니다 (약간의 작업만으로 "어쩌면 직렬화"를 처리 할 수 있지만 엉망이 될 수 있습니다).
데이터 (바이너리 및 단항)에서 수행 할 연산 집합을 저장하고 전달 된 연산을 번들로 전달할 수있는 시스템을 상상할 수도 있습니다. 이 시점에서 우리는 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)...}) >;
meta
개체는 기본적으로 수동으로 구현 된 가상 함수 테이블입니다. 주어진 some
의 메모리 오버 헤드를 하나의 포인터 (저장소 버퍼 위에 있음)로 줄입니다. 위에서 알 수 있듯이
live example
, 그것은 매우 실용적이다.
create
은 두 번 이상 호출해도 동일한 유형 T
에 대해 동일한 meta
에 대한 포인터를 반환한다는 점에 유의하십시오.
위의 테스트에서 코드 경로의 절반 정도를 연습했습니다. 다른 것들은 아마도 버그를 가지고있을 것입니다.
some_that_fits
을 사용하면 일련의 유형을 전달할 수 있으며 해당 유형에 맞는 some
유형을 반환합니다.
상기 저장된 유형에 의해 저장된 유형에 대한 조작에 의해 생성 된 예외를 제외하고는 예외가 발생하지 않습니다. 가능하면 유형을 맞추기 위해 컴파일 타임에 테스트합니다.
내 데이터의 오프셋에서 시작할 때 더 큰 정렬, 작은 저장 유형에 대한 지원을 추가 할 수 있습니까?
거기에 그런 유형이 있는지 모릅니다. 그러나 필요하다면'variant '에 타입 중 하나로서'any'를 추가하십시오. (참고 : 나는 이것을 직접 시도하지 않았습니다.) – Niall
'any'는 힙에서 할당합니다. 'variant'는 할 필요가없고'variant_any'는 가질 필요가 없습니다. – user1095108
'any'를 기반으로 한 스택을 가진 하나의 catch는 복사 및 이동 등입니다. 컴파일러가 주어진 유형에 대해 올바른 코드를 생성 할 수있는 방법을 찾아야합니다. 흥미있는 생각. – Niall