Quellcode durchsuchen

Add move assignment and move constructor to Variant.
Add `_to_variant` helper functions to `gdvirtual.gen.inc` to work around an MSVC bug.

Lukas Tenbrink vor 8 Monaten
Ursprung
Commit
34fa0bf3ec
2 geänderte Dateien mit 48 neuen und 1 gelöschten Zeilen
  1. 34 1
      core/object/make_virtuals.py
  2. 14 0
      core/variant/variant.h

+ 34 - 1
core/object/make_virtuals.py

@@ -127,7 +127,7 @@ def generate_version(argcount, const=False, returns=False, required=False):
             callptrargsptr += ", "
         argtext += f"m_type{i + 1}"
         callargtext += f"m_type{i + 1} arg{i + 1}"
-        callsiargs += f"Variant(arg{i + 1})"
+        callsiargs += f"_to_variant(arg{i + 1})"
         callsiargptrs += f"&vargs[{i}]"
         callptrargs += (
             f"PtrToArg<m_type{i + 1}>::EncodeT argval{i + 1} = (PtrToArg<m_type{i + 1}>::EncodeT)arg{i + 1};\\\n"
@@ -185,6 +185,8 @@ def run(target, source, env):
 
 #include "core/object/script_instance.h"
 
+#include <utility>
+
 #ifdef TOOLS_ENABLED
 #define GDVIRTUAL_TRACK(m_virtual, m_initialized)\\
 	if (_get_extension()->reloadable) {\\
@@ -198,6 +200,37 @@ def run(target, source, env):
 #define GDVIRTUAL_TRACK(m_virtual, m_initialized)
 #endif
 
+// MSVC WORKAROUND START
+// FIXME The below helper functions are needed to work around an MSVC bug.
+// They should be removed (by modifying core/object/make_virtuals.py) once the bug ceases to be triggered.
+// The bug is triggered by the following code:
+// `Variant(arg)`
+// Through the introduction of the move constructor, MSVC forgets that `operator Variant()`
+// is also a valid way to resolve this call. So for some argument types, it fails the call because
+// it cannot convert to `Variant`.
+// The function `_to_variant` helps the compiler select `.operator Variant()` for appropriate arguments using SFINAE.
+
+template <typename T, typename = void>
+struct has_variant_operator : std::false_type {};
+
+template <typename T>
+struct has_variant_operator<T, std::void_t<decltype(std::declval<T>().operator Variant())>> : std::true_type {};
+
+// Function that is enabled if T has `.operator Variant()`.
+template <typename T>
+_ALWAYS_INLINE_ typename std::enable_if<has_variant_operator<T>::value, Variant>::type
+_to_variant(T&& t) {
+    return std::forward<T>(t).operator Variant();
+}
+
+// Function that is enabled if T does not have `.operator Variant()`.
+template <typename T>
+_ALWAYS_INLINE_ typename std::enable_if<!has_variant_operator<T>::value, Variant>::type
+_to_variant(T&& t) {
+    return Variant(std::forward<T>(t));
+}
+// MSVC WORKAROUND END
+
 """
 
     for i in range(max_versions + 1):

+ 14 - 0
core/variant/variant.h

@@ -811,11 +811,25 @@ public:
 	static void construct_from_string(const String &p_string, Variant &r_value, ObjectConstruct p_obj_construct = nullptr, void *p_construct_ud = nullptr);
 
 	void operator=(const Variant &p_variant); // only this is enough for all the other types
+	void operator=(Variant &&p_variant) {
+		if (unlikely(this == &p_variant)) {
+			return;
+		}
+		clear();
+		type = p_variant.type;
+		_data = p_variant._data;
+		p_variant.type = NIL;
+	}
 
 	static void register_types();
 	static void unregister_types();
 
 	Variant(const Variant &p_variant);
+	Variant(Variant &&p_variant) {
+		type = p_variant.type;
+		_data = p_variant._data;
+		p_variant.type = NIL;
+	}
 	_FORCE_INLINE_ Variant() {}
 	_FORCE_INLINE_ ~Variant() {
 		clear();