2014-11-27 1 views
6

any의 이점 중 하나는 variant을 초과하지 않습니다. 모든 유형을 지정할 필요가 없으며 포함될 수 있습니다. 사람들이 variant 유형이 늘어날 수 있기 때문에 어떤 유형에서는 any으로 전환하는 경향이 있습니다. 더 이상 모든 유형을 추적하지 않기 때문입니다. 나는 anyvariant 사이의 잡종이 가능하다고 생각한다. 의 "placeholder"(배치를 통해 new)은 aligned_storage에 저장 될 수 있으며, 최대 크기 유형의 샘플에서 constexpr 함수 또는 템플리트 메타 함수로 계산 된 크기가 저장 될 수 있습니다. 반면에 사용자는 any에 포함될 수있는 모든 유형을 지정할 필요가 없습니다. any도 사용자가 aligned_storage보다 큰 값을 저장하려고하면 언제든지 던져 질 수 있습니다.부스트 또는 표준의 "변형"이없는 이유는 무엇입니까?

"variant_any"클래스가 존재합니까? 아이디어에 내재 된 문제가 있습니까?

+0

거기에 그런 유형이 있는지 모릅니다. 그러나 필요하다면'variant '에 타입 중 하나로서'any'를 추가하십시오. (참고 : 나는 이것을 직접 시도하지 않았습니다.) – Niall

+0

'any'는 힙에서 할당합니다. 'variant'는 할 필요가없고'variant_any'는 가질 필요가 없습니다. – user1095108

+0

'any'를 기반으로 한 스택을 가진 하나의 catch는 복사 및 이동 등입니다. 컴파일러가 주어진 유형에 대해 올바른 코드를 생성 할 수있는 방법을 찾아야합니다. 흥미있는 생각. – Niall

답변

2

여기는 기본 some입니다.

T copy/assign/move/etc는 emplace으로 구현할 수 있습니다. can_store<T>을 사용하는 SFINAE는 some이 실제로 저장할 수있는 유형 만 지정할 수 있도록하여 불필요한 예외를 방지합니다.

현재 some에서 이동하면 콘텐츠가 이동하는 대신 내용이 삭제됩니다. 그리고 some은 비어있을 수 있습니다 ("nulllable"입니다).

load_from은 다른 some의 '실패 할 수있는'복사본 생성자입니다. 실패하면 false을 반환합니다. 나는 더 작은 some (복사/할당 연산자조차도)에서 '실패 할 수 없다'를 추가하여 완성 할 수 있습니다.

some_meta은 수동 가상 함수 테이블입니다. 하나는 크기가 someT 유형에 저장됩니다. some이 사용하고자하는 유형 T (이 경우 이동 및 소멸 복사)과 유형 (크기, 정렬 및 유형 ID)에 대한 일부 데이터에 유형 지우기 작업을 저장합니다. 비교 및 직렬화와 같은 추가 작업으로 보강 할 수 있습니다. 2 진 연산의 경우 "일치 유형 없음"을 처리하는 논리를 고려해야합니다. 직렬화 같은 것들에 대해서는 에서 무료 함수 serializedeserialize을 호출해야합니다. 두 경우 모두 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 유형을 반환합니다.

상기 저장된 유형에 의해 저장된 유형에 대한 조작에 의해 생성 된 예외를 제외하고는 예외가 발생하지 않습니다. 가능하면 유형을 맞추기 위해 컴파일 타임에 테스트합니다.

내 데이터의 오프셋에서 시작할 때 더 큰 정렬, 작은 저장 유형에 대한 지원을 추가 할 수 있습니까?

+0

이것은 하이브리드를 향한 '변형'경로입니다. 'any'로 시작하는 다른 경로도 있는데,'C++ '에서 타입 삭제가 가능한 2 가지 방법을 반영합니다. – user1095108

+0

@ user1095108 어떤 방식으로? 초기 실패는 분명합니다. 예외 기반 확장은 쉽습니다. Admittdly 나는'any()'가 가지고있는'.type()'메소드를 가지고 있지 않지만, 그것은 또한 사소한 것이다. 나는 또한'swap '을 놓치고있다. 그러나 그것은 양쪽 인터페이스에있다. 'any_cast '대신에 .get '을 사용하고 있습니까? 그건 나에게 피상적 인 것처럼 보인다. 뭔가 다른게 있니? – Yakk

+0

클래스 템플릿이 좋다. 나는 비판하지 않았다. 타입 지우는 두 가지 방법이 있습니다 : 하나는 함수 포인터를 사용하고, 다른 하나는 가상 함수를 통해 수행합니다. 여러분의 클래스는''any''와 같은''variant''와 비슷합니다. 그것은 제가 말하고 싶은 것입니다. 그러나 여기에는 불가능한 '변형'이있을 수 있습니다. 예를 들어 비교 연산자와 지원 스트림은 성가시다. – user1095108

관련 문제