Skip to content

Allow overriding monostate and bad_variant_access to improve compatibility with other C++11+ header-only libraries #82

Description

@MostafaNanticock

Description

We are using variant-lite in a project alongside tl::optional and other header-only C++11+ compatibility libraries. To provide a unified interface for downstream users, we expose them under a std_compat namespace:

namespace std_compat
{
    template <class T>
    using optional = tl::optional<T>;

    using tl::bad_optional_access;
    using tl::in_place_t;
    using tl::in_place;
    using tl::nullopt;
    using tl::nullopt_t;
    using tl::make_optional;
    using tl::swap;
}
namespace std_compat
{
    template <class... Types>
    using variant = nonstd::variant<Types...>;

    using nonstd::bad_variant_access;
    using nonstd::get;
    using nonstd::get_if;
    using nonstd::holds_alternative;
    using nonstd::visit;
    using nonstd::variant_npos;
    using nonstd::variant_size;
    using nonstd::variant_size_v;
    using nonstd::variant_alternative;
    using nonstd::variant_alternative_t;
}

Problem

  1. monostate conflicts:
    Both tl::optional and variant-lite require a monostate type.
    On MSVC 17.10.35122 (with C++17 enabled), the CRT also provides std::monostate.
    This results in three different monostate definitions that are not interchangeable, causing compilation errors when the libraries are used together.

  2. bad_variant_access mismatch:
    The MSVC CRT provides std::bad_variant_access.
    variant-lite uses its own nonstd::bad_variant_access.
    Third-party code using our std_compat::variant must catch both exceptions to be safe, which is error-prone and unintuitive.

Proposed solution:

Add configuration macros to variant-lite to allow users to override the monostate and bad_variant_access implementations with their own (or the standard library’s) versions.

For example:

#ifdef variant_CONFIG_BAD_VARIANT_ACCESS
using variant_CONFIG_BAD_VARIANT_ACCESS;
#else
class variant_nodiscard bad_variant_access : public ::std::exception
{
public:
#if variant_CPP11_OR_GREATER
    virtual const char* what() const variant_noexcept variant_override
#else
    virtual const char* what() const throw()
#endif
    {
        return "bad variant access";
    }
};
#endif
#ifdef variant_CONFIG_MONOSTATE
using variant_CONFIG_MONOSTATE;
#else
class monostate {};

struct in_place_t {
    explicit in_place_t() = default;
};

static constexpr in_place_t in_place{};
#endif

Benefits:

  • Avoids duplicate/conflicting type definitions when integrating with other compatibility libraries.
  • Allows seamless use of the standard library’s monostate and bad_variant_access when available.
  • Reduces exception-handling boilerplate for downstream users.
  • Keeps variant-lite flexible without breaking existing behavior.

If this approach does not conflict with any existing design decisions or constraints in variant-lite, I’m happy to prepare and submit the implementation as a PR.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions