1

In short, problem is name resolution.

Explanation: I'm enumerating error codes and use them to derive/implement std::error_condition class, however there is a problem when wrapping implementation into 2 separate namespaces, one for enums and one for code implementation.

It's not easy to explain so I made a minimum compilable code which you can just copy/paste into editor and try to compile.

I've put a (NOTE:) comment on top of a code which is what I do to make the code compile, but I need separate namespaces as shown in the code below but name resolution doesn't work, and I don't know how to fix it.

#include <string>
#include <system_error>


// *NOTE*: if we rename this from 'enums' to 'app' (comment below) and do
// the needed name change changes in the code it will compile just fine
//namespace app
namespace enums
{
    // error type enum
    enum class Error
    {
        SignalFail,         // failed to set up or execute signal
        BadArgument,        // bad argument passed to function
        InvalidPointer,     // Pointer is invalid
        NoImplementation,   // No implementation
        OutOfRange,         // Out of range
        AlocationFailed,    // Failed to alocate memory
        MemoryReadFailed,   // Failed to read memory
        Unexpected          // Unexpected execution flow
    };

    // error condition enum
    enum class Condition
    {
        code_error,
        unknown_error
    };
}

// Register Error and Condition as error enum and condition enum
namespace std
{
    template<>
    struct is_error_code_enum<enums::Error> :
        public true_type {};

    template<>
    struct is_error_condition_enum<enums::Condition> :
        public true_type {};
}

namespace app
{
    // Category types are used to identify the source of an error.
    // They also define the relation between error_code and error_condition objects of its category,
    // as well as the message set for error_code objects.
    class error_category_t :
        public std::error_category
    {
    public:
        error_category_t() noexcept {};
        inline const char* name() const noexcept override;
        std::error_condition default_error_condition(int err_value) const noexcept override;
        inline bool equivalent(const std::error_code& err_code, int err_value) const noexcept override;
        inline bool equivalent(int err_value, const std::error_condition& err_cond) const noexcept override;
        std::string message(int ev) const override;
    private:
        error_category_t(const error_category_t&) = delete;
        error_category_t(error_category_t&&) = delete;
        error_category_t& operator=(const error_category_t&) = delete;
        error_category_t& operator=(error_category_t&&) = delete;
    } const error_category;

    const char* error_category_t::name() const noexcept
    {
        return "app category";
    }

    bool error_category_t::equivalent(const std::error_code& err_code, int err_value) const noexcept
    {
        return *this == err_code.category() &&
            static_cast<int>(default_error_condition(err_code.value()).value()) == err_value;
    }

    bool error_category_t::equivalent(int err_value, const std::error_condition& err_cond) const noexcept
    {
        return default_error_condition(err_value) == err_cond;
    }

    //
    // make_error_condition overload to generate custom conditions:
    // This function is called by error_condition's constructor for error condition enum types,
    // and should be overloaded for all custom error condition enum types in order to
    // provide a mechanism to generate the appropriate error_condition objects from them.
    //
    inline std::error_condition make_error_condition(enums::Condition ec) noexcept
    {
        return std::error_condition(static_cast<int>(ec), error_category);
    }

    //
    // This function is called by error_code's constructor for error code enum types
    //
    inline std::error_code make_error_code(enums::Error code) noexcept
    {
        return std::error_code(static_cast<int>(code), error_category);
    }
}

namespace app
{
    std::error_condition error_category_t::default_error_condition(int err_value) const noexcept
    {
        switch (static_cast<enums::Error>(err_value))
        {
        case enums::Error::SignalFail:
        case enums::Error::BadArgument:
        case enums::Error::InvalidPointer:
        case enums::Error::NoImplementation:
        case enums::Error::OutOfRange:
        case enums::Error::AlocationFailed:
        case enums::Error::MemoryReadFailed:
        case enums::Error::Unexpected:
            return std::error_condition(enums::Condition::code_error);
        default:
            return std::error_condition(enums::Condition::unknown_error);
        }
    }

    std::string error_category_t::message(int err_value) const
    {
        switch (static_cast<enums::Error>(err_value))
        {
        case enums::Error::SignalFail:
            return "Signaling failed";
        case enums::Error::BadArgument:
            return "Bad Argument";
        case enums::Error::InvalidPointer:
            return "Invalid pointer";
        case enums::Error::NoImplementation:
            return "No implementation";
        case enums::Error::OutOfRange:
            return "Out of range";
        case enums::Error::AlocationFailed:
            return "Memory allocation failed";
        case enums::Error::MemoryReadFailed:
            return "Memory read failed";
        case enums::Error::Unexpected:
            return "Unexpected execution flow";
        default:
            return "Unknown error";
        }
    }
}

1 Answers1

1

While I would personally recommend that you put the enums in the same namespace as the category, the key problem is the make_error_condition and make_error_code functions must be in the same namespace as the enums. Both the std::error_code and std::error_condition constructors perform an ADL lookup of those two functions.

inline std::error_condition make_error_condition(enums::Condition ec) noexcept
inline std::error_code make_error_code(enums::Error code) noexcept
Dave S
  • 20,507
  • 3
  • 48
  • 68
  • I see, putting these 2 into enums namespace works, but what is ADL lookup? I saw these terms in std headers I think. so there is no way to keep the out of enums namespace? –  Jun 12 '19 at 10:57
  • Thank you a lot for answer, I understand the problem after reading this: https://stackoverflow.com/questions/8111677/what-is-argument-dependent-lookup-aka-adl-or-koenig-lookup –  Jun 12 '19 at 11:08