As C++11 introduced move semantics, I am wondering if function could return both value and operation status. Actually it is nothing difficult to be implemented, but I am about to start a new huge project and do not know if I should go this way, or use something old fashioned. So I am very curious about your opinion.
Please consider the following notation:
class File
{
FILE *file = nullptr;
public:
Result<void> open(const char *fileName);
Result<void> close();
Result<size_t> size();
Result<void> seek(size_t newPosition);
Result<size_t> position();
Result<char> readCharacter();
};
Lets now analyze an usage example:
Result<void> processFile(const char *fileName)
{
File file;
auto result = file.open(fileName);
if (!result.isSuccess())
return result;
auto fileSize = file.size();
if (!fileSize.isSuccess())
return fileSize;
for (size_t i = 0; i < fileSize; i++) {
auto character = file.readCharacter();
if (!character.isSuccess())
return character;
if (character < 'a' || character > 'z')
return Error::invalidData;
// processfileCharacter(character);
}
return Error::success;
}
As you can see, error management becomes extremely simple. Moreover, when I write header only code, both GCC and MSVC produces very optimal code when optimization is enabled. I very like this notation and do not see any serious disadvantages. But I would love to hear your opinion.
Implementation
If you would like to test it, please enjoy the code:
enum class Error : int
{
success,
unknown,
invalidData
// ...
};
Result class:
template <typename Type = void>
class Result
{
Error error = Error::success;
Type data;
public:
Result() = default;
Result(Result &&result) = default;
Result(const Result &result) = default;
template <typename OtherType> Result(const Result<OtherType> &result) : error(result.error) {}
Result & operator =(Result &&result) = default;
Result & operator =(const Result &result) = default;
template <typename OtherType> Result & operator =(const Result<OtherType> &result) { error = result; return *this; }
Result(const Type &data) : data(data) {}
Result(Type &&data) : data(std::move(data)) {}
Result(const Error &error) : error(error) {}
Result(Error &&error) : error(std::move(error)) {}
operator Type& () { return data; }
operator const Type& () const { return data; }
operator const Error() const { return error; }
bool isSuccess() const { return error == Error::success; }
};
Specialization for void:
template <>
class Result<void>
{
Error error = Error::success;
public:
Result() = default;
Result(Result &&result) = default;
Result(const Result &result) = default;
template <typename OtherType> Result(const Result<OtherType> &result) : error(result.error) {}
Result & operator =(Result &&result) = default;
Result & operator =(const Result &result) = default;
template <typename OtherType> Result & operator =(const Result<OtherType> &result) { error = result; return *this; }
Result(const Error &error) : error(error) {}
Result(Error &&error) : error(std::move(error)) {}
operator const Error() const { return error; }
bool isSuccess() const { return error == Error::success; }
};
Aucun commentaire:
Enregistrer un commentaire