mercredi 20 décembre 2017

C++11 Return value and error pair

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