Как мне сделать упрощенную версию Boost :: Вариант? - PullRequest
4 голосов
/ 03 апреля 2012

У меня гораздо более простой набор требований, и мне не нужно много вариантов машины.Я также не хочу зависеть от наддува, если смогу помочь.

Мне нужно хранить любой произвольный тип, известный во время компиляции (это может быть 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_;
};

Ответы [ 2 ]

2 голосов
/ 03 апреля 2012

Пример того, как обрабатывать 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_;
};

Несмотря на то, что код не проверен, могут быть некоторые изломы.

2 голосов
/ 03 апреля 2012

[Изменить: вопрос был отредактирован, добавив пример кода, после того, как я написал это].

кажется , что то, что вам нужно, это способ возврата либо действительного результата (произвольного типа) из функции, либо чего-то, что указывает на ошибку.

Если это так, то приведенный ниже код станет хорошим способом решения вашей проблемы, а именно, части возврата результата произвольного типа (этот класс похож на boost::optional, который, в свою очередь, основан на 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
...