mirror of https://gitee.com/bigwinds/arangodb
243 lines
7.9 KiB
C++
243 lines
7.9 KiB
C++
////////////////////////////////////////////////////////////////////////////////
|
|
/// DISCLAIMER
|
|
///
|
|
/// Copyright 2018 ArangoDB GmbH, Cologne, Germany
|
|
///
|
|
/// Licensed under the Apache License, Version 2.0 (the "License");
|
|
/// you may not use this file except in compliance with the License.
|
|
/// You may obtain a copy of the License at
|
|
///
|
|
/// http://www.apache.org/licenses/LICENSE-2.0
|
|
///
|
|
/// Unless required by applicable law or agreed to in writing, software
|
|
/// distributed under the License is distributed on an "AS IS" BASIS,
|
|
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
/// See the License for the specific language governing permissions and
|
|
/// limitations under the License.
|
|
///
|
|
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
|
///
|
|
/// @author Tobias Gödderz
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#ifndef ARANGODB_BASICS_RESULT_T_H
|
|
#define ARANGODB_BASICS_RESULT_T_H
|
|
|
|
#include <type_traits>
|
|
|
|
#include <boost/optional.hpp>
|
|
|
|
#include "Basics/Common.h"
|
|
#include "Basics/Result.h"
|
|
|
|
namespace arangodb {
|
|
|
|
// @brief Extension of Result which, on success, contains a value of type T.
|
|
//
|
|
// @details
|
|
// A `ResultT x` is expected to hold a value *if and only if* x.ok()!
|
|
// So this behaves more like a variant, even if it always contains a Result.
|
|
// This is to easily obtain compatibility with existing Result objects.
|
|
//
|
|
// A successful ResultT can be explicitly created via
|
|
// ResultT::success(value);
|
|
// and an erroneous using
|
|
// ResultT::error(TRI_SOME_ERROR)
|
|
// or
|
|
// ResultT::error(TRI_ANOTHER_ERROR, "a custom message")
|
|
// . Never pass TRI_ERROR_NO_ERROR (i.e. 0) here! Use ::success() for that.
|
|
//
|
|
// Successful construction is also allowed via the constructor(s)
|
|
// ResultT(T val)
|
|
// while error construction is allowed via the constructor(s)
|
|
// ResultT(Result other)
|
|
// . Both of those allow implicit conversion, so if you have a function
|
|
// returning `ResultT<SomeType>` and a `SomeType value` you can use
|
|
// return value;
|
|
// or, having a `Result result` with result.fail() being true,
|
|
// return result;
|
|
// .
|
|
// Some implicit conversions are disabled when they could cause ambiguity.
|
|
template <typename T>
|
|
class ResultT {
|
|
public:
|
|
ResultT static success(T const& val) {
|
|
return ResultT(val, TRI_ERROR_NO_ERROR);
|
|
}
|
|
|
|
ResultT static success(T&& val) {
|
|
return ResultT(std::move(val), TRI_ERROR_NO_ERROR);
|
|
}
|
|
|
|
ResultT static error(int errorNumber) {
|
|
return ResultT(boost::none, errorNumber);
|
|
}
|
|
|
|
ResultT static error(int errorNumber, std::string const& errorMessage) {
|
|
return ResultT(boost::none, errorNumber, errorMessage);
|
|
}
|
|
|
|
ResultT static error(Result const& other) {
|
|
TRI_ASSERT(other.fail());
|
|
return ResultT(boost::none, other);
|
|
}
|
|
|
|
ResultT static error(Result&& other) {
|
|
TRI_ASSERT(other.fail());
|
|
return ResultT(boost::none, std::move(other));
|
|
}
|
|
|
|
// This is disabled if U is implicitly convertible to Result
|
|
// (e.g., if U = int) to avoid ambiguous construction.
|
|
// Use ::success() or ::error() instead in that case.
|
|
template <typename U = T, typename = std::enable_if_t<!std::is_convertible<U, Result>::value>>
|
|
// This is not explicit on purpose
|
|
// NOLINTNEXTLINE(google-explicit-constructor)
|
|
ResultT(Result const& other) : _result(other) {
|
|
// .ok() is not allowed here, as _val should be expected to be initialized
|
|
// iff .ok() is true.
|
|
TRI_ASSERT(other.fail());
|
|
}
|
|
|
|
// This is disabled if U is implicitly convertible to Result
|
|
// (e.g., if U = int) to avoid ambiguous construction.
|
|
// Use ::success() or ::error() instead in that case.
|
|
template <typename U = T, typename = std::enable_if_t<!std::is_convertible<U, Result>::value>>
|
|
// This is not explicit on purpose
|
|
// NOLINTNEXTLINE(google-explicit-constructor)
|
|
ResultT(Result&& other) : _result(std::move(other)) {
|
|
// .ok() is not allowed here, as _val should be expected to be initialized
|
|
// iff .ok() is true.
|
|
TRI_ASSERT(other.fail());
|
|
}
|
|
|
|
// This is disabled if U is implicitly convertible to Result
|
|
// (e.g., if U = int) to avoid ambiguous construction.
|
|
// Use ::success() or ::error() instead in that case.
|
|
template <typename U = T, typename = std::enable_if_t<!std::is_convertible<U, Result>::value>>
|
|
// This is not explicit on purpose
|
|
// NOLINTNEXTLINE(google-explicit-constructor)
|
|
ResultT(T&& val) : ResultT(std::move(val), TRI_ERROR_NO_ERROR) {}
|
|
|
|
// This is disabled if U is implicitly convertible to Result
|
|
// (e.g., if U = int) to avoid ambiguous construction.
|
|
// Use ::success() or ::error() instead in that case.
|
|
template <typename U = T, typename = std::enable_if_t<!std::is_convertible<U, Result>::value>>
|
|
// This is not explicit on purpose
|
|
// NOLINTNEXTLINE(google-explicit-constructor)
|
|
ResultT(T const& val) : ResultT(val, TRI_ERROR_NO_ERROR) {}
|
|
|
|
ResultT() = delete;
|
|
|
|
ResultT& operator=(T const& val_) {
|
|
_val = val_;
|
|
return *this;
|
|
}
|
|
|
|
ResultT& operator=(T&& val_) {
|
|
_val = std::move(val_);
|
|
return *this;
|
|
}
|
|
|
|
// These would be very convenient, but also make it very easy to accidentally
|
|
// use the value of an error-result. So don't add them.
|
|
//
|
|
// operator T() const { return get(); }
|
|
// operator T &() { return get(); }
|
|
|
|
T* operator->() { return &get(); }
|
|
|
|
T const* operator->() const { return &get(); }
|
|
|
|
T& operator*() & { return get(); }
|
|
|
|
T const& operator*() const& { return get(); }
|
|
|
|
T&& operator*() && { return get(); }
|
|
|
|
T const&& operator*() const&& { return get(); }
|
|
|
|
// Allow convenient check to allow for code like
|
|
// if (res) { /* use res.get() */ } else { /* handle error res.result() */ }
|
|
// . Is disabled for bools to avoid accidental confusion of
|
|
// if (res)
|
|
// with
|
|
// if (res.get())
|
|
// .
|
|
template <typename U = T, typename = std::enable_if_t<!std::is_same<U, bool>::value>>
|
|
explicit operator bool() const {
|
|
return ok();
|
|
}
|
|
|
|
T const& get() const { return _val.get(); }
|
|
|
|
T& get() { return _val.get(); }
|
|
|
|
ResultT map(ResultT<T> (*fun)(T const& val)) const {
|
|
if (ok()) {
|
|
return ResultT<T>::success(fun(get()));
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
bool operator==(ResultT<T> const& other) const {
|
|
if (this->ok() && other.ok()) {
|
|
return this->get() == other.get();
|
|
}
|
|
if (this->fail() && other.fail()) {
|
|
return this->errorNumber() == other.errorNumber() &&
|
|
this->errorMessage() == other.errorMessage();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
template <typename U>
|
|
bool operator==(ResultT<U> const& other) const {
|
|
if (this->fail() && other.fail()) {
|
|
return this->errorNumber() == other.errorNumber() &&
|
|
this->errorMessage() == other.errorMessage();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// forwarded methods
|
|
bool ok() const { return _result.ok(); }
|
|
bool fail() const { return _result.fail(); }
|
|
bool is(int code) { return _result.is(code); }
|
|
int errorNumber() const { return _result.errorNumber(); }
|
|
std::string errorMessage() const { return _result.errorMessage(); }
|
|
|
|
// access methods
|
|
Result const& result() const& { return _result; }
|
|
Result result() && { return std::move(_result); }
|
|
|
|
protected:
|
|
Result _result;
|
|
boost::optional<T> _val;
|
|
|
|
ResultT(boost::optional<T>&& val_, int errorNumber)
|
|
: _result(errorNumber), _val(std::move(val_)) {}
|
|
|
|
ResultT(boost::optional<T>&& val_, int errorNumber, std::string const& errorMessage)
|
|
: _result(errorNumber, errorMessage), _val(val_) {}
|
|
|
|
ResultT(boost::optional<T> const& val_, int errorNumber)
|
|
: _result(errorNumber), _val(std::move(val_)) {}
|
|
|
|
ResultT(boost::optional<T> const& val_, int errorNumber, std::string const& errorMessage)
|
|
: _result(errorNumber, errorMessage), _val(val_) {}
|
|
|
|
ResultT(boost::optional<T>&& val_, Result result)
|
|
: _result(std::move(result)), _val(std::move(val_)) {}
|
|
|
|
ResultT(boost::optional<T>&& val_, Result&& result)
|
|
: _result(std::move(result)), _val(std::move(val_)) {}
|
|
};
|
|
|
|
} // namespace arangodb
|
|
|
|
#endif // ARANGODB_BASICS_RESULT_T_H
|