Skip to content

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

@MostafaNanticock

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

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions