public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH 0/4] Add variant type
@ 2022-02-01 14:07 Simon Marchi
  2022-02-01 14:07 ` [PATCH 1/4] gdb: remove internalvar_funcs::destroy Simon Marchi
                   ` (4 more replies)
  0 siblings, 5 replies; 18+ messages in thread
From: Simon Marchi @ 2022-02-01 14:07 UTC (permalink / raw)
  To: gdb-patches

I sometimes encounter situations where I think using std::variant would
be useful.  The benefits would be to be able to use non-POD types in the
alternatives, have them automatically constructed when making an
alternative active, and automatically destructing them when switching to
another alternative of destroying the variant.  Also, the validation of
access to the alternatives, to prevent accessing an alternative that is
not the current one.

However, std::variant is only available in C++17.  I suggest that we
import a variant implementation to allow us to use it right now, a bit
like we did for optional and string_view.  We could maybe import the
implementation from libstdc++, like we did for these last two types, but
that sounds really tedious (we would also want to import any tests).
Instead, we can take advantage of a popular implementation distributed
as a single header file.  I chose this one:

  https://github.com/martinmoene/variant-lite

because it is close to the std implementation, and the license (Boost
Software License) is clear and compatible with the GPL v3.

The first two patches of this series make minor adjustements.

The third patch adds the variant-lite header file, but does not use it
yet.

The fourth patch changes the internalval implementation to use a
variant, to show how it can be used, and so that we can discuss on how
we would like our usage of variant to look like.  This usage will then
probably be used as a template for future uses.

Simon Marchi (4):
  gdb: remove internalvar_funcs::destroy
  gdb: constify parameter of value_copy
  gdbsupport: add variant-lite
  gdb: make internalvar use a variant

 gdb/break-catch-throw.c        |    1 -
 gdb/inferior.c                 |    1 -
 gdb/infrun.c                   |    1 -
 gdb/probe.c                    |    1 -
 gdb/thread.c                   |    2 -
 gdb/tracepoint.c               |    1 -
 gdb/value.c                    |  352 ++--
 gdb/value.h                    |    8 +-
 gdb/windows-tdep.c             |    1 -
 gdbsupport/variant/README      |   11 +
 gdbsupport/variant/variant.hpp | 2742 ++++++++++++++++++++++++++++++++
 11 files changed, 2946 insertions(+), 175 deletions(-)
 create mode 100644 gdbsupport/variant/README
 create mode 100644 gdbsupport/variant/variant.hpp


base-commit: e327c35ef5768789d3ba41a629f178f5eec32790
-- 
2.34.1


^ permalink raw reply	[flat|nested] 18+ messages in thread

* [PATCH 1/4] gdb: remove internalvar_funcs::destroy
  2022-02-01 14:07 [PATCH 0/4] Add variant type Simon Marchi
@ 2022-02-01 14:07 ` Simon Marchi
  2022-03-04 16:15   ` Tom Tromey
  2022-02-01 14:07 ` [PATCH 2/4] gdb: constify parameter of value_copy Simon Marchi
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 18+ messages in thread
From: Simon Marchi @ 2022-02-01 14:07 UTC (permalink / raw)
  To: gdb-patches

No kind of internal var uses it remove it.  This makes the transition to
using a variant easier, since we don't need to think about where this
should be called (in a destructor or not), if it can throw, etc.

Change-Id: Iebbc867d1ce6716480450d9790410d6684cbe4dd
---
 gdb/break-catch-throw.c | 1 -
 gdb/inferior.c          | 1 -
 gdb/infrun.c            | 1 -
 gdb/probe.c             | 1 -
 gdb/thread.c            | 2 --
 gdb/tracepoint.c        | 1 -
 gdb/value.c             | 5 -----
 gdb/value.h             | 6 ------
 gdb/windows-tdep.c      | 1 -
 9 files changed, 19 deletions(-)

diff --git a/gdb/break-catch-throw.c b/gdb/break-catch-throw.c
index d34e5f9ee537..fcd644e6a5f4 100644
--- a/gdb/break-catch-throw.c
+++ b/gdb/break-catch-throw.c
@@ -510,7 +510,6 @@ static const struct internalvar_funcs exception_funcs =
 {
   compute_exception,
   NULL,
-  NULL
 };
 
 \f
diff --git a/gdb/inferior.c b/gdb/inferior.c
index bebddb44173c..0457c7096eca 100644
--- a/gdb/inferior.c
+++ b/gdb/inferior.c
@@ -980,7 +980,6 @@ static const struct internalvar_funcs inferior_funcs =
 {
   inferior_id_make_value,
   NULL,
-  NULL
 };
 
 \f
diff --git a/gdb/infrun.c b/gdb/infrun.c
index 2e7ed15723fd..483034884b5a 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -9366,7 +9366,6 @@ static const struct internalvar_funcs siginfo_funcs =
 {
   siginfo_make_value,
   NULL,
-  NULL
 };
 
 /* Callback for infrun's target events source.  This is marked when a
diff --git a/gdb/probe.c b/gdb/probe.c
index 278b36c3ce84..f0a40f5face1 100644
--- a/gdb/probe.c
+++ b/gdb/probe.c
@@ -866,7 +866,6 @@ static const struct internalvar_funcs probe_funcs =
 {
   compute_probe_arg,
   compile_probe_arg,
-  NULL
 };
 
 
diff --git a/gdb/thread.c b/gdb/thread.c
index 611be3f4633a..b260a30882bc 100644
--- a/gdb/thread.c
+++ b/gdb/thread.c
@@ -2124,7 +2124,6 @@ static const struct internalvar_funcs thread_funcs =
 {
   thread_id_per_inf_num_make_value,
   NULL,
-  NULL
 };
 
 /* Implementation of `gthread' variable.  */
@@ -2133,7 +2132,6 @@ static const struct internalvar_funcs gthread_funcs =
 {
   global_thread_id_make_value,
   NULL,
-  NULL
 };
 
 void _initialize_thread ();
diff --git a/gdb/tracepoint.c b/gdb/tracepoint.c
index 506af3c527ec..c59d44ac74a4 100644
--- a/gdb/tracepoint.c
+++ b/gdb/tracepoint.c
@@ -3985,7 +3985,6 @@ traceframe_available_memory (std::vector<mem_range> *result,
 static const struct internalvar_funcs sdata_funcs =
 {
   sdata_make_value,
-  NULL,
   NULL
 };
 
diff --git a/gdb/value.c b/gdb/value.c
index 7bd9891b3e97..0ccc41b9278f 100644
--- a/gdb/value.c
+++ b/gdb/value.c
@@ -2449,11 +2449,6 @@ clear_internalvar (struct internalvar *var)
       xfree (var->u.string);
       break;
 
-    case INTERNALVAR_MAKE_VALUE:
-      if (var->u.make_value.functions->destroy != NULL)
-	var->u.make_value.functions->destroy (var->u.make_value.data);
-      break;
-
     default:
       break;
     }
diff --git a/gdb/value.h b/gdb/value.h
index 0de4b5f3aefc..f7b29022d37c 100644
--- a/gdb/value.h
+++ b/gdb/value.h
@@ -1002,12 +1002,6 @@ struct internalvar_funcs
 			 struct agent_expr *expr,
 			 struct axs_value *value,
 			 void *data);
-
-  /* If non-NULL, this is called to destroy DATA.  The DATA argument
-     passed to this function is the same argument that was passed to
-     `create_internalvar_type_lazy'.  */
-
-  void (*destroy) (void *data);
 };
 
 extern struct internalvar *create_internalvar_type_lazy (const char *name,
diff --git a/gdb/windows-tdep.c b/gdb/windows-tdep.c
index 13be3e4e461d..91fecd630d7c 100644
--- a/gdb/windows-tdep.c
+++ b/gdb/windows-tdep.c
@@ -970,7 +970,6 @@ static const struct internalvar_funcs tlb_funcs =
 {
   tlb_make_value,
   NULL,
-  NULL
 };
 
 /* Layout of an element of a PE's Import Directory Table.  Based on:
-- 
2.34.1


^ permalink raw reply	[flat|nested] 18+ messages in thread

* [PATCH 2/4] gdb: constify parameter of value_copy
  2022-02-01 14:07 [PATCH 0/4] Add variant type Simon Marchi
  2022-02-01 14:07 ` [PATCH 1/4] gdb: remove internalvar_funcs::destroy Simon Marchi
@ 2022-02-01 14:07 ` Simon Marchi
  2022-03-04 16:16   ` Tom Tromey
  2022-02-01 14:07 ` [PATCH 3/4] gdbsupport: add variant-lite header Simon Marchi
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 18+ messages in thread
From: Simon Marchi @ 2022-02-01 14:07 UTC (permalink / raw)
  To: gdb-patches

In a following patch, I have a const value I want to copy using a
value_copy.  However, value_copy takes a non-const source value, at the
moment.  Change the paramter to be const,

If the source value is not lazy, we currently call
value_contents_all_raw, which calls allocate_value_contents, to get a
view on the contents.  They both take a non-const value, that's a
problem.  My first attempt at solving it was to add a const version of
value_contents_all_raw, make allocate_value_contents take a const value,
and either:

 - make value::contents mutable
 - make allocate_value_contents cast away the const

The idea being that allocating the value contents buffer does modify the
value at the bit level, but logically that doesn't change its state.

That was getting a bit complicated, so what I ended up doing is make
value_copy not call value_contents_all_raw.  We know at this point that
the value is not lazy, so value::contents must have been allocate
already.

Change-Id: I3741ab362bce14315f712ec24064ccc17e3578d4
---
 gdb/value.c | 13 +++++++++----
 gdb/value.h |  2 +-
 2 files changed, 10 insertions(+), 5 deletions(-)

diff --git a/gdb/value.c b/gdb/value.c
index 0ccc41b9278f..2c8af4b9d5b5 100644
--- a/gdb/value.c
+++ b/gdb/value.c
@@ -1703,7 +1703,7 @@ value_release_to_mark (const struct value *mark)
    but it's a different block of storage.  */
 
 struct value *
-value_copy (struct value *arg)
+value_copy (const value *arg)
 {
   struct type *encl_type = value_enclosing_type (arg);
   struct value *val;
@@ -1713,7 +1713,7 @@ value_copy (struct value *arg)
   else
     val = allocate_value (encl_type);
   val->type = arg->type;
-  VALUE_LVAL (val) = VALUE_LVAL (arg);
+  VALUE_LVAL (val) = arg->lval;
   val->location = arg->location;
   val->offset = arg->offset;
   val->bitpos = arg->bitpos;
@@ -1727,8 +1727,13 @@ value_copy (struct value *arg)
   val->initialized = arg->initialized;
 
   if (!value_lazy (val))
-    copy (value_contents_all_raw (arg),
-	  value_contents_all_raw (val));
+    {
+      gdb_assert (arg->contents != nullptr);
+      ULONGEST length = TYPE_LENGTH (value_enclosing_type (arg));
+      const auto &arg_view
+	= gdb::make_array_view (arg->contents.get (), length);
+      copy (arg_view, value_contents_all_raw (val));
+    }
 
   val->unavailable = arg->unavailable;
   val->optimized_out = arg->optimized_out;
diff --git a/gdb/value.h b/gdb/value.h
index f7b29022d37c..4cd2044ac7cf 100644
--- a/gdb/value.h
+++ b/gdb/value.h
@@ -1127,7 +1127,7 @@ extern void preserve_values (struct objfile *);
 
 /* From values.c */
 
-extern struct value *value_copy (struct value *);
+extern struct value *value_copy (const value *);
 
 extern struct value *value_non_lval (struct value *);
 
-- 
2.34.1


^ permalink raw reply	[flat|nested] 18+ messages in thread

* [PATCH 3/4] gdbsupport: add variant-lite header
  2022-02-01 14:07 [PATCH 0/4] Add variant type Simon Marchi
  2022-02-01 14:07 ` [PATCH 1/4] gdb: remove internalvar_funcs::destroy Simon Marchi
  2022-02-01 14:07 ` [PATCH 2/4] gdb: constify parameter of value_copy Simon Marchi
@ 2022-02-01 14:07 ` Simon Marchi
  2022-02-01 14:07 ` [PATCH 4/4] gdb: make internalvar use a variant Simon Marchi
  2022-02-03  0:02 ` [PATCH 0/4] Add variant type Andrew Burgess
  4 siblings, 0 replies; 18+ messages in thread
From: Simon Marchi @ 2022-02-01 14:07 UTC (permalink / raw)
  To: gdb-patches

Add the header-only variant-lite library, taken from [1].:

This allows us to use variants right now, instead of waiting for
std::variant (which was added in C++17).

The license for variant-lite is BSL (Boost Software License), which is
compatible with GPL v3, according to [2].

I placed variant.hpp in a directory on its own, to separate it from the
rest of our code (although that's not strictly necessary).  variant.hpp
comes from commit f1af3518e4c2 in the upstream repo:

    https://github.com/martinmoene/variant-lite/blob/f1af3518e4c28f12b09839b9d2ee37984cbf137a/include/nonstd/variant.hpp

I also added a README explaining where this file comes from, and what to
do if we need to update it.

[1] https://github.com/martinmoene/variant-lite
[2] https://www.gnu.org/licenses/license-list.en.html#boost

Change-Id: Ib7c95a284f313fc7de2dbe1ed87249141aa97916
---
 gdbsupport/variant/README      |   11 +
 gdbsupport/variant/variant.hpp | 2742 ++++++++++++++++++++++++++++++++
 2 files changed, 2753 insertions(+)
 create mode 100644 gdbsupport/variant/README
 create mode 100644 gdbsupport/variant/variant.hpp

diff --git a/gdbsupport/variant/README b/gdbsupport/variant/README
new file mode 100644
index 000000000000..d233766b8ce9
--- /dev/null
+++ b/gdbsupport/variant/README
@@ -0,0 +1,11 @@
+This directory contains a variant implementation taken from:
+
+  https://github.com/martinmoene/variant-lite
+
+To update, copy the latest version of
+
+  https://github.com/martinmoene/variant-lite/blob/master/include/nonstd/variant.hpp
+
+here.  Please make sure to mention in the git commit message (of the commit
+importing the new variant version) the git hash of the upstream repo that the
+new version was taken from.
diff --git a/gdbsupport/variant/variant.hpp b/gdbsupport/variant/variant.hpp
new file mode 100644
index 000000000000..c978de0932e4
--- /dev/null
+++ b/gdbsupport/variant/variant.hpp
@@ -0,0 +1,2742 @@
+// Copyright 2016-2018 by Martin Moene
+//
+// https://github.com/martinmoene/variant-lite
+//
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+#pragma once
+
+#ifndef NONSTD_VARIANT_LITE_HPP
+#define NONSTD_VARIANT_LITE_HPP
+
+#define variant_lite_MAJOR  2
+#define variant_lite_MINOR  0
+#define variant_lite_PATCH  0
+
+#define variant_lite_VERSION  variant_STRINGIFY(variant_lite_MAJOR) "." variant_STRINGIFY(variant_lite_MINOR) "." variant_STRINGIFY(variant_lite_PATCH)
+
+#define variant_STRINGIFY(  x )  variant_STRINGIFY_( x )
+#define variant_STRINGIFY_( x )  #x
+
+// variant-lite configuration:
+
+#define variant_VARIANT_DEFAULT  0
+#define variant_VARIANT_NONSTD   1
+#define variant_VARIANT_STD      2
+
+// tweak header support:
+
+#ifdef __has_include
+# if __has_include(<nonstd/variant.tweak.hpp>)
+#  include <nonstd/variant.tweak.hpp>
+# endif
+#define variant_HAVE_TWEAK_HEADER  1
+#else
+#define variant_HAVE_TWEAK_HEADER  0
+//# pragma message("variant.hpp: Note: Tweak header not supported.")
+#endif
+
+// variant selection and configuration:
+
+#ifndef  variant_CONFIG_OMIT_VARIANT_SIZE_V_MACRO
+# define variant_CONFIG_OMIT_VARIANT_SIZE_V_MACRO  0
+#endif
+
+#ifndef  variant_CONFIG_OMIT_VARIANT_ALTERNATIVE_T_MACRO
+# define variant_CONFIG_OMIT_VARIANT_ALTERNATIVE_T_MACRO  0
+#endif
+
+// Control presence of exception handling (try and auto discover):
+
+#ifndef variant_CONFIG_NO_EXCEPTIONS
+# if defined(_MSC_VER)
+#  include <cstddef>    // for _HAS_EXCEPTIONS
+# endif
+# if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || (_HAS_EXCEPTIONS)
+#  define variant_CONFIG_NO_EXCEPTIONS  0
+# else
+#  define variant_CONFIG_NO_EXCEPTIONS  1
+# endif
+#endif
+
+// C++ language version detection (C++20 is speculative):
+// Note: VC14.0/1900 (VS2015) lacks too much from C++14.
+
+#ifndef   variant_CPLUSPLUS
+# if defined(_MSVC_LANG ) && !defined(__clang__)
+#  define variant_CPLUSPLUS  (_MSC_VER == 1900 ? 201103L : _MSVC_LANG )
+# else
+#  define variant_CPLUSPLUS  __cplusplus
+# endif
+#endif
+
+#define variant_CPP98_OR_GREATER  ( variant_CPLUSPLUS >= 199711L )
+#define variant_CPP11_OR_GREATER  ( variant_CPLUSPLUS >= 201103L )
+#define variant_CPP11_OR_GREATER_ ( variant_CPLUSPLUS >= 201103L )
+#define variant_CPP14_OR_GREATER  ( variant_CPLUSPLUS >= 201402L )
+#define variant_CPP17_OR_GREATER  ( variant_CPLUSPLUS >= 201703L )
+#define variant_CPP20_OR_GREATER  ( variant_CPLUSPLUS >= 202000L )
+
+// Use C++17 std::variant if available and requested:
+
+#if variant_CPP17_OR_GREATER && defined(__has_include )
+# if __has_include( <variant> )
+#  define variant_HAVE_STD_VARIANT  1
+# else
+#  define variant_HAVE_STD_VARIANT  0
+# endif
+#else
+# define  variant_HAVE_STD_VARIANT  0
+#endif
+
+#if !defined( variant_CONFIG_SELECT_VARIANT )
+# define variant_CONFIG_SELECT_VARIANT  ( variant_HAVE_STD_VARIANT ? variant_VARIANT_STD : variant_VARIANT_NONSTD )
+#endif
+
+#define  variant_USES_STD_VARIANT  ( (variant_CONFIG_SELECT_VARIANT == variant_VARIANT_STD) || ((variant_CONFIG_SELECT_VARIANT == variant_VARIANT_DEFAULT) && variant_HAVE_STD_VARIANT) )
+
+//
+// in_place: code duplicated in any-lite, expected-lite, optional-lite, value-ptr-lite, variant-lite:
+//
+
+#ifndef nonstd_lite_HAVE_IN_PLACE_TYPES
+#define nonstd_lite_HAVE_IN_PLACE_TYPES  1
+
+// C++17 std::in_place in <utility>:
+
+#if variant_CPP17_OR_GREATER
+
+#include <utility>
+
+namespace nonstd {
+
+using std::in_place;
+using std::in_place_type;
+using std::in_place_index;
+using std::in_place_t;
+using std::in_place_type_t;
+using std::in_place_index_t;
+
+#define nonstd_lite_in_place_t(      T)  std::in_place_t
+#define nonstd_lite_in_place_type_t( T)  std::in_place_type_t<T>
+#define nonstd_lite_in_place_index_t(K)  std::in_place_index_t<K>
+
+#define nonstd_lite_in_place(      T)    std::in_place_t{}
+#define nonstd_lite_in_place_type( T)    std::in_place_type_t<T>{}
+#define nonstd_lite_in_place_index(K)    std::in_place_index_t<K>{}
+
+} // namespace nonstd
+
+#else // variant_CPP17_OR_GREATER
+
+#include <cstddef>
+
+namespace nonstd {
+namespace detail {
+
+template< class T >
+struct in_place_type_tag {};
+
+template< std::size_t K >
+struct in_place_index_tag {};
+
+} // namespace detail
+
+struct in_place_t {};
+
+template< class T >
+inline in_place_t in_place( detail::in_place_type_tag<T> = detail::in_place_type_tag<T>() )
+{
+    return in_place_t();
+}
+
+template< std::size_t K >
+inline in_place_t in_place( detail::in_place_index_tag<K> = detail::in_place_index_tag<K>() )
+{
+    return in_place_t();
+}
+
+template< class T >
+inline in_place_t in_place_type( detail::in_place_type_tag<T> = detail::in_place_type_tag<T>() )
+{
+    return in_place_t();
+}
+
+template< std::size_t K >
+inline in_place_t in_place_index( detail::in_place_index_tag<K> = detail::in_place_index_tag<K>() )
+{
+    return in_place_t();
+}
+
+// mimic templated typedef:
+
+#define nonstd_lite_in_place_t(      T)  nonstd::in_place_t(&)( nonstd::detail::in_place_type_tag<T>  )
+#define nonstd_lite_in_place_type_t( T)  nonstd::in_place_t(&)( nonstd::detail::in_place_type_tag<T>  )
+#define nonstd_lite_in_place_index_t(K)  nonstd::in_place_t(&)( nonstd::detail::in_place_index_tag<K> )
+
+#define nonstd_lite_in_place(      T)    nonstd::in_place_type<T>
+#define nonstd_lite_in_place_type( T)    nonstd::in_place_type<T>
+#define nonstd_lite_in_place_index(K)    nonstd::in_place_index<K>
+
+} // namespace nonstd
+
+#endif // variant_CPP17_OR_GREATER
+#endif // nonstd_lite_HAVE_IN_PLACE_TYPES
+
+// in_place_index-like disambiguation tag identical for all C++ versions:
+
+namespace nonstd {
+namespace variants {
+namespace detail {
+
+template< std::size_t K >
+struct index_tag_t {};
+
+template< std::size_t K >
+inline void index_tag ( index_tag_t<K> = index_tag_t<K>() ) { }
+
+#define variant_index_tag_t(K)  void(&)( nonstd::variants::detail::index_tag_t<K> )
+#define variant_index_tag(K)    nonstd::variants::detail::index_tag<K>
+
+} // namespace detail
+} // namespace variants
+} // namespace nonstd
+
+//
+// Use C++17 std::variant:
+//
+
+#if variant_USES_STD_VARIANT
+
+#include <functional>   // std::hash<>
+#include <variant>
+
+#if ! variant_CONFIG_OMIT_VARIANT_SIZE_V_MACRO
+# define variant_size_V(T)  nonstd::variant_size<T>::value
+#endif
+
+#if ! variant_CONFIG_OMIT_VARIANT_ALTERNATIVE_T_MACRO
+# define variant_alternative_T(K,T)  typename nonstd::variant_alternative<K,T >::type
+#endif
+
+namespace nonstd {
+
+    using std::variant;
+    using std::monostate;
+    using std::bad_variant_access;
+    using std::variant_size;
+    using std::variant_size_v;
+    using std::variant_alternative;
+    using std::variant_alternative_t;
+    using std::hash;
+
+    using std::visit;
+    using std::holds_alternative;
+    using std::get;
+    using std::get_if;
+    using std::operator==;
+    using std::operator!=;
+    using std::operator<;
+    using std::operator<=;
+    using std::operator>;
+    using std::operator>=;
+    using std::swap;
+
+    constexpr auto variant_npos = std::variant_npos;
+}
+
+#else // variant_USES_STD_VARIANT
+
+#include <cstddef>
+#include <limits>
+#include <new>
+#include <utility>
+
+#if variant_CONFIG_NO_EXCEPTIONS
+# include <cassert>
+#else
+# include <stdexcept>
+#endif
+
+// variant-lite type and visitor argument count configuration (script/generate_header.py):
+
+#define variant_CONFIG_MAX_TYPE_COUNT  16
+#define variant_CONFIG_MAX_VISITOR_ARG_COUNT  5
+
+// variant-lite alignment configuration:
+
+#ifndef  variant_CONFIG_MAX_ALIGN_HACK
+# define variant_CONFIG_MAX_ALIGN_HACK  0
+#endif
+
+#ifndef  variant_CONFIG_ALIGN_AS
+// no default, used in #if defined()
+#endif
+
+#ifndef  variant_CONFIG_ALIGN_AS_FALLBACK
+# define variant_CONFIG_ALIGN_AS_FALLBACK  double
+#endif
+
+// half-open range [lo..hi):
+#define variant_BETWEEN( v, lo, hi ) ( (lo) <= (v) && (v) < (hi) )
+
+// Compiler versions:
+//
+// MSVC++  6.0  _MSC_VER == 1200  variant_COMPILER_MSVC_VERSION ==  60  (Visual Studio 6.0)
+// MSVC++  7.0  _MSC_VER == 1300  variant_COMPILER_MSVC_VERSION ==  70  (Visual Studio .NET 2002)
+// MSVC++  7.1  _MSC_VER == 1310  variant_COMPILER_MSVC_VERSION ==  71  (Visual Studio .NET 2003)
+// MSVC++  8.0  _MSC_VER == 1400  variant_COMPILER_MSVC_VERSION ==  80  (Visual Studio 2005)
+// MSVC++  9.0  _MSC_VER == 1500  variant_COMPILER_MSVC_VERSION ==  90  (Visual Studio 2008)
+// MSVC++ 10.0  _MSC_VER == 1600  variant_COMPILER_MSVC_VERSION == 100  (Visual Studio 2010)
+// MSVC++ 11.0  _MSC_VER == 1700  variant_COMPILER_MSVC_VERSION == 110  (Visual Studio 2012)
+// MSVC++ 12.0  _MSC_VER == 1800  variant_COMPILER_MSVC_VERSION == 120  (Visual Studio 2013)
+// MSVC++ 14.0  _MSC_VER == 1900  variant_COMPILER_MSVC_VERSION == 140  (Visual Studio 2015)
+// MSVC++ 14.1  _MSC_VER >= 1910  variant_COMPILER_MSVC_VERSION == 141  (Visual Studio 2017)
+// MSVC++ 14.2  _MSC_VER >= 1920  variant_COMPILER_MSVC_VERSION == 142  (Visual Studio 2019)
+
+#if defined(_MSC_VER ) && !defined(__clang__)
+# define variant_COMPILER_MSVC_VER      (_MSC_VER )
+# define variant_COMPILER_MSVC_VERSION  (_MSC_VER / 10 - 10 * ( 5 + (_MSC_VER < 1900 ) ) )
+#else
+# define variant_COMPILER_MSVC_VER      0
+# define variant_COMPILER_MSVC_VERSION  0
+#endif
+
+#define variant_COMPILER_VERSION( major, minor, patch )  ( 10 * ( 10 * (major) + (minor) ) + (patch) )
+
+#if defined(__clang__)
+# define variant_COMPILER_CLANG_VERSION  variant_COMPILER_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__)
+#else
+# define variant_COMPILER_CLANG_VERSION  0
+#endif
+
+#if defined(__GNUC__) && !defined(__clang__)
+# define variant_COMPILER_GNUC_VERSION  variant_COMPILER_VERSION(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)
+#else
+# define variant_COMPILER_GNUC_VERSION  0
+#endif
+
+#if variant_BETWEEN( variant_COMPILER_MSVC_VER, 1300, 1900 )
+# pragma warning( push )
+# pragma warning( disable: 4345 )   // initialization behavior changed
+#endif
+
+// Presence of language and library features:
+
+#define variant_HAVE( feature )  ( variant_HAVE_##feature )
+
+#ifdef _HAS_CPP0X
+# define variant_HAS_CPP0X  _HAS_CPP0X
+#else
+# define variant_HAS_CPP0X  0
+#endif
+
+// Unless defined otherwise below, consider VC14 as C++11 for variant-lite:
+
+#if variant_COMPILER_MSVC_VER >= 1900
+# undef  variant_CPP11_OR_GREATER
+# define variant_CPP11_OR_GREATER  1
+#endif
+
+#define variant_CPP11_90   (variant_CPP11_OR_GREATER_ || variant_COMPILER_MSVC_VER >= 1500)
+#define variant_CPP11_100  (variant_CPP11_OR_GREATER_ || variant_COMPILER_MSVC_VER >= 1600)
+#define variant_CPP11_110  (variant_CPP11_OR_GREATER_ || variant_COMPILER_MSVC_VER >= 1700)
+#define variant_CPP11_120  (variant_CPP11_OR_GREATER_ || variant_COMPILER_MSVC_VER >= 1800)
+#define variant_CPP11_140  (variant_CPP11_OR_GREATER_ || variant_COMPILER_MSVC_VER >= 1900)
+#define variant_CPP11_141  (variant_CPP11_OR_GREATER_ || variant_COMPILER_MSVC_VER >= 1910)
+
+#define variant_CPP14_000  (variant_CPP14_OR_GREATER)
+#define variant_CPP17_000  (variant_CPP17_OR_GREATER)
+
+// Presence of C++11 language features:
+
+#define variant_HAVE_CONSTEXPR_11       variant_CPP11_140
+#define variant_HAVE_INITIALIZER_LIST   variant_CPP11_120
+#define variant_HAVE_NOEXCEPT           variant_CPP11_140
+#define variant_HAVE_NULLPTR            variant_CPP11_100
+#define variant_HAVE_OVERRIDE           variant_CPP11_140
+
+// Presence of C++14 language features:
+
+#define variant_HAVE_CONSTEXPR_14       variant_CPP14_000
+
+// Presence of C++17 language features:
+
+// no flag
+
+// Presence of C++ library features:
+
+#define variant_HAVE_CONDITIONAL        variant_CPP11_120
+#define variant_HAVE_REMOVE_CV          variant_CPP11_120
+#define variant_HAVE_STD_ADD_POINTER    variant_CPP11_100
+#define variant_HAVE_TYPE_TRAITS        variant_CPP11_90
+#define variant_HAVE_ENABLE_IF          variant_CPP11_100
+#define variant_HAVE_IS_SAME            variant_CPP11_100
+
+#define variant_HAVE_TR1_TYPE_TRAITS    (!! variant_COMPILER_GNUC_VERSION )
+#define variant_HAVE_TR1_ADD_POINTER    (!! variant_COMPILER_GNUC_VERSION || variant_CPP11_90)
+
+// C++ feature usage:
+
+#if variant_HAVE_CONSTEXPR_11
+# define variant_constexpr constexpr
+#else
+# define variant_constexpr /*constexpr*/
+#endif
+
+#if variant_HAVE_CONSTEXPR_14
+# define variant_constexpr14 constexpr
+#else
+# define variant_constexpr14 /*constexpr*/
+#endif
+
+#if variant_HAVE_NOEXCEPT
+# define variant_noexcept noexcept
+#else
+# define variant_noexcept /*noexcept*/
+#endif
+
+#if variant_HAVE_NULLPTR
+# define variant_nullptr nullptr
+#else
+# define variant_nullptr NULL
+#endif
+
+#if variant_HAVE_OVERRIDE
+# define variant_override override
+#else
+# define variant_override /*override*/
+#endif
+
+// additional includes:
+
+#if variant_CPP11_OR_GREATER
+# include <functional>      // std::hash
+#endif
+
+#if variant_HAVE_INITIALIZER_LIST
+# include <initializer_list>
+#endif
+
+#if variant_HAVE_TYPE_TRAITS
+# include <type_traits>
+#elif variant_HAVE_TR1_TYPE_TRAITS
+# include <tr1/type_traits>
+#endif
+
+//
+// variant:
+//
+
+namespace nonstd { namespace variants {
+
+// C++11 emulation:
+
+namespace std11 {
+
+#if variant_HAVE_STD_ADD_POINTER
+
+using std::add_pointer;
+
+#elif variant_HAVE_TR1_ADD_POINTER
+
+using std::tr1::add_pointer;
+
+#else
+
+template< class T > struct remove_reference     { typedef T type; };
+template< class T > struct remove_reference<T&> { typedef T type; };
+
+template< class T > struct add_pointer
+{
+    typedef typename remove_reference<T>::type * type;
+};
+
+#endif // variant_HAVE_STD_ADD_POINTER
+
+#if variant_HAVE_REMOVE_CV
+
+using std::remove_cv;
+
+#else
+
+template< class T > struct remove_const          { typedef T type; };
+template< class T > struct remove_const<const T> { typedef T type; };
+
+template< class T > struct remove_volatile             { typedef T type; };
+template< class T > struct remove_volatile<volatile T> { typedef T type; };
+
+template< class T >
+struct remove_cv
+{
+    typedef typename remove_volatile<typename remove_const<T>::type>::type type;
+};
+
+#endif // variant_HAVE_REMOVE_CV
+
+#if variant_HAVE_CONDITIONAL
+
+using std::conditional;
+
+#else
+
+template< bool Cond, class Then, class Else >
+struct conditional;
+
+template< class Then, class Else >
+struct conditional< true , Then, Else > { typedef Then type; };
+
+template< class Then, class Else >
+struct conditional< false, Then, Else > { typedef Else type; };
+
+#endif // variant_HAVE_CONDITIONAL
+
+#if variant_HAVE_ENABLE_IF
+
+using std::enable_if;
+
+#else
+
+template< bool B, class T = void >
+struct enable_if { };
+
+template< class T >
+struct enable_if< true, T > { typedef T type; };
+
+#endif // variant_HAVE_ENABLE_IF
+
+#if variant_HAVE_IS_SAME
+
+using std::is_same;
+
+#else
+
+template< class T, class U >
+struct is_same {
+    enum V { value = 0 } ;
+};
+
+template< class T >
+struct is_same< T, T > {
+    enum V { value = 1 } ;
+};
+
+#endif // variant_HAVE_IS_SAME
+
+} // namespace std11
+
+// Method enabling
+
+#if variant_CPP11_OR_GREATER
+
+#define variant_REQUIRES_T(...) \
+    , typename std::enable_if< (__VA_ARGS__), int >::type = 0
+
+#define variant_REQUIRES_R(R, ...) \
+    typename std::enable_if< (__VA_ARGS__), R>::type
+
+#define variant_REQUIRES_A(...) \
+    , typename std::enable_if< (__VA_ARGS__), void*>::type = nullptr
+
+#endif // variant_CPP11_OR_GREATER
+
+#define variant_REQUIRES_0(...) \
+    template< bool B = (__VA_ARGS__), typename std11::enable_if<B, int>::type = 0 >
+
+#define variant_REQUIRES_B(...) \
+    , bool B = (__VA_ARGS__), typename std11::enable_if<B, int>::type = 0
+
+/// type traits C++17:
+
+namespace std17 {
+
+#if variant_CPP17_OR_GREATER
+
+using std::is_swappable;
+using std::is_nothrow_swappable;
+
+#elif variant_CPP11_OR_GREATER
+
+namespace detail {
+
+using std::swap;
+
+struct is_swappable
+{
+    template< typename T, typename = decltype( swap( std::declval<T&>(), std::declval<T&>() ) ) >
+    static std::true_type test( int );
+
+    template< typename >
+    static std::false_type test(...);
+};
+
+struct is_nothrow_swappable
+{
+    // wrap noexcept(epr) in separate function as work-around for VC140 (VS2015):
+
+    template< typename T >
+    static constexpr bool test()
+    {
+        return noexcept( swap( std::declval<T&>(), std::declval<T&>() ) );
+    }
+
+    template< typename T >
+    static auto test( int ) -> std::integral_constant<bool, test<T>()>{}
+
+    template< typename >
+    static std::false_type test(...);
+};
+
+} // namespace detail
+
+// is [nothow] swappable:
+
+template< typename T >
+struct is_swappable : decltype( detail::is_swappable::test<T>(0) ){};
+
+template< typename T >
+struct is_nothrow_swappable : decltype( detail::is_nothrow_swappable::test<T>(0) ){};
+
+#endif // variant_CPP17_OR_GREATER
+
+} // namespace std17
+
+// detail:
+
+namespace detail {
+
+// typelist:
+
+#define variant_TL1( T1 ) detail::typelist< T1, detail::nulltype >
+#define variant_TL2( T1, T2) detail::typelist< T1, variant_TL1( T2) >
+#define variant_TL3( T1, T2, T3) detail::typelist< T1, variant_TL2( T2, T3) >
+#define variant_TL4( T1, T2, T3, T4) detail::typelist< T1, variant_TL3( T2, T3, T4) >
+#define variant_TL5( T1, T2, T3, T4, T5) detail::typelist< T1, variant_TL4( T2, T3, T4, T5) >
+#define variant_TL6( T1, T2, T3, T4, T5, T6) detail::typelist< T1, variant_TL5( T2, T3, T4, T5, T6) >
+#define variant_TL7( T1, T2, T3, T4, T5, T6, T7) detail::typelist< T1, variant_TL6( T2, T3, T4, T5, T6, T7) >
+#define variant_TL8( T1, T2, T3, T4, T5, T6, T7, T8) detail::typelist< T1, variant_TL7( T2, T3, T4, T5, T6, T7, T8) >
+#define variant_TL9( T1, T2, T3, T4, T5, T6, T7, T8, T9) detail::typelist< T1, variant_TL8( T2, T3, T4, T5, T6, T7, T8, T9) >
+#define variant_TL10( T1, T2, T3, T4, T5, T6, T7, T8, T9, T10) detail::typelist< T1, variant_TL9( T2, T3, T4, T5, T6, T7, T8, T9, T10) >
+#define variant_TL11( T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11) detail::typelist< T1, variant_TL10( T2, T3, T4, T5, T6, T7, T8, T9, T10, T11) >
+#define variant_TL12( T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12) detail::typelist< T1, variant_TL11( T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12) >
+#define variant_TL13( T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13) detail::typelist< T1, variant_TL12( T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13) >
+#define variant_TL14( T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14) detail::typelist< T1, variant_TL13( T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14) >
+#define variant_TL15( T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15) detail::typelist< T1, variant_TL14( T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15) >
+#define variant_TL16( T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16) detail::typelist< T1, variant_TL15( T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16) >
+
+
+// variant parameter unused type tags:
+
+template< class T >
+struct TX : T
+{
+                        inline TX<T> operator+ (           ) const { return TX<T>();  }
+                        inline TX<T> operator- (           ) const { return TX<T>();  }
+
+                        inline TX<T> operator! (           ) const { return TX<T>();  }
+                        inline TX<T> operator~ (           ) const { return TX<T>();  }
+
+                        inline TX<T>*operator& (           ) const { return variant_nullptr; }
+
+    template< class U > inline TX<T> operator* ( U const & ) const { return TX<T>();  }
+    template< class U > inline TX<T> operator/ ( U const & ) const { return TX<T>();  }
+
+    template< class U > inline TX<T> operator% ( U const & ) const { return TX<T>();  }
+    template< class U > inline TX<T> operator+ ( U const & ) const { return TX<T>();  }
+    template< class U > inline TX<T> operator- ( U const & ) const { return TX<T>();  }
+
+    template< class U > inline TX<T> operator<<( U const & ) const { return TX<T>();  }
+    template< class U > inline TX<T> operator>>( U const & ) const { return TX<T>();  }
+
+                        inline bool  operator==( T const & ) const { return false; }
+                        inline bool  operator< ( T const & ) const { return false; }
+
+    template< class U > inline TX<T> operator& ( U const & ) const { return TX<T>();  }
+    template< class U > inline TX<T> operator| ( U const & ) const { return TX<T>();  }
+    template< class U > inline TX<T> operator^ ( U const & ) const { return TX<T>();  }
+
+    template< class U > inline TX<T> operator&&( U const & ) const { return TX<T>();  }
+    template< class U > inline TX<T> operator||( U const & ) const { return TX<T>();  }
+};
+
+struct S0{}; typedef TX<S0> T0;
+struct S1{}; typedef TX<S1> T1;
+struct S2{}; typedef TX<S2> T2;
+struct S3{}; typedef TX<S3> T3;
+struct S4{}; typedef TX<S4> T4;
+struct S5{}; typedef TX<S5> T5;
+struct S6{}; typedef TX<S6> T6;
+struct S7{}; typedef TX<S7> T7;
+struct S8{}; typedef TX<S8> T8;
+struct S9{}; typedef TX<S9> T9;
+struct S10{}; typedef TX<S10> T10;
+struct S11{}; typedef TX<S11> T11;
+struct S12{}; typedef TX<S12> T12;
+struct S13{}; typedef TX<S13> T13;
+struct S14{}; typedef TX<S14> T14;
+struct S15{}; typedef TX<S15> T15;
+
+
+struct nulltype{};
+
+template< class Head, class Tail >
+struct typelist
+{
+    typedef Head head;
+    typedef Tail tail;
+};
+
+// typelist max element size:
+
+template< class List >
+struct typelist_max;
+
+template<>
+struct typelist_max< nulltype >
+{
+    enum V { value = 0 } ;
+    typedef void type;
+};
+
+template< class Head, class Tail >
+struct typelist_max< typelist<Head, Tail> >
+{
+private:
+    enum TV { tail_value = size_t( typelist_max<Tail>::value ) };
+
+    typedef typename typelist_max<Tail>::type tail_type;
+
+public:
+    enum V { value = (sizeof( Head ) > tail_value) ? sizeof( Head ) : std::size_t( tail_value ) } ;
+
+    typedef typename std11::conditional< (sizeof( Head ) > tail_value), Head, tail_type>::type type;
+};
+
+#if variant_CPP11_OR_GREATER
+
+// typelist max alignof element type:
+
+template< class List >
+struct typelist_max_alignof;
+
+template<>
+struct typelist_max_alignof< nulltype >
+{
+    enum V { value = 0 } ;
+};
+
+template< class Head, class Tail >
+struct typelist_max_alignof< typelist<Head, Tail> >
+{
+private:
+    enum TV { tail_value = size_t( typelist_max_alignof<Tail>::value ) };
+
+public:
+    enum V { value = (alignof( Head ) > tail_value) ? alignof( Head ) : std::size_t( tail_value ) };
+};
+
+#endif
+
+// typelist size (length):
+
+template< class List >
+struct typelist_size
+{
+   enum V { value = 1 };
+};
+
+template<> struct typelist_size< T0 > { enum V { value = 0 }; };
+template<> struct typelist_size< T1 > { enum V { value = 0 }; };
+template<> struct typelist_size< T2 > { enum V { value = 0 }; };
+template<> struct typelist_size< T3 > { enum V { value = 0 }; };
+template<> struct typelist_size< T4 > { enum V { value = 0 }; };
+template<> struct typelist_size< T5 > { enum V { value = 0 }; };
+template<> struct typelist_size< T6 > { enum V { value = 0 }; };
+template<> struct typelist_size< T7 > { enum V { value = 0 }; };
+template<> struct typelist_size< T8 > { enum V { value = 0 }; };
+template<> struct typelist_size< T9 > { enum V { value = 0 }; };
+template<> struct typelist_size< T10 > { enum V { value = 0 }; };
+template<> struct typelist_size< T11 > { enum V { value = 0 }; };
+template<> struct typelist_size< T12 > { enum V { value = 0 }; };
+template<> struct typelist_size< T13 > { enum V { value = 0 }; };
+template<> struct typelist_size< T14 > { enum V { value = 0 }; };
+template<> struct typelist_size< T15 > { enum V { value = 0 }; };
+
+
+template<> struct typelist_size< nulltype > { enum V { value = 0 } ; };
+
+template< class Head, class Tail >
+struct typelist_size< typelist<Head, Tail> >
+{
+    enum V { value = size_t(typelist_size<Head>::value) + size_t(typelist_size<Tail>::value) };
+};
+
+// typelist index of type:
+
+template< class List, class T >
+struct typelist_index_of;
+
+template< class T >
+struct typelist_index_of< nulltype, T >
+{
+    enum V { value = -1 };
+};
+
+template< class Tail, class T >
+struct typelist_index_of< typelist<T, Tail>, T >
+{
+    enum V { value = 0 };
+};
+
+template< class Head, class Tail, class T >
+struct typelist_index_of< typelist<Head, Tail>, T >
+{
+private:
+    enum TV { nextVal = typelist_index_of<Tail, T>::value };
+
+public:
+    enum V { value = nextVal == -1 ? -1 : 1 + nextVal } ;
+};
+
+// typelist type at index:
+
+template< class List, std::size_t i>
+struct typelist_type_at;
+
+template< class Head, class Tail >
+struct typelist_type_at< typelist<Head, Tail>, 0 >
+{
+    typedef Head type;
+};
+
+template< class Head, class Tail, std::size_t i >
+struct typelist_type_at< typelist<Head, Tail>, i >
+{
+    typedef typename typelist_type_at<Tail, i - 1>::type type;
+};
+
+// typelist type is unique:
+
+template< class List, std::size_t CmpIndex, std::size_t LastChecked = typelist_size<List>::value >
+struct typelist_type_is_unique
+{
+private:
+    typedef typename typelist_type_at<List, CmpIndex>::type cmp_type;
+    typedef typename typelist_type_at<List, LastChecked - 1>::type cur_type;
+
+public:
+    enum V { value = ((CmpIndex == (LastChecked - 1)) | !std11::is_same<cmp_type, cur_type>::value)
+        && typelist_type_is_unique<List, CmpIndex, LastChecked - 1>::value } ;
+};
+
+template< class List, std::size_t CmpIndex >
+struct typelist_type_is_unique< List, CmpIndex, 0 >
+{
+    enum V { value = 1 } ;
+};
+
+template< class List, class T >
+struct typelist_contains_unique_type : typelist_type_is_unique< List, typelist_index_of< List, T >::value >
+{
+};
+
+#if variant_CONFIG_MAX_ALIGN_HACK
+
+// Max align, use most restricted type for alignment:
+
+#define variant_UNIQUE(  name )       variant_UNIQUE2( name, __LINE__ )
+#define variant_UNIQUE2( name, line ) variant_UNIQUE3( name, line )
+#define variant_UNIQUE3( name, line ) name ## line
+
+#define variant_ALIGN_TYPE( type ) \
+    type variant_UNIQUE( _t ); struct_t< type > variant_UNIQUE( _st )
+
+template< class T >
+struct struct_t { T _; };
+
+union max_align_t
+{
+    variant_ALIGN_TYPE( char );
+    variant_ALIGN_TYPE( short int );
+    variant_ALIGN_TYPE( int );
+    variant_ALIGN_TYPE( long int  );
+    variant_ALIGN_TYPE( float  );
+    variant_ALIGN_TYPE( double );
+    variant_ALIGN_TYPE( long double );
+    variant_ALIGN_TYPE( char * );
+    variant_ALIGN_TYPE( short int * );
+    variant_ALIGN_TYPE( int *  );
+    variant_ALIGN_TYPE( long int * );
+    variant_ALIGN_TYPE( float * );
+    variant_ALIGN_TYPE( double * );
+    variant_ALIGN_TYPE( long double * );
+    variant_ALIGN_TYPE( void * );
+
+#ifdef HAVE_LONG_LONG
+    variant_ALIGN_TYPE( long long );
+#endif
+
+    struct Unknown;
+
+    Unknown ( * variant_UNIQUE(_) )( Unknown );
+    Unknown * Unknown::* variant_UNIQUE(_);
+    Unknown ( Unknown::* variant_UNIQUE(_) )( Unknown );
+
+    struct_t< Unknown ( * )( Unknown)         > variant_UNIQUE(_);
+    struct_t< Unknown * Unknown::*            > variant_UNIQUE(_);
+    struct_t< Unknown ( Unknown::* )(Unknown) > variant_UNIQUE(_);
+};
+
+#undef variant_UNIQUE
+#undef variant_UNIQUE2
+#undef variant_UNIQUE3
+
+#undef variant_ALIGN_TYPE
+
+#elif defined( variant_CONFIG_ALIGN_AS ) // variant_CONFIG_MAX_ALIGN_HACK
+
+// Use user-specified type for alignment:
+
+#define variant_ALIGN_AS( unused ) \
+    variant_CONFIG_ALIGN_AS
+
+#else // variant_CONFIG_MAX_ALIGN_HACK
+
+// Determine POD type to use for alignment:
+
+#define variant_ALIGN_AS( to_align ) \
+    typename detail::type_of_size< detail::alignment_types, detail::alignment_of< to_align >::value >::type
+
+template< typename T >
+struct alignment_of;
+
+template< typename T >
+struct alignment_of_hack
+{
+    char c;
+    T t;
+    alignment_of_hack();
+};
+
+template< size_t A, size_t S >
+struct alignment_logic
+{
+    enum V { value = A < S ? A : S };
+};
+
+template< typename T >
+struct alignment_of
+{
+    enum V { value = alignment_logic<
+        sizeof( alignment_of_hack<T> ) - sizeof(T), sizeof(T) >::value };
+};
+
+template< typename List, size_t N >
+struct type_of_size
+{
+    typedef typename std11::conditional<
+        N == sizeof( typename List::head ),
+            typename List::head,
+            typename type_of_size<typename List::tail, N >::type >::type type;
+};
+
+template< size_t N >
+struct type_of_size< nulltype, N >
+{
+    typedef variant_CONFIG_ALIGN_AS_FALLBACK type;
+};
+
+template< typename T>
+struct struct_t { T _; };
+
+#define variant_ALIGN_TYPE( type ) \
+    typelist< type , typelist< struct_t< type >
+
+struct Unknown;
+
+typedef
+    variant_ALIGN_TYPE( char ),
+    variant_ALIGN_TYPE( short ),
+    variant_ALIGN_TYPE( int ),
+    variant_ALIGN_TYPE( long ),
+    variant_ALIGN_TYPE( float ),
+    variant_ALIGN_TYPE( double ),
+    variant_ALIGN_TYPE( long double ),
+
+    variant_ALIGN_TYPE( char *),
+    variant_ALIGN_TYPE( short * ),
+    variant_ALIGN_TYPE( int * ),
+    variant_ALIGN_TYPE( long * ),
+    variant_ALIGN_TYPE( float * ),
+    variant_ALIGN_TYPE( double * ),
+    variant_ALIGN_TYPE( long double * ),
+
+    variant_ALIGN_TYPE( Unknown ( * )( Unknown ) ),
+    variant_ALIGN_TYPE( Unknown * Unknown::*     ),
+    variant_ALIGN_TYPE( Unknown ( Unknown::* )( Unknown ) ),
+
+    nulltype
+    > > > > > > >    > > > > > > >
+    > > > > > > >    > > > > > > >
+    > > > > > >
+    alignment_types;
+
+#undef variant_ALIGN_TYPE
+
+#endif // variant_CONFIG_MAX_ALIGN_HACK
+
+#if variant_CPP11_OR_GREATER
+
+template< typename T>
+inline std::size_t hash( T const & v )
+{
+    return std::hash<T>()( v );
+}
+
+inline std::size_t hash( T0 const & ) { return 0; }
+inline std::size_t hash( T1 const & ) { return 0; }
+inline std::size_t hash( T2 const & ) { return 0; }
+inline std::size_t hash( T3 const & ) { return 0; }
+inline std::size_t hash( T4 const & ) { return 0; }
+inline std::size_t hash( T5 const & ) { return 0; }
+inline std::size_t hash( T6 const & ) { return 0; }
+inline std::size_t hash( T7 const & ) { return 0; }
+inline std::size_t hash( T8 const & ) { return 0; }
+inline std::size_t hash( T9 const & ) { return 0; }
+inline std::size_t hash( T10 const & ) { return 0; }
+inline std::size_t hash( T11 const & ) { return 0; }
+inline std::size_t hash( T12 const & ) { return 0; }
+inline std::size_t hash( T13 const & ) { return 0; }
+inline std::size_t hash( T14 const & ) { return 0; }
+inline std::size_t hash( T15 const & ) { return 0; }
+
+
+#endif // variant_CPP11_OR_GREATER
+
+
+
+
+
+template< class T0, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13, class T14, class T15 >
+struct helper
+{
+    typedef signed char type_index_t;
+    typedef variant_TL16( T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15 ) variant_types;
+
+    template< class U >
+    static U * as( void * data )
+    {
+        return reinterpret_cast<U*>( data );
+    }
+
+    template< class U >
+    static U const * as( void const * data )
+    {
+        return reinterpret_cast<const U*>( data );
+    }
+
+    static type_index_t to_index_t( std::size_t index )
+    {
+        return static_cast<type_index_t>( index );
+    }
+
+    static void destroy( type_index_t index, void * data )
+    {
+        switch ( index )
+        {
+        case 0: as<T0>( data )->~T0(); break;
+        case 1: as<T1>( data )->~T1(); break;
+        case 2: as<T2>( data )->~T2(); break;
+        case 3: as<T3>( data )->~T3(); break;
+        case 4: as<T4>( data )->~T4(); break;
+        case 5: as<T5>( data )->~T5(); break;
+        case 6: as<T6>( data )->~T6(); break;
+        case 7: as<T7>( data )->~T7(); break;
+        case 8: as<T8>( data )->~T8(); break;
+        case 9: as<T9>( data )->~T9(); break;
+        case 10: as<T10>( data )->~T10(); break;
+        case 11: as<T11>( data )->~T11(); break;
+        case 12: as<T12>( data )->~T12(); break;
+        case 13: as<T13>( data )->~T13(); break;
+        case 14: as<T14>( data )->~T14(); break;
+        case 15: as<T15>( data )->~T15(); break;
+        
+        }
+    }
+
+#if variant_CPP11_OR_GREATER
+    template< class T, class... Args >
+    static type_index_t construct_t( void * data, Args&&... args )
+    {
+        new( data ) T( std::forward<Args>(args)... );
+
+        return to_index_t( detail::typelist_index_of< variant_types, T>::value );
+    }
+
+    template< std::size_t K, class... Args >
+    static type_index_t construct_i( void * data, Args&&... args )
+    {
+        using type = typename detail::typelist_type_at< variant_types, K >::type;
+
+        construct_t< type >( data, std::forward<Args>(args)... );
+
+        return to_index_t( K );
+    }
+
+    static type_index_t move_construct( type_index_t const from_index, void * from_value, void * to_value )
+    {
+        switch ( from_index )
+        {
+        case 0: new( to_value ) T0( std::move( *as<T0>( from_value ) ) ); break;
+        case 1: new( to_value ) T1( std::move( *as<T1>( from_value ) ) ); break;
+        case 2: new( to_value ) T2( std::move( *as<T2>( from_value ) ) ); break;
+        case 3: new( to_value ) T3( std::move( *as<T3>( from_value ) ) ); break;
+        case 4: new( to_value ) T4( std::move( *as<T4>( from_value ) ) ); break;
+        case 5: new( to_value ) T5( std::move( *as<T5>( from_value ) ) ); break;
+        case 6: new( to_value ) T6( std::move( *as<T6>( from_value ) ) ); break;
+        case 7: new( to_value ) T7( std::move( *as<T7>( from_value ) ) ); break;
+        case 8: new( to_value ) T8( std::move( *as<T8>( from_value ) ) ); break;
+        case 9: new( to_value ) T9( std::move( *as<T9>( from_value ) ) ); break;
+        case 10: new( to_value ) T10( std::move( *as<T10>( from_value ) ) ); break;
+        case 11: new( to_value ) T11( std::move( *as<T11>( from_value ) ) ); break;
+        case 12: new( to_value ) T12( std::move( *as<T12>( from_value ) ) ); break;
+        case 13: new( to_value ) T13( std::move( *as<T13>( from_value ) ) ); break;
+        case 14: new( to_value ) T14( std::move( *as<T14>( from_value ) ) ); break;
+        case 15: new( to_value ) T15( std::move( *as<T15>( from_value ) ) ); break;
+        
+        }
+        return from_index;
+    }
+
+    static type_index_t move_assign( type_index_t const from_index, void * from_value, void * to_value )
+    {
+        switch ( from_index )
+        {
+        case 0: *as<T0>( to_value ) = std::move( *as<T0>( from_value ) ); break;
+        case 1: *as<T1>( to_value ) = std::move( *as<T1>( from_value ) ); break;
+        case 2: *as<T2>( to_value ) = std::move( *as<T2>( from_value ) ); break;
+        case 3: *as<T3>( to_value ) = std::move( *as<T3>( from_value ) ); break;
+        case 4: *as<T4>( to_value ) = std::move( *as<T4>( from_value ) ); break;
+        case 5: *as<T5>( to_value ) = std::move( *as<T5>( from_value ) ); break;
+        case 6: *as<T6>( to_value ) = std::move( *as<T6>( from_value ) ); break;
+        case 7: *as<T7>( to_value ) = std::move( *as<T7>( from_value ) ); break;
+        case 8: *as<T8>( to_value ) = std::move( *as<T8>( from_value ) ); break;
+        case 9: *as<T9>( to_value ) = std::move( *as<T9>( from_value ) ); break;
+        case 10: *as<T10>( to_value ) = std::move( *as<T10>( from_value ) ); break;
+        case 11: *as<T11>( to_value ) = std::move( *as<T11>( from_value ) ); break;
+        case 12: *as<T12>( to_value ) = std::move( *as<T12>( from_value ) ); break;
+        case 13: *as<T13>( to_value ) = std::move( *as<T13>( from_value ) ); break;
+        case 14: *as<T14>( to_value ) = std::move( *as<T14>( from_value ) ); break;
+        case 15: *as<T15>( to_value ) = std::move( *as<T15>( from_value ) ); break;
+        
+        }
+        return from_index;
+    }
+#endif
+
+    static type_index_t copy_construct( type_index_t const from_index, const void * from_value, void * to_value )
+    {
+        switch ( from_index )
+        {
+        case 0: new( to_value ) T0( *as<T0>( from_value ) ); break;
+        case 1: new( to_value ) T1( *as<T1>( from_value ) ); break;
+        case 2: new( to_value ) T2( *as<T2>( from_value ) ); break;
+        case 3: new( to_value ) T3( *as<T3>( from_value ) ); break;
+        case 4: new( to_value ) T4( *as<T4>( from_value ) ); break;
+        case 5: new( to_value ) T5( *as<T5>( from_value ) ); break;
+        case 6: new( to_value ) T6( *as<T6>( from_value ) ); break;
+        case 7: new( to_value ) T7( *as<T7>( from_value ) ); break;
+        case 8: new( to_value ) T8( *as<T8>( from_value ) ); break;
+        case 9: new( to_value ) T9( *as<T9>( from_value ) ); break;
+        case 10: new( to_value ) T10( *as<T10>( from_value ) ); break;
+        case 11: new( to_value ) T11( *as<T11>( from_value ) ); break;
+        case 12: new( to_value ) T12( *as<T12>( from_value ) ); break;
+        case 13: new( to_value ) T13( *as<T13>( from_value ) ); break;
+        case 14: new( to_value ) T14( *as<T14>( from_value ) ); break;
+        case 15: new( to_value ) T15( *as<T15>( from_value ) ); break;
+        
+        }
+        return from_index;
+    }
+
+    static type_index_t copy_assign( type_index_t const from_index, const void * from_value, void * to_value )
+    {
+        switch ( from_index )
+        {
+        case 0: *as<T0>( to_value ) = *as<T0>( from_value ); break;
+        case 1: *as<T1>( to_value ) = *as<T1>( from_value ); break;
+        case 2: *as<T2>( to_value ) = *as<T2>( from_value ); break;
+        case 3: *as<T3>( to_value ) = *as<T3>( from_value ); break;
+        case 4: *as<T4>( to_value ) = *as<T4>( from_value ); break;
+        case 5: *as<T5>( to_value ) = *as<T5>( from_value ); break;
+        case 6: *as<T6>( to_value ) = *as<T6>( from_value ); break;
+        case 7: *as<T7>( to_value ) = *as<T7>( from_value ); break;
+        case 8: *as<T8>( to_value ) = *as<T8>( from_value ); break;
+        case 9: *as<T9>( to_value ) = *as<T9>( from_value ); break;
+        case 10: *as<T10>( to_value ) = *as<T10>( from_value ); break;
+        case 11: *as<T11>( to_value ) = *as<T11>( from_value ); break;
+        case 12: *as<T12>( to_value ) = *as<T12>( from_value ); break;
+        case 13: *as<T13>( to_value ) = *as<T13>( from_value ); break;
+        case 14: *as<T14>( to_value ) = *as<T14>( from_value ); break;
+        case 15: *as<T15>( to_value ) = *as<T15>( from_value ); break;
+        
+        }
+        return from_index;
+    }
+};
+
+} // namespace detail
+
+//
+// Variant:
+//
+
+template< class T0, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13, class T14, class T15 >
+class variant;
+
+// 19.7.8 Class monostate
+
+class monostate{};
+
+// 19.7.9 monostate relational operators
+
+inline variant_constexpr bool operator< ( monostate, monostate ) variant_noexcept { return false; }
+inline variant_constexpr bool operator> ( monostate, monostate ) variant_noexcept { return false; }
+inline variant_constexpr bool operator<=( monostate, monostate ) variant_noexcept { return true;  }
+inline variant_constexpr bool operator>=( monostate, monostate ) variant_noexcept { return true;  }
+inline variant_constexpr bool operator==( monostate, monostate ) variant_noexcept { return true;  }
+inline variant_constexpr bool operator!=( monostate, monostate ) variant_noexcept { return false; }
+
+// 19.7.4 variant helper classes
+
+// obtain the size of the variant's list of alternatives at compile time
+
+template< class T >
+struct variant_size; /* undefined */
+
+template< class T0, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13, class T14, class T15 >
+struct variant_size< variant<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15> >
+{
+    enum _ { value = detail::typelist_size< variant_TL16(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15) >::value };
+};
+
+#if variant_CPP14_OR_GREATER
+template< class T >
+constexpr std::size_t variant_size_v = variant_size<T>::value;
+#endif
+
+#if ! variant_CONFIG_OMIT_VARIANT_SIZE_V_MACRO
+# define variant_size_V(T)  nonstd::variant_size<T>::value
+#endif
+
+// obtain the type of the alternative specified by its index, at compile time:
+
+template< std::size_t K, class T >
+struct variant_alternative; /* undefined */
+
+template< std::size_t K, class T0, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13, class T14, class T15 >
+struct variant_alternative< K, variant<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15> >
+{
+    typedef typename detail::typelist_type_at<variant_TL16(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15), K>::type type;
+};
+
+#if variant_CPP11_OR_GREATER
+template< std::size_t K, class T >
+using variant_alternative_t = typename variant_alternative<K, T>::type;
+#endif
+
+#if ! variant_CONFIG_OMIT_VARIANT_ALTERNATIVE_T_MACRO
+# define variant_alternative_T(K,T)  typename nonstd::variant_alternative<K,T >::type
+#endif
+
+// NTS:implement specializes the std::uses_allocator type trait
+// std::uses_allocator<nonstd::variant>
+
+// index of the variant in the invalid state (constant)
+
+#if variant_CPP11_OR_GREATER
+variant_constexpr std::size_t variant_npos = static_cast<std::size_t>( -1 );
+#else
+static const std::size_t variant_npos = static_cast<std::size_t>( -1 );
+#endif
+
+#if ! variant_CONFIG_NO_EXCEPTIONS
+
+// 19.7.11 Class bad_variant_access
+
+class 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 // variant_CONFIG_NO_EXCEPTIONS
+
+// 19.7.3 Class template variant
+
+template<
+    class T0,
+    class T1 = detail::T1,
+    class T2 = detail::T2,
+    class T3 = detail::T3,
+    class T4 = detail::T4,
+    class T5 = detail::T5,
+    class T6 = detail::T6,
+    class T7 = detail::T7,
+    class T8 = detail::T8,
+    class T9 = detail::T9,
+    class T10 = detail::T10,
+    class T11 = detail::T11,
+    class T12 = detail::T12,
+    class T13 = detail::T13,
+    class T14 = detail::T14,
+    class T15 = detail::T15
+    >
+class variant
+{
+    typedef detail::helper< T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15 > helper_type;
+    typedef variant_TL16( T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15 ) variant_types;
+
+public:
+    // 19.7.3.1 Constructors
+
+    variant() : type_index( 0 ) { new( ptr() ) T0(); }
+
+#if variant_CPP11_OR_GREATER
+    template < variant_index_tag_t( 0 ) = variant_index_tag( 0 )
+        variant_REQUIRES_B(detail::typelist_type_is_unique< variant_types, 0 >::value) >
+    variant( T0 const & t0 ) : type_index( 0 ) { new( ptr() ) T0( t0 ); }
+
+    template < variant_index_tag_t( 1 ) = variant_index_tag( 1 )
+        variant_REQUIRES_B(detail::typelist_type_is_unique< variant_types, 1 >::value) >
+    variant( T1 const & t1 ) : type_index( 1 ) { new( ptr() ) T1( t1 ); }
+
+    template < variant_index_tag_t( 2 ) = variant_index_tag( 2 )
+        variant_REQUIRES_B(detail::typelist_type_is_unique< variant_types, 2 >::value) >
+    variant( T2 const & t2 ) : type_index( 2 ) { new( ptr() ) T2( t2 ); }
+
+    template < variant_index_tag_t( 3 ) = variant_index_tag( 3 )
+        variant_REQUIRES_B(detail::typelist_type_is_unique< variant_types, 3 >::value) >
+    variant( T3 const & t3 ) : type_index( 3 ) { new( ptr() ) T3( t3 ); }
+
+    template < variant_index_tag_t( 4 ) = variant_index_tag( 4 )
+        variant_REQUIRES_B(detail::typelist_type_is_unique< variant_types, 4 >::value) >
+    variant( T4 const & t4 ) : type_index( 4 ) { new( ptr() ) T4( t4 ); }
+
+    template < variant_index_tag_t( 5 ) = variant_index_tag( 5 )
+        variant_REQUIRES_B(detail::typelist_type_is_unique< variant_types, 5 >::value) >
+    variant( T5 const & t5 ) : type_index( 5 ) { new( ptr() ) T5( t5 ); }
+
+    template < variant_index_tag_t( 6 ) = variant_index_tag( 6 )
+        variant_REQUIRES_B(detail::typelist_type_is_unique< variant_types, 6 >::value) >
+    variant( T6 const & t6 ) : type_index( 6 ) { new( ptr() ) T6( t6 ); }
+
+    template < variant_index_tag_t( 7 ) = variant_index_tag( 7 )
+        variant_REQUIRES_B(detail::typelist_type_is_unique< variant_types, 7 >::value) >
+    variant( T7 const & t7 ) : type_index( 7 ) { new( ptr() ) T7( t7 ); }
+
+    template < variant_index_tag_t( 8 ) = variant_index_tag( 8 )
+        variant_REQUIRES_B(detail::typelist_type_is_unique< variant_types, 8 >::value) >
+    variant( T8 const & t8 ) : type_index( 8 ) { new( ptr() ) T8( t8 ); }
+
+    template < variant_index_tag_t( 9 ) = variant_index_tag( 9 )
+        variant_REQUIRES_B(detail::typelist_type_is_unique< variant_types, 9 >::value) >
+    variant( T9 const & t9 ) : type_index( 9 ) { new( ptr() ) T9( t9 ); }
+
+    template < variant_index_tag_t( 10 ) = variant_index_tag( 10 )
+        variant_REQUIRES_B(detail::typelist_type_is_unique< variant_types, 10 >::value) >
+    variant( T10 const & t10 ) : type_index( 10 ) { new( ptr() ) T10( t10 ); }
+
+    template < variant_index_tag_t( 11 ) = variant_index_tag( 11 )
+        variant_REQUIRES_B(detail::typelist_type_is_unique< variant_types, 11 >::value) >
+    variant( T11 const & t11 ) : type_index( 11 ) { new( ptr() ) T11( t11 ); }
+
+    template < variant_index_tag_t( 12 ) = variant_index_tag( 12 )
+        variant_REQUIRES_B(detail::typelist_type_is_unique< variant_types, 12 >::value) >
+    variant( T12 const & t12 ) : type_index( 12 ) { new( ptr() ) T12( t12 ); }
+
+    template < variant_index_tag_t( 13 ) = variant_index_tag( 13 )
+        variant_REQUIRES_B(detail::typelist_type_is_unique< variant_types, 13 >::value) >
+    variant( T13 const & t13 ) : type_index( 13 ) { new( ptr() ) T13( t13 ); }
+
+    template < variant_index_tag_t( 14 ) = variant_index_tag( 14 )
+        variant_REQUIRES_B(detail::typelist_type_is_unique< variant_types, 14 >::value) >
+    variant( T14 const & t14 ) : type_index( 14 ) { new( ptr() ) T14( t14 ); }
+
+    template < variant_index_tag_t( 15 ) = variant_index_tag( 15 )
+        variant_REQUIRES_B(detail::typelist_type_is_unique< variant_types, 15 >::value) >
+    variant( T15 const & t15 ) : type_index( 15 ) { new( ptr() ) T15( t15 ); }
+
+#else
+
+    variant( T0 const & t0 ) : type_index( 0 ) { new( ptr() ) T0( t0 ); }
+    variant( T1 const & t1 ) : type_index( 1 ) { new( ptr() ) T1( t1 ); }
+    variant( T2 const & t2 ) : type_index( 2 ) { new( ptr() ) T2( t2 ); }
+    variant( T3 const & t3 ) : type_index( 3 ) { new( ptr() ) T3( t3 ); }
+    variant( T4 const & t4 ) : type_index( 4 ) { new( ptr() ) T4( t4 ); }
+    variant( T5 const & t5 ) : type_index( 5 ) { new( ptr() ) T5( t5 ); }
+    variant( T6 const & t6 ) : type_index( 6 ) { new( ptr() ) T6( t6 ); }
+    variant( T7 const & t7 ) : type_index( 7 ) { new( ptr() ) T7( t7 ); }
+    variant( T8 const & t8 ) : type_index( 8 ) { new( ptr() ) T8( t8 ); }
+    variant( T9 const & t9 ) : type_index( 9 ) { new( ptr() ) T9( t9 ); }
+    variant( T10 const & t10 ) : type_index( 10 ) { new( ptr() ) T10( t10 ); }
+    variant( T11 const & t11 ) : type_index( 11 ) { new( ptr() ) T11( t11 ); }
+    variant( T12 const & t12 ) : type_index( 12 ) { new( ptr() ) T12( t12 ); }
+    variant( T13 const & t13 ) : type_index( 13 ) { new( ptr() ) T13( t13 ); }
+    variant( T14 const & t14 ) : type_index( 14 ) { new( ptr() ) T14( t14 ); }
+    variant( T15 const & t15 ) : type_index( 15 ) { new( ptr() ) T15( t15 ); }
+    
+#endif
+
+#if variant_CPP11_OR_GREATER
+    template < variant_index_tag_t( 0 ) = variant_index_tag( 0 )
+        variant_REQUIRES_B(detail::typelist_type_is_unique< variant_types, 0 >::value) >
+    variant( T0 && t0 )
+        : type_index( 0 ) { new( ptr() ) T0( std::move(t0) ); }
+
+    template < variant_index_tag_t( 1 ) = variant_index_tag( 1 )
+        variant_REQUIRES_B(detail::typelist_type_is_unique< variant_types, 1 >::value) >
+    variant( T1 && t1 )
+        : type_index( 1 ) { new( ptr() ) T1( std::move(t1) ); }
+
+    template < variant_index_tag_t( 2 ) = variant_index_tag( 2 )
+        variant_REQUIRES_B(detail::typelist_type_is_unique< variant_types, 2 >::value) >
+    variant( T2 && t2 )
+        : type_index( 2 ) { new( ptr() ) T2( std::move(t2) ); }
+
+    template < variant_index_tag_t( 3 ) = variant_index_tag( 3 )
+        variant_REQUIRES_B(detail::typelist_type_is_unique< variant_types, 3 >::value) >
+    variant( T3 && t3 )
+        : type_index( 3 ) { new( ptr() ) T3( std::move(t3) ); }
+
+    template < variant_index_tag_t( 4 ) = variant_index_tag( 4 )
+        variant_REQUIRES_B(detail::typelist_type_is_unique< variant_types, 4 >::value) >
+    variant( T4 && t4 )
+        : type_index( 4 ) { new( ptr() ) T4( std::move(t4) ); }
+
+    template < variant_index_tag_t( 5 ) = variant_index_tag( 5 )
+        variant_REQUIRES_B(detail::typelist_type_is_unique< variant_types, 5 >::value) >
+    variant( T5 && t5 )
+        : type_index( 5 ) { new( ptr() ) T5( std::move(t5) ); }
+
+    template < variant_index_tag_t( 6 ) = variant_index_tag( 6 )
+        variant_REQUIRES_B(detail::typelist_type_is_unique< variant_types, 6 >::value) >
+    variant( T6 && t6 )
+        : type_index( 6 ) { new( ptr() ) T6( std::move(t6) ); }
+
+    template < variant_index_tag_t( 7 ) = variant_index_tag( 7 )
+        variant_REQUIRES_B(detail::typelist_type_is_unique< variant_types, 7 >::value) >
+    variant( T7 && t7 )
+        : type_index( 7 ) { new( ptr() ) T7( std::move(t7) ); }
+
+    template < variant_index_tag_t( 8 ) = variant_index_tag( 8 )
+        variant_REQUIRES_B(detail::typelist_type_is_unique< variant_types, 8 >::value) >
+    variant( T8 && t8 )
+        : type_index( 8 ) { new( ptr() ) T8( std::move(t8) ); }
+
+    template < variant_index_tag_t( 9 ) = variant_index_tag( 9 )
+        variant_REQUIRES_B(detail::typelist_type_is_unique< variant_types, 9 >::value) >
+    variant( T9 && t9 )
+        : type_index( 9 ) { new( ptr() ) T9( std::move(t9) ); }
+
+    template < variant_index_tag_t( 10 ) = variant_index_tag( 10 )
+        variant_REQUIRES_B(detail::typelist_type_is_unique< variant_types, 10 >::value) >
+    variant( T10 && t10 )
+        : type_index( 10 ) { new( ptr() ) T10( std::move(t10) ); }
+
+    template < variant_index_tag_t( 11 ) = variant_index_tag( 11 )
+        variant_REQUIRES_B(detail::typelist_type_is_unique< variant_types, 11 >::value) >
+    variant( T11 && t11 )
+        : type_index( 11 ) { new( ptr() ) T11( std::move(t11) ); }
+
+    template < variant_index_tag_t( 12 ) = variant_index_tag( 12 )
+        variant_REQUIRES_B(detail::typelist_type_is_unique< variant_types, 12 >::value) >
+    variant( T12 && t12 )
+        : type_index( 12 ) { new( ptr() ) T12( std::move(t12) ); }
+
+    template < variant_index_tag_t( 13 ) = variant_index_tag( 13 )
+        variant_REQUIRES_B(detail::typelist_type_is_unique< variant_types, 13 >::value) >
+    variant( T13 && t13 )
+        : type_index( 13 ) { new( ptr() ) T13( std::move(t13) ); }
+
+    template < variant_index_tag_t( 14 ) = variant_index_tag( 14 )
+        variant_REQUIRES_B(detail::typelist_type_is_unique< variant_types, 14 >::value) >
+    variant( T14 && t14 )
+        : type_index( 14 ) { new( ptr() ) T14( std::move(t14) ); }
+
+    template < variant_index_tag_t( 15 ) = variant_index_tag( 15 )
+        variant_REQUIRES_B(detail::typelist_type_is_unique< variant_types, 15 >::value) >
+    variant( T15 && t15 )
+        : type_index( 15 ) { new( ptr() ) T15( std::move(t15) ); }
+#endif
+
+    variant(variant const & other)
+    : type_index( other.type_index )
+    {
+        (void) helper_type::copy_construct( other.type_index, other.ptr(), ptr() );
+    }
+
+#if variant_CPP11_OR_GREATER
+
+    variant( variant && other ) noexcept(
+        std::is_nothrow_move_constructible<T0>::value &&
+        std::is_nothrow_move_constructible<T1>::value &&
+        std::is_nothrow_move_constructible<T2>::value &&
+        std::is_nothrow_move_constructible<T3>::value &&
+        std::is_nothrow_move_constructible<T4>::value &&
+        std::is_nothrow_move_constructible<T5>::value &&
+        std::is_nothrow_move_constructible<T6>::value &&
+        std::is_nothrow_move_constructible<T7>::value &&
+        std::is_nothrow_move_constructible<T8>::value &&
+        std::is_nothrow_move_constructible<T9>::value &&
+        std::is_nothrow_move_constructible<T10>::value &&
+        std::is_nothrow_move_constructible<T11>::value &&
+        std::is_nothrow_move_constructible<T12>::value &&
+        std::is_nothrow_move_constructible<T13>::value &&
+        std::is_nothrow_move_constructible<T14>::value &&
+        std::is_nothrow_move_constructible<T15>::value)
+        : type_index( other.type_index )
+    {
+        (void) helper_type::move_construct( other.type_index, other.ptr(), ptr() );
+    }
+
+    template< std::size_t K >
+    using type_at_t = typename detail::typelist_type_at< variant_types, K >::type;
+
+    template< class T, class... Args
+        variant_REQUIRES_T( std::is_constructible< T, Args...>::value )
+    >
+    explicit variant( nonstd_lite_in_place_type_t(T), Args&&... args)
+    {
+        type_index = variant_npos_internal();
+        type_index = helper_type::template construct_t<T>( ptr(), std::forward<Args>(args)... );
+    }
+
+    template< class T, class U, class... Args
+        variant_REQUIRES_T( std::is_constructible< T, std::initializer_list<U>&, Args...>::value )
+    >
+    explicit variant( nonstd_lite_in_place_type_t(T), std::initializer_list<U> il, Args&&... args )
+    {
+        type_index = variant_npos_internal();
+        type_index = helper_type::template construct_t<T>( ptr(), il, std::forward<Args>(args)... );
+    }
+
+    template< std::size_t K, class... Args
+        variant_REQUIRES_T( std::is_constructible< type_at_t<K>, Args...>::value )
+    >
+    explicit variant( nonstd_lite_in_place_index_t(K), Args&&... args )
+    {
+        type_index = variant_npos_internal();
+        type_index = helper_type::template construct_i<K>( ptr(), std::forward<Args>(args)... );
+    }
+
+    template< size_t K, class U, class... Args
+        variant_REQUIRES_T( std::is_constructible< type_at_t<K>, std::initializer_list<U>&, Args...>::value )
+    >
+    explicit variant( nonstd_lite_in_place_index_t(K), std::initializer_list<U> il, Args&&... args )
+    {
+        type_index = variant_npos_internal();
+        type_index = helper_type::template construct_i<K>( ptr(), il, std::forward<Args>(args)... );
+    }
+
+#endif // variant_CPP11_OR_GREATER
+
+    // 19.7.3.2 Destructor
+
+    ~variant()
+    {
+        if ( ! valueless_by_exception() )
+        {
+            helper_type::destroy( type_index, ptr() );
+        }
+    }
+
+    // 19.7.3.3 Assignment
+
+    variant & operator=( variant const & other )
+    {
+        return copy_assign( other );
+    }
+
+#if variant_CPP11_OR_GREATER
+
+    variant & operator=( variant && other ) noexcept(
+        std::is_nothrow_move_assignable<T0>::value &&
+        std::is_nothrow_move_assignable<T1>::value &&
+        std::is_nothrow_move_assignable<T2>::value &&
+        std::is_nothrow_move_assignable<T3>::value &&
+        std::is_nothrow_move_assignable<T4>::value &&
+        std::is_nothrow_move_assignable<T5>::value &&
+        std::is_nothrow_move_assignable<T6>::value &&
+        std::is_nothrow_move_assignable<T7>::value &&
+        std::is_nothrow_move_assignable<T8>::value &&
+        std::is_nothrow_move_assignable<T9>::value &&
+        std::is_nothrow_move_assignable<T10>::value &&
+        std::is_nothrow_move_assignable<T11>::value &&
+        std::is_nothrow_move_assignable<T12>::value &&
+        std::is_nothrow_move_assignable<T13>::value &&
+        std::is_nothrow_move_assignable<T14>::value &&
+        std::is_nothrow_move_assignable<T15>::value)
+        {
+        return move_assign( std::move( other ) );
+    }
+
+    template < variant_index_tag_t( 0 ) = variant_index_tag( 0 )
+        variant_REQUIRES_B(detail::typelist_type_is_unique< variant_types, 0 >::value) >
+    variant & operator=( T0 &&      t0 ) { return assign_value<0>( std::move( t0 ) ); }
+
+    template < variant_index_tag_t( 1 ) = variant_index_tag( 1 )
+        variant_REQUIRES_B(detail::typelist_type_is_unique< variant_types, 1 >::value) >
+    variant & operator=( T1 &&      t1 ) { return assign_value<1>( std::move( t1 ) ); }
+
+    template < variant_index_tag_t( 2 ) = variant_index_tag( 2 )
+        variant_REQUIRES_B(detail::typelist_type_is_unique< variant_types, 2 >::value) >
+    variant & operator=( T2 &&      t2 ) { return assign_value<2>( std::move( t2 ) ); }
+
+    template < variant_index_tag_t( 3 ) = variant_index_tag( 3 )
+        variant_REQUIRES_B(detail::typelist_type_is_unique< variant_types, 3 >::value) >
+    variant & operator=( T3 &&      t3 ) { return assign_value<3>( std::move( t3 ) ); }
+
+    template < variant_index_tag_t( 4 ) = variant_index_tag( 4 )
+        variant_REQUIRES_B(detail::typelist_type_is_unique< variant_types, 4 >::value) >
+    variant & operator=( T4 &&      t4 ) { return assign_value<4>( std::move( t4 ) ); }
+
+    template < variant_index_tag_t( 5 ) = variant_index_tag( 5 )
+        variant_REQUIRES_B(detail::typelist_type_is_unique< variant_types, 5 >::value) >
+    variant & operator=( T5 &&      t5 ) { return assign_value<5>( std::move( t5 ) ); }
+
+    template < variant_index_tag_t( 6 ) = variant_index_tag( 6 )
+        variant_REQUIRES_B(detail::typelist_type_is_unique< variant_types, 6 >::value) >
+    variant & operator=( T6 &&      t6 ) { return assign_value<6>( std::move( t6 ) ); }
+
+    template < variant_index_tag_t( 7 ) = variant_index_tag( 7 )
+        variant_REQUIRES_B(detail::typelist_type_is_unique< variant_types, 7 >::value) >
+    variant & operator=( T7 &&      t7 ) { return assign_value<7>( std::move( t7 ) ); }
+
+    template < variant_index_tag_t( 8 ) = variant_index_tag( 8 )
+        variant_REQUIRES_B(detail::typelist_type_is_unique< variant_types, 8 >::value) >
+    variant & operator=( T8 &&      t8 ) { return assign_value<8>( std::move( t8 ) ); }
+
+    template < variant_index_tag_t( 9 ) = variant_index_tag( 9 )
+        variant_REQUIRES_B(detail::typelist_type_is_unique< variant_types, 9 >::value) >
+    variant & operator=( T9 &&      t9 ) { return assign_value<9>( std::move( t9 ) ); }
+
+    template < variant_index_tag_t( 10 ) = variant_index_tag( 10 )
+        variant_REQUIRES_B(detail::typelist_type_is_unique< variant_types, 10 >::value) >
+    variant & operator=( T10 &&      t10 ) { return assign_value<10>( std::move( t10 ) ); }
+
+    template < variant_index_tag_t( 11 ) = variant_index_tag( 11 )
+        variant_REQUIRES_B(detail::typelist_type_is_unique< variant_types, 11 >::value) >
+    variant & operator=( T11 &&      t11 ) { return assign_value<11>( std::move( t11 ) ); }
+
+    template < variant_index_tag_t( 12 ) = variant_index_tag( 12 )
+        variant_REQUIRES_B(detail::typelist_type_is_unique< variant_types, 12 >::value) >
+    variant & operator=( T12 &&      t12 ) { return assign_value<12>( std::move( t12 ) ); }
+
+    template < variant_index_tag_t( 13 ) = variant_index_tag( 13 )
+        variant_REQUIRES_B(detail::typelist_type_is_unique< variant_types, 13 >::value) >
+    variant & operator=( T13 &&      t13 ) { return assign_value<13>( std::move( t13 ) ); }
+
+    template < variant_index_tag_t( 14 ) = variant_index_tag( 14 )
+        variant_REQUIRES_B(detail::typelist_type_is_unique< variant_types, 14 >::value) >
+    variant & operator=( T14 &&      t14 ) { return assign_value<14>( std::move( t14 ) ); }
+
+    template < variant_index_tag_t( 15 ) = variant_index_tag( 15 )
+        variant_REQUIRES_B(detail::typelist_type_is_unique< variant_types, 15 >::value) >
+    variant & operator=( T15 &&      t15 ) { return assign_value<15>( std::move( t15 ) ); }
+
+#endif
+
+#if variant_CPP11_OR_GREATER
+
+    template < variant_index_tag_t( 0 ) = variant_index_tag( 0 )
+        variant_REQUIRES_B(detail::typelist_type_is_unique< variant_types, 0 >::value) >
+    variant & operator=( T0 const & t0 ) { return assign_value<0>( t0 ); }
+
+    template < variant_index_tag_t( 1 ) = variant_index_tag( 1 )
+        variant_REQUIRES_B(detail::typelist_type_is_unique< variant_types, 1 >::value) >
+    variant & operator=( T1 const & t1 ) { return assign_value<1>( t1 ); }
+
+    template < variant_index_tag_t( 2 ) = variant_index_tag( 2 )
+        variant_REQUIRES_B(detail::typelist_type_is_unique< variant_types, 2 >::value) >
+    variant & operator=( T2 const & t2 ) { return assign_value<2>( t2 ); }
+
+    template < variant_index_tag_t( 3 ) = variant_index_tag( 3 )
+        variant_REQUIRES_B(detail::typelist_type_is_unique< variant_types, 3 >::value) >
+    variant & operator=( T3 const & t3 ) { return assign_value<3>( t3 ); }
+
+    template < variant_index_tag_t( 4 ) = variant_index_tag( 4 )
+        variant_REQUIRES_B(detail::typelist_type_is_unique< variant_types, 4 >::value) >
+    variant & operator=( T4 const & t4 ) { return assign_value<4>( t4 ); }
+
+    template < variant_index_tag_t( 5 ) = variant_index_tag( 5 )
+        variant_REQUIRES_B(detail::typelist_type_is_unique< variant_types, 5 >::value) >
+    variant & operator=( T5 const & t5 ) { return assign_value<5>( t5 ); }
+
+    template < variant_index_tag_t( 6 ) = variant_index_tag( 6 )
+        variant_REQUIRES_B(detail::typelist_type_is_unique< variant_types, 6 >::value) >
+    variant & operator=( T6 const & t6 ) { return assign_value<6>( t6 ); }
+
+    template < variant_index_tag_t( 7 ) = variant_index_tag( 7 )
+        variant_REQUIRES_B(detail::typelist_type_is_unique< variant_types, 7 >::value) >
+    variant & operator=( T7 const & t7 ) { return assign_value<7>( t7 ); }
+
+    template < variant_index_tag_t( 8 ) = variant_index_tag( 8 )
+        variant_REQUIRES_B(detail::typelist_type_is_unique< variant_types, 8 >::value) >
+    variant & operator=( T8 const & t8 ) { return assign_value<8>( t8 ); }
+
+    template < variant_index_tag_t( 9 ) = variant_index_tag( 9 )
+        variant_REQUIRES_B(detail::typelist_type_is_unique< variant_types, 9 >::value) >
+    variant & operator=( T9 const & t9 ) { return assign_value<9>( t9 ); }
+
+    template < variant_index_tag_t( 10 ) = variant_index_tag( 10 )
+        variant_REQUIRES_B(detail::typelist_type_is_unique< variant_types, 10 >::value) >
+    variant & operator=( T10 const & t10 ) { return assign_value<10>( t10 ); }
+
+    template < variant_index_tag_t( 11 ) = variant_index_tag( 11 )
+        variant_REQUIRES_B(detail::typelist_type_is_unique< variant_types, 11 >::value) >
+    variant & operator=( T11 const & t11 ) { return assign_value<11>( t11 ); }
+
+    template < variant_index_tag_t( 12 ) = variant_index_tag( 12 )
+        variant_REQUIRES_B(detail::typelist_type_is_unique< variant_types, 12 >::value) >
+    variant & operator=( T12 const & t12 ) { return assign_value<12>( t12 ); }
+
+    template < variant_index_tag_t( 13 ) = variant_index_tag( 13 )
+        variant_REQUIRES_B(detail::typelist_type_is_unique< variant_types, 13 >::value) >
+    variant & operator=( T13 const & t13 ) { return assign_value<13>( t13 ); }
+
+    template < variant_index_tag_t( 14 ) = variant_index_tag( 14 )
+        variant_REQUIRES_B(detail::typelist_type_is_unique< variant_types, 14 >::value) >
+    variant & operator=( T14 const & t14 ) { return assign_value<14>( t14 ); }
+
+    template < variant_index_tag_t( 15 ) = variant_index_tag( 15 )
+        variant_REQUIRES_B(detail::typelist_type_is_unique< variant_types, 15 >::value) >
+    variant & operator=( T15 const & t15 ) { return assign_value<15>( t15 ); }
+
+#else
+
+    variant & operator=( T0 const & t0 ) { return assign_value<0>( t0 ); }
+    variant & operator=( T1 const & t1 ) { return assign_value<1>( t1 ); }
+    variant & operator=( T2 const & t2 ) { return assign_value<2>( t2 ); }
+    variant & operator=( T3 const & t3 ) { return assign_value<3>( t3 ); }
+    variant & operator=( T4 const & t4 ) { return assign_value<4>( t4 ); }
+    variant & operator=( T5 const & t5 ) { return assign_value<5>( t5 ); }
+    variant & operator=( T6 const & t6 ) { return assign_value<6>( t6 ); }
+    variant & operator=( T7 const & t7 ) { return assign_value<7>( t7 ); }
+    variant & operator=( T8 const & t8 ) { return assign_value<8>( t8 ); }
+    variant & operator=( T9 const & t9 ) { return assign_value<9>( t9 ); }
+    variant & operator=( T10 const & t10 ) { return assign_value<10>( t10 ); }
+    variant & operator=( T11 const & t11 ) { return assign_value<11>( t11 ); }
+    variant & operator=( T12 const & t12 ) { return assign_value<12>( t12 ); }
+    variant & operator=( T13 const & t13 ) { return assign_value<13>( t13 ); }
+    variant & operator=( T14 const & t14 ) { return assign_value<14>( t14 ); }
+    variant & operator=( T15 const & t15 ) { return assign_value<15>( t15 ); }
+    
+#endif
+
+    std::size_t index() const
+    {
+        return variant_npos_internal() == type_index ? variant_npos : static_cast<std::size_t>( type_index );
+    }
+
+    // 19.7.3.4 Modifiers
+
+#if variant_CPP11_OR_GREATER
+
+    template< class T, class... Args
+        variant_REQUIRES_T( std::is_constructible< T, Args...>::value )
+        variant_REQUIRES_T( detail::typelist_contains_unique_type< variant_types, T >::value )
+    >
+    T& emplace( Args&&... args )
+    {
+        helper_type::destroy( type_index, ptr() );
+        type_index = variant_npos_internal();
+        type_index = helper_type::template construct_t<T>( ptr(), std::forward<Args>(args)... );
+
+        return *as<T>();
+    }
+
+    template< class T, class U, class... Args
+        variant_REQUIRES_T( std::is_constructible< T, std::initializer_list<U>&, Args...>::value )
+        variant_REQUIRES_T( detail::typelist_contains_unique_type< variant_types, T >::value )
+    >
+    T& emplace( std::initializer_list<U> il, Args&&... args )
+    {
+        helper_type::destroy( type_index, ptr() );
+        type_index = variant_npos_internal();
+        type_index = helper_type::template construct_t<T>( ptr(), il, std::forward<Args>(args)... );
+
+        return *as<T>();
+    }
+
+    template< size_t K, class... Args
+        variant_REQUIRES_T( std::is_constructible< type_at_t<K>, Args...>::value )
+    >
+    variant_alternative_t<K, variant> & emplace( Args&&... args )
+    {
+        return this->template emplace< type_at_t<K> >( std::forward<Args>(args)... );
+    }
+
+    template< size_t K, class U, class... Args
+        variant_REQUIRES_T( std::is_constructible< type_at_t<K>, std::initializer_list<U>&, Args...>::value )
+    >
+    variant_alternative_t<K, variant> & emplace( std::initializer_list<U> il, Args&&... args )
+    {
+        return this->template emplace< type_at_t<K> >( il, std::forward<Args>(args)... );
+    }
+
+#endif // variant_CPP11_OR_GREATER
+
+    // 19.7.3.5 Value status
+
+    bool valueless_by_exception() const
+    {
+        return type_index == variant_npos_internal();
+    }
+
+    // 19.7.3.6 Swap
+
+    void swap( variant & other )
+#if variant_CPP11_OR_GREATER
+        noexcept(
+            std::is_nothrow_move_constructible<T0>::value && std17::is_nothrow_swappable<T0>::value &&
+            std::is_nothrow_move_constructible<T1>::value && std17::is_nothrow_swappable<T1>::value &&
+            std::is_nothrow_move_constructible<T2>::value && std17::is_nothrow_swappable<T2>::value &&
+            std::is_nothrow_move_constructible<T3>::value && std17::is_nothrow_swappable<T3>::value &&
+            std::is_nothrow_move_constructible<T4>::value && std17::is_nothrow_swappable<T4>::value &&
+            std::is_nothrow_move_constructible<T5>::value && std17::is_nothrow_swappable<T5>::value &&
+            std::is_nothrow_move_constructible<T6>::value && std17::is_nothrow_swappable<T6>::value &&
+            std::is_nothrow_move_constructible<T7>::value && std17::is_nothrow_swappable<T7>::value &&
+            std::is_nothrow_move_constructible<T8>::value && std17::is_nothrow_swappable<T8>::value &&
+            std::is_nothrow_move_constructible<T9>::value && std17::is_nothrow_swappable<T9>::value &&
+            std::is_nothrow_move_constructible<T10>::value && std17::is_nothrow_swappable<T10>::value &&
+            std::is_nothrow_move_constructible<T11>::value && std17::is_nothrow_swappable<T11>::value &&
+            std::is_nothrow_move_constructible<T12>::value && std17::is_nothrow_swappable<T12>::value &&
+            std::is_nothrow_move_constructible<T13>::value && std17::is_nothrow_swappable<T13>::value &&
+            std::is_nothrow_move_constructible<T14>::value && std17::is_nothrow_swappable<T14>::value &&
+            std::is_nothrow_move_constructible<T15>::value && std17::is_nothrow_swappable<T15>::value 
+            
+        )
+#endif
+    {
+        if ( valueless_by_exception() && other.valueless_by_exception() )
+        {
+            // no effect
+        }
+        else if ( type_index == other.type_index )
+        {
+            this->swap_value( type_index, other );
+        }
+        else
+        {
+#if variant_CPP11_OR_GREATER
+            variant tmp( std::move( *this ) );
+            *this = std::move( other );
+            other = std::move( tmp );
+#else
+            variant tmp( *this );
+            *this = other;
+            other = tmp;
+#endif
+        }
+    }
+
+    //
+    // non-standard:
+    //
+
+    template< class T >
+    static variant_constexpr std::size_t index_of() variant_noexcept
+    {
+        return to_size_t( detail::typelist_index_of<variant_types, typename std11::remove_cv<T>::type >::value );
+    }
+
+    template< class T >
+    T & get()
+    {
+#if variant_CONFIG_NO_EXCEPTIONS
+        assert( index_of<T>() == index() );
+#else
+        if ( index_of<T>() != index() )
+        {
+            throw bad_variant_access();
+        }
+#endif
+        return *as<T>();
+    }
+
+    template< class T >
+    T const & get() const
+    {
+#if variant_CONFIG_NO_EXCEPTIONS
+        assert( index_of<T>() == index() );
+#else
+        if ( index_of<T>() != index() )
+        {
+            throw bad_variant_access();
+        }
+#endif
+        return *as<const T>();
+    }
+
+    template< std::size_t K >
+    typename variant_alternative< K, variant >::type &
+    get()
+    {
+        return this->template get< typename detail::typelist_type_at< variant_types, K >::type >();
+    }
+
+    template< std::size_t K >
+    typename variant_alternative< K, variant >::type const &
+    get() const
+    {
+        return this->template get< typename detail::typelist_type_at< variant_types, K >::type >();
+    }
+
+private:
+    typedef typename helper_type::type_index_t type_index_t;
+
+    void * ptr() variant_noexcept
+    {
+        return &data;
+    }
+
+    void const * ptr() const variant_noexcept
+    {
+        return &data;
+    }
+
+    template< class U >
+    U * as()
+    {
+        return reinterpret_cast<U*>( ptr() );
+    }
+
+    template< class U >
+    U const * as() const
+    {
+        return reinterpret_cast<U const *>( ptr() );
+    }
+
+    template< class U >
+    static variant_constexpr std::size_t to_size_t( U index )
+    {
+        return static_cast<std::size_t>( index );
+    }
+
+    variant_constexpr type_index_t variant_npos_internal() const variant_noexcept
+    {
+        return static_cast<type_index_t>( -1 );
+    }
+
+    variant & copy_assign( variant const & other )
+    {
+        if ( valueless_by_exception() && other.valueless_by_exception() )
+        {
+            // no effect
+        }
+        else if ( ! valueless_by_exception() && other.valueless_by_exception() )
+        {
+            helper_type::destroy( type_index, ptr() );
+            type_index = variant_npos_internal();
+        }
+        else if ( index() == other.index() )
+        {
+            type_index = helper_type::copy_assign( other.type_index, other.ptr(), ptr() );
+        }
+        else
+        {
+            helper_type::destroy( type_index, ptr() );
+            type_index = variant_npos_internal();
+            type_index = helper_type::copy_construct( other.type_index, other.ptr(), ptr() );
+        }
+        return *this;
+    }
+
+#if variant_CPP11_OR_GREATER
+
+    variant & move_assign( variant && other )
+    {
+        if ( valueless_by_exception() && other.valueless_by_exception() )
+        {
+            // no effect
+        }
+        else if ( ! valueless_by_exception() && other.valueless_by_exception() )
+        {
+            helper_type::destroy( type_index, ptr() );
+            type_index = variant_npos_internal();
+        }
+        else if ( index() == other.index() )
+        {
+            type_index = helper_type::move_assign( other.type_index, other.ptr(), ptr() );
+        }
+        else
+        {
+            helper_type::destroy( type_index, ptr() );
+            type_index = variant_npos_internal();
+            type_index = helper_type::move_construct( other.type_index, other.ptr(), ptr() );
+        }
+        return *this;
+    }
+
+    template< std::size_t K, class T >
+    variant & assign_value( T && value )
+    {
+        if( index() == K )
+        {
+            *as<T>() = std::forward<T>( value );
+        }
+        else
+        {
+            helper_type::destroy( type_index, ptr() );
+            type_index = variant_npos_internal();
+            new( ptr() ) T( std::forward<T>( value ) );
+            type_index = K;
+        }
+        return *this;
+    }
+
+#endif // variant_CPP11_OR_GREATER
+
+    template< std::size_t K, class T >
+    variant & assign_value( T const & value )
+    {
+        if( index() == K )
+        {
+            *as<T>() = value;
+        }
+        else
+        {
+            helper_type::destroy( type_index, ptr() );
+            type_index = variant_npos_internal();
+            new( ptr() ) T( value );
+            type_index = K;
+        }
+        return *this;
+    }
+
+    void swap_value( type_index_t index, variant & other )
+    {
+        using std::swap;
+        switch( index )
+        {
+            case 0: swap( this->get<0>(), other.get<0>() ); break;
+            case 1: swap( this->get<1>(), other.get<1>() ); break;
+            case 2: swap( this->get<2>(), other.get<2>() ); break;
+            case 3: swap( this->get<3>(), other.get<3>() ); break;
+            case 4: swap( this->get<4>(), other.get<4>() ); break;
+            case 5: swap( this->get<5>(), other.get<5>() ); break;
+            case 6: swap( this->get<6>(), other.get<6>() ); break;
+            case 7: swap( this->get<7>(), other.get<7>() ); break;
+            case 8: swap( this->get<8>(), other.get<8>() ); break;
+            case 9: swap( this->get<9>(), other.get<9>() ); break;
+            case 10: swap( this->get<10>(), other.get<10>() ); break;
+            case 11: swap( this->get<11>(), other.get<11>() ); break;
+            case 12: swap( this->get<12>(), other.get<12>() ); break;
+            case 13: swap( this->get<13>(), other.get<13>() ); break;
+            case 14: swap( this->get<14>(), other.get<14>() ); break;
+            case 15: swap( this->get<15>(), other.get<15>() ); break;
+            
+        }
+    }
+
+private:
+    enum { data_size  = detail::typelist_max< variant_types >::value };
+
+#if variant_CPP11_OR_GREATER
+
+    enum { data_align = detail::typelist_max_alignof< variant_types >::value };
+
+    using aligned_storage_t = typename std::aligned_storage< data_size, data_align >::type;
+    aligned_storage_t data;
+
+#elif variant_CONFIG_MAX_ALIGN_HACK
+
+    typedef union { unsigned char data[ data_size ]; } aligned_storage_t;
+
+    detail::max_align_t hack;
+    aligned_storage_t data;
+
+#else
+    typedef typename detail::typelist_max< variant_types >::type max_type;
+
+    typedef variant_ALIGN_AS( max_type ) align_as_type;
+
+    typedef union { align_as_type data[ 1 + ( data_size - 1 ) / sizeof(align_as_type) ]; } aligned_storage_t;
+    aligned_storage_t data;
+
+// #   undef variant_ALIGN_AS
+
+#endif // variant_CONFIG_MAX_ALIGN_HACK
+
+    type_index_t type_index;
+};
+
+// 19.7.5 Value access
+
+template< class T, class T0, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13, class T14, class T15 >
+inline bool holds_alternative( variant<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15> const & v ) variant_noexcept
+{
+    return v.index() == variant<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15>::template index_of<T>();
+}
+
+template< class R, class T0, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13, class T14, class T15 >
+inline R & get( variant<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15> & v, nonstd_lite_in_place_type_t(R) = nonstd_lite_in_place_type(R) )
+{
+    return v.template get<R>();
+}
+
+template< class R, class T0, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13, class T14, class T15 >
+inline R const & get( variant<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15> const & v, nonstd_lite_in_place_type_t(R) = nonstd_lite_in_place_type(R) )
+{
+    return v.template get<R>();
+}
+
+template< std::size_t K, class T0, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13, class T14, class T15 >
+inline typename variant_alternative< K, variant<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15> >::type &
+get( variant<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15> & v, nonstd_lite_in_place_index_t(K) = nonstd_lite_in_place_index(K) )
+{
+#if variant_CONFIG_NO_EXCEPTIONS
+    assert( K == v.index() );
+#else
+    if ( K != v.index() )
+    {
+        throw bad_variant_access();
+    }
+#endif
+    return v.template get<K>();
+}
+
+template< std::size_t K, class T0, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13, class T14, class T15 >
+inline typename variant_alternative< K, variant<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15> >::type const &
+get( variant<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15> const & v, nonstd_lite_in_place_index_t(K) = nonstd_lite_in_place_index(K) )
+{
+#if variant_CONFIG_NO_EXCEPTIONS
+    assert( K == v.index() );
+#else
+    if ( K != v.index() )
+    {
+        throw bad_variant_access();
+    }
+#endif
+    return v.template get<K>();
+}
+
+#if variant_CPP11_OR_GREATER
+
+template< class R, class T0, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13, class T14, class T15 >
+inline R && get( variant<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15> && v, nonstd_lite_in_place_type_t(R) = nonstd_lite_in_place_type(R) )
+{
+    return std::move(v.template get<R>());
+}
+
+template< class R, class T0, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13, class T14, class T15 >
+inline R const && get( variant<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15> const && v, nonstd_lite_in_place_type_t(R) = nonstd_lite_in_place_type(R) )
+{
+    return std::move(v.template get<R>());
+}
+
+template< std::size_t K, class T0, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13, class T14, class T15 >
+inline typename variant_alternative< K, variant<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15> >::type &&
+get( variant<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15> && v, nonstd_lite_in_place_index_t(K) = nonstd_lite_in_place_index(K) )
+{
+#if variant_CONFIG_NO_EXCEPTIONS
+    assert( K == v.index() );
+#else
+    if ( K != v.index() )
+    {
+        throw bad_variant_access();
+    }
+#endif
+    return std::move(v.template get<K>());
+}
+
+template< std::size_t K, class T0, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13, class T14, class T15 >
+inline typename variant_alternative< K, variant<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15> >::type const &&
+get( variant<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15> const && v, nonstd_lite_in_place_index_t(K) = nonstd_lite_in_place_index(K) )
+{
+#if variant_CONFIG_NO_EXCEPTIONS
+    assert( K == v.index() );
+#else
+    if ( K != v.index() )
+    {
+        throw bad_variant_access();
+    }
+#endif
+    return std::move(v.template get<K>());
+}
+
+#endif // variant_CPP11_OR_GREATER
+
+template< class T, class T0, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13, class T14, class T15 >
+inline typename std11::add_pointer<T>::type
+get_if( variant<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15> * pv, nonstd_lite_in_place_type_t(T) = nonstd_lite_in_place_type(T) )
+{
+    return ( pv->index() == variant<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15>::template index_of<T>() ) ? &get<T>( *pv ) : variant_nullptr;
+}
+
+template< class T, class T0, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13, class T14, class T15 >
+inline typename std11::add_pointer<const T>::type
+get_if( variant<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15> const * pv, nonstd_lite_in_place_type_t(T) = nonstd_lite_in_place_type(T))
+{
+    return ( pv->index() == variant<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15>::template index_of<T>() ) ? &get<T>( *pv ) : variant_nullptr;
+}
+
+template< std::size_t K, class T0, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13, class T14, class T15 >
+inline typename std11::add_pointer< typename variant_alternative<K, variant<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15> >::type >::type
+get_if( variant<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15> * pv, nonstd_lite_in_place_index_t(K) = nonstd_lite_in_place_index(K) )
+{
+    return ( pv->index() == K ) ? &get<K>( *pv ) : variant_nullptr;
+}
+
+template< std::size_t K, class T0, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13, class T14, class T15 >
+inline typename std11::add_pointer< const typename variant_alternative<K, variant<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15> >::type >::type
+get_if( variant<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15> const * pv, nonstd_lite_in_place_index_t(K) = nonstd_lite_in_place_index(K) )
+{
+    return ( pv->index() == K ) ? &get<K>( *pv )  : variant_nullptr;
+}
+
+// 19.7.10 Specialized algorithms
+
+template< class T0, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13, class T14, class T15
+#if variant_CPP11_OR_GREATER
+    variant_REQUIRES_T(
+        std::is_move_constructible<T0>::value && std17::is_swappable<T0>::value &&
+        std::is_move_constructible<T1>::value && std17::is_swappable<T1>::value &&
+        std::is_move_constructible<T2>::value && std17::is_swappable<T2>::value &&
+        std::is_move_constructible<T3>::value && std17::is_swappable<T3>::value &&
+        std::is_move_constructible<T4>::value && std17::is_swappable<T4>::value &&
+        std::is_move_constructible<T5>::value && std17::is_swappable<T5>::value &&
+        std::is_move_constructible<T6>::value && std17::is_swappable<T6>::value &&
+        std::is_move_constructible<T7>::value && std17::is_swappable<T7>::value &&
+        std::is_move_constructible<T8>::value && std17::is_swappable<T8>::value &&
+        std::is_move_constructible<T9>::value && std17::is_swappable<T9>::value &&
+        std::is_move_constructible<T10>::value && std17::is_swappable<T10>::value &&
+        std::is_move_constructible<T11>::value && std17::is_swappable<T11>::value &&
+        std::is_move_constructible<T12>::value && std17::is_swappable<T12>::value &&
+        std::is_move_constructible<T13>::value && std17::is_swappable<T13>::value &&
+        std::is_move_constructible<T14>::value && std17::is_swappable<T14>::value &&
+        std::is_move_constructible<T15>::value && std17::is_swappable<T15>::value 
+         )
+#endif
+>
+inline void swap(
+    variant<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15> & a,
+    variant<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15> & b )
+#if variant_CPP11_OR_GREATER
+    noexcept( noexcept( a.swap( b ) ) )
+#endif
+{
+    a.swap( b );
+}
+
+// 19.7.7 Visitation
+
+// Variant 'visitor' implementation
+
+namespace detail
+{
+
+template< typename R, typename VT >
+struct VisitorApplicatorImpl
+{
+    template< typename Visitor, typename T >
+    static R apply(Visitor const& v, T const& arg)
+    {
+        return v(arg);
+    }
+};
+
+template< typename R, typename VT >
+struct VisitorApplicatorImpl<R, TX<VT> >
+{
+    template< typename Visitor, typename T >
+    static R apply(Visitor const&, T)
+    {
+        // prevent default construction of a const reference, see issue #39:
+        std::terminate();
+    }
+};
+
+template<typename R>
+struct VisitorApplicator;
+
+template< typename R, typename Visitor, typename V1 >
+struct VisitorUnwrapper;
+
+#if variant_CPP11_OR_GREATER
+template< size_t NumVars, typename R, typename Visitor, typename ... T >
+#else
+template< size_t NumVars, typename R, typename Visitor, typename T1, typename T2 = S0, typename T3 = S0, typename T4 = S0, typename T5 = S0 >
+#endif
+struct TypedVisitorUnwrapper;
+
+template< typename R, typename Visitor, typename T2 >
+struct TypedVisitorUnwrapper<2, R, Visitor, T2>
+{
+    const Visitor& visitor;
+    T2 const& val2;
+    
+    TypedVisitorUnwrapper(const Visitor& visitor_, T2 const& val2_)
+        : visitor(visitor_)
+        , val2(val2_)
+        
+    {
+    }
+
+    template<typename T>
+    R operator()(const T& val1) const
+    {
+        return visitor(val1, val2);
+    }
+};
+
+template< typename R, typename Visitor, typename T2, typename T3 >
+struct TypedVisitorUnwrapper<3, R, Visitor, T2, T3>
+{
+    const Visitor& visitor;
+    T2 const& val2;
+    T3 const& val3;
+    
+    TypedVisitorUnwrapper(const Visitor& visitor_, T2 const& val2_, T3 const& val3_)
+        : visitor(visitor_)
+        , val2(val2_)
+        , val3(val3_)
+        
+    {
+    }
+
+    template<typename T>
+    R operator()(const T& val1) const
+    {
+        return visitor(val1, val2, val3);
+    }
+};
+
+template< typename R, typename Visitor, typename T2, typename T3, typename T4 >
+struct TypedVisitorUnwrapper<4, R, Visitor, T2, T3, T4>
+{
+    const Visitor& visitor;
+    T2 const& val2;
+    T3 const& val3;
+    T4 const& val4;
+    
+    TypedVisitorUnwrapper(const Visitor& visitor_, T2 const& val2_, T3 const& val3_, T4 const& val4_)
+        : visitor(visitor_)
+        , val2(val2_)
+        , val3(val3_)
+        , val4(val4_)
+        
+    {
+    }
+
+    template<typename T>
+    R operator()(const T& val1) const
+    {
+        return visitor(val1, val2, val3, val4);
+    }
+};
+
+template< typename R, typename Visitor, typename T2, typename T3, typename T4, typename T5 >
+struct TypedVisitorUnwrapper<5, R, Visitor, T2, T3, T4, T5>
+{
+    const Visitor& visitor;
+    T2 const& val2;
+    T3 const& val3;
+    T4 const& val4;
+    T5 const& val5;
+    
+    TypedVisitorUnwrapper(const Visitor& visitor_, T2 const& val2_, T3 const& val3_, T4 const& val4_, T5 const& val5_)
+        : visitor(visitor_)
+        , val2(val2_)
+        , val3(val3_)
+        , val4(val4_)
+        , val5(val5_)
+        
+    {
+    }
+
+    template<typename T>
+    R operator()(const T& val1) const
+    {
+        return visitor(val1, val2, val3, val4, val5);
+    }
+};
+
+
+
+template<typename R, typename Visitor, typename V2>
+struct VisitorUnwrapper
+{
+    const Visitor& visitor;
+    const V2& r;
+
+    VisitorUnwrapper(const Visitor& visitor_, const V2& r_)
+        : visitor(visitor_)
+        , r(r_)
+    {
+    }
+
+    
+    template< typename T1 >
+    R operator()(T1 const& val1) const
+    {
+        typedef TypedVisitorUnwrapper<2, R, Visitor, T1> visitor_type;
+        return VisitorApplicator<R>::apply(visitor_type(visitor, val1), r);
+    }
+    
+    template< typename T1, typename T2 >
+    R operator()(T1 const& val1, T2 const& val2) const
+    {
+        typedef TypedVisitorUnwrapper<3, R, Visitor, T1, T2> visitor_type;
+        return VisitorApplicator<R>::apply(visitor_type(visitor, val1, val2), r);
+    }
+    
+    template< typename T1, typename T2, typename T3 >
+    R operator()(T1 const& val1, T2 const& val2, T3 const& val3) const
+    {
+        typedef TypedVisitorUnwrapper<4, R, Visitor, T1, T2, T3> visitor_type;
+        return VisitorApplicator<R>::apply(visitor_type(visitor, val1, val2, val3), r);
+    }
+    
+    template< typename T1, typename T2, typename T3, typename T4 >
+    R operator()(T1 const& val1, T2 const& val2, T3 const& val3, T4 const& val4) const
+    {
+        typedef TypedVisitorUnwrapper<5, R, Visitor, T1, T2, T3, T4> visitor_type;
+        return VisitorApplicator<R>::apply(visitor_type(visitor, val1, val2, val3, val4), r);
+    }
+    
+    template< typename T1, typename T2, typename T3, typename T4, typename T5 >
+    R operator()(T1 const& val1, T2 const& val2, T3 const& val3, T4 const& val4, T5 const& val5) const
+    {
+        typedef TypedVisitorUnwrapper<6, R, Visitor, T1, T2, T3, T4, T5> visitor_type;
+        return VisitorApplicator<R>::apply(visitor_type(visitor, val1, val2, val3, val4, val5), r);
+    }
+    
+};
+
+
+template<typename R>
+struct VisitorApplicator
+{
+    template<typename Visitor, typename V1>
+    static R apply(const Visitor& v, const V1& arg)
+    {
+        switch( arg.index() )
+        {
+            case 0: return apply_visitor<0>(v, arg);
+            case 1: return apply_visitor<1>(v, arg);
+            case 2: return apply_visitor<2>(v, arg);
+            case 3: return apply_visitor<3>(v, arg);
+            case 4: return apply_visitor<4>(v, arg);
+            case 5: return apply_visitor<5>(v, arg);
+            case 6: return apply_visitor<6>(v, arg);
+            case 7: return apply_visitor<7>(v, arg);
+            case 8: return apply_visitor<8>(v, arg);
+            case 9: return apply_visitor<9>(v, arg);
+            case 10: return apply_visitor<10>(v, arg);
+            case 11: return apply_visitor<11>(v, arg);
+            case 12: return apply_visitor<12>(v, arg);
+            case 13: return apply_visitor<13>(v, arg);
+            case 14: return apply_visitor<14>(v, arg);
+            case 15: return apply_visitor<15>(v, arg);
+            
+            // prevent default construction of a const reference, see issue #39:
+            default: std::terminate();
+        }
+    }
+
+    template<size_t Idx, typename Visitor, typename V1>
+    static R apply_visitor(const Visitor& v, const V1& arg)
+    {
+
+#if variant_CPP11_OR_GREATER
+        typedef typename variant_alternative<Idx, typename std::decay<V1>::type>::type value_type;
+#else
+        typedef typename variant_alternative<Idx, V1>::type value_type;
+#endif
+        return VisitorApplicatorImpl<R, value_type>::apply(v, get<Idx>(arg));
+    }
+
+#if variant_CPP11_OR_GREATER
+    template<typename Visitor, typename V1, typename V2, typename ... V>
+    static R apply(const Visitor& v, const V1& arg1, const V2& arg2, const V ... args)
+    {
+        typedef VisitorUnwrapper<R, Visitor, V1> Unwrapper;
+        Unwrapper unwrapper(v, arg1);
+        return apply(unwrapper, arg2, args ...);
+    }
+#else
+    
+    template< typename Visitor, typename V1, typename V2 >
+    static R apply(const Visitor& v, V1 const& arg1, V2 const& arg2)
+    {
+        typedef VisitorUnwrapper<R, Visitor, V1> Unwrapper;
+        Unwrapper unwrapper(v, arg1);
+        return apply(unwrapper, arg2);
+    }
+    
+    template< typename Visitor, typename V1, typename V2, typename V3 >
+    static R apply(const Visitor& v, V1 const& arg1, V2 const& arg2, V3 const& arg3)
+    {
+        typedef VisitorUnwrapper<R, Visitor, V1> Unwrapper;
+        Unwrapper unwrapper(v, arg1);
+        return apply(unwrapper, arg2, arg3);
+    }
+    
+    template< typename Visitor, typename V1, typename V2, typename V3, typename V4 >
+    static R apply(const Visitor& v, V1 const& arg1, V2 const& arg2, V3 const& arg3, V4 const& arg4)
+    {
+        typedef VisitorUnwrapper<R, Visitor, V1> Unwrapper;
+        Unwrapper unwrapper(v, arg1);
+        return apply(unwrapper, arg2, arg3, arg4);
+    }
+    
+    template< typename Visitor, typename V1, typename V2, typename V3, typename V4, typename V5 >
+    static R apply(const Visitor& v, V1 const& arg1, V2 const& arg2, V3 const& arg3, V4 const& arg4, V5 const& arg5)
+    {
+        typedef VisitorUnwrapper<R, Visitor, V1> Unwrapper;
+        Unwrapper unwrapper(v, arg1);
+        return apply(unwrapper, arg2, arg3, arg4, arg5);
+    }
+    
+#endif
+};
+
+#if variant_CPP11_OR_GREATER
+template< size_t NumVars, typename Visitor, typename ... V >
+struct VisitorImpl
+{
+    typedef decltype(std::declval<Visitor>()(get<0>(static_cast<const V&>(std::declval<V>()))...)) result_type;
+    typedef VisitorApplicator<result_type> applicator_type;
+};
+#endif
+} // detail
+
+#if variant_CPP11_OR_GREATER
+// No perfect forwarding here in order to simplify code
+template< typename Visitor, typename ... V >
+inline auto visit(Visitor const& v, V const& ... vars) -> typename detail::VisitorImpl<sizeof ... (V), Visitor, V... > ::result_type
+{
+    typedef detail::VisitorImpl<sizeof ... (V), Visitor, V... > impl_type;
+    return impl_type::applicator_type::apply(v, vars...);
+}
+#else
+
+template< typename R, typename Visitor, typename V1 >
+inline R visit(const Visitor& v, V1 const& arg1)
+{
+    return detail::VisitorApplicator<R>::apply(v, arg1);
+}
+
+template< typename R, typename Visitor, typename V1, typename V2 >
+inline R visit(const Visitor& v, V1 const& arg1, V2 const& arg2)
+{
+    return detail::VisitorApplicator<R>::apply(v, arg1, arg2);
+}
+
+template< typename R, typename Visitor, typename V1, typename V2, typename V3 >
+inline R visit(const Visitor& v, V1 const& arg1, V2 const& arg2, V3 const& arg3)
+{
+    return detail::VisitorApplicator<R>::apply(v, arg1, arg2, arg3);
+}
+
+template< typename R, typename Visitor, typename V1, typename V2, typename V3, typename V4 >
+inline R visit(const Visitor& v, V1 const& arg1, V2 const& arg2, V3 const& arg3, V4 const& arg4)
+{
+    return detail::VisitorApplicator<R>::apply(v, arg1, arg2, arg3, arg4);
+}
+
+template< typename R, typename Visitor, typename V1, typename V2, typename V3, typename V4, typename V5 >
+inline R visit(const Visitor& v, V1 const& arg1, V2 const& arg2, V3 const& arg3, V4 const& arg4, V5 const& arg5)
+{
+    return detail::VisitorApplicator<R>::apply(v, arg1, arg2, arg3, arg4, arg5);
+}
+
+#endif
+
+// 19.7.6 Relational operators
+
+namespace detail {
+
+template< class Variant >
+struct Comparator
+{
+    static inline bool equal( Variant const & v, Variant const & w )
+    {
+        switch( v.index() )
+        {
+            case 0: return get<0>( v ) == get<0>( w );
+            case 1: return get<1>( v ) == get<1>( w );
+            case 2: return get<2>( v ) == get<2>( w );
+            case 3: return get<3>( v ) == get<3>( w );
+            case 4: return get<4>( v ) == get<4>( w );
+            case 5: return get<5>( v ) == get<5>( w );
+            case 6: return get<6>( v ) == get<6>( w );
+            case 7: return get<7>( v ) == get<7>( w );
+            case 8: return get<8>( v ) == get<8>( w );
+            case 9: return get<9>( v ) == get<9>( w );
+            case 10: return get<10>( v ) == get<10>( w );
+            case 11: return get<11>( v ) == get<11>( w );
+            case 12: return get<12>( v ) == get<12>( w );
+            case 13: return get<13>( v ) == get<13>( w );
+            case 14: return get<14>( v ) == get<14>( w );
+            case 15: return get<15>( v ) == get<15>( w );
+            
+            default: return false;
+        }
+    }
+
+    static inline bool less_than( Variant const & v, Variant const & w )
+    {
+        switch( v.index() )
+        {
+            case 0: return get<0>( v ) < get<0>( w );
+            case 1: return get<1>( v ) < get<1>( w );
+            case 2: return get<2>( v ) < get<2>( w );
+            case 3: return get<3>( v ) < get<3>( w );
+            case 4: return get<4>( v ) < get<4>( w );
+            case 5: return get<5>( v ) < get<5>( w );
+            case 6: return get<6>( v ) < get<6>( w );
+            case 7: return get<7>( v ) < get<7>( w );
+            case 8: return get<8>( v ) < get<8>( w );
+            case 9: return get<9>( v ) < get<9>( w );
+            case 10: return get<10>( v ) < get<10>( w );
+            case 11: return get<11>( v ) < get<11>( w );
+            case 12: return get<12>( v ) < get<12>( w );
+            case 13: return get<13>( v ) < get<13>( w );
+            case 14: return get<14>( v ) < get<14>( w );
+            case 15: return get<15>( v ) < get<15>( w );
+            
+            default: return false;
+        }
+    }
+};
+
+} //namespace detail
+
+template< class T0, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13, class T14, class T15 >
+inline bool operator==(
+    variant<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15> const & v,
+    variant<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15> const & w )
+{
+    if      ( v.index() != w.index()     ) return false;
+    else if ( v.valueless_by_exception() ) return true;
+    else                                   return detail::Comparator< variant<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15> >::equal( v, w );
+}
+
+template< class T0, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13, class T14, class T15 >
+inline bool operator!=(
+    variant<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15> const & v,
+    variant<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15> const & w )
+{
+    return ! ( v == w );
+}
+
+template< class T0, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13, class T14, class T15 >
+inline bool operator<(
+    variant<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15> const & v,
+    variant<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15> const & w )
+{
+    if      ( w.valueless_by_exception() ) return false;
+    else if ( v.valueless_by_exception() ) return true;
+    else if ( v.index() < w.index()      ) return true;
+    else if ( v.index() > w.index()      ) return false;
+    else                                   return detail::Comparator< variant<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15> >::less_than( v, w );
+}
+
+template< class T0, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13, class T14, class T15 >
+inline bool operator>(
+    variant<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15> const & v,
+    variant<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15> const & w )
+{
+    return w < v;
+}
+
+template< class T0, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13, class T14, class T15 >
+inline bool operator<=(
+    variant<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15> const & v,
+    variant<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15> const & w )
+{
+    return ! ( v > w );
+}
+
+template< class T0, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13, class T14, class T15 >
+inline bool operator>=(
+    variant<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15> const & v,
+    variant<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15> const & w )
+{
+    return ! ( v < w );
+}
+
+} // namespace variants
+
+using namespace variants;
+
+} // namespace nonstd
+
+#if variant_CPP11_OR_GREATER
+
+// 19.7.12 Hash support
+
+namespace std {
+
+template<>
+struct hash< nonstd::monostate >
+{
+    std::size_t operator()( nonstd::monostate ) const variant_noexcept
+    {
+        return 42;
+    }
+};
+
+template< class T0, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13, class T14, class T15 >
+struct hash< nonstd::variant<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15> >
+{
+    std::size_t operator()( nonstd::variant<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15> const & v ) const variant_noexcept
+    {
+        namespace nvd = nonstd::variants::detail;
+
+        switch( v.index() )
+        {
+            case 0: return nvd::hash( 0 ) ^ nvd::hash( get<0>( v ) );
+            case 1: return nvd::hash( 1 ) ^ nvd::hash( get<1>( v ) );
+            case 2: return nvd::hash( 2 ) ^ nvd::hash( get<2>( v ) );
+            case 3: return nvd::hash( 3 ) ^ nvd::hash( get<3>( v ) );
+            case 4: return nvd::hash( 4 ) ^ nvd::hash( get<4>( v ) );
+            case 5: return nvd::hash( 5 ) ^ nvd::hash( get<5>( v ) );
+            case 6: return nvd::hash( 6 ) ^ nvd::hash( get<6>( v ) );
+            case 7: return nvd::hash( 7 ) ^ nvd::hash( get<7>( v ) );
+            case 8: return nvd::hash( 8 ) ^ nvd::hash( get<8>( v ) );
+            case 9: return nvd::hash( 9 ) ^ nvd::hash( get<9>( v ) );
+            case 10: return nvd::hash( 10 ) ^ nvd::hash( get<10>( v ) );
+            case 11: return nvd::hash( 11 ) ^ nvd::hash( get<11>( v ) );
+            case 12: return nvd::hash( 12 ) ^ nvd::hash( get<12>( v ) );
+            case 13: return nvd::hash( 13 ) ^ nvd::hash( get<13>( v ) );
+            case 14: return nvd::hash( 14 ) ^ nvd::hash( get<14>( v ) );
+            case 15: return nvd::hash( 15 ) ^ nvd::hash( get<15>( v ) );
+            
+            default: return 0;
+        }
+    }
+};
+
+} //namespace std
+
+#endif // variant_CPP11_OR_GREATER
+
+#if variant_BETWEEN( variant_COMPILER_MSVC_VER, 1300, 1900 )
+# pragma warning( pop )
+#endif
+
+#endif // variant_USES_STD_VARIANT
+
+#endif // NONSTD_VARIANT_LITE_HPP
-- 
2.34.1


^ permalink raw reply	[flat|nested] 18+ messages in thread

* [PATCH 4/4] gdb: make internalvar use a variant
  2022-02-01 14:07 [PATCH 0/4] Add variant type Simon Marchi
                   ` (2 preceding siblings ...)
  2022-02-01 14:07 ` [PATCH 3/4] gdbsupport: add variant-lite header Simon Marchi
@ 2022-02-01 14:07 ` Simon Marchi
  2022-03-04 16:23   ` Tom Tromey
  2022-02-03  0:02 ` [PATCH 0/4] Add variant type Andrew Burgess
  4 siblings, 1 reply; 18+ messages in thread
From: Simon Marchi @ 2022-02-01 14:07 UTC (permalink / raw)
  To: gdb-patches

Change the union in internalvar to be a variant.  This union holds
different data, based on the internalvar kind, which is stored in
internalvar::kind.

The advantage of a variant here is that we can more easily use non-POD
types in the alternatives.  And since it remembers which alternative is
the current one, accesses are checked so that accesses to the wrong
alternative throw an exception (see question about that below).

This is the first use of a variant in our code base, and it will
probably be used as a template for future uses.  So all suggestions
about how the variant is used are welcome.

Define one struct type for each kind of data we want to store in the
variant (for each internalvar kind), that makes the alternatives clear.
Remove the internalvar::kind field, as it becomes redundant with the
variant.  Keeping it just consumes more space, but most importantly  we
run the risk of having it and the variant out of sync.  The variant's
selector becomes the source of truth for the internalvar kind.

However, I find that being able to get the kind of an internalvar as an
enumerator quite handy.  Plus, it would be quite a bit of work to
rewrite all the code to replace

  if (var->kind == INTERNALVAR_VALUE)

with

  if (nonstd::holds_alternative<internalvar_value> (var->v))

and

  switch (var->kind)

with some functor + std::visit.  I also think it makes the code less
readable.  So I added a kind method to internalvar, to get an
internalvar_kind value from the variant's active alternative.  That's
perhaps not the most efficient thing, but it helps a lot.

Open questions:

 - The library uses std::variant if it is available (so, if building
   with C++17).  I think there are slight differences between the std
   and nonstd versions of variant.  I can't really explain what they
   are, I am not expert enough in C++.  But for example, when I had not
   made my visitor's methods const, the code would compile with
   std::variant but not nonstd::variant.  This means we could get some
   occasional build failures if some people build in C++11/14 and others
   C++17.  One way to avoid this would be to define the macro
   variant_CONFIG_SELECT_VARIANT so that we always use nonstd::variant,
   regardless of the C++ version.  The small downside is that we
   wouldn't be testing against std::variant, so some day in the future
   we want to make the switch to use std::variant, we'll have some fixes
   to do.

 - The library has the option [2] of being built with or without
   exception.  For example, that decides what to do if one tries to
   access an alternative that is not the active one:

     https://github.com/martinmoene/variant-lite/blob/f1af3518e4c28f12b09839b9d2ee37984cbf137a/include/nonstd/variant.hpp#L2107-L2114

   With exceptions enabled, bad_variant_access is thrown.  Without
   exceptions enabled, an assert() fails.  In our context, it would be
   nice if we could call gdb_assert fails, such that we get the usual
   "internal error" message if that ever happens.  I don't see an easy
   way to do this other than modifying the header file itself.

[1] https://github.com/martinmoene/variant-lite#select-stdvariant-or-nonstdvariant
[2] https://github.com/martinmoene/variant-lite/#disable-exceptions

Change-Id: I15957e091f740441128301e2bc603771f18771b5
---
 gdb/value.c | 334 ++++++++++++++++++++++++++++------------------------
 1 file changed, 183 insertions(+), 151 deletions(-)

diff --git a/gdb/value.c b/gdb/value.c
index 2c8af4b9d5b5..97ae469f670d 100644
--- a/gdb/value.c
+++ b/gdb/value.c
@@ -46,6 +46,7 @@
 #include "cli/cli-style.h"
 #include "expop.h"
 #include "inferior.h"
+#include "gdbsupport/variant/variant.hpp"
 
 /* Definition of a user function.  */
 struct internal_function
@@ -1989,41 +1990,50 @@ enum internalvar_kind
   INTERNALVAR_STRING,
 };
 
-union internalvar_data
+/* Used with INTERNALVAR_VOID.  */
+struct internalvar_void
+{};
+
+/* Value object used with INTERNALVAR_VALUE.  */
+struct internalvar_value
 {
-  /* A value object used with INTERNALVAR_VALUE.  */
-  struct value *value;
+  value_ref_ptr value;
+};
 
-  /* The call-back routine used with INTERNALVAR_MAKE_VALUE.  */
-  struct
-  {
-    /* The functions to call.  */
-    const struct internalvar_funcs *functions;
+/* Call-back routine used with INTERNALVAR_MAKE_VALUE.  */
+struct internalvar_make_value
+{
+  /* The functions to call.  */
+  const struct internalvar_funcs *functions;
 
-    /* The function's user-data.  */
-    void *data;
-  } make_value;
+  /* The function's user-data.  */
+  void *data;
+};
 
-  /* The internal function used with INTERNALVAR_FUNCTION.  */
-  struct
-  {
-    struct internal_function *function;
-    /* True if this is the canonical name for the function.  */
-    int canonical;
-  } fn;
+/* The internal function used with INTERNALVAR_FUNCTION.  */
+struct internalvar_function
+{
+  struct internal_function *function;
 
-  /* An integer value used with INTERNALVAR_INTEGER.  */
-  struct
-  {
-    /* If type is non-NULL, it will be used as the type to generate
-       a value for this internal variable.  If type is NULL, a default
-       integer type for the architecture is used.  */
-    struct type *type;
-    LONGEST val;
-  } integer;
+  /* True if this is the canonical name for the function.  */
+  int canonical;
+};
+
+/* An integer value used with INTERNALVAR_INTEGER.  */
+
+struct internalvar_integer
+{
+  /* If type is non-NULL, it will be used as the type to generate
+     a value for this internal variable.  If type is NULL, a default
+     integer type for the architecture is used.  */
+  struct type *type;
+  LONGEST val;
+};
 
+struct internalvar_string
+{
   /* A string value used with INTERNALVAR_STRING.  */
-  char *string;
+  std::string string;
 };
 
 /* Internal variables.  These are variables within the debugger
@@ -2033,16 +2043,45 @@ union internalvar_data
 
 struct internalvar
 {
-  struct internalvar *next;
-  char *name;
+  /* Return the kind of the internalvar.  */
+  internalvar_kind kind () const
+  {
+    struct visitor
+    {
+      internalvar_kind operator() (const internalvar_void &void_) const
+      { return INTERNALVAR_VOID; }
+
+      internalvar_kind operator() (const internalvar_value &value) const
+      { return INTERNALVAR_VALUE; }
+
+      internalvar_kind operator() (const internalvar_make_value &make_value) const
+      { return INTERNALVAR_MAKE_VALUE; }
+
+      internalvar_kind operator() (const internalvar_function &void_) const
+      { return INTERNALVAR_FUNCTION; }
+
+      internalvar_kind operator() (const internalvar_integer &integer) const
+      { return INTERNALVAR_INTEGER; }
 
-  /* We support various different kinds of content of an internal variable.
-     enum internalvar_kind specifies the kind, and union internalvar_data
-     provides the data associated with this particular kind.  */
+      internalvar_kind operator() (const internalvar_string &string) const
+      { return INTERNALVAR_STRING; }
+    };
 
-  enum internalvar_kind kind;
+    return nonstd::visit (visitor {}, this->v);
+  }
 
-  union internalvar_data u;
+  struct internalvar *next = nullptr;
+  char *name = nullptr;
+
+  /* Data associated to each kind of internalvar.  The active variant
+     alternative indicates the kind of the internalvar.  */
+  nonstd::variant<
+    internalvar_void,
+    internalvar_value,
+    internalvar_make_value,
+    internalvar_function,
+    internalvar_integer,
+    internalvar_string> v;
 };
 
 static struct internalvar *internalvars;
@@ -2081,7 +2120,7 @@ init_if_undefined_command (const char* args, int from_tty)
 
   /* Only evaluate the expression if the lvalue is void.
      This may still fail if the expression is invalid.  */
-  if (intvar->kind == INTERNALVAR_VOID)
+  if (intvar->kind () == INTERNALVAR_VOID)
     evaluate_expression (expr.get ());
 }
 
@@ -2126,12 +2165,13 @@ complete_internalvar (completion_tracker &tracker, const char *name)
 struct internalvar *
 create_internalvar (const char *name)
 {
-  struct internalvar *var = XNEW (struct internalvar);
+  struct internalvar *var = new internalvar;
 
   var->name = xstrdup (name);
-  var->kind = INTERNALVAR_VOID;
   var->next = internalvars;
   internalvars = var;
+  var->v = internalvar_void {};
+
   return var;
 }
 
@@ -2148,10 +2188,7 @@ create_internalvar_type_lazy (const char *name,
 			      void *data)
 {
   struct internalvar *var = create_internalvar (name);
-
-  var->kind = INTERNALVAR_MAKE_VALUE;
-  var->u.make_value.functions = funcs;
-  var->u.make_value.data = data;
+  var->v = internalvar_make_value { funcs, data };
   return var;
 }
 
@@ -2162,12 +2199,15 @@ compile_internalvar_to_ax (struct internalvar *var,
 			   struct agent_expr *expr,
 			   struct axs_value *value)
 {
-  if (var->kind != INTERNALVAR_MAKE_VALUE
-      || var->u.make_value.functions->compile_to_ax == NULL)
+  if (var->kind () != INTERNALVAR_MAKE_VALUE)
     return 0;
 
-  var->u.make_value.functions->compile_to_ax (var, expr, value,
-					      var->u.make_value.data);
+  const auto &make_value = nonstd::get<internalvar_make_value> (var->v);
+
+  if (make_value.functions->compile_to_ax == nullptr)
+    return 0;
+
+  make_value.functions->compile_to_ax (var, expr, value, make_value.data);
   return 1;
 }
 
@@ -2213,7 +2253,7 @@ value_of_internalvar (struct gdbarch *gdbarch, struct internalvar *var)
       return val;
     }
 
-  switch (var->kind)
+  switch (var->kind ())
     {
     case INTERNALVAR_VOID:
       val = allocate_value (builtin_type (gdbarch)->builtin_void);
@@ -2224,28 +2264,45 @@ value_of_internalvar (struct gdbarch *gdbarch, struct internalvar *var)
       break;
 
     case INTERNALVAR_INTEGER:
-      if (!var->u.integer.type)
-	val = value_from_longest (builtin_type (gdbarch)->builtin_int,
-				  var->u.integer.val);
-      else
-	val = value_from_longest (var->u.integer.type, var->u.integer.val);
-      break;
+      {
+	const auto &integer = nonstd::get<internalvar_integer> (var->v);
+
+	if (integer.type == nullptr)
+	  val = value_from_longest (builtin_type (gdbarch)->builtin_int,
+				    integer.val);
+	else
+	  val = value_from_longest (integer.type, integer.val);
+
+	break;
+      }
 
     case INTERNALVAR_STRING:
-      val = value_cstring (var->u.string, strlen (var->u.string),
-			   builtin_type (gdbarch)->builtin_char);
-      break;
+      {
+	const auto &string = nonstd::get<internalvar_string> (var->v);
+
+	val = value_cstring (string.string.c_str (), string.string.size (),
+			     builtin_type (gdbarch)->builtin_char);
+	break;
+      }
 
     case INTERNALVAR_VALUE:
-      val = value_copy (var->u.value);
-      if (value_lazy (val))
-	value_fetch_lazy (val);
-      break;
+      {
+	const auto &value = nonstd::get<internalvar_value> (var->v);
+
+	val = value_copy (value.value.get ());
+	if (value_lazy (val))
+	  value_fetch_lazy (val);
+
+	break;
+      }
 
     case INTERNALVAR_MAKE_VALUE:
-      val = (*var->u.make_value.functions->make_value) (gdbarch, var,
-							var->u.make_value.data);
-      break;
+      {
+	const auto &make_value = nonstd::get<internalvar_make_value> (var->v);
+
+	val = make_value.functions->make_value (gdbarch, var, make_value.data);
+	break;
+      }
 
     default:
       internal_error (__FILE__, __LINE__, _("bad kind"));
@@ -2268,8 +2325,7 @@ value_of_internalvar (struct gdbarch *gdbarch, struct internalvar *var)
      altogether.  At the moment, this seems like the behavior we
      want.  */
 
-  if (var->kind != INTERNALVAR_MAKE_VALUE
-      && val->lval != lval_computed)
+  if (var->kind () != INTERNALVAR_MAKE_VALUE && val->lval != lval_computed)
     {
       VALUE_LVAL (val) = lval_internalvar;
       VALUE_INTERNALVAR (val) = var;
@@ -2281,19 +2337,21 @@ value_of_internalvar (struct gdbarch *gdbarch, struct internalvar *var)
 int
 get_internalvar_integer (struct internalvar *var, LONGEST *result)
 {
-  if (var->kind == INTERNALVAR_INTEGER)
+  if (var->kind () == INTERNALVAR_INTEGER)
     {
-      *result = var->u.integer.val;
+      const auto &integer = nonstd::get<internalvar_integer> (var->v);
+      *result = integer.val;
       return 1;
     }
 
-  if (var->kind == INTERNALVAR_VALUE)
+  if (var->kind () == INTERNALVAR_VALUE)
     {
-      struct type *type = check_typedef (value_type (var->u.value));
+      const auto &value = nonstd::get<internalvar_value> (var->v);
+      struct type *type = check_typedef (value_type (value.value.get ()));
 
       if (type->code () == TYPE_CODE_INT)
 	{
-	  *result = value_as_long (var->u.value);
+	  *result = value_as_long (value.value.get ());
 	  return 1;
 	}
     }
@@ -2305,11 +2363,14 @@ static int
 get_internalvar_function (struct internalvar *var,
 			  struct internal_function **result)
 {
-  switch (var->kind)
+  switch (var->kind ())
     {
     case INTERNALVAR_FUNCTION:
-      *result = var->u.fn.function;
-      return 1;
+      {
+	const auto &function = nonstd::get<internalvar_function> (var->v);
+	*result = function.function;
+	return 1;
+      }
 
     default:
       return 0;
@@ -2325,20 +2386,23 @@ set_internalvar_component (struct internalvar *var,
   struct gdbarch *arch;
   int unit_size;
 
-  switch (var->kind)
+  switch (var->kind ())
     {
     case INTERNALVAR_VALUE:
-      addr = value_contents_writeable (var->u.value).data ();
-      arch = get_value_arch (var->u.value);
-      unit_size = gdbarch_addressable_memory_unit_size (arch);
-
-      if (bitsize)
-	modify_field (value_type (var->u.value), addr + offset,
-		      value_as_long (newval), bitpos, bitsize);
-      else
-	memcpy (addr + offset * unit_size, value_contents (newval).data (),
-		TYPE_LENGTH (value_type (newval)));
-      break;
+      {
+	const auto &value = nonstd::get<internalvar_value> (var->v);
+	addr = value_contents_writeable (value.value.get ()).data ();
+	arch = get_value_arch (value.value.get ());
+	unit_size = gdbarch_addressable_memory_unit_size (arch);
+
+	if (bitsize)
+	  modify_field (value_type (value.value.get ()), addr + offset,
+			value_as_long (newval), bitpos, bitsize);
+	else
+	  memcpy (addr + offset * unit_size, value_contents (newval).data (),
+		  TYPE_LENGTH (value_type (newval)));
+	break;
+      }
 
     default:
       /* We can never get a component of any other kind.  */
@@ -2349,29 +2413,30 @@ set_internalvar_component (struct internalvar *var,
 void
 set_internalvar (struct internalvar *var, struct value *val)
 {
-  enum internalvar_kind new_kind;
-  union internalvar_data new_data = { 0 };
-
-  if (var->kind == INTERNALVAR_FUNCTION && var->u.fn.canonical)
+  if (var->kind () == INTERNALVAR_FUNCTION
+      && nonstd::get<internalvar_function> (var->v).canonical)
     error (_("Cannot overwrite convenience function %s"), var->name);
 
   /* Prepare new contents.  */
   switch (check_typedef (value_type (val))->code ())
     {
     case TYPE_CODE_VOID:
-      new_kind = INTERNALVAR_VOID;
+      var->v = internalvar_void {};
       break;
 
     case TYPE_CODE_INTERNAL_FUNCTION:
-      gdb_assert (VALUE_LVAL (val) == lval_internalvar);
-      new_kind = INTERNALVAR_FUNCTION;
-      get_internalvar_function (VALUE_INTERNALVAR (val),
-				&new_data.fn.function);
-      /* Copies created here are never canonical.  */
-      break;
+      {
+	gdb_assert (VALUE_LVAL (val) == lval_internalvar);
+
+	internal_function *func;
+	get_internalvar_function (VALUE_INTERNALVAR (val), &func);
+
+	/* Copies created here are never canonical.  */
+	var->v = internalvar_function { func, 0 };
+	break;
+      }
 
     default:
-      new_kind = INTERNALVAR_VALUE;
       struct value *copy = value_copy (val);
       copy->modifiable = 1;
 
@@ -2382,10 +2447,8 @@ set_internalvar (struct internalvar *var, struct value *val)
 	value_fetch_lazy (copy);
 
       /* Release the value from the value chain to prevent it from being
-	 deleted by free_all_values.  From here on this function should not
-	 call error () until new_data is installed into the var->u to avoid
-	 leaking memory.  */
-      new_data.value = release_value (copy).release ();
+	 deleted by free_all_values.  */
+      var->v = internalvar_value { release_value (copy) };
 
       /* Internal variables which are created from values with a dynamic
 	 location don't need the location property of the origin anymore.
@@ -2393,73 +2456,35 @@ set_internalvar (struct internalvar *var, struct value *val)
 	 when accessing the value.
 	 If we keep it, we would still refer to the origin value.
 	 Remove the location property in case it exist.  */
-      value_type (new_data.value)->remove_dyn_prop (DYN_PROP_DATA_LOCATION);
-
+      value_type (copy)->remove_dyn_prop (DYN_PROP_DATA_LOCATION);
       break;
     }
-
-  /* Clean up old contents.  */
-  clear_internalvar (var);
-
-  /* Switch over.  */
-  var->kind = new_kind;
-  var->u = new_data;
-  /* End code which must not call error().  */
 }
 
 void
 set_internalvar_integer (struct internalvar *var, LONGEST l)
 {
-  /* Clean up old contents.  */
-  clear_internalvar (var);
-
-  var->kind = INTERNALVAR_INTEGER;
-  var->u.integer.type = NULL;
-  var->u.integer.val = l;
+  var->v = internalvar_integer { nullptr, l };
 }
 
 void
 set_internalvar_string (struct internalvar *var, const char *string)
 {
-  /* Clean up old contents.  */
-  clear_internalvar (var);
-
-  var->kind = INTERNALVAR_STRING;
-  var->u.string = xstrdup (string);
+  var->v = internalvar_string { string };
 }
 
 static void
 set_internalvar_function (struct internalvar *var, struct internal_function *f)
 {
-  /* Clean up old contents.  */
-  clear_internalvar (var);
-
-  var->kind = INTERNALVAR_FUNCTION;
-  var->u.fn.function = f;
-  var->u.fn.canonical = 1;
   /* Variables installed here are always the canonical version.  */
+  var->v = internalvar_function { f, 1 };
 }
 
 void
 clear_internalvar (struct internalvar *var)
 {
-  /* Clean up old contents.  */
-  switch (var->kind)
-    {
-    case INTERNALVAR_VALUE:
-      value_decref (var->u.value);
-      break;
-
-    case INTERNALVAR_STRING:
-      xfree (var->u.string);
-      break;
-
-    default:
-      break;
-    }
-
   /* Reset to void kind.  */
-  var->kind = INTERNALVAR_VOID;
+  var->v = internalvar_void {};
 }
 
 const char *
@@ -2579,17 +2604,24 @@ static void
 preserve_one_internalvar (struct internalvar *var, struct objfile *objfile,
 			  htab_t copied_types)
 {
-  switch (var->kind)
+  switch (var->kind ())
     {
     case INTERNALVAR_INTEGER:
-      if (var->u.integer.type
-	  && var->u.integer.type->objfile_owner () == objfile)
-	var->u.integer.type
-	  = copy_type_recursive (objfile, var->u.integer.type, copied_types);
-      break;
+      {
+	auto &integer = nonstd::get<internalvar_integer> (var->v);
+
+	if (integer.type != nullptr
+	    && integer.type->objfile_owner () == objfile)
+	  integer.type
+	    = copy_type_recursive (objfile, integer.type, copied_types);
+
+	break;
+      }
 
     case INTERNALVAR_VALUE:
-      preserve_one_value (var->u.value, objfile, copied_types);
+      preserve_one_value
+	(nonstd::get<internalvar_value> (var->v).value.get (), objfile,
+	 copied_types);
       break;
     }
 }
-- 
2.34.1


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH 0/4] Add variant type
  2022-02-01 14:07 [PATCH 0/4] Add variant type Simon Marchi
                   ` (3 preceding siblings ...)
  2022-02-01 14:07 ` [PATCH 4/4] gdb: make internalvar use a variant Simon Marchi
@ 2022-02-03  0:02 ` Andrew Burgess
  2022-02-03  1:32   ` Simon Marchi
  4 siblings, 1 reply; 18+ messages in thread
From: Andrew Burgess @ 2022-02-03  0:02 UTC (permalink / raw)
  To: Simon Marchi; +Cc: gdb-patches

* Simon Marchi via Gdb-patches <gdb-patches@sourceware.org> [2022-02-01 09:07:13 -0500]:

> I sometimes encounter situations where I think using std::variant would
> be useful.  The benefits would be to be able to use non-POD types in the
> alternatives, have them automatically constructed when making an
> alternative active, and automatically destructing them when switching to
> another alternative of destroying the variant.  Also, the validation of
> access to the alternatives, to prevent accessing an alternative that is
> not the current one.
> 
> However, std::variant is only available in C++17.  I suggest that we
> import a variant implementation to allow us to use it right now, a bit
> like we did for optional and string_view.  We could maybe import the
> implementation from libstdc++, like we did for these last two types, but
> that sounds really tedious (we would also want to import any tests).
> Instead, we can take advantage of a popular implementation distributed
> as a single header file.  I chose this one:
> 
>   https://github.com/martinmoene/variant-lite
> 
> because it is close to the std implementation, and the license (Boost
> Software License) is clear and compatible with the GPL v3.

Is this compatible with the copyright assignment requirement of
contributing to GDB?

I'm assuming copyright of that file is not assigned to FSF.

My concern would be, what if code was copied from that file into the
rest of GDB?  Does that cause us problems?

Not trying to cause problems, just making sure we've considered this.

Thanks,
Andrew


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH 0/4] Add variant type
  2022-02-03  0:02 ` [PATCH 0/4] Add variant type Andrew Burgess
@ 2022-02-03  1:32   ` Simon Marchi
  2022-02-04 12:44     ` Andrew Burgess
  0 siblings, 1 reply; 18+ messages in thread
From: Simon Marchi @ 2022-02-03  1:32 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: gdb-patches

On 2022-02-02 19:02, Andrew Burgess wrote:
> * Simon Marchi via Gdb-patches <gdb-patches@sourceware.org> [2022-02-01 09:07:13 -0500]:
> 
>> I sometimes encounter situations where I think using std::variant would
>> be useful.  The benefits would be to be able to use non-POD types in the
>> alternatives, have them automatically constructed when making an
>> alternative active, and automatically destructing them when switching to
>> another alternative of destroying the variant.  Also, the validation of
>> access to the alternatives, to prevent accessing an alternative that is
>> not the current one.
>>
>> However, std::variant is only available in C++17.  I suggest that we
>> import a variant implementation to allow us to use it right now, a bit
>> like we did for optional and string_view.  We could maybe import the
>> implementation from libstdc++, like we did for these last two types, but
>> that sounds really tedious (we would also want to import any tests).
>> Instead, we can take advantage of a popular implementation distributed
>> as a single header file.  I chose this one:
>>
>>   https://github.com/martinmoene/variant-lite
>>
>> because it is close to the std implementation, and the license (Boost
>> Software License) is clear and compatible with the GPL v3.
> 
> Is this compatible with the copyright assignment requirement of
> contributing to GDB?
> 
> I'm assuming copyright of that file is not assigned to FSF.

Exactly, we're merely redistributing it (as its license allows).  Its
header does not say "Copyright FSF" like the other files.

> My concern would be, what if code was copied from that file into the
> rest of GDB?  Does that cause us problems?

Well, we should avoid that :).  I see it exactly like zlib, which is also bundled.

Simon

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH 0/4] Add variant type
  2022-02-03  1:32   ` Simon Marchi
@ 2022-02-04 12:44     ` Andrew Burgess
  2022-02-04 13:19       ` Simon Marchi
  0 siblings, 1 reply; 18+ messages in thread
From: Andrew Burgess @ 2022-02-04 12:44 UTC (permalink / raw)
  To: Simon Marchi; +Cc: gdb-patches

* Simon Marchi via Gdb-patches <gdb-patches@sourceware.org> [2022-02-02 20:32:45 -0500]:

> On 2022-02-02 19:02, Andrew Burgess wrote:
> > * Simon Marchi via Gdb-patches <gdb-patches@sourceware.org> [2022-02-01 09:07:13 -0500]:
> > 
> >> I sometimes encounter situations where I think using std::variant would
> >> be useful.  The benefits would be to be able to use non-POD types in the
> >> alternatives, have them automatically constructed when making an
> >> alternative active, and automatically destructing them when switching to
> >> another alternative of destroying the variant.  Also, the validation of
> >> access to the alternatives, to prevent accessing an alternative that is
> >> not the current one.
> >>
> >> However, std::variant is only available in C++17.  I suggest that we
> >> import a variant implementation to allow us to use it right now, a bit
> >> like we did for optional and string_view.  We could maybe import the
> >> implementation from libstdc++, like we did for these last two types, but
> >> that sounds really tedious (we would also want to import any tests).
> >> Instead, we can take advantage of a popular implementation distributed
> >> as a single header file.  I chose this one:
> >>
> >>   https://github.com/martinmoene/variant-lite
> >>
> >> because it is close to the std implementation, and the license (Boost
> >> Software License) is clear and compatible with the GPL v3.
> > 
> > Is this compatible with the copyright assignment requirement of
> > contributing to GDB?
> > 
> > I'm assuming copyright of that file is not assigned to FSF.
> 
> Exactly, we're merely redistributing it (as its license allows).  Its
> header does not say "Copyright FSF" like the other files.
> 
> > My concern would be, what if code was copied from that file into the
> > rest of GDB?  Does that cause us problems?
> 
> Well, we should avoid that :).  I see it exactly like zlib, which is also bundled.

OK.  Thank you for the explanation.

I took a look through this whole series, and it all looks good to me.

Thanks,
Andrew


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH 0/4] Add variant type
  2022-02-04 12:44     ` Andrew Burgess
@ 2022-02-04 13:19       ` Simon Marchi
  0 siblings, 0 replies; 18+ messages in thread
From: Simon Marchi @ 2022-02-04 13:19 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: gdb-patches



On 2022-02-04 07:44, Andrew Burgess wrote:
> OK.  Thank you for the explanation.
> 
> I took a look through this whole series, and it all looks good to me.
> 
> Thanks,
> Andrew

Thanks.  I'll wait for possibly more feedback, and intend to merge this
only after the creation of the GDB 12 branch.

Simon

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH 1/4] gdb: remove internalvar_funcs::destroy
  2022-02-01 14:07 ` [PATCH 1/4] gdb: remove internalvar_funcs::destroy Simon Marchi
@ 2022-03-04 16:15   ` Tom Tromey
  2022-03-06 16:33     ` Simon Marchi
  0 siblings, 1 reply; 18+ messages in thread
From: Tom Tromey @ 2022-03-04 16:15 UTC (permalink / raw)
  To: Simon Marchi via Gdb-patches

>>>>> "Simon" == Simon Marchi via Gdb-patches <gdb-patches@sourceware.org> writes:

Simon> No kind of internal var uses it remove it.  This makes the transition to
Simon> using a variant easier, since we don't need to think about where this
Simon> should be called (in a destructor or not), if it can throw, etc.

It seems to me that this one could go in right away.

Tom

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH 2/4] gdb: constify parameter of value_copy
  2022-02-01 14:07 ` [PATCH 2/4] gdb: constify parameter of value_copy Simon Marchi
@ 2022-03-04 16:16   ` Tom Tromey
  2022-03-06 16:33     ` Simon Marchi
  0 siblings, 1 reply; 18+ messages in thread
From: Tom Tromey @ 2022-03-04 16:16 UTC (permalink / raw)
  To: Simon Marchi via Gdb-patches

>>>>> "Simon" == Simon Marchi via Gdb-patches <gdb-patches@sourceware.org> writes:

Simon> In a following patch, I have a const value I want to copy using a
Simon> value_copy.  However, value_copy takes a non-const source value, at the
Simon> moment.  Change the paramter to be const,

This seems like a reasonable cleanup that could go in immediately.

Tom

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH 4/4] gdb: make internalvar use a variant
  2022-02-01 14:07 ` [PATCH 4/4] gdb: make internalvar use a variant Simon Marchi
@ 2022-03-04 16:23   ` Tom Tromey
  2022-03-07 12:12     ` Pedro Alves
  0 siblings, 1 reply; 18+ messages in thread
From: Tom Tromey @ 2022-03-04 16:23 UTC (permalink / raw)
  To: Simon Marchi via Gdb-patches

Simon> The advantage of a variant here is that we can more easily use non-POD
Simon> types in the alternatives.  And since it remembers which alternative is
Simon> the current one, accesses are checked so that accesses to the wrong
Simon> alternative throw an exception (see question about that below).

I was wondering if we really need internalvars that can "switch" like
this.  Like, could we just subclass internalvar instead and get rid of
the "kind" / payload stuff.

[...]
Simon> with some functor + std::visit.

FWIW I tend to dislike the visitor pattern anyhow.  IME it is just a
pain to use well -- lots of callbacks, the code becomes unreadable.

Simon>  - The library uses std::variant if it is available (so, if building
Simon>    with C++17).  I think there are slight differences between the std
Simon>    and nonstd versions of variant.  I can't really explain what they
Simon>    are, I am not expert enough in C++.  But for example, when I had not
Simon>    made my visitor's methods const, the code would compile with
Simon>    std::variant but not nonstd::variant.  This means we could get some
Simon>    occasional build failures if some people build in C++11/14 and others
Simon>    C++17.  One way to avoid this would be to define the macro
Simon>    variant_CONFIG_SELECT_VARIANT so that we always use nonstd::variant,
Simon>    regardless of the C++ version.  The small downside is that we
Simon>    wouldn't be testing against std::variant, so some day in the future
Simon>    we want to make the switch to use std::variant, we'll have some fixes
Simon>    to do.

We already have a problem along these lines with string_view, I think,
where we imported one that allows nullptr, then the standard changed(?).
On the whole I'd prefer we try to stick to the standard stuff, if
possible, because once these divergences come in they seem hard to root
out.

Simon>    With exceptions enabled, bad_variant_access is thrown.  Without
Simon>    exceptions enabled, an assert() fails.  In our context, it would be
Simon>    nice if we could call gdb_assert fails, such that we get the usual
Simon>    "internal error" message if that ever happens.  I don't see an easy
Simon>    way to do this other than modifying the header file itself.

Yeah, not sure what to do about that except write some kind of wrapper.

Tom

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH 1/4] gdb: remove internalvar_funcs::destroy
  2022-03-04 16:15   ` Tom Tromey
@ 2022-03-06 16:33     ` Simon Marchi
  0 siblings, 0 replies; 18+ messages in thread
From: Simon Marchi @ 2022-03-06 16:33 UTC (permalink / raw)
  To: Tom Tromey, Simon Marchi via Gdb-patches



On 2022-03-04 11:15, Tom Tromey wrote:
>>>>>> "Simon" == Simon Marchi via Gdb-patches <gdb-patches@sourceware.org> writes:
> 
> Simon> No kind of internal var uses it remove it.  This makes the transition to
> Simon> using a variant easier, since we don't need to think about where this
> Simon> should be called (in a destructor or not), if it can throw, etc.
> 
> It seems to me that this one could go in right away.
> 
> Tom

Done.

Simon

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH 2/4] gdb: constify parameter of value_copy
  2022-03-04 16:16   ` Tom Tromey
@ 2022-03-06 16:33     ` Simon Marchi
  0 siblings, 0 replies; 18+ messages in thread
From: Simon Marchi @ 2022-03-06 16:33 UTC (permalink / raw)
  To: Tom Tromey, Simon Marchi via Gdb-patches



On 2022-03-04 11:16, Tom Tromey wrote:
>>>>>> "Simon" == Simon Marchi via Gdb-patches <gdb-patches@sourceware.org> writes:
> 
> Simon> In a following patch, I have a const value I want to copy using a
> Simon> value_copy.  However, value_copy takes a non-const source value, at the
> Simon> moment.  Change the paramter to be const,
> 
> This seems like a reasonable cleanup that could go in immediately.
> 
> Tom

Done.

Simon

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH 4/4] gdb: make internalvar use a variant
  2022-03-04 16:23   ` Tom Tromey
@ 2022-03-07 12:12     ` Pedro Alves
  2022-03-16  2:06       ` Simon Marchi
  0 siblings, 1 reply; 18+ messages in thread
From: Pedro Alves @ 2022-03-07 12:12 UTC (permalink / raw)
  To: Tom Tromey, Simon Marchi via Gdb-patches

On 2022-03-04 16:23, Tom Tromey wrote:
> Simon> The advantage of a variant here is that we can more easily use non-POD
> Simon> types in the alternatives.  And since it remembers which alternative is
> Simon> the current one, accesses are checked so that accesses to the wrong
> Simon> alternative throw an exception (see question about that below).
> 
> I was wondering if we really need internalvars that can "switch" like
> this.  Like, could we just subclass internalvar instead and get rid of
> the "kind" / payload stuff.
> 
> [...]
> Simon> with some functor + std::visit.
> 
> FWIW I tend to dislike the visitor pattern anyhow.  IME it is just a
> pain to use well -- lots of callbacks, the code becomes unreadable.

+1, my thoughts exactly.

It requires more C++ expertise than most of other things to know to write such code.  Something that should be
simple is complicated.  C++ is really lacking proper pattern matching and builtin variant types, to make
variants easy to use.  But if we use it in a contained way like basically glorified tagged union to get rid
of some boilerplace we have to write today,  I guess it's OK.


Even if we use a variant in the internalvar case, I'd think just assuming that enum values
have the same order as the variant types would be so much easier to follow and code.

I mean, something like this:

  /* Return the kind of the internalvar.  */
  internalvar_kind kind () const
  { return (enum internalvar_kind) v.index (); }

Instead of the currently proposed:

  /* Return the kind of the internalvar.  */
  internalvar_kind kind () const
  {
    struct visitor
    {
      internalvar_kind operator() (const internalvar_void &void_) const
      { return INTERNALVAR_VOID; }

      internalvar_kind operator() (const internalvar_value &value) const
      { return INTERNALVAR_VALUE; }

      internalvar_kind operator() (const internalvar_make_value &make_value) const
      { return INTERNALVAR_MAKE_VALUE; }

      internalvar_kind operator() (const internalvar_function &void_) const
      { return INTERNALVAR_FUNCTION; }

      internalvar_kind operator() (const internalvar_integer &integer) const
      { return INTERNALVAR_INTEGER; }
 
      internalvar_kind operator() (const internalvar_string &string) const
      { return INTERNALVAR_STRING; }
    };

    return nonstd::visit (visitor {}, this->v);
  }


How debuggeable is nonstd::variant?  When printing a tagged union in GDB, it's easy to figure out the
union's current value.  What does printing a nonstd::variant (not mapped to the std variant) look
like?  AFAICT from variant.cpp source file, the variant storage is an untyped buffer:

    enum { data_align = detail::typelist_max_alignof< variant_types >::value };

    using aligned_storage_t = typename std::aligned_storage< data_size, data_align >::type;
    aligned_storage_t data;

so I guess printing a nonstd::variant results in pretty opaque output.  We'd need a pretty
printer to fix this.  Or maybe we just assume that people developing/debugging GDB build
it against a C++17 or higher compiler?  (Not sure that's a great assumption.)


I wonder whether we should do:

 namespace gdb {
   using namespace nonstd;
 }

and then use gdb::variant throughout, instead of "nonstd::variant".  At least "gdb"
is three letters, and thus less code/indentation churn when we someday switch to "std".
Also less churn if we move to some other variant type, though not sure that's likely
after we start using one.

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH 4/4] gdb: make internalvar use a variant
  2022-03-07 12:12     ` Pedro Alves
@ 2022-03-16  2:06       ` Simon Marchi
  2022-03-16 13:26         ` Pedro Alves
  0 siblings, 1 reply; 18+ messages in thread
From: Simon Marchi @ 2022-03-16  2:06 UTC (permalink / raw)
  To: Pedro Alves, Tom Tromey, Simon Marchi via Gdb-patches

> It requires more C++ expertise than most of other things to know to write such code.  Something that should be
> simple is complicated.  C++ is really lacking proper pattern matching and builtin variant types, to make
> variants easy to use.  But if we use it in a contained way like basically glorified tagged union to get rid
> of some boilerplace we have to write today,  I guess it's OK.

Yes, that was my thought.  The intent is replacing those instances of
"enum kind + union data" with something that can hold non-trivial
objects, calls constructors / destructors on data fields automatically,
validates that we don't access a wrong (inactive) field.

That being said, it's likely that many instances of this pattern are in
structures that aren't even new'ed / delete'd today, so it's not as if
we could use variants in them tomorrow.

> How debuggeable is nonstd::variant?  When printing a tagged union in GDB, it's easy to figure out the
> union's current value.  What does printing a nonstd::variant (not mapped to the std variant) look
> like? 

With nonstd:


$1 = {
  next = 0x60600000ff20,
  name = 0x602000085330 "salut",
  v = {
    data = {
      __data = '\276' <repeats 32 times>,
      __align = {<No data fields>}
    },
    type_index = 0 '\000'
  }
}

With std:


$1 = {
  next = 0x60600000ff20,
  name = 0x602000085350 "salut",
  v = std::variant<internalvar_void, internalvar_value, internalvar_make_value, internalvar_function, internalvar_integer, internalvar_string> [index 0] = {{<No data fields>}}
}

> AFAICT from variant.cpp source file, the variant storage is an untyped buffer:
> 
>     enum { data_align = detail::typelist_max_alignof< variant_types >::value };
> 
>     using aligned_storage_t = typename std::aligned_storage< data_size, data_align >::type;
>     aligned_storage_t data;
> 
> so I guess printing a nonstd::variant results in pretty opaque output.  We'd need a pretty
> printer to fix this.  Or maybe we just assume that people developing/debugging GDB build
> it against a C++17 or higher compiler?  (Not sure that's a great assumption.)

Do you know off-hand if the std::variant pretty printer is supposed to
show the active data field?

> I wonder whether we should do:
> 
>  namespace gdb {
>    using namespace nonstd;
>  }
> 
> and then use gdb::variant throughout, instead of "nonstd::variant".  At least "gdb"
> is three letters, and thus less code/indentation churn when we someday switch to "std".
> Also less churn if we move to some other variant type, though not sure that's likely
> after we start using one.

Good idea.

But now I'm not even convinced myself of the usefulness of having an
std::variant-like in the tree.  The internalvar change is really just an
example, but it doesn't really need changing (and Tom suggeted what's
probably a better way to change it if we wanted to).  I wanted to use a
variant in some TUI patch that I started, but haven't finished.  So, I
guess we have it here as a reference if the need ever comes up again.

Simon

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH 4/4] gdb: make internalvar use a variant
  2022-03-16  2:06       ` Simon Marchi
@ 2022-03-16 13:26         ` Pedro Alves
  2022-03-16 13:28           ` Simon Marchi
  0 siblings, 1 reply; 18+ messages in thread
From: Pedro Alves @ 2022-03-16 13:26 UTC (permalink / raw)
  To: Simon Marchi, Tom Tromey, Simon Marchi via Gdb-patches

On 2022-03-16 02:06, Simon Marchi wrote:

> With std:
> 
> 
> $1 = {
>   next = 0x60600000ff20,
>   name = 0x602000085350 "salut",
>   v = std::variant<internalvar_void, internalvar_value, internalvar_make_value, internalvar_function, internalvar_integer, internalvar_string> [index 0] = {{<No data fields>}}
> }
> 
>> AFAICT from variant.cpp source file, the variant storage is an untyped buffer:
>>
>>     enum { data_align = detail::typelist_max_alignof< variant_types >::value };
>>
>>     using aligned_storage_t = typename std::aligned_storage< data_size, data_align >::type;
>>     aligned_storage_t data;
>>
>> so I guess printing a nonstd::variant results in pretty opaque output.  We'd need a pretty
>> printer to fix this.  Or maybe we just assume that people developing/debugging GDB build
>> it against a C++17 or higher compiler?  (Not sure that's a great assumption.)
> 
> Do you know off-hand if the std::variant pretty printer is supposed to
> show the active data field?

I think that's already shown in your "with std" output above.  It just happens that
internalvar_void has no data fields.  E.g., with:
$ cat ./variant.cc 

#include <string>
#include <variant>

int
main ()
{
  std::variant<std::string, int> v;

  v = "hello";
  v = 1;

  return 0;
}

Stepping through the assignment lines, I get:

 (gdb) p v
 $1 = std::variant<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, int> [index 0] = {"hello"}
 (gdb) n
 (gdb) p v
 $2 = std::variant<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, int> [index 1] = {1}

I wouldn't say this is super ideal output, as it forces you to count the types to
know what index 0 and 1 are, though, as well as puts the data far off to the right.

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH 4/4] gdb: make internalvar use a variant
  2022-03-16 13:26         ` Pedro Alves
@ 2022-03-16 13:28           ` Simon Marchi
  0 siblings, 0 replies; 18+ messages in thread
From: Simon Marchi @ 2022-03-16 13:28 UTC (permalink / raw)
  To: Pedro Alves, Tom Tromey, Simon Marchi via Gdb-patches

On 2022-03-16 09:26, Pedro Alves wrote:
> On 2022-03-16 02:06, Simon Marchi wrote:
> 
>> With std:
>>
>>
>> $1 = {
>>   next = 0x60600000ff20,
>>   name = 0x602000085350 "salut",
>>   v = std::variant<internalvar_void, internalvar_value, internalvar_make_value, internalvar_function, internalvar_integer, internalvar_string> [index 0] = {{<No data fields>}}
>> }
>>
>>> AFAICT from variant.cpp source file, the variant storage is an untyped buffer:
>>>
>>>     enum { data_align = detail::typelist_max_alignof< variant_types >::value };
>>>
>>>     using aligned_storage_t = typename std::aligned_storage< data_size, data_align >::type;
>>>     aligned_storage_t data;
>>>
>>> so I guess printing a nonstd::variant results in pretty opaque output.  We'd need a pretty
>>> printer to fix this.  Or maybe we just assume that people developing/debugging GDB build
>>> it against a C++17 or higher compiler?  (Not sure that's a great assumption.)
>>
>> Do you know off-hand if the std::variant pretty printer is supposed to
>> show the active data field?
> 
> I think that's already shown in your "with std" output above.  It just happens that
> internalvar_void has no data fields.  E.g., with:
> $ cat ./variant.cc 
> 
> #include <string>
> #include <variant>
> 
> int
> main ()
> {
>   std::variant<std::string, int> v;
> 
>   v = "hello";
>   v = 1;
> 
>   return 0;
> }
> 
> Stepping through the assignment lines, I get:
> 
>  (gdb) p v
>  $1 = std::variant<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, int> [index 0] = {"hello"}
>  (gdb) n
>  (gdb) p v
>  $2 = std::variant<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, int> [index 1] = {1}
> 
> I wouldn't say this is super ideal output, as it forces you to count the types to
> know what index 0 and 1 are, though, as well as puts the data far off to the right.

Oh, yeah, thanks for pointing that out, seems obvious now.  I was
assigning an integer, so I thought I was printing an integer
internalval, but no.

Simon

^ permalink raw reply	[flat|nested] 18+ messages in thread

end of thread, other threads:[~2022-03-16 13:29 UTC | newest]

Thread overview: 18+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-02-01 14:07 [PATCH 0/4] Add variant type Simon Marchi
2022-02-01 14:07 ` [PATCH 1/4] gdb: remove internalvar_funcs::destroy Simon Marchi
2022-03-04 16:15   ` Tom Tromey
2022-03-06 16:33     ` Simon Marchi
2022-02-01 14:07 ` [PATCH 2/4] gdb: constify parameter of value_copy Simon Marchi
2022-03-04 16:16   ` Tom Tromey
2022-03-06 16:33     ` Simon Marchi
2022-02-01 14:07 ` [PATCH 3/4] gdbsupport: add variant-lite header Simon Marchi
2022-02-01 14:07 ` [PATCH 4/4] gdb: make internalvar use a variant Simon Marchi
2022-03-04 16:23   ` Tom Tromey
2022-03-07 12:12     ` Pedro Alves
2022-03-16  2:06       ` Simon Marchi
2022-03-16 13:26         ` Pedro Alves
2022-03-16 13:28           ` Simon Marchi
2022-02-03  0:02 ` [PATCH 0/4] Add variant type Andrew Burgess
2022-02-03  1:32   ` Simon Marchi
2022-02-04 12:44     ` Andrew Burgess
2022-02-04 13:19       ` Simon Marchi

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).