blob: fcb1fac14145c50eb969d7f398b6a8b71ca2e09f [file] [log] [blame]
/*
* Copyright (C) 2018 The Android Open Source Project
*
* 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.
*/
#ifndef OBOE_RESULT_WITH_VALUE_H
#define OBOE_RESULT_WITH_VALUE_H
#include "oboe/Definitions.h"
#include <iostream>
#include <sstream>
namespace oboe {
/**
* A ResultWithValue can store both the result of an operation (either OK or an error) and a value.
*
* It has been designed for cases where the caller needs to know whether an operation succeeded and,
* if it did, a value which was obtained during the operation.
*
* For example, when reading from a stream the caller needs to know the result of the read operation
* and, if it was successful, how many frames were read. Note that ResultWithValue can be evaluated
* as a boolean so it's simple to check whether the result is OK.
*
* <code>
* ResultWithValue<int32_t> resultOfRead = myStream.read(&buffer, numFrames, timeoutNanoseconds);
*
* if (resultOfRead) {
* LOGD("Frames read: %d", resultOfRead.value());
* } else {
* LOGD("Error reading from stream: %s", resultOfRead.error());
* }
* </code>
*/
template <typename T>
class ResultWithValue {
public:
/**
* Construct a ResultWithValue containing an error result.
*
* @param error The error
*/
ResultWithValue(oboe::Result error)
: mValue{}
, mError(error) {}
/**
* Construct a ResultWithValue containing an OK result and a value.
*
* @param value the value to store
*/
explicit ResultWithValue(T value)
: mValue(value)
, mError(oboe::Result::OK) {}
/**
* Get the result.
*
* @return the result
*/
oboe::Result error() const {
return mError;
}
/**
* Get the value
* @return
*/
T value() const {
return mValue;
}
/**
* @return true if OK
*/
explicit operator bool() const { return mError == oboe::Result::OK; }
/**
* Quick way to check for an error.
*
* The caller could write something like this:
* <code>
* if (!result) { printf("Got error %s\n", convertToText(result.error())); }
* </code>
*
* @return true if an error occurred
*/
bool operator !() const { return mError != oboe::Result::OK; }
/**
* Implicitly convert to a Result. This enables easy comparison with Result values. Example:
*
* <code>
* ResultWithValue result = openStream();
* if (result == Result::ErrorNoMemory){ // tell user they're out of memory }
* </code>
*/
operator Result() const {
return mError;
}
/**
* Create a ResultWithValue from a number. If the number is positive the ResultWithValue will
* have a result of Result::OK and the value will contain the number. If the number is negative
* the result will be obtained from the negative number (numeric error codes can be found in
* AAudio.h) and the value will be null.
*
*/
static ResultWithValue<T> createBasedOnSign(T numericResult){
// Ensure that the type is either an integer or float
static_assert(std::is_arithmetic<T>::value,
"createBasedOnSign can only be called for numeric types (int or float)");
if (numericResult >= 0){
return ResultWithValue<T>(numericResult);
} else {
return ResultWithValue<T>(static_cast<Result>(numericResult));
}
}
private:
const T mValue;
const oboe::Result mError;
};
/**
* If the result is `OK` then return the value, otherwise return a human-readable error message.
*/
template <typename T>
std::ostream& operator<<(std::ostream &strm, const ResultWithValue<T> &result) {
if (!result) {
strm << convertToText(result.error());
} else {
strm << result.value();
}
return strm;
}
} // namespace oboe
#endif //OBOE_RESULT_WITH_VALUE_H