aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2021-01-22 08:39:10 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2021-01-22 09:34:29 +0200
commit6e91cb7cdb0c4f000a79d20d8578890d56bcdc84 (patch)
tree7ba42ef488534c19bf2eafbd8d289e08036f11b7
parentd51892e33a0fe69e743e02d9620312133a7ac61d (diff)
Add support for optional pair halves in variable values
-rw-r--r--libbuild2/variable.cxx12
-rw-r--r--libbuild2/variable.hxx122
-rw-r--r--libbuild2/variable.ixx25
-rw-r--r--libbuild2/variable.txx513
4 files changed, 486 insertions, 186 deletions
diff --git a/libbuild2/variable.cxx b/libbuild2/variable.cxx
index f5476b5..47703da 100644
--- a/libbuild2/variable.cxx
+++ b/libbuild2/variable.cxx
@@ -1832,8 +1832,20 @@ namespace build2
value_traits<vector<pair<string, string>>>;
template struct LIBBUILD2_DEFEXPORT
+ value_traits<vector<pair<string, optional<string>>>>;
+
+ template struct LIBBUILD2_DEFEXPORT
+ value_traits<vector<pair<optional<string>, string>>>;
+
+ template struct LIBBUILD2_DEFEXPORT
value_traits<std::map<string, string>>;
template struct LIBBUILD2_DEFEXPORT
+ value_traits<std::map<string, optional<string>>>;
+
+ template struct LIBBUILD2_DEFEXPORT
+ value_traits<std::map<optional<string>, string>>;
+
+ template struct LIBBUILD2_DEFEXPORT
value_traits<std::map<project_name, dir_path>>;
}
diff --git a/libbuild2/variable.hxx b/libbuild2/variable.hxx
index a671978..a9dde7f 100644
--- a/libbuild2/variable.hxx
+++ b/libbuild2/variable.hxx
@@ -610,6 +610,9 @@ namespace build2
// static const build2::value_type value_type;
// };
+ template <typename T>
+ struct value_traits<const T>: value_traits<T> {};
+
// Convert name to a simple value. Throw invalid_argument (with a message)
// if the name is not a valid representation of value (in which case the
// name remains unchanged for diagnostics). The second version is called for
@@ -897,6 +900,9 @@ namespace build2
// half of a pair). If both are empty then this is an empty value (and not a
// pair of two empties).
//
+ // @@ Maybe we should redo this with optional<> to signify which half can
+ // be missing?
+ //
template <>
struct LIBBUILD2_SYMEXPORT value_traits<name_pair>
{
@@ -1004,9 +1010,67 @@ namespace build2
static const build2::value_type value_type;
};
+ // optional<T>
+ //
+ // This is an incomplete implementation meant to provide enough support only
+ // to be usable as elements of containers.
+ //
+ template <typename T>
+ struct value_traits<optional<T>>
+ {
+ static int compare (const optional<T>&, const optional<T>&);
+ };
+
+ // pair<F, S>
+ //
+ // Either F or S can be optional<T> making the corresponding half of the
+ // pair optional.
+ //
+ // This is an incomplete implementation meant to provide enough support only
+ // to be usable as elements of containers.
+ //
+ template <typename F, typename S>
+ struct pair_value_traits
+ {
+ static pair<F, S>
+ convert (name&&, name*, const char*, const char*, const variable*);
+
+ static void
+ reverse (const F&, const S&, names&);
+ };
+
+ template <typename F, typename S>
+ struct pair_value_traits<F, optional<S>>
+ {
+ static pair<F, optional<S>>
+ convert (name&&, name*, const char*, const char*, const variable*);
+
+ static void
+ reverse (const F&, const optional<S>&, names&);
+ };
+
+ template <typename F, typename S>
+ struct pair_value_traits<optional<F>, S>
+ {
+ static pair<optional<F>, S>
+ convert (name&&, name*, const char*, const char*, const variable*);
+
+ static void
+ reverse (const optional<F>&, const S&, names&);
+ };
+
+ template <typename F, typename S>
+ struct value_traits<pair<F, S>>: pair_value_traits<F, S>
+ {
+ static int compare (const pair<F, S>&, const pair<F, S>&);
+ };
+
// vector<T>
//
template <typename T>
+ struct vector_value_type;
+
+ template <typename T>
struct value_traits<vector<T>>
{
static_assert (sizeof (vector<T>) <= value::size_, "insufficient space");
@@ -1018,20 +1082,17 @@ namespace build2
static bool empty (const vector<T>& x) {return x.empty ();}
static const vector<T> empty_instance;
-
- // Make sure these are static-initialized together. Failed that VC will
- // make sure it's done in the wrong order.
- //
- struct value_type_ex: build2::value_type
- {
- string type_name;
- value_type_ex (value_type&&);
- };
- static const value_type_ex value_type;
+ static const vector_value_type<T> value_type;
};
// vector<pair<K, V>>
//
+ // Either K or V can be optional<T> making the corresponding half of the
+ // pair optional.
+ //
+ template <typename K, typename V>
+ struct pair_vector_value_type;
+
template <typename K, typename V>
struct value_traits<vector<pair<K, V>>>
{
@@ -1045,20 +1106,16 @@ namespace build2
static bool empty (const vector<pair<K, V>>& x) {return x.empty ();}
static const vector<pair<K, V>> empty_instance;
-
- // Make sure these are static-initialized together. Failed that VC will
- // make sure it's done in the wrong order.
- //
- struct value_type_ex: build2::value_type
- {
- string type_name;
- value_type_ex (value_type&&);
- };
- static const value_type_ex value_type;
+ static const pair_vector_value_type<K, V> value_type;
};
// map<K, V>
//
+ // Either K or V can be optional<T> making the key or value optional.
+ //
+ template <typename K, typename V>
+ struct map_value_type;
+
template <typename K, typename V>
struct value_traits<std::map<K, V>>
{
@@ -1073,16 +1130,7 @@ namespace build2
static bool empty (const map<K, V>& x) {return x.empty ();}
static const map<K, V> empty_instance;
-
- // Make sure these are static-initialized together. Failed that VC will
- // make sure it's done in the wrong order.
- //
- struct value_type_ex: build2::value_type
- {
- string type_name;
- value_type_ex (value_type&&);
- };
- static const value_type_ex value_type;
+ static const map_value_type<K, V> value_type;
};
// Explicitly pre-instantiate and export value_traits templates for
@@ -1102,10 +1150,22 @@ namespace build2
value_traits<vector<pair<string, string>>>;
extern template struct LIBBUILD2_DECEXPORT
+ value_traits<vector<pair<string, optional<string>>>>;
+
+ extern template struct LIBBUILD2_DECEXPORT
+ value_traits<vector<pair<optional<string>, string>>>;
+
+ extern template struct LIBBUILD2_DECEXPORT
value_traits<std::map<string, string>>;
extern template struct LIBBUILD2_DECEXPORT
- value_traits<std::map<project_name, dir_path>>;
+ value_traits<std::map<string, optional<string>>>;
+
+ extern template struct LIBBUILD2_DECEXPORT
+ value_traits<std::map<optional<string>, string>>;
+
+ extern template struct LIBBUILD2_DECEXPORT
+ value_traits<std::map<project_name, dir_path>>; // var_subprojects
// Project-wide (as opposed to global) variable overrides (see context ctor
// for details).
diff --git a/libbuild2/variable.ixx b/libbuild2/variable.ixx
index c8f9541..9b24b9d 100644
--- a/libbuild2/variable.ixx
+++ b/libbuild2/variable.ixx
@@ -728,6 +728,31 @@ namespace build2
: s);
}
+ // optional<T>
+ //
+ template <typename T>
+ inline int value_traits<optional<T>>::
+ compare (const optional<T>& l, const optional<T>& r)
+ {
+ return l
+ ? (r ? value_traits<T>::compare (*l, *r) : 1)
+ : (r ? -1 : 0);
+ }
+
+ // pair<F, S> value
+ //
+ template <typename F, typename S>
+ inline int value_traits<pair<F, S>>::
+ compare (const pair<F, S>& l, const pair<F, S>& r)
+ {
+ int i (value_traits<F>::compare (l.first, r.first));
+
+ if (i == 0)
+ i = value_traits<S>::compare (l.second, r.second);
+
+ return i;
+ }
+
// vector<T> value
//
template <typename T>
diff --git a/libbuild2/variable.txx b/libbuild2/variable.txx
index 8fa1d7c..9c9c867 100644
--- a/libbuild2/variable.txx
+++ b/libbuild2/variable.txx
@@ -241,9 +241,228 @@ namespace build2
return value_traits<T>::compare (l.as<T> (), r.as<T> ());
}
- // vector<T> value
+ // pair<F, S> value
//
+ template <typename F, typename S>
+ pair<F, S> pair_value_traits<F, S>::
+ convert (name&& l, name* r,
+ const char* type, const char* what, const variable* var)
+ {
+ if (!l.pair)
+ {
+ diag_record dr (fail);
+
+ dr << type << ' ' << what << (*what != '\0' ? " " : "")
+ << "pair expected instead of '" << l << "'";
+
+ if (var != nullptr)
+ dr << " in variable " << var->name;
+ }
+
+ if (l.pair != '@')
+ {
+ diag_record dr (fail);
+
+ dr << "unexpected pair style for "
+ << type << ' ' << what << (*what != '\0' ? " " : "")
+ << "key-value pair '"
+ << l << "'" << l.pair << "'" << *r << "'";
+
+ if (var != nullptr)
+ dr << " in variable " << var->name;
+ }
+
+ try
+ {
+ F f (value_traits<F>::convert (move (l), nullptr));
+
+ try
+ {
+ S s (value_traits<S>::convert (move (*r), nullptr));
+
+ return pair<F, S> (move (f), move (s));
+ }
+ catch (const invalid_argument&)
+ {
+ diag_record dr (fail);
+
+ dr << "invalid " << value_traits<S>::value_type.name
+ << " second have of pair '" << *r << "'";
+
+ if (var != nullptr)
+ dr << " in variable " << var->name;
+
+ dr << endf;
+ }
+ }
+ catch (const invalid_argument&)
+ {
+ diag_record dr (fail);
+
+ dr << "invalid " << value_traits<F>::value_type.name
+ << " first have of pair '" << l << "'";
+
+ if (var != nullptr)
+ dr << " in variable " << var->name;
+
+ dr << endf;
+ }
+ }
+
+ template <typename F, typename S>
+ pair<F, optional<S>> pair_value_traits<F, optional<S>>::
+ convert (name&& l, name* r,
+ const char* type, const char* what, const variable* var)
+ {
+ if (l.pair && l.pair != '@')
+ {
+ diag_record dr (fail);
+
+ dr << "unexpected pair style for "
+ << type << ' ' << what << (*what != '\0' ? " " : "")
+ << "key-value pair '"
+ << l << "'" << l.pair << "'" << *r << "'";
+
+ if (var != nullptr)
+ dr << " in variable " << var->name;
+ }
+
+ try
+ {
+ F f (value_traits<F>::convert (move (l), nullptr));
+
+ try
+ {
+ optional<S> s;
+
+ if (l.pair)
+ s = value_traits<S>::convert (move (*r), nullptr);
+
+ return pair<F, optional<S>> (move (f), move (s));
+ }
+ catch (const invalid_argument&)
+ {
+ diag_record dr (fail);
+
+ dr << "invalid " << value_traits<S>::value_type.name
+ << " second have of pair '" << *r << "'";
+
+ if (var != nullptr)
+ dr << " in variable " << var->name;
+
+ dr << endf;
+ }
+ }
+ catch (const invalid_argument&)
+ {
+ diag_record dr (fail);
+
+ dr << "invalid " << value_traits<F>::value_type.name
+ << " first have of pair '" << l << "'";
+ if (var != nullptr)
+ dr << " in variable " << var->name;
+
+ dr << endf;
+ }
+ }
+
+ template <typename F, typename S>
+ pair<optional<F>, S> pair_value_traits<optional<F>, S>::
+ convert (name&& l, name* r,
+ const char* type, const char* what, const variable* var)
+ {
+ if (l.pair && l.pair != '@')
+ {
+ diag_record dr (fail);
+
+ dr << "unexpected pair style for "
+ << type << ' ' << what << (*what != '\0' ? " " : "")
+ << "key-value pair '"
+ << l << "'" << l.pair << "'" << *r << "'";
+
+ if (var != nullptr)
+ dr << " in variable " << var->name;
+ }
+
+ try
+ {
+ optional<F> f;
+
+ if (l.pair)
+ {
+ f = value_traits<F>::convert (move (l), nullptr);
+ l = move (*r); // Shift.
+ }
+
+ try
+ {
+ S s (value_traits<S>::convert (move (l), nullptr));
+
+ return pair<optional<F>, S> (move (f), move (s));
+ }
+ catch (const invalid_argument&)
+ {
+ diag_record dr (fail);
+
+ dr << "invalid " << value_traits<S>::value_type.name
+ << " second have of pair '" << l << "'";
+
+ if (var != nullptr)
+ dr << " in variable " << var->name;
+
+ dr << endf;
+ }
+ }
+ catch (const invalid_argument&)
+ {
+ diag_record dr (fail);
+
+ dr << "invalid " << value_traits<F>::value_type.name
+ << " first have of pair '" << l << "'";
+
+ if (var != nullptr)
+ dr << " in variable " << var->name;
+
+ dr << endf;
+ }
+ }
+
+ template <typename F, typename S>
+ void pair_value_traits<F, S>::
+ reverse (const F& f, const S& s, names& ns)
+ {
+ ns.push_back (value_traits<F>::reverse (f));
+ ns.back ().pair = '@';
+ ns.push_back (value_traits<S>::reverse (s));
+ }
+
+ template <typename F, typename S>
+ void pair_value_traits<F, optional<S>>::
+ reverse (const F& f, const optional<S>& s, names& ns)
+ {
+ ns.push_back (value_traits<F>::reverse (f));
+ if (s)
+ {
+ ns.back ().pair = '@';
+ ns.push_back (value_traits<S>::reverse (*s));
+ }
+ }
+
+ template <typename F, typename S>
+ void pair_value_traits<optional<F>, S>::
+ reverse (const optional<F>& f, const S& s, names& ns)
+ {
+ if (f)
+ {
+ ns.push_back (value_traits<F>::reverse (*f));
+ ns.back ().pair = '@';
+ }
+ ns.push_back (value_traits<S>::reverse (s));
+ }
+
+ // vector<T> value
+ //
template <typename T>
vector<T> value_traits<vector<T>>::
convert (names&& ns)
@@ -396,21 +615,28 @@ namespace build2
return 0;
}
+ // Make sure these are static-initialized together. Failed that VC will make
+ // sure it's done in the wrong order.
+ //
template <typename T>
- value_traits<vector<T>>::value_type_ex::
- value_type_ex (value_type&& v)
- : value_type (move (v))
+ struct vector_value_type: value_type
{
- type_name = value_traits<T>::type_name;
- type_name += 's';
- name = type_name.c_str ();
- }
+ string type_name;
+
+ vector_value_type (value_type&& v)
+ : value_type (move (v))
+ {
+ type_name = value_traits<T>::type_name;
+ type_name += 's';
+ name = type_name.c_str ();
+ }
+ };
template <typename T>
const vector<T> value_traits<vector<T>>::empty_instance;
template <typename T>
- const typename value_traits<vector<T>>::value_type_ex
+ const vector_value_type<T>
value_traits<vector<T>>::value_type = build2::value_type // VC14 wants =.
{
nullptr, // Patched above.
@@ -444,63 +670,14 @@ namespace build2
for (auto i (ns.begin ()); i != ns.end (); ++i)
{
name& l (*i);
+ name* r (l.pair ? &*++i : nullptr);
- if (!l.pair)
- {
- diag_record dr (fail);
-
- dr << value_traits<vector<pair<K, V>>>::value_type.name
- << " key-value pair expected instead of '" << l << "'";
+ p.push_back (value_traits<pair<K, V>>::convert (
+ move (l), r,
+ value_traits<vector<pair<K, V>>>::value_type.name,
+ "element",
+ var));
- if (var != nullptr)
- dr << " in variable " << var->name;
- }
-
- name& r (*++i); // Got to have the second half of the pair.
-
- if (l.pair != '@')
- {
- diag_record dr (fail);
-
- dr << "unexpected pair style for "
- << value_traits<vector<pair<K, V>>>::value_type.name
- << " key-value '" << l << "'" << l.pair << "'" << r << "'";
-
- if (var != nullptr)
- dr << " in variable " << var->name;
- }
-
- try
- {
- K k (value_traits<K>::convert (move (l), nullptr));
-
- try
- {
- V v (value_traits<V>::convert (move (r), nullptr));
-
- p.emplace_back (move (k), move (v));
- }
- catch (const invalid_argument&)
- {
- diag_record dr (fail);
-
- dr << "invalid " << value_traits<V>::value_type.name
- << " element value '" << r << "'";
-
- if (var != nullptr)
- dr << " in variable " << var->name;
- }
- }
- catch (const invalid_argument&)
- {
- diag_record dr (fail);
-
- dr << "invalid " << value_traits<K>::value_type.name
- << " element key '" << l << "'";
-
- if (var != nullptr)
- dr << " in variable " << var->name;
- }
}
}
@@ -522,11 +699,7 @@ namespace build2
s.reserve (2 * vv.size ());
for (const auto& p: vv)
- {
- s.push_back (value_traits<K>::reverse (p.first));
- s.back ().pair = '@';
- s.push_back (value_traits<V>::reverse (p.second));
- }
+ value_traits<pair<K, V>>::reverse (p.first, p.second, s);
return s;
}
@@ -543,9 +716,7 @@ namespace build2
for (; li != le && ri != re; ++li, ++ri)
{
- int r;
- if ((r = value_traits<K>::compare (li->first, ri->first)) != 0 ||
- (r = value_traits<V>::compare (li->second, ri->second)) != 0)
+ if (int r = value_traits<pair<K, V>>::compare (*li, *ri))
return r;
}
@@ -558,23 +729,66 @@ namespace build2
return 0;
}
+ // Make sure these are static-initialized together. Failed that VC will make
+ // sure it's done in the wrong order.
+ //
template <typename K, typename V>
- value_traits<vector<pair<K, V>>>::value_type_ex::
- value_type_ex (value_type&& v)
- : value_type (move (v))
+ struct pair_vector_value_type: value_type
{
- type_name = value_traits<K>::type_name;
- type_name += '_';
- type_name += value_traits<V>::type_name;
- type_name += "_pair_vector";
- name = type_name.c_str ();
- }
+ string type_name;
+
+ pair_vector_value_type (value_type&& v)
+ : value_type (move (v))
+ {
+ type_name = value_traits<K>::type_name;
+ type_name += '_';
+ type_name += value_traits<V>::type_name;
+ type_name += "_pair_vector";
+ name = type_name.c_str ();
+ }
+ };
+
+ // This is beyond our static initialization order control skills, so we hack
+ // it up for now.
+ //
+ template <typename K, typename V>
+ struct pair_vector_value_type<K, optional<V>>: value_type
+ {
+ string type_name;
+
+ pair_vector_value_type (value_type&& v)
+ : value_type (move (v))
+ {
+ type_name = value_traits<K>::type_name;
+ type_name += "_optional_";
+ type_name += value_traits<V>::type_name;
+ type_name += "_pair_vector";
+ name = type_name.c_str ();
+ }
+ };
+
+ template <typename K, typename V>
+ struct pair_vector_value_type<optional<K>, V>: value_type
+ {
+ string type_name;
+
+ pair_vector_value_type (value_type&& v)
+ : value_type (move (v))
+ {
+ type_name = "optional_";
+ type_name += value_traits<K>::type_name;
+ type_name += '_';
+ type_name += value_traits<V>::type_name;
+ type_name += "_pair_vector";
+ name = type_name.c_str ();
+ }
+ };
template <typename K, typename V>
const vector<pair<K, V>> value_traits<vector<pair<K, V>>>::empty_instance;
template <typename K, typename V>
- const typename value_traits<std::vector<pair<K, V>>>::value_type_ex
+ const pair_vector_value_type<K, V>
value_traits<vector<pair<K, V>>>::value_type = build2::value_type // VC14 wants =
{
nullptr, // Patched above.
@@ -610,63 +824,15 @@ namespace build2
for (auto i (ns.begin ()); i != ns.end (); ++i)
{
name& l (*i);
+ name* r (l.pair ? &*++i : nullptr);
- if (!l.pair)
- {
- diag_record dr (fail);
+ pair<K, V> v (value_traits<pair<K, V>>::convert (
+ move (l), r,
+ value_traits<map<K, V>>::value_type.name,
+ "element",
+ var));
- dr << value_traits<map<K, V>>::value_type.name << " key-value "
- << "pair expected instead of '" << l << "'";
-
- if (var != nullptr)
- dr << " in variable " << var->name;
- }
-
- name& r (*++i); // Got to have the second half of the pair.
-
- if (l.pair != '@')
- {
- diag_record dr (fail);
-
- dr << "unexpected pair style for "
- << value_traits<map<K, V>>::value_type.name << " key-value "
- << "'" << l << "'" << l.pair << "'" << r << "'";
-
- if (var != nullptr)
- dr << " in variable " << var->name;
- }
-
- try
- {
- K k (value_traits<K>::convert (move (l), nullptr));
-
- try
- {
- V v (value_traits<V>::convert (move (r), nullptr));
-
- p.emplace (move (k), move (v));
- }
- catch (const invalid_argument&)
- {
- diag_record dr (fail);
-
- dr << "invalid " << value_traits<V>::value_type.name
- << " element value '" << r << "'";
-
- if (var != nullptr)
- dr << " in variable " << var->name;
- }
- }
- catch (const invalid_argument&)
- {
- diag_record dr (fail);
-
- dr << "invalid " << value_traits<K>::value_type.name
- << " element key '" << l << "'";
-
- if (var != nullptr)
- dr << " in variable " << var->name;
- }
+ p.emplace (move (v.first), move (v.second));
}
}
@@ -692,11 +858,7 @@ namespace build2
s.reserve (2 * vm.size ());
for (const auto& p: vm)
- {
- s.push_back (value_traits<K>::reverse (p.first));
- s.back ().pair = '@';
- s.push_back (value_traits<V>::reverse (p.second));
- }
+ value_traits<pair<K, V>>::reverse (p.first, p.second, s);
return s;
}
@@ -715,9 +877,7 @@ namespace build2
for (; li != le && ri != re; ++li, ++ri)
{
- int r;
- if ((r = value_traits<K>::compare (li->first, ri->first)) != 0 ||
- (r = value_traits<V>::compare (li->second, ri->second)) != 0)
+ if (int r = value_traits<pair<const K, V>>::compare (*li, *ri))
return r;
}
@@ -730,23 +890,66 @@ namespace build2
return 0;
}
+ // Make sure these are static-initialized together. Failed that VC will make
+ // sure it's done in the wrong order.
+ //
template <typename K, typename V>
- value_traits<std::map<K, V>>::value_type_ex::
- value_type_ex (value_type&& v)
- : value_type (move (v))
+ struct map_value_type: value_type
{
- type_name = value_traits<K>::type_name;
- type_name += '_';
- type_name += value_traits<V>::type_name;
- type_name += "_map";
- name = type_name.c_str ();
- }
+ string type_name;
+
+ map_value_type (value_type&& v)
+ : value_type (move (v))
+ {
+ type_name = value_traits<K>::type_name;
+ type_name += '_';
+ type_name += value_traits<V>::type_name;
+ type_name += "_map";
+ name = type_name.c_str ();
+ }
+ };
+
+ // This is beyond our static initialization order control skills, so we hack
+ // it up for now.
+ //
+ template <typename K, typename V>
+ struct map_value_type<K, optional<V>>: value_type
+ {
+ string type_name;
+
+ map_value_type (value_type&& v)
+ : value_type (move (v))
+ {
+ type_name = value_traits<K>::type_name;
+ type_name += "_optional_";
+ type_name += value_traits<V>::type_name;
+ type_name += "_map";
+ name = type_name.c_str ();
+ }
+ };
+
+ template <typename K, typename V>
+ struct map_value_type<optional<K>, V>: value_type
+ {
+ string type_name;
+
+ map_value_type (value_type&& v)
+ : value_type (move (v))
+ {
+ type_name = "optional_";
+ type_name += value_traits<K>::type_name;
+ type_name += '_';
+ type_name += value_traits<V>::type_name;
+ type_name += "_map";
+ name = type_name.c_str ();
+ }
+ };
template <typename K, typename V>
const std::map<K, V> value_traits<std::map<K, V>>::empty_instance;
template <typename K, typename V>
- const typename value_traits<std::map<K, V>>::value_type_ex
+ const map_value_type<K, V>
value_traits<std::map<K, V>>::value_type = build2::value_type // VC14 wants =
{
nullptr, // Patched above.