////////////////////////////////////////////////////////////////////////////
//
// Copyright 2017 Realm Inc.
//
// 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 REALM_OS_UTIL_TAGGED_BOOL_HPP
#define REALM_OS_UTIL_TAGGED_BOOL_HPP

#include <type_traits>

namespace realm {
namespace util {
// A type factory which defines a type which is implicitly convertable to and
// from `bool`, but not to other TaggedBool types
//
// Usage:
// using IsIndexed = util::TaggedBool<class IsIndexedTag>;
// using IsPrimary = util::TaggedBool<class IsPrimaryTag>;
// void foo(IsIndexed is_indexed, IsPrimary is_primary);
//
// foo(IsIndexed{true}, IsPrimary{false}); // compiles
// foo(IsPrimary{true}, IsIndexed{false}); // doesn't compile
template <typename Tag>
struct TaggedBool {
    // Allow explicit construction from anything convertible to bool
    constexpr explicit TaggedBool(bool v) noexcept
        : m_value(v)
    {
    }

    // Allow implicit construction from *just* bool and not things convertible
    // to bool (such as other types of tagged bools)
    template <typename Bool, typename = typename std::enable_if<std::is_same<Bool, bool>::value>::type>
    constexpr TaggedBool(Bool v) noexcept
        : m_value(v)
    {
    }

    constexpr TaggedBool(TaggedBool const& v) noexcept = default;
    constexpr TaggedBool& operator=(TaggedBool const& v) noexcept = default;

    constexpr operator bool() const noexcept
    {
        return m_value;
    }
    constexpr TaggedBool operator!() const noexcept
    {
        return TaggedBool{!m_value};
    }

    friend constexpr bool operator==(TaggedBool l, TaggedBool r) noexcept
    {
        return l.m_value == r.m_value;
    }
    friend constexpr bool operator!=(TaggedBool l, TaggedBool r) noexcept
    {
        return l.m_value != r.m_value;
    }

private:
    bool m_value;
};

} // namespace util
} // namespace realm
#endif // REALM_OS_UTIL_TAGGED_BOOL_HPP
