2012-04-03 42 views
4

我有一套簡單得多的要求,不需要很多變體的機器。如果我能幫上忙的話,我也不想依靠提振。如何製作boost :: variant的簡化版本?

我需要存儲編譯時已知的任意類型(可能是void)。它可以是可移植的,也可以是可複製的,如果其中任何一個拋出,則允許所含值不確定。

而不是此值,它也可能包含::std::exception_ptr::std::error_code

::boost::variant<T, ::std::exception_ptr, ::std::error_code>將工作,如果T被允許是無效的。除此之外,::boost::variant提供了我在這種情況下實際並不需要的'永不保證'。而且,如果我理解它是如何正確工作的,它與可移動但不能複製的類型不兼容。

現在我正在寫很多重複的代碼,我不應該編寫來分別處理這些類型。我還存儲了每種類型的對象的副本,並標記了哪些相關的值。最後,void給出了整個系統的內容,並要求我在各處寫專業化。

有沒有更好的方法?

這裏是我所擁有的一個簡單例子。這基本上是用來固定結果傳輸到另一個線程,有點像一個有前途的類:

template <typename ResultType> 
class stored_result { 
public: 
    stored_result() : is_val_(false), is_err_(false), is_exception_(false) { } 

    void set_bad_result(::std::error err) { 
     is_err_ = true; 
     error_ = ::std::move(err); 
    } 
    void set_bad_result(::std::exception_ptr exception) { 
     is_exception_ = true; 
     exception_ = ::std::move(exception); 
    } 
    void set_result(ResultType res) { 
     is_val_ = true; 
     val_ = ::std::move(res); 
    } 

    ResultType result() { 
     if (is_val_) { 
     is_val_ = false; 
     return ::std::move(val_); 
     } else if (is_exception_) { 
     is_exception_ = false; 
     ::std::rethrow_exception(::std::move(exception_)); 
     } else if (is_error_) { 
     is_error_ = false; 
     throw ::std::system_error(error_); 
     } else { 
     throw ::std::runtime_error("Asked for result when there was none."); 
     } 
    } 

private: 
    bool is_val_, is_err_, is_exception_; 
    T val_; 
    ::std::exception_ptr exception_; 
    ::std::error_code error_; 
}; 
+0

您可以以透明的方式將'void'映射到唯一的空白類型給用戶。這將允許您重用'boost :: variant',但不需要專業化幫助。 'boost :: variant'的最大亮點是移動結構。 – 2012-04-03 03:10:29

+0

這聽起來很像過早的優化。 – 2012-04-03 03:12:49

+0

@LucDanton:_nod_是的,我在另一個答案中看到了這一點。我想我可以把我的頭圍繞着它如何工作。 – Omnifarious 2012-04-03 03:13:14

回答

2

如何以透明的方式處理void一個例子與代碼,你現在所擁有的:

struct empty_type {}; 

template<typename T> 
using Store = typename std::conditional<std::is_void<T>::value, empty_type, T>::type; 

template<typename T> 
T&& 
restore(T&& t) 
{ 
    return std::forward<T>(t); 
} 

void 
restore(empty_type) 
{} 

template <typename ResultType> 
class stored_result { 
public: 
    // snipped everything that is left unchanged 

    template< 
     typename U = ResultType 
     , typename std::enable_if< 
      !std::is_void<U>::value 
      , int 
     >::type = 0 
    >   
    void set_result(U res) { 
     is_val_ = true; 
     val_ = std::move(res); 
    } 

    template< 
     typename U = ResultType 
     , typename std::enable_if< 
      std::is_void<U>::value 
      , int 
     >::type = 0 
    >   
    void set_result() { 
     is_val_ = true; 
    } 

    ResultType result() { 
     if (is_val_) { 
      is_val_ = false; 
      return restore(std::move(val_)); 
     } else if { 
      // rest as before 
    } 

private: 
    Store<T> val_; 
}; 

儘管代碼是未經測試可能有一定的缺陷。

+0

這很聰明。我不會考慮以這種方式使用'enable_if',我應該這樣做。我想要一種方法來使替換失敗達到我的目標,但我想不出如何去做。 – Omnifarious 2012-04-03 04:59:42

+0

'return restore(std :: move(val _));''給我錯誤返回一個void函數的值。 : - /我很擔心這一點。 – Omnifarious 2012-04-04 07:31:34

+0

@Omnifarious您能否提供一個最簡單的例子?在我的結尾,正確的超載'恢復'被拿起。 – 2012-04-04 08:41:23

2

[編輯:問題是編輯,添加示例代碼,我寫了這個之後。

出現您所追求的是從函數返回有效結果(任意類型)或表示失敗的方法。

如果是,則下面的代碼去朝着解決您的問題,返回任意類型的結果,即部分(這個類是類似於boost::optional,而這又是基於Barton和Nackman的Fallible類)的好方法。

對於錯誤指示,只需更換與該錯誤信息的布爾值,並替換概念「無」與概念「失敗」:

#pragma once 
// #include <cpp/optional.h> 
// Author: Alf P. Steinbach. License: Boost 1.0 


//----------------------------------------------- Dependencies: 

#include <cpp/type_properties.h>  // cpp::IsPod_ 
#include <cpp/throwing.h>    // cpp::hopefully, cpp::throwX 
#include <vector>      // std::vector 


//----------------------------------------------- Interface: 

namespace cpp { 

    namespace detail { 
     using std::vector; 

     template< class Type, Podness::Enum podNess = Podness::isPod > 
     class ValueWrapper_ 
     { 
     private: 
      Type v_; 
     public: 
      Type const& ref() const { return v_; } 

      ValueWrapper_() {}  // No initialization 
      ValueWrapper_(Type const v): v_(v) {} 
     }; 

     template< class Type > 
     struct ValueWrapper_< Type, Podness::isNotPod > 
     { 
     private: 
      vector<Type> v_;  // General but incurs allocation overhead. 
     public: 
      Type const& ref() const { return v_[0]; } 

      ValueWrapper_() {}  // Supports apparent no initialization. 
      ValueWrapper_(Type const v): v_(1, v) {} 
     }; 
    } // namespace detail 

    template< class Type > 
    class Optional_ 
    { 
    private: 
     typedef detail::ValueWrapper_<Type, Podness_<Type>::value > Wrapper; 

     Wrapper const value_; 
     bool const  isNone_; 

     Optional_& operator=(Optional_ const&);   // No such. 

    public: 
     bool isNone() const { return isNone_; } 

     Type const& value() const 
     { 
      hopefully(!isNone_) 
       || throwX("Optional_::value(): there is no value"); 
      return value_.ref(); 
     } 

     Optional_(): isNone_(true) {} 
     Optional_(Type const& v): value_(v), isNone_(false) {} 

     static Optional_ none() { return Optional_(); } 
    }; 

    template<> 
    class Optional_<void> 
    { 
    private: 
     Optional_& operator=(Optional_ const&);   // No such. 

    public: 
     bool isNone() const { return true; } 

     void value() const 
     { 
      throwX("Optional_::value(): there is no value"); 
     } 

     Optional_() {} 
     static Optional_ none() { return Optional_(); } 
    }; 
} // namespace cpp 
+0

是的,這有助於我想通過這個問題,我現在看到我怎麼能做到這一點,把所有不同的值重疊在一起對我來說並不重要,正如你指出的那樣,過早的優化,解決了其餘的 – Omnifarious 2012-04-03 03:42:58

+0

這是一個非常有趣的方式來處理一個實際上可能不存在的值,現在我依賴這個值作爲默認的可構造值,但它會很好,在未來工作f或只能移動或只能複製可構造的值。不幸的是,我現在最大的困擾就是無效地處理了空白,而且Luc比我創建的解決方案有更好的解決方案,最終創建了大量重複的代碼。所以我接受他的。但是謝謝你! – Omnifarious 2012-04-04 19:27:48