diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md
index 7f34e404d..798f210ef 100644
--- a/ISSUE_TEMPLATE.md
+++ b/ISSUE_TEMPLATE.md
@@ -4,7 +4,7 @@ https://github.com/flaxengine/FlaxEngine/issues?q=is%3Aissue
**Issue description:**
-
+
**Steps to reproduce:**
diff --git a/Source/Editor/CustomEditors/Editors/GenericEditor.cs b/Source/Editor/CustomEditors/Editors/GenericEditor.cs
index 8510fa9ff..6a20b6a28 100644
--- a/Source/Editor/CustomEditors/Editors/GenericEditor.cs
+++ b/Source/Editor/CustomEditors/Editors/GenericEditor.cs
@@ -419,12 +419,18 @@ namespace FlaxEditor.CustomEditors.Editors
}
}
+ ///
+ internal override void Initialize(CustomEditorPresenter presenter, LayoutElementsContainer layout, ValueContainer values)
+ {
+ _isNull = values != null && values.IsNull;
+
+ base.Initialize(presenter, layout, values);
+ }
+
///
public override void Initialize(LayoutElementsContainer layout)
{
- var values = Values;
_visibleIfCaches = null;
- _isNull = values != null && values.IsNull;
// Collect items to edit
List items;
diff --git a/Source/Editor/Editor.cpp b/Source/Editor/Editor.cpp
index f33d27dbe..ce42154da 100644
--- a/Source/Editor/Editor.cpp
+++ b/Source/Editor/Editor.cpp
@@ -388,6 +388,105 @@ int32 Editor::LoadProduct()
projectPath.Clear();
}
+ // Create new project option
+ if (CommandLine::Options.NewProject)
+ {
+ if (projectPath.IsEmpty())
+ projectPath = Platform::GetWorkingDirectory();
+ else if (!FileSystem::DirectoryExists(projectPath))
+ FileSystem::CreateDirectory(projectPath);
+ FileSystem::NormalizePath(projectPath);
+
+ String folderName = StringUtils::GetFileName(projectPath);
+ String tmpName;
+ for (int32 i = 0; i < folderName.Length(); i++)
+ {
+ Char c = folderName[i];
+ if (StringUtils::IsAlnum(c) && c != ' ' && c != '.')
+ tmpName += c;
+ }
+
+ // Create project file
+ ProjectInfo newProject;
+ newProject.Name = MoveTemp(tmpName);
+ newProject.ProjectPath = projectPath / newProject.Name + TEXT(".flaxproj");
+ newProject.ProjectFolderPath = projectPath;
+ newProject.Version = Version(1, 0);
+ newProject.Company = TEXT("My Company");
+ newProject.MinEngineVersion = FLAXENGINE_VERSION;
+ newProject.GameTarget = TEXT("GameTarget");
+ newProject.EditorTarget = TEXT("GameEditorTarget");
+ auto& flaxRef = newProject.References.AddOne();
+ flaxRef.Name = TEXT("$(EnginePath)/Flax.flaxproj");
+ flaxRef.Project = nullptr;
+ if (newProject.SaveProject())
+ return 10;
+
+ // Generate source files
+ if (FileSystem::CreateDirectory(projectPath / TEXT("Content")))
+ return 11;
+ if (FileSystem::CreateDirectory(projectPath / TEXT("Source/Game")))
+ return 11;
+ bool failed = File::WriteAllText(projectPath / TEXT("Source/GameTarget.Build.cs"),TEXT(
+ "using Flax.Build;\n"
+ "\n"
+ "public class GameTarget : GameProjectTarget\n"
+ "{\n"
+ " /// \n"
+ " public override void Init()\n"
+ " {\n"
+ " base.Init();\n"
+ "\n"
+ " // Reference the modules for game\n"
+ " Modules.Add(\"Game\");\n"
+ " }\n"
+ "}\n"), Encoding::Unicode);
+ failed |= File::WriteAllText(projectPath / TEXT("Source/GameEditorTarget.Build.cs"),TEXT(
+ "using Flax.Build;\n"
+ "\n"
+ "public class GameEditorTarget : GameProjectEditorTarget\n"
+ "{\n"
+ " /// \n"
+ " public override void Init()\n"
+ " {\n"
+ " base.Init();\n"
+ "\n"
+ " // Reference the modules for editor\n"
+ " Modules.Add(\"Game\");\n"
+ " }\n"
+ "}\n"), Encoding::Unicode);
+ failed |= File::WriteAllText(projectPath / TEXT("Source/Game/Game.Build.cs"),TEXT(
+ "using Flax.Build;\n"
+ "using Flax.Build.NativeCpp;\n"
+ "\n"
+ "public class Game : GameModule\n"
+ "{\n"
+ " /// \n"
+ " public override void Init()\n"
+ " {\n"
+ " base.Init();\n"
+ "\n"
+ " // C#-only scripting\n"
+ " BuildNativeCode = false;\n"
+ " }\n"
+ "\n"
+ " /// \n"
+ " public override void Setup(BuildOptions options)\n"
+ " {\n"
+ " base.Setup(options);\n"
+ "\n"
+ " options.ScriptingAPI.IgnoreMissingDocumentationWarnings = true;\n"
+ "\n"
+ " // Here you can modify the build options for your game module\n"
+ " // To reference another module use: options.PublicDependencies.Add(\"Audio\");\n"
+ " // To add C++ define use: options.PublicDefinitions.Add(\"COMPILE_WITH_FLAX\");\n"
+ " // To learn more see scripting documentation.\n"
+ " }\n"
+ "}\n"), Encoding::Unicode);
+ if (failed)
+ return 12;
+ }
+
// Missing project case
if (projectPath.IsEmpty())
{
diff --git a/Source/Editor/Editor.cs b/Source/Editor/Editor.cs
index 19d699726..4831b9958 100644
--- a/Source/Editor/Editor.cs
+++ b/Source/Editor/Editor.cs
@@ -272,8 +272,10 @@ namespace FlaxEditor
module.OnEndInit();
}
- internal void Init(bool isHeadless, bool skipCompile, Guid startupScene)
+ internal void Init(bool isHeadless, bool skipCompile, bool newProject, Guid startupScene)
{
+ if (newProject)
+ InitProject();
EnsureState();
_isHeadlessMode = isHeadless;
_startupSceneCmdLine = startupScene;
@@ -473,6 +475,33 @@ namespace FlaxEditor
}
}
+ private void InitProject()
+ {
+ // Initialize empty project with default game configuration
+ Log("Initialize new project");
+ var project = GameProject;
+ var gameSettings = new GameSettings
+ {
+ ProductName = project.Name,
+ CompanyName = project.Company,
+ };
+ GameSettings.Save(gameSettings);
+ GameSettings.Save(new TimeSettings());
+ GameSettings.Save(new AudioSettings());
+ GameSettings.Save(new PhysicsSettings());
+ GameSettings.Save(new LayersAndTagsSettings());
+ GameSettings.Save(new InputSettings());
+ GameSettings.Save(new GraphicsSettings());
+ GameSettings.Save(new NavigationSettings());
+ GameSettings.Save(new LocalizationSettings());
+ GameSettings.Save(new BuildSettings());
+ GameSettings.Save(new StreamingSettings());
+ GameSettings.Save(new WindowsPlatformSettings());
+ GameSettings.Save(new LinuxPlatformSettings());
+ GameSettings.Save(new AndroidPlatformSettings());
+ GameSettings.Save(new UWPPlatformSettings());
+ }
+
internal void OnPlayBeginning()
{
for (int i = 0; i < _modules.Count; i++)
diff --git a/Source/Editor/GUI/ContextMenu/ContextMenu.cs b/Source/Editor/GUI/ContextMenu/ContextMenu.cs
index 966f3502b..bf32fc802 100644
--- a/Source/Editor/GUI/ContextMenu/ContextMenu.cs
+++ b/Source/Editor/GUI/ContextMenu/ContextMenu.cs
@@ -456,18 +456,30 @@ namespace FlaxEditor.GUI.ContextMenu
if (base.OnKeyDown(key))
return true;
- // Se;ect the first item
- if (key == KeyboardKeys.ArrowDown)
+ switch (key)
{
+ case KeyboardKeys.ArrowDown:
for (int i = 0; i < _panel.Children.Count; i++)
{
- if (_panel.Children[i] is ContextMenuButton item && item.Visible)
+ if (_panel.Children[i] is ContextMenuButton item && item.Visible && item.Enabled)
{
item.Focus();
_panel.ScrollViewTo(item);
return true;
}
}
+ break;
+ case KeyboardKeys.ArrowUp:
+ for (int i = _panel.Children.Count - 1; i >= 0; i--)
+ {
+ if (_panel.Children[i] is ContextMenuButton item && item.Visible && item.Enabled)
+ {
+ item.Focus();
+ _panel.ScrollViewTo(item);
+ return true;
+ }
+ }
+ break;
}
return false;
diff --git a/Source/Editor/GUI/ContextMenu/ContextMenuButton.cs b/Source/Editor/GUI/ContextMenu/ContextMenuButton.cs
index c4bdbce68..dc48cae73 100644
--- a/Source/Editor/GUI/ContextMenu/ContextMenuButton.cs
+++ b/Source/Editor/GUI/ContextMenu/ContextMenuButton.cs
@@ -183,7 +183,7 @@ namespace FlaxEditor.GUI.ContextMenu
case KeyboardKeys.ArrowUp:
for (int i = IndexInParent - 1; i >= 0; i--)
{
- if (ParentContextMenu.ItemsContainer.Children[i] is ContextMenuButton item && item.Visible)
+ if (ParentContextMenu.ItemsContainer.Children[i] is ContextMenuButton item && item.Visible && item.Enabled)
{
item.Focus();
ParentContextMenu.ItemsContainer.ScrollViewTo(item);
@@ -194,7 +194,7 @@ namespace FlaxEditor.GUI.ContextMenu
case KeyboardKeys.ArrowDown:
for (int i = IndexInParent + 1; i < ParentContextMenu.ItemsContainer.Children.Count; i++)
{
- if (ParentContextMenu.ItemsContainer.Children[i] is ContextMenuButton item && item.Visible)
+ if (ParentContextMenu.ItemsContainer.Children[i] is ContextMenuButton item && item.Visible && item.Enabled)
{
item.Focus();
ParentContextMenu.ItemsContainer.ScrollViewTo(item);
diff --git a/Source/Editor/Managed/ManagedEditor.cpp b/Source/Editor/Managed/ManagedEditor.cpp
index 6a83ceeb5..089faeb7f 100644
--- a/Source/Editor/Managed/ManagedEditor.cpp
+++ b/Source/Editor/Managed/ManagedEditor.cpp
@@ -175,7 +175,7 @@ ManagedEditor::~ManagedEditor()
void ManagedEditor::Init()
{
// Note: editor modules should perform quite fast init, any longer things should be done in async during 'editor splash screen time
- void* args[3];
+ void* args[4];
MClass* mclass = GetClass();
if (mclass == nullptr)
{
@@ -194,14 +194,16 @@ void ManagedEditor::Init()
MonoObject* exception = nullptr;
bool isHeadless = CommandLine::Options.Headless.IsTrue();
bool skipCompile = CommandLine::Options.SkipCompile.IsTrue();
+ bool newProject = CommandLine::Options.NewProject.IsTrue();
args[0] = &isHeadless;
args[1] = &skipCompile;
+ args[2] = &newProject;
Guid sceneId;
if (!CommandLine::Options.Play.HasValue() || (CommandLine::Options.Play.HasValue() && Guid::Parse(CommandLine::Options.Play.GetValue(), sceneId)))
{
sceneId = Guid::Empty;
}
- args[2] = &sceneId;
+ args[3] = &sceneId;
initMethod->Invoke(instance, args, &exception);
if (exception)
{
diff --git a/Source/Editor/Scripting/ScriptsBuilder.cpp b/Source/Editor/Scripting/ScriptsBuilder.cpp
index 23cf84981..af12d8708 100644
--- a/Source/Editor/Scripting/ScriptsBuilder.cpp
+++ b/Source/Editor/Scripting/ScriptsBuilder.cpp
@@ -239,7 +239,6 @@ bool ScriptsBuilder::RunBuildTool(const StringView& args)
cmdLine.Append(buildToolPath);
cmdLine.Append(TEXT("\" "));
cmdLine.Append(args.Get(), args.Length());
- cmdLine.Append(TEXT('\0'));
// TODO: Set env var for the mono MONO_GC_PARAMS=nursery-size64m to boost build performance -> profile it
// Call build tool
diff --git a/Source/Editor/Utilities/EditorUtilities.cpp b/Source/Editor/Utilities/EditorUtilities.cpp
index 85d409b92..1b271d3d1 100644
--- a/Source/Editor/Utilities/EditorUtilities.cpp
+++ b/Source/Editor/Utilities/EditorUtilities.cpp
@@ -816,6 +816,6 @@ bool EditorUtilities::ReplaceInFile(const StringView& file, const StringView& fi
String text;
if (File::ReadAllText(file, text))
return true;
- text.Replace(findWhat.GetText(), replaceWith.GetText());
+ text.Replace(findWhat.Get(), findWhat.Length(), replaceWith.Get(), replaceWith.Length());
return File::WriteAllText(file, text, Encoding::ANSI);
}
diff --git a/Source/Editor/Windows/Assets/ModelWindow.cs b/Source/Editor/Windows/Assets/ModelWindow.cs
index be7140d61..f08445862 100644
--- a/Source/Editor/Windows/Assets/ModelWindow.cs
+++ b/Source/Editor/Windows/Assets/ModelWindow.cs
@@ -538,9 +538,9 @@ namespace FlaxEditor.Windows.Assets
for (int i = 0; i < meshData.IndexBuffer.Length; i += 3)
{
// Cache triangle indices
- int i0 = meshData.IndexBuffer[i + 0];
- int i1 = meshData.IndexBuffer[i + 1];
- int i2 = meshData.IndexBuffer[i + 2];
+ uint i0 = meshData.IndexBuffer[i + 0];
+ uint i1 = meshData.IndexBuffer[i + 1];
+ uint i2 = meshData.IndexBuffer[i + 2];
// Cache triangle uvs positions and transform positions to output target
Vector2 uv0 = meshData.VertexBuffer[i0].TexCoord * uvScale;
@@ -562,9 +562,9 @@ namespace FlaxEditor.Windows.Assets
for (int i = 0; i < meshData.IndexBuffer.Length; i += 3)
{
// Cache triangle indices
- int i0 = meshData.IndexBuffer[i + 0];
- int i1 = meshData.IndexBuffer[i + 1];
- int i2 = meshData.IndexBuffer[i + 2];
+ uint i0 = meshData.IndexBuffer[i + 0];
+ uint i1 = meshData.IndexBuffer[i + 1];
+ uint i2 = meshData.IndexBuffer[i + 2];
// Cache triangle uvs positions and transform positions to output target
Vector2 uv0 = meshData.VertexBuffer[i0].LightmapUVs * uvScale;
diff --git a/Source/Editor/Windows/Assets/SkinnedModelWindow.cs b/Source/Editor/Windows/Assets/SkinnedModelWindow.cs
index 76639709c..ea5240cb5 100644
--- a/Source/Editor/Windows/Assets/SkinnedModelWindow.cs
+++ b/Source/Editor/Windows/Assets/SkinnedModelWindow.cs
@@ -645,9 +645,9 @@ namespace FlaxEditor.Windows.Assets
for (int i = 0; i < meshData.IndexBuffer.Length; i += 3)
{
// Cache triangle indices
- int i0 = meshData.IndexBuffer[i + 0];
- int i1 = meshData.IndexBuffer[i + 1];
- int i2 = meshData.IndexBuffer[i + 2];
+ uint i0 = meshData.IndexBuffer[i + 0];
+ uint i1 = meshData.IndexBuffer[i + 1];
+ uint i2 = meshData.IndexBuffer[i + 2];
// Cache triangle uvs positions and transform positions to output target
Vector2 uv0 = meshData.VertexBuffer[i0].TexCoord * uvScale;
@@ -820,7 +820,7 @@ namespace FlaxEditor.Windows.Assets
private struct MeshData
{
- public int[] IndexBuffer;
+ public uint[] IndexBuffer;
public SkinnedMesh.Vertex[] VertexBuffer;
}
diff --git a/Source/Editor/Windows/OutputLogWindow.cs b/Source/Editor/Windows/OutputLogWindow.cs
index 18ec939c9..ae1c5baf7 100644
--- a/Source/Editor/Windows/OutputLogWindow.cs
+++ b/Source/Editor/Windows/OutputLogWindow.cs
@@ -192,6 +192,7 @@ namespace FlaxEditor.Windows
_contextMenu.AddButton("Clear log", Clear);
_contextMenu.AddButton("Copy selection", _output.Copy);
_contextMenu.AddButton("Select All", _output.SelectAll);
+ _contextMenu.AddButton("Show in explorer", () => FileSystem.ShowFileExplorer(Path.Combine(Globals.ProjectFolder, "Logs")));
_contextMenu.AddButton("Scroll to bottom", () => { _vScroll.TargetValue = _vScroll.Maximum; }).Icon = Editor.Icons.ArrowDown12;
// Setup editor options
diff --git a/Source/Engine/Content/Assets/VisualScript.cpp b/Source/Engine/Content/Assets/VisualScript.cpp
index 184360f1f..fe3279856 100644
--- a/Source/Engine/Content/Assets/VisualScript.cpp
+++ b/Source/Engine/Content/Assets/VisualScript.cpp
@@ -1222,6 +1222,11 @@ Asset::LoadResult VisualScript::load()
method.ProfilerName.Get()[assetName.Length()] = ':';
method.ProfilerName.Get()[assetName.Length() + 1] = ':';
Platform::MemoryCopy(method.ProfilerName.Get() + assetName.Length() + 2, method.Name.Get(), method.Name.Length());
+ method.ProfilerData.name = method.ProfilerName.Get();
+ method.ProfilerData.function = method.Name.Get();
+ method.ProfilerData.file = nullptr;
+ method.ProfilerData.line = 0;
+ method.ProfilerData.color = 0;
}
#endif
@@ -2139,7 +2144,7 @@ VisualScriptingBinaryModule* VisualScripting::GetBinaryModule()
Variant VisualScripting::Invoke(VisualScript::Method* method, ScriptingObject* instance, Span parameters)
{
CHECK_RETURN(method && method->Script->IsLoaded(), Variant::Zero);
- PROFILE_CPU_NAMED(*method->ProfilerName);
+ PROFILE_CPU_SRC_LOC(method->ProfilerData);
// Add to the calling stack
ScopeContext scope;
diff --git a/Source/Engine/Content/Assets/VisualScript.h b/Source/Engine/Content/Assets/VisualScript.h
index db6b5ebd3..3ea98e9a3 100644
--- a/Source/Engine/Content/Assets/VisualScript.h
+++ b/Source/Engine/Content/Assets/VisualScript.h
@@ -5,6 +5,9 @@
#include "../BinaryAsset.h"
#include "Engine/Scripting/BinaryModule.h"
#include "Engine/Visject/VisjectGraph.h"
+#if COMPILE_WITH_PROFILER
+#include "Engine/Profiler/ProfilerSrcLoc.h"
+#endif
#define VISUAL_SCRIPT_GRAPH_MAX_CALL_STACK 250
#define VISUAL_SCRIPT_DEBUGGING USE_EDITOR
@@ -118,6 +121,7 @@ public:
Array> ParamNames;
#if COMPILE_WITH_PROFILER
StringAnsi ProfilerName;
+ SourceLocationData ProfilerData;
#endif
};
diff --git a/Source/Engine/Content/Content.cpp b/Source/Engine/Content/Content.cpp
index 98e2c241c..abe40e285 100644
--- a/Source/Engine/Content/Content.cpp
+++ b/Source/Engine/Content/Content.cpp
@@ -384,12 +384,12 @@ Asset* Content::LoadAsyncInternal(const StringView& internalPath, MClass* type)
CHECK_RETURN(type, nullptr);
const auto scriptingType = Scripting::FindScriptingType(type->GetFullName());
if (scriptingType)
- return LoadAsyncInternal(internalPath.GetText(), scriptingType);
+ return LoadAsyncInternal(internalPath, scriptingType);
LOG(Error, "Failed to find asset type '{0}'.", String(type->GetFullName()));
return nullptr;
}
-Asset* Content::LoadAsyncInternal(const Char* internalPath, const ScriptingTypeHandle& type)
+Asset* Content::LoadAsyncInternal(const StringView& internalPath, const ScriptingTypeHandle& type)
{
#if USE_EDITOR
const String path = Globals::EngineContentFolder / internalPath + ASSET_FILES_EXTENSION_WITH_DOT;
@@ -411,6 +411,11 @@ Asset* Content::LoadAsyncInternal(const Char* internalPath, const ScriptingTypeH
return asset;
}
+Asset* Content::LoadAsyncInternal(const Char* internalPath, const ScriptingTypeHandle& type)
+{
+ return LoadAsyncInternal(StringView(internalPath), type);
+}
+
FLAXENGINE_API Asset* LoadAsset(const Guid& id, const ScriptingTypeHandle& type)
{
return Content::LoadAsync(id, type);
diff --git a/Source/Engine/Content/Content.h b/Source/Engine/Content/Content.h
index da558dc5f..c8e3cad90 100644
--- a/Source/Engine/Content/Content.h
+++ b/Source/Engine/Content/Content.h
@@ -187,6 +187,14 @@ public:
/// The loaded asset or null if failed.
API_FUNCTION(Attributes="HideInEditor") static Asset* LoadAsyncInternal(const StringView& internalPath, MClass* type);
+ ///
+ /// Loads internal engine asset and holds it until it won't be referenced by any object. Returns null if asset is missing. Actual asset data loading is performed on a other thread in async.
+ ///
+ /// The path of the asset relative to the engine internal content (excluding the extension).
+ /// The asset type. If loaded object has different type (excluding types derived from the given) the loading fails.
+ /// The loaded asset or null if failed.
+ static Asset* LoadAsyncInternal(const StringView& internalPath, const ScriptingTypeHandle& type);
+
///
/// Loads internal engine asset and holds it until it won't be referenced by any object. Returns null if asset is missing. Actual asset data loading is performed on a other thread in async.
///
diff --git a/Source/Engine/Core/Config/GameSettings.cs b/Source/Engine/Core/Config/GameSettings.cs
index b9ba4e110..d5dfd3726 100644
--- a/Source/Engine/Core/Config/GameSettings.cs
+++ b/Source/Engine/Core/Config/GameSettings.cs
@@ -296,7 +296,7 @@ namespace FlaxEditor.Content.Settings
}
// Create new settings asset and link it to the game settings
- var path = StringUtils.CombinePaths(Globals.ProjectContentFolder, CustomEditors.CustomEditorsUtil.GetPropertyNameUI(typeof(T).Name) + ".json");
+ var path = StringUtils.CombinePaths(Globals.ProjectContentFolder, "Settings", CustomEditors.CustomEditorsUtil.GetPropertyNameUI(typeof(T).Name) + ".json");
if (Editor.SaveJsonAsset(path, obj))
return true;
asset = FlaxEngine.Content.LoadAsync(path);
diff --git a/Source/Engine/Core/Types/String.cpp b/Source/Engine/Core/Types/String.cpp
index 7919d513d..2ea3c7dc5 100644
--- a/Source/Engine/Core/Types/String.cpp
+++ b/Source/Engine/Core/Types/String.cpp
@@ -226,7 +226,9 @@ bool String::IsANSI() const
bool String::StartsWith(const StringView& prefix, StringSearchCase searchCase) const
{
- if (prefix.IsEmpty() || prefix.Length() > Length())
+ if (prefix.IsEmpty())
+ return true;
+ if (prefix.Length() > Length())
return false;
if (searchCase == StringSearchCase::IgnoreCase)
return !StringUtils::CompareIgnoreCase(this->GetText(), *prefix, prefix.Length());
@@ -235,7 +237,9 @@ bool String::StartsWith(const StringView& prefix, StringSearchCase searchCase) c
bool String::EndsWith(const StringView& suffix, StringSearchCase searchCase) const
{
- if (suffix.IsEmpty() || suffix.Length() > Length())
+ if (suffix.IsEmpty())
+ return true;
+ if (suffix.Length() > Length())
return false;
if (searchCase == StringSearchCase::IgnoreCase)
return !StringUtils::CompareIgnoreCase(&(*this)[Length() - suffix.Length()], *suffix);
diff --git a/Source/Engine/Core/Types/String.h b/Source/Engine/Core/Types/String.h
index 6871cb287..3485a1093 100644
--- a/Source/Engine/Core/Types/String.h
+++ b/Source/Engine/Core/Types/String.h
@@ -65,10 +65,11 @@ public:
///
/// Lexicographically tests how this string compares to the other given string.
+ /// In case sensitive mode 'A' is less than 'a'.
///
/// The another string test against.
/// The case sensitivity mode.
- /// 0 if equal, -1 if less than, 1 if greater than.
+ /// 0 if equal, negative number if less than, positive number if greater than.
int32 Compare(const StringBase& str, StringSearchCase searchCase = StringSearchCase::CaseSensitive) const
{
if (searchCase == StringSearchCase::CaseSensitive)
@@ -352,7 +353,7 @@ public:
bool StartsWith(T c, StringSearchCase searchCase = StringSearchCase::CaseSensitive) const
{
const int32 length = Length();
- if (searchCase == StringSearchCase::IgnoreCase)
+ if (searchCase == StringSearchCase::CaseSensitive)
return length > 0 && _data[0] == c;
return length > 0 && StringUtils::ToLower(_data[0]) == StringUtils::ToLower(c);
}
@@ -360,14 +361,16 @@ public:
bool EndsWith(T c, StringSearchCase searchCase = StringSearchCase::CaseSensitive) const
{
const int32 length = Length();
- if (searchCase == StringSearchCase::IgnoreCase)
+ if (searchCase == StringSearchCase::CaseSensitive)
return length > 0 && _data[length - 1] == c;
return length > 0 && StringUtils::ToLower(_data[length - 1]) == StringUtils::ToLower(c);
}
bool StartsWith(const StringBase& prefix, StringSearchCase searchCase = StringSearchCase::CaseSensitive) const
{
- if (prefix.IsEmpty() || Length() < prefix.Length())
+ if (prefix.IsEmpty())
+ return true;
+ if (Length() < prefix.Length())
return false;
if (searchCase == StringSearchCase::IgnoreCase)
return StringUtils::CompareIgnoreCase(this->GetText(), *prefix, prefix.Length()) == 0;
@@ -376,7 +379,9 @@ public:
bool EndsWith(const StringBase& suffix, StringSearchCase searchCase = StringSearchCase::CaseSensitive) const
{
- if (suffix.IsEmpty() || Length() < suffix.Length())
+ if (suffix.IsEmpty())
+ return true;
+ if (Length() < suffix.Length())
return false;
if (searchCase == StringSearchCase::IgnoreCase)
return StringUtils::CompareIgnoreCase(&(*this)[Length() - suffix.Length()], *suffix) == 0;
@@ -413,67 +418,94 @@ public:
return replacedChars;
}
+ ///
+ /// Replaces all occurences of searchText within current string with replacementText.
+ ///
+ /// String to search for. If empty or null no replacements are done.
+ /// String to replace with. Null is treated as empty string.
+ /// Number of replacements made. (In case-sensitive mode if search text and replacement text are equal no replacements are done, and zero is returned.)
int32 Replace(const T* searchText, const T* replacementText, StringSearchCase searchCase = StringSearchCase::CaseSensitive)
{
- int32 replacedCount = 0;
- if (HasChars() && searchText && *searchText && replacementText && (searchCase == StringSearchCase::IgnoreCase || StringUtils::Compare(searchText, replacementText) != 0))
+ const int32 searchTextLength = StringUtils::Length(searchText);
+ const int32 replacementTextLength = StringUtils::Length(replacementText);
+ return Replace(searchText, searchTextLength, replacementText, replacementTextLength, searchCase);
+ }
+
+ ///
+ /// Replaces all occurences of searchText within current string with replacementText.
+ ///
+ /// String to search for.
+ /// Length of searchText. Must be greater than zero.
+ /// String to replace with. Null is treated as empty string.
+ /// Length of replacementText.
+ /// Number of replacements made (in other words number of occurences of searchText).
+ int32 Replace(const T* searchText, int32 searchTextLength, const T* replacementText, int32 replacementTextLength, StringSearchCase searchCase = StringSearchCase::CaseSensitive)
+ {
+ if (!HasChars())
+ return 0;
+
+ if (searchTextLength == 0)
{
- const int32 searchTextLength = StringUtils::Length(searchText);
- const int32 replacementTextLength = StringUtils::Length(replacementText);
- if (searchTextLength == replacementTextLength)
+ ASSERT(false); // Empty search text never makes sense, and is always sign of a bug in calling code.
+ return 0;
+ }
+
+ int32 replacedCount = 0;
+
+ if (searchTextLength == replacementTextLength)
+ {
+ T* pos = (T*)(searchCase == StringSearchCase::IgnoreCase ? StringUtils::FindIgnoreCase(_data, searchText) : StringUtils::Find(_data, searchText));
+ while (pos != nullptr)
{
- T* pos = (T*)(searchCase == StringSearchCase::IgnoreCase ? StringUtils::FindIgnoreCase(_data, searchText) : StringUtils::Find(_data, searchText));
- while (pos != nullptr)
- {
- replacedCount++;
+ replacedCount++;
- for (int32 i = 0; i < replacementTextLength; i++)
- pos[i] = replacementText[i];
+ for (int32 i = 0; i < replacementTextLength; i++)
+ pos[i] = replacementText[i];
- if (pos + searchTextLength - **this < Length())
- pos = (T*)(searchCase == StringSearchCase::IgnoreCase ? StringUtils::FindIgnoreCase(pos + searchTextLength, searchText) : StringUtils::Find(pos + searchTextLength, searchText));
- else
- break;
- }
+ if (pos + searchTextLength - **this < Length())
+ pos = (T*)(searchCase == StringSearchCase::IgnoreCase ? StringUtils::FindIgnoreCase(pos + searchTextLength, searchText) : StringUtils::Find(pos + searchTextLength, searchText));
+ else
+ break;
}
- else if (Contains(searchText, searchCase))
+ }
+ else if (Contains(searchText, searchCase))
+ {
+ T* readPosition = _data;
+ T* searchPosition = (T*)(searchCase == StringSearchCase::IgnoreCase ? StringUtils::FindIgnoreCase(readPosition, searchText) : StringUtils::Find(readPosition, searchText));
+ while (searchPosition != nullptr)
{
- T* readPosition = _data;
- T* searchPosition = (T*)(searchCase == StringSearchCase::IgnoreCase ? StringUtils::FindIgnoreCase(readPosition, searchText) : StringUtils::Find(readPosition, searchText));
- while (searchPosition != nullptr)
- {
- replacedCount++;
- readPosition = searchPosition + searchTextLength;
- searchPosition = (T*)(searchCase == StringSearchCase::IgnoreCase ? StringUtils::FindIgnoreCase(readPosition, searchText) : StringUtils::Find(readPosition, searchText));
- }
-
- const auto oldLength = _length;
- const auto oldData = _data;
- _length += replacedCount * (replacementTextLength - searchTextLength);
- _data = (T*)Platform::Allocate((_length + 1) * sizeof(T), 16);
-
- T* writePosition = _data;
- readPosition = oldData;
+ replacedCount++;
+ readPosition = searchPosition + searchTextLength;
searchPosition = (T*)(searchCase == StringSearchCase::IgnoreCase ? StringUtils::FindIgnoreCase(readPosition, searchText) : StringUtils::Find(readPosition, searchText));
- while (searchPosition != nullptr)
- {
- const int32 writeOffset = (int32)(searchPosition - readPosition);
- Platform::MemoryCopy(writePosition, readPosition, writeOffset * sizeof(T));
- writePosition += writeOffset;
-
- Platform::MemoryCopy(writePosition, replacementText, replacementTextLength * sizeof(T));
- writePosition += replacementTextLength;
-
- readPosition = searchPosition + searchTextLength;
- searchPosition = (T*)(searchCase == StringSearchCase::IgnoreCase ? StringUtils::FindIgnoreCase(readPosition, searchText) : StringUtils::Find(readPosition, searchText));
- }
-
- const int32 writeOffset = (int32)(oldData - readPosition) + oldLength;
- Platform::MemoryCopy(writePosition, readPosition, writeOffset * sizeof(T));
-
- _data[_length] = 0;
- Platform::Free(oldData);
}
+
+ const auto oldLength = _length;
+ const auto oldData = _data;
+ _length += replacedCount * (replacementTextLength - searchTextLength);
+ _data = (T*)Platform::Allocate((_length + 1) * sizeof(T), 16);
+
+ T* writePosition = _data;
+ readPosition = oldData;
+ searchPosition = (T*)(searchCase == StringSearchCase::IgnoreCase ? StringUtils::FindIgnoreCase(readPosition, searchText) : StringUtils::Find(readPosition, searchText));
+ while (searchPosition != nullptr)
+ {
+ const int32 writeOffset = (int32)(searchPosition - readPosition);
+ Platform::MemoryCopy(writePosition, readPosition, writeOffset * sizeof(T));
+ writePosition += writeOffset;
+
+ if (replacementTextLength > 0)
+ Platform::MemoryCopy(writePosition, replacementText, replacementTextLength * sizeof(T));
+ writePosition += replacementTextLength;
+
+ readPosition = searchPosition + searchTextLength;
+ searchPosition = (T*)(searchCase == StringSearchCase::IgnoreCase ? StringUtils::FindIgnoreCase(readPosition, searchText) : StringUtils::Find(readPosition, searchText));
+ }
+
+ const int32 writeOffset = (int32)(oldData - readPosition) + oldLength;
+ Platform::MemoryCopy(writePosition, readPosition, writeOffset * sizeof(T));
+
+ _data[_length] = 0;
+ Platform::Free(oldData);
}
return replacedCount;
diff --git a/Source/Engine/Core/Types/StringView.cpp b/Source/Engine/Core/Types/StringView.cpp
index 606e37ad1..4df8bc8e9 100644
--- a/Source/Engine/Core/Types/StringView.cpp
+++ b/Source/Engine/Core/Types/StringView.cpp
@@ -18,12 +18,12 @@ StringView::StringView(const String& str)
bool StringView::operator==(const String& other) const
{
- return StringUtils::Compare(this->GetText(), *other) == 0;
+ return this->Compare(StringView(other)) == 0;
}
bool StringView::operator!=(const String& other) const
{
- return StringUtils::Compare(this->GetText(), *other) != 0;
+ return this->Compare(StringView(other)) != 0;
}
StringView StringView::Left(int32 count) const
@@ -62,12 +62,12 @@ StringAnsi StringView::ToStringAnsi() const
bool operator==(const String& a, const StringView& b)
{
- return a.Length() == b.Length() && StringUtils::Compare(a.GetText(), b.GetText(), b.Length()) == 0;
+ return a.Length() == b.Length() && StringUtils::Compare(a.GetText(), b.GetNonTerminatedText(), b.Length()) == 0;
}
bool operator!=(const String& a, const StringView& b)
{
- return a.Length() != b.Length() || StringUtils::Compare(a.GetText(), b.GetText(), b.Length()) != 0;
+ return a.Length() != b.Length() || StringUtils::Compare(a.GetText(), b.GetNonTerminatedText(), b.Length()) != 0;
}
StringAnsiView StringAnsiView::Empty;
@@ -79,12 +79,12 @@ StringAnsiView::StringAnsiView(const StringAnsi& str)
bool StringAnsiView::operator==(const StringAnsi& other) const
{
- return StringUtils::Compare(this->GetText(), *other) == 0;
+ return this->Compare(StringAnsiView(other)) == 0;
}
bool StringAnsiView::operator!=(const StringAnsi& other) const
{
- return StringUtils::Compare(this->GetText(), *other) != 0;
+ return this->Compare(StringAnsiView(other)) != 0;
}
StringAnsi StringAnsiView::Substring(int32 startIndex) const
@@ -111,10 +111,10 @@ StringAnsi StringAnsiView::ToStringAnsi() const
bool operator==(const StringAnsi& a, const StringAnsiView& b)
{
- return a.Length() == b.Length() && StringUtils::Compare(a.GetText(), b.GetText(), b.Length()) == 0;
+ return a.Length() == b.Length() && StringUtils::Compare(a.GetText(), b.GetNonTerminatedText(), b.Length()) == 0;
}
bool operator!=(const StringAnsi& a, const StringAnsiView& b)
{
- return a.Length() != b.Length() || StringUtils::Compare(a.GetText(), b.GetText(), b.Length()) != 0;
+ return a.Length() != b.Length() || StringUtils::Compare(a.GetText(), b.GetNonTerminatedText(), b.Length()) != 0;
}
diff --git a/Source/Engine/Core/Types/StringView.h b/Source/Engine/Core/Types/StringView.h
index 0ed2580ce..74f8e398e 100644
--- a/Source/Engine/Core/Types/StringView.h
+++ b/Source/Engine/Core/Types/StringView.h
@@ -54,18 +54,23 @@ public:
///
/// Lexicographically tests how this string compares to the other given string.
+ /// In case sensitive mode 'A' is less than 'a'.
///
/// The another string test against.
/// The case sensitivity mode.
- /// 0 if equal, -1 if less than, 1 if greater than.
+ /// 0 if equal, negative number if less than, positive number if greater than.
int32 Compare(const StringViewBase& str, StringSearchCase searchCase = StringSearchCase::CaseSensitive) const
{
- const int32 lengthDiff = Length() - str.Length();
- if (lengthDiff != 0)
- return lengthDiff;
- if (searchCase == StringSearchCase::CaseSensitive)
- return StringUtils::Compare(this->GetText(), str.GetText(), Length());
- return StringUtils::CompareIgnoreCase(this->GetText(), str.GetText(), Length());
+ const bool thisIsShorter = Length() < str.Length();
+ const int32 minLength = thisIsShorter ? Length() : str.Length();
+ const int32 prefixCompare = (searchCase == StringSearchCase::CaseSensitive)
+ ? StringUtils::Compare(this->GetNonTerminatedText(), str.GetNonTerminatedText(), minLength)
+ : StringUtils::CompareIgnoreCase(this->GetNonTerminatedText(), str.GetNonTerminatedText(), minLength);
+ if (prefixCompare != 0)
+ return prefixCompare;
+ if (Length() == str.Length())
+ return 0;
+ return thisIsShorter ? -1 : 1;
}
public:
@@ -95,7 +100,7 @@ public:
}
///
- /// Gets the pointer to the string.
+ /// Gets the pointer to the string. Pointer can be null, and won't be null-terminated.
///
FORCE_INLINE constexpr const T* operator*() const
{
@@ -103,7 +108,7 @@ public:
}
///
- /// Gets the pointer to the string.
+ /// Gets the pointer to the string. Pointer can be null, and won't be null-terminated.
///
FORCE_INLINE constexpr const T* Get() const
{
@@ -111,9 +116,9 @@ public:
}
///
- /// Gets the pointer to the string or to the static empty text if string is null. Returned pointer is always valid (read-only).
+ /// Gets the pointer to the string or to the static empty text if string is null. Returned pointer is always non-null, but is not null-terminated.
///
- FORCE_INLINE const T* GetText() const
+ FORCE_INLINE const T* GetNonTerminatedText() const
{
return _data ? _data : (const T*)TEXT("");
}
@@ -177,15 +182,17 @@ public:
{
if (prefix.IsEmpty() || Length() < prefix.Length())
return false;
+ // We know that this StringView is not empty, and therefore Get() below is valid.
if (searchCase == StringSearchCase::IgnoreCase)
- return StringUtils::CompareIgnoreCase(this->GetText(), *prefix, prefix.Length()) == 0;
- return StringUtils::Compare(this->GetText(), *prefix, prefix.Length()) == 0;
+ return StringUtils::CompareIgnoreCase(this->Get(), *prefix, prefix.Length()) == 0;
+ return StringUtils::Compare(this->Get(), *prefix, prefix.Length()) == 0;
}
bool EndsWith(const StringViewBase& suffix, StringSearchCase searchCase = StringSearchCase::IgnoreCase) const
{
if (suffix.IsEmpty() || Length() < suffix.Length())
return false;
+ // We know that this StringView is not empty, and therefore accessing data below is valid.
if (searchCase == StringSearchCase::IgnoreCase)
return StringUtils::CompareIgnoreCase(&(*this)[Length() - suffix.Length()], *suffix) == 0;
return StringUtils::Compare(&(*this)[Length() - suffix.Length()], *suffix) == 0;
@@ -232,7 +239,7 @@ public:
///
/// Initializes a new instance of the class.
///
- /// The characters sequence.
+ /// The characters sequence. If null, constructed StringView will be empty.
StringView(const Char* str)
{
_data = str;
@@ -242,7 +249,7 @@ public:
///
/// Initializes a new instance of the class.
///
- /// The characters sequence.
+ /// The characters sequence. Can be null if length is zero.
/// The characters sequence length (excluding null-terminator character).
constexpr StringView(const Char* str, int32 length)
: StringViewBase(str, length)
@@ -270,7 +277,7 @@ public:
/// True if this string is lexicographically equivalent to the other, otherwise false.
FORCE_INLINE bool operator==(const StringView& other) const
{
- return StringUtils::Compare(this->GetText(), other.GetText()) == 0;
+ return this->Compare(other) == 0;
}
///
@@ -280,7 +287,7 @@ public:
/// True if this string is lexicographically is not equivalent to the other, otherwise false.
FORCE_INLINE bool operator!=(const StringView& other) const
{
- return StringUtils::Compare(this->GetText(), other.GetText()) != 0;
+ return this->Compare(other) != 0;
}
///
@@ -290,7 +297,7 @@ public:
/// True if this string is lexicographically equivalent to the other, otherwise false.
FORCE_INLINE bool operator==(const Char* other) const
{
- return StringUtils::Compare(this->GetText(), other ? other : TEXT("")) == 0;
+ return this->Compare(StringView(other)) == 0;
}
///
@@ -300,7 +307,7 @@ public:
/// True if this string is lexicographically is not equivalent to the other, otherwise false.
FORCE_INLINE bool operator!=(const Char* other) const
{
- return StringUtils::Compare(this->GetText(), other ? other : TEXT("")) != 0;
+ return this->Compare(StringView(other)) != 0;
}
///
@@ -459,7 +466,7 @@ public:
/// True if this string is lexicographically equivalent to the other, otherwise false.
FORCE_INLINE bool operator==(const StringAnsiView& other) const
{
- return StringUtils::Compare(this->GetText(), other.GetText()) == 0;
+ return this->Compare(other) == 0;
}
///
@@ -469,7 +476,7 @@ public:
/// True if this string is lexicographically is not equivalent to the other, otherwise false.
FORCE_INLINE bool operator!=(const StringAnsiView& other) const
{
- return StringUtils::Compare(this->GetText(), other.GetText()) != 0;
+ return this->Compare(other) != 0;
}
///
@@ -479,7 +486,7 @@ public:
/// True if this string is lexicographically equivalent to the other, otherwise false.
FORCE_INLINE bool operator==(const char* other) const
{
- return StringUtils::Compare(this->GetText(), other ? other : "") == 0;
+ return this->Compare(StringAnsiView(other)) == 0;
}
///
@@ -489,7 +496,7 @@ public:
/// True if this string is lexicographically is not equivalent to the other, otherwise false.
FORCE_INLINE bool operator!=(const char* other) const
{
- return StringUtils::Compare(this->GetText(), other ? other : "") != 0;
+ return this->Compare(StringAnsiView(other)) != 0;
}
///
diff --git a/Source/Engine/Core/Types/Variant.h b/Source/Engine/Core/Types/Variant.h
index 0c8f06f15..df8c4aeea 100644
--- a/Source/Engine/Core/Types/Variant.h
+++ b/Source/Engine/Core/Types/Variant.h
@@ -270,8 +270,8 @@ public:
explicit operator float() const;
explicit operator double() const;
explicit operator void*() const;
- explicit operator StringView() const;
- explicit operator StringAnsiView() const;
+ explicit operator StringView() const; // Returned StringView, if not empty, is guaranteed to point to a null terminated buffer.
+ explicit operator StringAnsiView() const; // Returned StringView, if not empty, is guaranteed to point to a null terminated buffer.
explicit operator ScriptingObject*() const;
explicit operator struct _MonoObject*() const;
explicit operator Asset*() const;
diff --git a/Source/Engine/Engine/CommandLine.cpp b/Source/Engine/Engine/CommandLine.cpp
index 9f5078c64..e7fe07fb5 100644
--- a/Source/Engine/Engine/CommandLine.cpp
+++ b/Source/Engine/Engine/CommandLine.cpp
@@ -150,6 +150,7 @@ bool CommandLine::Parse(const Char* cmdLine)
PARSE_BOOL_SWITCH("-clearcache ", ClearCache);
PARSE_BOOL_SWITCH("-clearcooker ", ClearCookerCache);
PARSE_ARG_SWITCH("-project ", Project);
+ PARSE_BOOL_SWITCH("-new ", NewProject);
PARSE_BOOL_SWITCH("-genprojectfiles ", GenProjectFiles);
PARSE_ARG_SWITCH("-build ", Build);
PARSE_BOOL_SWITCH("-skipcompile ", SkipCompile);
diff --git a/Source/Engine/Engine/CommandLine.h b/Source/Engine/Engine/CommandLine.h
index d9d3547d3..41f985dee 100644
--- a/Source/Engine/Engine/CommandLine.h
+++ b/Source/Engine/Engine/CommandLine.h
@@ -134,6 +134,11 @@ public:
///
String Project;
+ ///
+ /// -new (generates the project files inside the specified project folder or uses current workspace folder)
+ ///
+ Nullable NewProject;
+
///
/// -genprojectfiles (generates the scripts project files)
///
diff --git a/Source/Engine/Graphics/Mesh.cs b/Source/Engine/Graphics/Mesh.cs
index f96b4ce63..6d855788a 100644
--- a/Source/Engine/Graphics/Mesh.cs
+++ b/Source/Engine/Graphics/Mesh.cs
@@ -144,7 +144,7 @@ namespace FlaxEngine
if (colors != null && colors.Length != vertices.Length)
throw new ArgumentOutOfRangeException(nameof(colors));
- if (Internal_UpdateMeshInt(
+ if (Internal_UpdateMeshUInt(
__unmanagedPtr,
vertices.Length,
triangles.Length / 3,
@@ -190,7 +190,100 @@ namespace FlaxEngine
if (colors != null && colors.Count != vertices.Count)
throw new ArgumentOutOfRangeException(nameof(colors));
- if (Internal_UpdateMeshInt(
+ if (Internal_UpdateMeshUInt(
+ __unmanagedPtr,
+ vertices.Count,
+ triangles.Count / 3,
+ Utils.ExtractArrayFromList(vertices),
+ Utils.ExtractArrayFromList(triangles),
+ Utils.ExtractArrayFromList(normals),
+ Utils.ExtractArrayFromList(tangents),
+ Utils.ExtractArrayFromList(uv),
+ Utils.ExtractArrayFromList(colors)
+ ))
+ throw new FlaxException("Failed to update mesh data.");
+ }
+
+ ///
+ /// Updates the model mesh vertex and index buffer data.
+ /// Can be used only for virtual assets (see and ).
+ /// Mesh data will be cached and uploaded to the GPU with a delay.
+ ///
+ /// The mesh vertices positions. Cannot be null.
+ /// The mesh index buffer (clockwise triangles). Uses 32-bit stride buffer. Cannot be null.
+ /// The normal vectors (per vertex).
+ /// The normal vectors (per vertex). Use null to compute them from normal vectors.
+ /// The texture coordinates (per vertex).
+ /// The vertex colors (per vertex).
+ public void UpdateMesh(Vector3[] vertices, uint[] triangles, Vector3[] normals = null, Vector3[] tangents = null, Vector2[] uv = null, Color32[] colors = null)
+ {
+ // Validate state and input
+ if (!ParentModel.IsVirtual)
+ throw new InvalidOperationException("Only virtual models can be updated at runtime.");
+ if (vertices == null)
+ throw new ArgumentNullException(nameof(vertices));
+ if (triangles == null)
+ throw new ArgumentNullException(nameof(triangles));
+ if (triangles.Length == 0 || triangles.Length % 3 != 0)
+ throw new ArgumentOutOfRangeException(nameof(triangles));
+ if (normals != null && normals.Length != vertices.Length)
+ throw new ArgumentOutOfRangeException(nameof(normals));
+ if (tangents != null && tangents.Length != vertices.Length)
+ throw new ArgumentOutOfRangeException(nameof(tangents));
+ if (tangents != null && normals == null)
+ throw new ArgumentException("If you specify tangents then you need to also provide normals for the mesh.");
+ if (uv != null && uv.Length != vertices.Length)
+ throw new ArgumentOutOfRangeException(nameof(uv));
+ if (colors != null && colors.Length != vertices.Length)
+ throw new ArgumentOutOfRangeException(nameof(colors));
+
+ if (Internal_UpdateMeshUInt(
+ __unmanagedPtr,
+ vertices.Length,
+ triangles.Length / 3,
+ vertices, triangles,
+ normals,
+ tangents,
+ uv,
+ colors
+ ))
+ throw new FlaxException("Failed to update mesh data.");
+ }
+
+ ///
+ /// Updates the model mesh vertex and index buffer data.
+ /// Can be used only for virtual assets (see and ).
+ /// Mesh data will be cached and uploaded to the GPU with a delay.
+ ///
+ /// The mesh vertices positions. Cannot be null.
+ /// The mesh index buffer (clockwise triangles). Uses 32-bit stride buffer. Cannot be null.
+ /// The normal vectors (per vertex).
+ /// The normal vectors (per vertex). Use null to compute them from normal vectors.
+ /// The texture coordinates (per vertex).
+ /// The vertex colors (per vertex).
+ public void UpdateMesh(List vertices, List triangles, List normals = null, List tangents = null, List uv = null, List colors = null)
+ {
+ // Validate state and input
+ if (!ParentModel.IsVirtual)
+ throw new InvalidOperationException("Only virtual models can be updated at runtime.");
+ if (vertices == null)
+ throw new ArgumentNullException(nameof(vertices));
+ if (triangles == null)
+ throw new ArgumentNullException(nameof(triangles));
+ if (triangles.Count == 0 || triangles.Count % 3 != 0)
+ throw new ArgumentOutOfRangeException(nameof(triangles));
+ if (normals != null && normals.Count != vertices.Count)
+ throw new ArgumentOutOfRangeException(nameof(normals));
+ if (tangents != null && tangents.Count != vertices.Count)
+ throw new ArgumentOutOfRangeException(nameof(tangents));
+ if (tangents != null && normals == null)
+ throw new ArgumentException("If you specify tangents then you need to also provide normals for the mesh.");
+ if (uv != null && uv.Count != vertices.Count)
+ throw new ArgumentOutOfRangeException(nameof(uv));
+ if (colors != null && colors.Count != vertices.Count)
+ throw new ArgumentOutOfRangeException(nameof(colors));
+
+ if (Internal_UpdateMeshUInt(
__unmanagedPtr,
vertices.Count,
triangles.Count / 3,
@@ -314,7 +407,7 @@ namespace FlaxEngine
if (triangles.Length == 0 || triangles.Length % 3 != 0)
throw new ArgumentOutOfRangeException(nameof(triangles));
- if (Internal_UpdateTrianglesInt(
+ if (Internal_UpdateTrianglesUInt(
__unmanagedPtr,
triangles.Length / 3,
triangles
@@ -338,7 +431,7 @@ namespace FlaxEngine
if (triangles.Count == 0 || triangles.Count % 3 != 0)
throw new ArgumentOutOfRangeException(nameof(triangles));
- if (Internal_UpdateTrianglesInt(
+ if (Internal_UpdateTrianglesUInt(
__unmanagedPtr,
triangles.Count / 3,
Utils.ExtractArrayFromList(triangles)
@@ -499,10 +592,10 @@ namespace FlaxEngine
/// If mesh index buffer format (see ) is then it's faster to call .
/// If set to true the data will be downloaded from the GPU, otherwise it can be loaded from the drive (source asset file) or from memory (if cached). Downloading mesh from GPU requires this call to be made from the other thread than main thread. Virtual assets are always downloaded from GPU memory due to lack of dedicated storage container for the asset data.
/// The gathered data.
- public int[] DownloadIndexBuffer(bool forceGpu = false)
+ public uint[] DownloadIndexBuffer(bool forceGpu = false)
{
var triangles = TriangleCount;
- var result = new int[triangles * 3];
+ var result = new uint[triangles * 3];
if (Internal_DownloadBuffer(__unmanagedPtr, forceGpu, result, (int)InternalBufferType.IB32))
throw new FlaxException("Failed to download mesh data.");
return result;
diff --git a/Source/Engine/Graphics/Models/Mesh.cpp b/Source/Engine/Graphics/Models/Mesh.cpp
index ad64a3f61..3ae9b969f 100644
--- a/Source/Engine/Graphics/Models/Mesh.cpp
+++ b/Source/Engine/Graphics/Models/Mesh.cpp
@@ -608,7 +608,7 @@ ScriptingObject* Mesh::GetParentModel()
return _model;
}
-bool Mesh::UpdateMeshInt(int32 vertexCount, int32 triangleCount, MonoArray* verticesObj, MonoArray* trianglesObj, MonoArray* normalsObj, MonoArray* tangentsObj, MonoArray* uvObj, MonoArray* colorsObj)
+bool Mesh::UpdateMeshUInt(int32 vertexCount, int32 triangleCount, MonoArray* verticesObj, MonoArray* trianglesObj, MonoArray* normalsObj, MonoArray* tangentsObj, MonoArray* uvObj, MonoArray* colorsObj)
{
return ::UpdateMesh(this, (uint32)vertexCount, (uint32)triangleCount, verticesObj, trianglesObj, normalsObj, tangentsObj, uvObj, colorsObj);
}
@@ -618,7 +618,7 @@ bool Mesh::UpdateMeshUShort(int32 vertexCount, int32 triangleCount, MonoArray* v
return ::UpdateMesh(this, (uint32)vertexCount, (uint32)triangleCount, verticesObj, trianglesObj, normalsObj, tangentsObj, uvObj, colorsObj);
}
-bool Mesh::UpdateTrianglesInt(int32 triangleCount, MonoArray* trianglesObj)
+bool Mesh::UpdateTrianglesUInt(int32 triangleCount, MonoArray* trianglesObj)
{
return ::UpdateTriangles(this, triangleCount, trianglesObj);
}
diff --git a/Source/Engine/Graphics/Models/Mesh.h b/Source/Engine/Graphics/Models/Mesh.h
index 4af23d3fe..483f15153 100644
--- a/Source/Engine/Graphics/Models/Mesh.h
+++ b/Source/Engine/Graphics/Models/Mesh.h
@@ -404,9 +404,9 @@ private:
// Internal bindings
API_FUNCTION(NoProxy) ScriptingObject* GetParentModel();
- API_FUNCTION(NoProxy) bool UpdateMeshInt(int32 vertexCount, int32 triangleCount, MonoArray* verticesObj, MonoArray* trianglesObj, MonoArray* normalsObj, MonoArray* tangentsObj, MonoArray* uvObj, MonoArray* colorsObj);
+ API_FUNCTION(NoProxy) bool UpdateMeshUInt(int32 vertexCount, int32 triangleCount, MonoArray* verticesObj, MonoArray* trianglesObj, MonoArray* normalsObj, MonoArray* tangentsObj, MonoArray* uvObj, MonoArray* colorsObj);
API_FUNCTION(NoProxy) bool UpdateMeshUShort(int32 vertexCount, int32 triangleCount, MonoArray* verticesObj, MonoArray* trianglesObj, MonoArray* normalsObj, MonoArray* tangentsObj, MonoArray* uvObj, MonoArray* colorsObj);
- API_FUNCTION(NoProxy) bool UpdateTrianglesInt(int32 triangleCount, MonoArray* trianglesObj);
+ API_FUNCTION(NoProxy) bool UpdateTrianglesUInt(int32 triangleCount, MonoArray* trianglesObj);
API_FUNCTION(NoProxy) bool UpdateTrianglesUShort(int32 triangleCount, MonoArray* trianglesObj);
API_FUNCTION(NoProxy) bool DownloadBuffer(bool forceGpu, MonoArray* resultObj, int32 typeI);
};
diff --git a/Source/Engine/Graphics/Models/SkinnedMesh.cpp b/Source/Engine/Graphics/Models/SkinnedMesh.cpp
index b5c67bff9..e75136cf2 100644
--- a/Source/Engine/Graphics/Models/SkinnedMesh.cpp
+++ b/Source/Engine/Graphics/Models/SkinnedMesh.cpp
@@ -418,9 +418,9 @@ bool UpdateMesh(SkinnedMesh* mesh, MonoArray* verticesObj, MonoArray* trianglesO
return mesh->UpdateMesh(vertexCount, triangleCount, vb.Get(), ib);
}
-bool SkinnedMesh::UpdateMeshInt(MonoArray* verticesObj, MonoArray* trianglesObj, MonoArray* blendIndicesObj, MonoArray* blendWeightsObj, MonoArray* normalsObj, MonoArray* tangentsObj, MonoArray* uvObj)
+bool SkinnedMesh::UpdateMeshUInt(MonoArray* verticesObj, MonoArray* trianglesObj, MonoArray* blendIndicesObj, MonoArray* blendWeightsObj, MonoArray* normalsObj, MonoArray* tangentsObj, MonoArray* uvObj)
{
- return ::UpdateMesh(this, verticesObj, trianglesObj, blendIndicesObj, blendWeightsObj, normalsObj, tangentsObj, uvObj);
+ return ::UpdateMesh(this, verticesObj, trianglesObj, blendIndicesObj, blendWeightsObj, normalsObj, tangentsObj, uvObj);
}
bool SkinnedMesh::UpdateMeshUShort(MonoArray* verticesObj, MonoArray* trianglesObj, MonoArray* blendIndicesObj, MonoArray* blendWeightsObj, MonoArray* normalsObj, MonoArray* tangentsObj, MonoArray* uvObj)
diff --git a/Source/Engine/Graphics/Models/SkinnedMesh.h b/Source/Engine/Graphics/Models/SkinnedMesh.h
index 108f36b33..462286b44 100644
--- a/Source/Engine/Graphics/Models/SkinnedMesh.h
+++ b/Source/Engine/Graphics/Models/SkinnedMesh.h
@@ -117,6 +117,19 @@ public:
return UpdateMesh(vertexCount, triangleCount, vb, ib, false);
}
+ ///
+ /// Updates the model mesh (used by the virtual models created with Init rather than Load).
+ ///
+ /// The amount of vertices in the vertex buffer.
+ /// The amount of triangles in the index buffer.
+ /// The vertex buffer data.
+ /// The index buffer in clockwise order.
+ /// True if failed, otherwise false.
+ FORCE_INLINE bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0SkinnedElementType* vb, uint32* ib)
+ {
+ return UpdateMesh(vertexCount, triangleCount, vb, ib, false);
+ }
+
///
/// Updates the model mesh (used by the virtual models created with Init rather than Load).
///
@@ -245,7 +258,7 @@ private:
// Internal bindings
API_FUNCTION(NoProxy) ScriptingObject* GetParentModel();
- API_FUNCTION(NoProxy) bool UpdateMeshInt(MonoArray* verticesObj, MonoArray* trianglesObj, MonoArray* blendIndicesObj, MonoArray* blendWeightsObj, MonoArray* normalsObj, MonoArray* tangentsObj, MonoArray* uvObj);
+ API_FUNCTION(NoProxy) bool UpdateMeshUInt(MonoArray* verticesObj, MonoArray* trianglesObj, MonoArray* blendIndicesObj, MonoArray* blendWeightsObj, MonoArray* normalsObj, MonoArray* tangentsObj, MonoArray* uvObj);
API_FUNCTION(NoProxy) bool UpdateMeshUShort(MonoArray* verticesObj, MonoArray* trianglesObj, MonoArray* blendIndicesObj, MonoArray* blendWeightsObj, MonoArray* normalsObj, MonoArray* tangentsObj, MonoArray* uvObj);
API_FUNCTION(NoProxy) bool DownloadBuffer(bool forceGpu, MonoArray* resultObj, int32 typeI);
};
diff --git a/Source/Engine/Graphics/SkinnedMesh.cs b/Source/Engine/Graphics/SkinnedMesh.cs
index ea4cf283e..ef5e5dcd2 100644
--- a/Source/Engine/Graphics/SkinnedMesh.cs
+++ b/Source/Engine/Graphics/SkinnedMesh.cs
@@ -130,7 +130,43 @@ namespace FlaxEngine
if (uv != null && uv.Length != vertices.Length)
throw new ArgumentOutOfRangeException(nameof(uv));
- if (Internal_UpdateMeshInt(__unmanagedPtr, vertices, triangles, blendIndices, blendWeights, normals, tangents, uv))
+ if (Internal_UpdateMeshUInt(__unmanagedPtr, vertices, triangles, blendIndices, blendWeights, normals, tangents, uv))
+ throw new FlaxException("Failed to update mesh data.");
+ }
+
+ ///
+ /// Updates the skinned model mesh vertex and index buffer data.
+ /// Can be used only for virtual assets (see and ).
+ /// Mesh data will be cached and uploaded to the GPU with a delay.
+ ///
+ /// The mesh vertices positions. Cannot be null.
+ /// The mesh index buffer (clockwise triangles). Uses 32-bit stride buffer. Cannot be null.
+ /// The skinned mesh blend indices buffer. Contains indices of the skeleton bones (up to 4 bones per vertex) to use for vertex position blending. Cannot be null.
+ /// The skinned mesh blend weights buffer (normalized). Contains weights per blend bone (up to 4 bones per vertex) of the skeleton bones to mix for vertex position blending. Cannot be null.
+ /// The normal vectors (per vertex).
+ /// The normal vectors (per vertex). Use null to compute them from normal vectors.
+ /// The texture coordinates (per vertex).
+ public void UpdateMesh(Vector3[] vertices, uint[] triangles, Int4[] blendIndices, Vector4[] blendWeights, Vector3[] normals = null, Vector3[] tangents = null, Vector2[] uv = null)
+ {
+ // Validate state and input
+ if (!ParentSkinnedModel.IsVirtual)
+ throw new InvalidOperationException("Only virtual skinned models can be updated at runtime.");
+ if (vertices == null)
+ throw new ArgumentNullException(nameof(vertices));
+ if (triangles == null)
+ throw new ArgumentNullException(nameof(triangles));
+ if (triangles.Length == 0 || triangles.Length % 3 != 0)
+ throw new ArgumentOutOfRangeException(nameof(triangles));
+ if (normals != null && normals.Length != vertices.Length)
+ throw new ArgumentOutOfRangeException(nameof(normals));
+ if (tangents != null && tangents.Length != vertices.Length)
+ throw new ArgumentOutOfRangeException(nameof(tangents));
+ if (tangents != null && normals == null)
+ throw new ArgumentException("If you specify tangents then you need to also provide normals for the mesh.");
+ if (uv != null && uv.Length != vertices.Length)
+ throw new ArgumentOutOfRangeException(nameof(uv));
+
+ if (Internal_UpdateMeshUInt(__unmanagedPtr, vertices, triangles, blendIndices, blendWeights, normals, tangents, uv))
throw new FlaxException("Failed to update mesh data.");
}
@@ -227,10 +263,10 @@ namespace FlaxEngine
/// If mesh index buffer format (see ) is then it's faster to call .
/// If set to true the data will be downloaded from the GPU, otherwise it can be loaded from the drive (source asset file) or from memory (if cached). Downloading mesh from GPU requires this call to be made from the other thread than main thread. Virtual assets are always downloaded from GPU memory due to lack of dedicated storage container for the asset data.
/// The gathered data.
- public int[] DownloadIndexBuffer(bool forceGpu = false)
+ public uint[] DownloadIndexBuffer(bool forceGpu = false)
{
var triangles = TriangleCount;
- var result = new int[triangles * 3];
+ var result = new uint[triangles * 3];
if (Internal_DownloadBuffer(__unmanagedPtr, forceGpu, result, (int)InternalBufferType.IB32))
throw new FlaxException("Failed to download mesh data.");
return result;
diff --git a/Source/Engine/Input/Input.cpp b/Source/Engine/Input/Input.cpp
index 44131504d..788ea6f2e 100644
--- a/Source/Engine/Input/Input.cpp
+++ b/Source/Engine/Input/Input.cpp
@@ -73,6 +73,7 @@ public:
}
void Update() override;
+ void Dispose() override;
};
InputService InputServiceInstance;
@@ -914,3 +915,24 @@ void InputService::Update()
}
}
}
+
+void InputService::Dispose()
+{
+ // Dispose input devices
+ if (Input::Mouse)
+ {
+ Input::Mouse->DeleteObject();
+ Input::Mouse = nullptr;
+ }
+ if (Input::Keyboard)
+ {
+ Input::Keyboard->DeleteObject();
+ Input::Keyboard = nullptr;
+ }
+ for (int32 i = 0; i < Input::Gamepads.Count(); i++)
+ Input::Gamepads[i]->DeleteObject();
+ Input::Gamepads.Clear();
+ for (int32 i = 0; i < Input::CustomDevices.Count(); i++)
+ Input::CustomDevices[i]->DeleteObject();
+ Input::CustomDevices.Clear();
+}
diff --git a/Source/Engine/Level/Actor.cpp b/Source/Engine/Level/Actor.cpp
index 076ac4608..1bbae93fa 100644
--- a/Source/Engine/Level/Actor.cpp
+++ b/Source/Engine/Level/Actor.cpp
@@ -1424,10 +1424,24 @@ Actor* Actor::Intersects(const Ray& ray, float& distance, Vector3& normal)
}
void Actor::LookAt(const Vector3& worldPos)
+{
+ const Quaternion orientation = LookingAt(worldPos);
+
+ SetOrientation(orientation);
+}
+
+void Actor::LookAt(const Vector3& worldPos, const Vector3& worldUp)
+{
+ const Quaternion orientation = LookingAt(worldPos, worldUp);
+
+ SetOrientation(orientation);
+}
+
+Quaternion Actor::LookingAt(const Vector3& worldPos)
{
const Vector3 direction = worldPos - _transform.Translation;
if (direction.LengthSquared() < ZeroTolerance)
- return;
+ return _parent->GetOrientation();
const Vector3 newForward = Vector3::Normalize(direction);
const Vector3 oldForward = _transform.Orientation * Vector3::Forward;
@@ -1447,26 +1461,25 @@ void Actor::LookAt(const Vector3& worldPos)
orientation = rotQuat * _transform.Orientation;
}
- SetOrientation(orientation);
+ return orientation;
}
-void Actor::LookAt(const Vector3& worldPos, const Vector3& worldUp)
+Quaternion Actor::LookingAt(const Vector3& worldPos, const Vector3& worldUp)
{
const Vector3 direction = worldPos - _transform.Translation;
if (direction.LengthSquared() < ZeroTolerance)
- return;
+ return _parent->GetOrientation();
const Vector3 forward = Vector3::Normalize(direction);
const Vector3 up = Vector3::Normalize(worldUp);
if (Math::IsOne(Vector3::Dot(forward, up)))
{
- LookAt(worldPos);
- return;
+ return LookingAt(worldPos);
}
Quaternion orientation;
Quaternion::LookRotation(direction, up, orientation);
- SetOrientation(orientation);
+ return orientation;
}
void WriteObjectToBytes(SceneObject* obj, rapidjson_flax::StringBuffer& buffer, MemoryWriteStream& output)
diff --git a/Source/Engine/Level/Actor.h b/Source/Engine/Level/Actor.h
index 890d9f2f1..56930eccf 100644
--- a/Source/Engine/Level/Actor.h
+++ b/Source/Engine/Level/Actor.h
@@ -792,6 +792,19 @@ public:
/// The up direction that Constrains y axis orientation to a plane this vector lies on. This rule might be broken if forward and up direction are nearly parallel.
API_FUNCTION() void LookAt(const Vector3& worldPos, const Vector3& worldUp);
+ ///
+ /// Gets rotation of the actor oriented towards the specified world position.
+ ///
+ /// The world position to orient towards.
+ API_FUNCTION() Quaternion LookingAt(const Vector3& worldPos);
+
+ ///
+ /// Gets rotation of the actor oriented towards the specified world position with upwards direction.
+ ///
+ /// The world position to orient towards.
+ /// The up direction that Constrains y axis orientation to a plane this vector lies on. This rule might be broken if forward and up direction are nearly parallel.
+ API_FUNCTION() Quaternion LookingAt(const Vector3& worldPos, const Vector3& worldUp);
+
public:
///
diff --git a/Source/Engine/Localization/Localization.cpp b/Source/Engine/Localization/Localization.cpp
index defab1c4f..7ac0a09ed 100644
--- a/Source/Engine/Localization/Localization.cpp
+++ b/Source/Engine/Localization/Localization.cpp
@@ -278,18 +278,18 @@ String Localization::GetPluralString(const String& id, int32 n, const String& fa
{
CHECK_RETURN(n >= 1, fallback);
n--;
- StringView result;
+ const String* result = nullptr;
for (auto& e : Instance.LocalizedStringTables)
{
const auto table = e.Get();
const auto messages = table ? table->Entries.TryGet(id) : nullptr;
if (messages && messages->Count() > n)
{
- result = messages->At(n);
+ result = &messages->At(n);
break;
}
}
- if (result.IsEmpty())
- result = fallback;
- return String::Format(result.GetText(), n);
+ if (!result)
+ result = &fallback;
+ return String::Format(result->GetText(), n);
}
diff --git a/Source/Engine/Networking/Drivers/ENetDriver.cpp b/Source/Engine/Networking/Drivers/ENetDriver.cpp
index 8d5b4b7f6..32ddfde01 100644
--- a/Source/Engine/Networking/Drivers/ENetDriver.cpp
+++ b/Source/Engine/Networking/Drivers/ENetDriver.cpp
@@ -153,7 +153,7 @@ void ENetDriver::Disconnect(const NetworkConnection& connection)
void* peer = nullptr;
if(_peerMap.TryGet(connectionId, peer))
{
- enet_peer_disconnect_now((ENetPeer*)_peer, 0);
+ enet_peer_disconnect_now((ENetPeer*)peer, 0);
_peerMap.Remove(connectionId);
}
else
diff --git a/Source/Engine/Physics/CollisionData.cpp b/Source/Engine/Physics/CollisionData.cpp
index 14396bf6a..10dc38e6b 100644
--- a/Source/Engine/Physics/CollisionData.cpp
+++ b/Source/Engine/Physics/CollisionData.cpp
@@ -78,6 +78,21 @@ bool CollisionData::CookCollision(CollisionDataType type, const Span& v
return CookCollision(type, &modelData, convexFlags, convexVertexLimit);
}
+bool CollisionData::CookCollision(CollisionDataType type, const Span& vertices, const Span& triangles, ConvexMeshGenerationFlags convexFlags, int32 convexVertexLimit)
+{
+ CHECK_RETURN(vertices.Length() != 0, true);
+ CHECK_RETURN(triangles.Length() != 0 && triangles.Length() % 3 == 0, true);
+ ModelData modelData;
+ modelData.LODs.Resize(1);
+ auto meshData = New();
+ modelData.LODs[0].Meshes.Add(meshData);
+ meshData->Positions.Set(vertices.Get(), vertices.Length());
+ meshData->Indices.Resize(triangles.Length());
+ for (int32 i = 0; i < triangles.Length(); i++)
+ meshData->Indices.Get()[i] = triangles.Get()[i];
+ return CookCollision(type, &modelData, convexFlags, convexVertexLimit);
+}
+
bool CollisionData::CookCollision(CollisionDataType type, ModelData* modelData, ConvexMeshGenerationFlags convexFlags, int32 convexVertexLimit)
{
// Validate state
diff --git a/Source/Engine/Physics/CollisionData.h b/Source/Engine/Physics/CollisionData.h
index 9436ac80d..19c1efa03 100644
--- a/Source/Engine/Physics/CollisionData.h
+++ b/Source/Engine/Physics/CollisionData.h
@@ -223,6 +223,19 @@ public:
/// The convex mesh vertex limit. Use values in range [8;255]
API_FUNCTION() bool CookCollision(CollisionDataType type, const Span& vertices, const Span& triangles, ConvexMeshGenerationFlags convexFlags = ConvexMeshGenerationFlags::None, int32 convexVertexLimit = 255);
+ ///
+ /// Cooks the mesh collision data and updates the virtual asset. action cannot be performed on a main thread.
+ ///
+ ///
+ /// Can be used only for virtual assets (see and ).
+ ///
+ /// The collision data type.
+ /// The source geometry vertex buffer with vertices positions. Cannot be empty.
+ /// The source data index buffer (triangles list). Uses 32-bit stride buffer. Cannot be empty. Length must be multiple of 3 (as 3 vertices build a triangle).
+ /// The convex mesh generation flags.
+ /// The convex mesh vertex limit. Use values in range [8;255]
+ API_FUNCTION() bool CookCollision(CollisionDataType type, const Span& vertices, const Span& triangles, ConvexMeshGenerationFlags convexFlags = ConvexMeshGenerationFlags::None, int32 convexVertexLimit = 255);
+
///
/// Cooks the mesh collision data and updates the virtual asset. action cannot be performed on a main thread.
///
diff --git a/Source/Engine/Platform/Base/PlatformBase.cpp b/Source/Engine/Platform/Base/PlatformBase.cpp
index 4459b4f37..e1383c152 100644
--- a/Source/Engine/Platform/Base/PlatformBase.cpp
+++ b/Source/Engine/Platform/Base/PlatformBase.cpp
@@ -167,7 +167,7 @@ void PlatformBase::Exit()
#if COMPILE_WITH_PROFILER
-#define TRACY_ENABLE_MEMORY (TRACY_ENABLE && !USE_EDITOR)
+#define TRACY_ENABLE_MEMORY (TRACY_ENABLE)
void PlatformBase::OnMemoryAlloc(void* ptr, uint64 size)
{
diff --git a/Source/Engine/Platform/Base/PlatformBase.h b/Source/Engine/Platform/Base/PlatformBase.h
index b6d5d67bd..ac2150c58 100644
--- a/Source/Engine/Platform/Base/PlatformBase.h
+++ b/Source/Engine/Platform/Base/PlatformBase.h
@@ -169,8 +169,8 @@ public:
///
/// Copy memory region
///
- /// Destination memory address
- /// Source memory address
+ /// Destination memory address. Must not be null, even if size is zero.
+ /// Source memory address. Must not be null, even if size is zero.
/// Size of the memory to copy in bytes
FORCE_INLINE static void MemoryCopy(void* dst, const void* src, uint64 size)
{
@@ -180,7 +180,7 @@ public:
///
/// Set memory region with given value
///
- /// Destination memory address
+ /// Destination memory address. Must not be null, even if size is zero.
/// Size of the memory to set in bytes
/// Value to set
FORCE_INLINE static void MemorySet(void* dst, uint64 size, int32 value)
@@ -191,7 +191,7 @@ public:
///
/// Clear memory region with zeros
///
- /// Destination memory address
+ /// Destination memory address. Must not be null, even if size is zero.
/// Size of the memory to clear in bytes
FORCE_INLINE static void MemoryClear(void* dst, uint64 size)
{
@@ -201,8 +201,8 @@ public:
///
/// Compare two blocks of the memory.
///
- /// The first buffer address.
- /// The second buffer address.
+ /// The first buffer address. Must not be null, even if size is zero.
+ /// The second buffer address. Must not be null, even if size is zero.
/// Size of the memory to compare in bytes.
FORCE_INLINE static int32 MemoryCompare(const void* buf1, const void* buf2, uint64 size)
{
diff --git a/Source/Engine/Platform/Linux/LinuxPlatform.cpp b/Source/Engine/Platform/Linux/LinuxPlatform.cpp
index f0339225a..49c4e3aaa 100644
--- a/Source/Engine/Platform/Linux/LinuxPlatform.cpp
+++ b/Source/Engine/Platform/Linux/LinuxPlatform.cpp
@@ -1659,7 +1659,7 @@ void LinuxClipboard::SetText(const StringView& text)
return;
X11::Window window = (X11::Window)mainWindow->GetNativePtr();
- Impl::ClipboardText.Set(text.GetText(), text.Length());
+ Impl::ClipboardText.Set(text.Get(), text.Length());
X11::XSetSelectionOwner(xDisplay, xAtomClipboard, window, CurrentTime); // CLIPBOARD
X11::XSetSelectionOwner(xDisplay, (X11::Atom)1, window, CurrentTime); // XA_PRIMARY
}
diff --git a/Source/Engine/Platform/StringUtils.h b/Source/Engine/Platform/StringUtils.h
index 101049b8e..4905ec646 100644
--- a/Source/Engine/Platform/StringUtils.h
+++ b/Source/Engine/Platform/StringUtils.h
@@ -120,36 +120,36 @@ public:
public:
- // Compare two strings with case sensitive
+ // Compare two strings with case sensitive. Strings must not be null.
static int32 Compare(const Char* str1, const Char* str2);
- // Compare two strings without case sensitive
+ // Compare two strings without case sensitive. Strings must not be null.
static int32 Compare(const Char* str1, const Char* str2, int32 maxCount);
- // Compare two strings without case sensitive
+ // Compare two strings without case sensitive. Strings must not be null.
static int32 CompareIgnoreCase(const Char* str1, const Char* str2);
- // Compare two strings without case sensitive
+ // Compare two strings without case sensitive. Strings must not be null.
static int32 CompareIgnoreCase(const Char* str1, const Char* str2, int32 maxCount);
- // Compare two strings with case sensitive
+ // Compare two strings with case sensitive. Strings must not be null.
static int32 Compare(const char* str1, const char* str2);
- // Compare two strings without case sensitive
+ // Compare two strings without case sensitive. Strings must not be null.
static int32 Compare(const char* str1, const char* str2, int32 maxCount);
- // Compare two strings without case sensitive
+ // Compare two strings without case sensitive. Strings must not be null.
static int32 CompareIgnoreCase(const char* str1, const char* str2);
- // Compare two strings without case sensitive
+ // Compare two strings without case sensitive. Strings must not be null.
static int32 CompareIgnoreCase(const char* str1, const char* str2, int32 maxCount);
public:
- // Get string length
+ // Get string length. Returns 0 if str is null.
static int32 Length(const Char* str);
- // Get string length
+ // Get string length. Returns 0 if str is null.
static int32 Length(const char* str);
// Copy string
diff --git a/Source/Engine/Platform/UWP/UWPPlatform.cpp b/Source/Engine/Platform/UWP/UWPPlatform.cpp
index b0fdf8f21..aeb799a2c 100644
--- a/Source/Engine/Platform/UWP/UWPPlatform.cpp
+++ b/Source/Engine/Platform/UWP/UWPPlatform.cpp
@@ -35,7 +35,7 @@ void RunUWP()
DialogResult MessageBox::Show(Window* parent, const StringView& text, const StringView& caption, MessageBoxButtons buttons, MessageBoxIcon icon)
{
- return (DialogResult)CUWPPlatform->ShowMessageDialog(parent ? parent->GetImpl() : nullptr, text.GetText(), caption.GetText(), (UWPPlatformImpl::MessageBoxButtons)buttons, (UWPPlatformImpl::MessageBoxIcon)icon);
+ return (DialogResult)CUWPPlatform->ShowMessageDialog(parent ? parent->GetImpl() : nullptr, String(text).GetText(), String(caption).GetText(), (UWPPlatformImpl::MessageBoxButtons)buttons, (UWPPlatformImpl::MessageBoxIcon)icon);
}
bool UWPPlatform::Init()
diff --git a/Source/Engine/Platform/Windows/WindowsClipboard.cpp b/Source/Engine/Platform/Windows/WindowsClipboard.cpp
index 196c1d7b3..056d187c6 100644
--- a/Source/Engine/Platform/Windows/WindowsClipboard.cpp
+++ b/Source/Engine/Platform/Windows/WindowsClipboard.cpp
@@ -23,9 +23,12 @@ void WindowsClipboard::Clear()
void WindowsClipboard::SetText(const StringView& text)
{
- const int32 size = (text.Length() + 1) * sizeof(Char);
- const HGLOBAL hMem = GlobalAlloc(GMEM_MOVEABLE, size);
- Platform::MemoryCopy(GlobalLock(hMem), text.GetText(), size);
+ const int32 sizeWithoutNull = text.Length() * sizeof(Char);
+ const HGLOBAL hMem = GlobalAlloc(GMEM_MOVEABLE, sizeWithoutNull + sizeof(Char));
+
+ Char* pMem = static_cast(GlobalLock(hMem));
+ Platform::MemoryCopy(pMem, text.GetNonTerminatedText(), sizeWithoutNull);
+ Platform::MemorySet(pMem + text.Length(), sizeof(Char), 0);
GlobalUnlock(hMem);
OpenClipboard(nullptr);
diff --git a/Source/Engine/Platform/Windows/WindowsPlatform.cpp b/Source/Engine/Platform/Windows/WindowsPlatform.cpp
index 759713f7c..ccd514c11 100644
--- a/Source/Engine/Platform/Windows/WindowsPlatform.cpp
+++ b/Source/Engine/Platform/Windows/WindowsPlatform.cpp
@@ -435,7 +435,7 @@ DialogResult MessageBox::Show(Window* parent, const StringView& text, const Stri
}
// Show dialog
- int result = MessageBoxW(parent ? static_cast(parent->GetNativePtr()) : nullptr, text.GetText(), caption.GetText(), flags);
+ int result = MessageBoxW(parent ? static_cast(parent->GetNativePtr()) : nullptr, String(text).GetText(), String(caption).GetText(), flags);
// Translate result to dialog result
DialogResult dialogResult;
@@ -948,10 +948,12 @@ int32 WindowsPlatform::StartProcess(const StringView& filename, const StringView
LOG(Info, "Working directory: {0}", workingDir);
}
+ String filenameString(filename);
+
SHELLEXECUTEINFOW shExecInfo = { 0 };
shExecInfo.cbSize = sizeof(SHELLEXECUTEINFOW);
shExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
- shExecInfo.lpFile = filename.GetText();
+ shExecInfo.lpFile = filenameString.GetText();
shExecInfo.lpParameters = args.HasChars() ? args.Get() : nullptr;
shExecInfo.lpDirectory = workingDir.HasChars() ? workingDir.Get() : nullptr;
shExecInfo.nShow = hiddenWindow ? SW_HIDE : SW_SHOW;
@@ -1109,7 +1111,7 @@ int32 WindowsPlatform::RunProcess(const StringView& cmdLine, const StringView& w
// Create the process
PROCESS_INFORMATION procInfo;
- if (!CreateProcessW(nullptr, const_cast(cmdLine.GetText()), nullptr, nullptr, TRUE, dwCreationFlags, (LPVOID)environmentStr, workingDir.HasChars() ? workingDir.Get() : nullptr, &startupInfoEx.StartupInfo, &procInfo))
+ if (!CreateProcessW(nullptr, const_cast(String(cmdLine).GetText()), nullptr, nullptr, TRUE, dwCreationFlags, (LPVOID)environmentStr, workingDir.HasChars() ? workingDir.Get() : nullptr, &startupInfoEx.StartupInfo, &procInfo))
{
LOG(Warning, "Cannot start process '{0}'. Error code: 0x{1:x}", cmdLine, static_cast(GetLastError()));
goto ERROR_EXIT;
diff --git a/Source/Engine/Profiler/ProfilerCPU.h b/Source/Engine/Profiler/ProfilerCPU.h
index 2a48e6c15..7ffdc85eb 100644
--- a/Source/Engine/Profiler/ProfilerCPU.h
+++ b/Source/Engine/Profiler/ProfilerCPU.h
@@ -386,33 +386,31 @@ struct TIsPODType
enum { Value = true };
};
+#include "ProfilerSrcLoc.h"
+
// Shortcut macros for profiling a single code block execution on CPU
// Use ZoneTransient for Tracy for code that can be hot-reloaded (eg. in Editor) or if name can be a variable
+#define PROFILE_CPU_USE_TRANSIENT_DATA 0
-#define PROFILE_CPU_NAMED(name) ZoneTransientN(___tracy_scoped_zone, name, true); ScopeProfileBlockCPU ProfileBlockCPU(name)
-
-#if defined(_MSC_VER)
-
-#if USE_EDITOR
-#define PROFILE_CPU() ZoneTransient(___tracy_scoped_zone, true); ScopeProfileBlockCPU ProfileBlockCPU(TEXT(__FUNCTION__))
-#else
-#define PROFILE_CPU() ZoneNamed(___tracy_scoped_zone, true); ScopeProfileBlockCPU ProfileBlockCPU(TEXT(__FUNCTION__))
-#endif
-
-#else
-
-#if USE_EDITOR
+#if PROFILE_CPU_USE_TRANSIENT_DATA
#define PROFILE_CPU() ZoneTransient(___tracy_scoped_zone, true); ScopeProfileBlockCPU ProfileBlockCPU(__FUNCTION__)
+#define PROFILE_CPU_NAMED(name) ZoneTransientN(___tracy_scoped_zone, name, true); ScopeProfileBlockCPU ProfileBlockCPU(name)
#else
#define PROFILE_CPU() ZoneNamed(___tracy_scoped_zone, true); ScopeProfileBlockCPU ProfileBlockCPU(__FUNCTION__)
+#define PROFILE_CPU_NAMED(name) ZoneNamedN(___tracy_scoped_zone, name, true); ScopeProfileBlockCPU ProfileBlockCPU(name)
#endif
+#ifdef TRACY_ENABLE
+#define PROFILE_CPU_SRC_LOC(srcLoc) tracy::ScopedZone ___tracy_scoped_zone( (tracy::SourceLocationData*)&(srcLoc) ); ScopeProfileBlockCPU ProfileBlockCPU((srcLoc).name)
+#else
+#define PROFILE_CPU_SRC_LOC(srcLoc) ScopeProfileBlockCPU ProfileBlockCPU((srcLoc).name)
#endif
#else
// Empty macros for disabled profiler
-#define PROFILE_CPU_NAMED(name)
#define PROFILE_CPU()
+#define PROFILE_CPU_NAMED(name)
+#define PROFILE_CPU_SRC_LOC(srcLoc)
#endif
diff --git a/Source/Engine/Profiler/ProfilerSrcLoc.h b/Source/Engine/Profiler/ProfilerSrcLoc.h
new file mode 100644
index 000000000..6ee116668
--- /dev/null
+++ b/Source/Engine/Profiler/ProfilerSrcLoc.h
@@ -0,0 +1,18 @@
+// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
+
+#pragma once
+
+#if COMPILE_WITH_PROFILER
+
+#include "Engine/Core/Types/BaseTypes.h"
+
+struct FLAXENGINE_API SourceLocationData
+{
+ const char* name;
+ const char* function;
+ const char* file;
+ uint32 line;
+ uint32 color;
+};
+
+#endif
diff --git a/Source/Engine/Scripting/ManagedCLR/MMethod.Mono.cpp b/Source/Engine/Scripting/ManagedCLR/MMethod.Mono.cpp
index 3fb8a16a4..6abff601d 100644
--- a/Source/Engine/Scripting/ManagedCLR/MMethod.Mono.cpp
+++ b/Source/Engine/Scripting/ManagedCLR/MMethod.Mono.cpp
@@ -60,18 +60,23 @@ MMethod::MMethod(MonoMethod* monoMethod, const char* name, MClass* parentClass)
ProfilerName.Get()[className.Length()] = ':';
ProfilerName.Get()[className.Length() + 1] = ':';
Platform::MemoryCopy(ProfilerName.Get() + className.Length() + 2, _name.Get(), _name.Length());
+ ProfilerData.name = ProfilerName.Get();
+ ProfilerData.function = _name.Get();
+ ProfilerData.file = nullptr;
+ ProfilerData.line = 0;
+ ProfilerData.color = 0;
#endif
}
MonoObject* MMethod::Invoke(void* instance, void** params, MonoObject** exception) const
{
- PROFILE_CPU_NAMED(*ProfilerName);
+ PROFILE_CPU_SRC_LOC(ProfilerData);
return mono_runtime_invoke(_monoMethod, instance, params, exception);
}
MonoObject* MMethod::InvokeVirtual(MonoObject* instance, void** params, MonoObject** exception) const
{
- PROFILE_CPU_NAMED(*ProfilerName);
+ PROFILE_CPU_SRC_LOC(ProfilerData);
MonoMethod* virtualMethod = mono_object_get_virtual_method(instance, _monoMethod);
return mono_runtime_invoke(virtualMethod, instance, params, exception);
}
diff --git a/Source/Engine/Scripting/ManagedCLR/MMethod.h b/Source/Engine/Scripting/ManagedCLR/MMethod.h
index 60701a9b3..46320013f 100644
--- a/Source/Engine/Scripting/ManagedCLR/MMethod.h
+++ b/Source/Engine/Scripting/ManagedCLR/MMethod.h
@@ -3,6 +3,9 @@
#pragma once
#include "Engine/Core/Collections/Array.h"
+#if COMPILE_WITH_PROFILER
+#include "Engine/Profiler/ProfilerSrcLoc.h"
+#endif
#include "MTypes.h"
///
@@ -42,6 +45,7 @@ public:
#if COMPILE_WITH_PROFILER
MString ProfilerName;
+ SourceLocationData ProfilerData;
#endif
#if USE_MONO
diff --git a/Source/Engine/Scripting/Scripting.Internal.cpp b/Source/Engine/Scripting/Scripting.Internal.cpp
index b437c442e..1f26fc8b5 100644
--- a/Source/Engine/Scripting/Scripting.Internal.cpp
+++ b/Source/Engine/Scripting/Scripting.Internal.cpp
@@ -9,13 +9,29 @@
#include "Engine/Scripting/ManagedCLR/MUtils.h"
#include "Engine/Core/ObjectsRemovalService.h"
#include "Engine/Profiler/Profiler.h"
+#if TRACY_ENABLE && !PROFILE_CPU_USE_TRANSIENT_DATA
+#include "Engine/Core/Collections/ChunkedArray.h"
+#endif
+#include "Engine/Core/Types/Pair.h"
#include "Engine/Threading/Threading.h"
#include
namespace ProfilerInternal
{
#if COMPILE_WITH_PROFILER
- Array ManagedEventsGPU;
+ Array> ManagedEventsGPU;
+#if TRACY_ENABLE && !PROFILE_CPU_USE_TRANSIENT_DATA
+ CriticalSection ManagedSourceLocationsLocker;
+
+ struct Location
+ {
+ String Name;
+ StringAnsi NameAnsi;
+ tracy::SourceLocationData SrcLocation;
+ };
+
+ ChunkedArray ManagedSourceLocations;
+#endif
#endif
void BeginEvent(MonoString* nameObj)
@@ -24,7 +40,34 @@ namespace ProfilerInternal
const StringView name((const Char*)mono_string_chars(nameObj), mono_string_length(nameObj));
ProfilerCPU::BeginEvent(*name);
#if TRACY_ENABLE
+#if PROFILE_CPU_USE_TRANSIENT_DATA
tracy::ScopedZone::Begin(__LINE__, __FILE__, strlen( __FILE__ ), __FUNCTION__, strlen( __FUNCTION__ ), name.Get(), name.Length() );
+#else
+ ScopeLock lock(ManagedSourceLocationsLocker);
+ tracy::SourceLocationData* srcLoc = nullptr;
+ for (auto e = ManagedSourceLocations.Begin(); e.IsNotEnd(); ++e)
+ {
+ if (name == e->Name)
+ {
+ srcLoc = &e->SrcLocation;
+ break;
+ }
+ }
+ if (!srcLoc)
+ {
+ auto& e = ManagedSourceLocations.AddOne();
+ e.Name = name;
+ e.NameAnsi = name.Get();
+ srcLoc = &e.SrcLocation;
+ srcLoc->name = e.NameAnsi.Get();
+ srcLoc->function = nullptr;
+ srcLoc->file = nullptr;
+ srcLoc->line = 0;
+ srcLoc->color = 0;
+ }
+ //static constexpr tracy::SourceLocationData tracySrcLoc{ nullptr, __FUNCTION__, __FILE__, (uint32_t)__LINE__, 0 };
+ tracy::ScopedZone::Begin(srcLoc);
+#endif
#endif
#endif
}
diff --git a/Source/Engine/Tests/TestMain.cpp b/Source/Engine/Tests/TestMain.cpp
new file mode 100644
index 000000000..29ad947d7
--- /dev/null
+++ b/Source/Engine/Tests/TestMain.cpp
@@ -0,0 +1,14 @@
+// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
+
+#if PLATFORM_WINDOWS || PLATFORM_LINUX
+
+#define CATCH_CONFIG_RUNNER
+#include
+
+int main(int argc, char* argv[])
+{
+ int result = Catch::Session().run(argc, argv);
+ return result;
+}
+
+#endif
diff --git a/Source/Engine/Tests/TestString.cpp b/Source/Engine/Tests/TestString.cpp
new file mode 100644
index 000000000..69028ddb6
--- /dev/null
+++ b/Source/Engine/Tests/TestString.cpp
@@ -0,0 +1,331 @@
+// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
+
+#include "Engine/Core/Types/String.h"
+#include "Engine/Core/Types/StringView.h"
+#include
+
+TEST_CASE("String Replace works") {
+ SECTION("Char, case sensitive") {
+ String str("hello HELLO");
+ CHECK(str.Replace('l', 'x', StringSearchCase::CaseSensitive) == 2);
+ CHECK(str == String("hexxo HELLO"));
+ }
+
+ SECTION("Char, ignore case") {
+ String str("hello HELLO");
+ CHECK(str.Replace('l', 'x', StringSearchCase::IgnoreCase) == 4);
+ CHECK(str == String("hexxo HExxO"));
+ }
+
+ SECTION("case sensitive") {
+ String str("hello HELLO this is me saying hello");
+ CHECK(str.Replace(TEXT("hello"), TEXT("hi"), StringSearchCase::CaseSensitive) == 2);
+ CHECK(str == String("hi HELLO this is me saying hi"));
+ }
+
+ SECTION("ignore case") {
+ String str("hello HELLO this is me saying hello");
+ CHECK(str.Replace(TEXT("hello"), TEXT("hi"), StringSearchCase::IgnoreCase) == 3);
+ CHECK(str == String("hi hi this is me saying hi"));
+ }
+
+ SECTION("case sensitive, search and replace texts identical") {
+ String str("hello HELLO this is me saying hello");
+ CHECK(str.Replace(TEXT("hello"), TEXT("hello"), StringSearchCase::CaseSensitive) == 2);
+ CHECK(str == String("hello HELLO this is me saying hello"));
+ }
+
+ SECTION("ignore case, search and replace texts identical") {
+ String str("hello HELLO this is me saying hello");
+ CHECK(str.Replace(TEXT("hello"), TEXT("hello"), StringSearchCase::IgnoreCase) == 3);
+ CHECK(str == String("hello hello this is me saying hello"));
+ }
+
+ SECTION("case sensitive, replace text empty") {
+ String str("hello HELLO this is me saying hello");
+ CHECK(str.Replace(TEXT("hello"), TEXT(""), StringSearchCase::CaseSensitive) == 2);
+ CHECK(str == String(" HELLO this is me saying "));
+ }
+
+ SECTION("ignore case, replace text empty") {
+ String str("hello HELLO this is me saying hello");
+ CHECK(str.Replace(TEXT("hello"), TEXT(""), StringSearchCase::IgnoreCase) == 3);
+ CHECK(str == String(" this is me saying "));
+ }
+
+ SECTION("no finds") {
+ String str("hello HELLO this is me saying hello");
+ CHECK(str.Replace(TEXT("bye"), TEXT("hi"), StringSearchCase::CaseSensitive) == 0);
+ CHECK(str.Replace(TEXT("bye"), TEXT("hi"), StringSearchCase::IgnoreCase) == 0);
+ CHECK(str == String("hello HELLO this is me saying hello"));
+ }
+
+ SECTION("empty input") {
+ String str("");
+ CHECK(str.Replace(TEXT("bye"), TEXT("hi"), StringSearchCase::CaseSensitive) == 0);
+ CHECK(str.Replace(TEXT("bye"), TEXT("hi"), StringSearchCase::IgnoreCase) == 0);
+ CHECK(str == String(""));
+ }
+}
+
+TEST_CASE("String Starts/EndsWith works") {
+ SECTION("StartsWith, case sensitive") {
+ SECTION("Char") {
+ CHECK(String("").StartsWith('h', StringSearchCase::CaseSensitive) == false);
+ CHECK(String("hello HELLO").StartsWith('h', StringSearchCase::CaseSensitive) == true);
+ CHECK(String("hello HELLO").StartsWith('H', StringSearchCase::CaseSensitive) == false);
+ }
+
+ SECTION("String") {
+ CHECK(String("hello HELLO").StartsWith(String(TEXT("hello")), StringSearchCase::CaseSensitive) == true);
+ CHECK(String("hello HELLO").StartsWith(String(TEXT("HELLO")), StringSearchCase::CaseSensitive) == false);
+ CHECK(String("hello HELLO").StartsWith(String(TEXT("")), StringSearchCase::CaseSensitive) == true);
+ CHECK(String("hello HELLO").StartsWith(String(TEXT("xxx")), StringSearchCase::CaseSensitive) == false);
+
+ CHECK(String("").StartsWith(String(TEXT("x")), StringSearchCase::CaseSensitive) == false);
+ CHECK(String("hello HELLO").StartsWith(String(TEXT("hello HELLOx")), StringSearchCase::CaseSensitive) == false);
+ CHECK(String("hello HELLO").StartsWith(String(TEXT("xhello HELLO")), StringSearchCase::CaseSensitive) == false);
+ }
+
+ SECTION("StringView") {
+ CHECK(String("hello HELLO").StartsWith(StringView(TEXT("hello")), StringSearchCase::CaseSensitive) == true);
+ CHECK(String("hello HELLO").StartsWith(StringView(TEXT("HELLO")), StringSearchCase::CaseSensitive) == false);
+ CHECK(String("hello HELLO").StartsWith(StringView(), StringSearchCase::CaseSensitive) == true);
+ CHECK(String("hello HELLO").StartsWith(StringView(TEXT("")), StringSearchCase::CaseSensitive) == true);
+ CHECK(String("hello HELLO").StartsWith(StringView(TEXT("xxx")), StringSearchCase::CaseSensitive) == false);
+
+ CHECK(String("").StartsWith(StringView(TEXT("x")), StringSearchCase::CaseSensitive) == false);
+ CHECK(String("hello HELLO").StartsWith(StringView(TEXT("hello HELLOx")), StringSearchCase::CaseSensitive) == false);
+ CHECK(String("hello HELLO").StartsWith(StringView(TEXT("xhello HELLO")), StringSearchCase::CaseSensitive) == false);
+ }
+ }
+
+ SECTION("StartsWith, ignore case") {
+ SECTION("Char") {
+ CHECK(String("").StartsWith('h', StringSearchCase::IgnoreCase) == false);
+ CHECK(String("hello HELLO").StartsWith('h', StringSearchCase::IgnoreCase) == true);
+ CHECK(String("hello HELLO").StartsWith('H', StringSearchCase::IgnoreCase) == true);
+ }
+
+ SECTION("String") {
+ CHECK(String("hello HELLO").StartsWith(String(TEXT("hello")), StringSearchCase::IgnoreCase) == true);
+ CHECK(String("hello HELLO").StartsWith(String(TEXT("HELLO")), StringSearchCase::IgnoreCase) == true);
+ CHECK(String("hello HELLO").StartsWith(String(TEXT("")), StringSearchCase::IgnoreCase) == true);
+ CHECK(String("hello HELLO").StartsWith(String(TEXT("xxx")), StringSearchCase::IgnoreCase) == false);
+
+ CHECK(String("").StartsWith(String(TEXT("x")), StringSearchCase::IgnoreCase) == false);
+ CHECK(String("hello HELLO").StartsWith(String(TEXT("hello HELLOx")), StringSearchCase::IgnoreCase) == false);
+ CHECK(String("hello HELLO").StartsWith(String(TEXT("xhello HELLO")), StringSearchCase::IgnoreCase) == false);
+ }
+
+ SECTION("StringView") {
+ CHECK(String("hello HELLO").StartsWith(StringView(TEXT("hello")), StringSearchCase::IgnoreCase) == true);
+ CHECK(String("hello HELLO").StartsWith(StringView(TEXT("HELLO")), StringSearchCase::IgnoreCase) == true);
+ CHECK(String("hello HELLO").StartsWith(StringView(), StringSearchCase::IgnoreCase) == true);
+ CHECK(String("hello HELLO").StartsWith(StringView(TEXT("")), StringSearchCase::IgnoreCase) == true);
+ CHECK(String("hello HELLO").StartsWith(StringView(TEXT("xxx")), StringSearchCase::IgnoreCase) == false);
+
+ CHECK(String("").StartsWith(StringView(TEXT("x")), StringSearchCase::IgnoreCase) == false);
+ CHECK(String("hello HELLO").StartsWith(StringView(TEXT("hello HELLOx")), StringSearchCase::IgnoreCase) == false);
+ CHECK(String("hello HELLO").StartsWith(StringView(TEXT("xhello HELLO")), StringSearchCase::IgnoreCase) == false);
+ }
+ }
+
+ SECTION("EndsWith, case sensitive") {
+ SECTION("Char") {
+ CHECK(String("").EndsWith('h', StringSearchCase::CaseSensitive) == false);
+ CHECK(String("hello HELLO").EndsWith('O', StringSearchCase::CaseSensitive) == true);
+ CHECK(String("hello HELLO").EndsWith('o', StringSearchCase::CaseSensitive) == false);
+ }
+
+ SECTION("String") {
+ CHECK(String("hello HELLO").EndsWith(String(TEXT("HELLO")), StringSearchCase::CaseSensitive) == true);
+ CHECK(String("hello HELLO").EndsWith(String(TEXT("hello")), StringSearchCase::CaseSensitive) == false);
+ CHECK(String("hello HELLO").EndsWith(String(TEXT("")), StringSearchCase::CaseSensitive) == true);
+ CHECK(String("hello HELLO").EndsWith(String(TEXT("xxx")), StringSearchCase::CaseSensitive) == false);
+
+ CHECK(String("").EndsWith(String(TEXT("x")), StringSearchCase::CaseSensitive) == false);
+ CHECK(String("hello HELLO").EndsWith(String(TEXT("hello HELLOx")), StringSearchCase::CaseSensitive) == false);
+ CHECK(String("hello HELLO").EndsWith(String(TEXT("xhello HELLO")), StringSearchCase::CaseSensitive) == false);
+ }
+
+ SECTION("StringView") {
+ CHECK(String("hello HELLO").EndsWith(StringView(TEXT("HELLO")), StringSearchCase::CaseSensitive) == true);
+ CHECK(String("hello HELLO").EndsWith(StringView(TEXT("hello")), StringSearchCase::CaseSensitive) == false);
+ CHECK(String("hello HELLO").EndsWith(StringView(), StringSearchCase::CaseSensitive) == true);
+ CHECK(String("hello HELLO").EndsWith(StringView(TEXT("")), StringSearchCase::CaseSensitive) == true);
+ CHECK(String("hello HELLO").EndsWith(StringView(TEXT("xxx")), StringSearchCase::CaseSensitive) == false);
+
+ CHECK(String("").EndsWith(StringView(TEXT("x")), StringSearchCase::CaseSensitive) == false);
+ CHECK(String("hello HELLO").EndsWith(StringView(TEXT("hello HELLOx")), StringSearchCase::CaseSensitive) == false);
+ CHECK(String("hello HELLO").EndsWith(StringView(TEXT("xhello HELLO")), StringSearchCase::CaseSensitive) == false);
+ }
+ }
+
+ SECTION("EndsWith, ignore case") {
+ SECTION("Char") {
+ CHECK(String("").EndsWith('h', StringSearchCase::IgnoreCase) == false);
+ CHECK(String("hello HELLO").EndsWith('O', StringSearchCase::IgnoreCase) == true);
+ CHECK(String("hello HELLO").EndsWith('o', StringSearchCase::IgnoreCase) == true);
+ }
+
+ SECTION("String") {
+ CHECK(String("hello HELLO").EndsWith(String(TEXT("HELLO")), StringSearchCase::IgnoreCase) == true);
+ CHECK(String("hello HELLO").EndsWith(String(TEXT("hello")), StringSearchCase::IgnoreCase) == true);
+ CHECK(String("hello HELLO").EndsWith(String(TEXT("")), StringSearchCase::IgnoreCase) == true);
+ CHECK(String("hello HELLO").EndsWith(String(TEXT("xxx")), StringSearchCase::IgnoreCase) == false);
+
+ CHECK(String("").EndsWith(String(TEXT("x")), StringSearchCase::IgnoreCase) == false);
+ CHECK(String("hello HELLO").EndsWith(String(TEXT("hello HELLOx")), StringSearchCase::IgnoreCase) == false);
+ CHECK(String("hello HELLO").EndsWith(String(TEXT("xhello HELLO")), StringSearchCase::IgnoreCase) == false);
+ }
+
+ SECTION("StringView") {
+ CHECK(String("hello HELLO").EndsWith(StringView(TEXT("HELLO")), StringSearchCase::IgnoreCase) == true);
+ CHECK(String("hello HELLO").EndsWith(StringView(TEXT("hello")), StringSearchCase::IgnoreCase) == true);
+ CHECK(String("hello HELLO").EndsWith(StringView(), StringSearchCase::IgnoreCase) == true);
+ CHECK(String("hello HELLO").EndsWith(StringView(TEXT("")), StringSearchCase::IgnoreCase) == true);
+ CHECK(String("hello HELLO").EndsWith(StringView(TEXT("xxx")), StringSearchCase::IgnoreCase) == false);
+
+ CHECK(String("").EndsWith(StringView(TEXT("x")), StringSearchCase::IgnoreCase) == false);
+ CHECK(String("hello HELLO").EndsWith(StringView(TEXT("hello HELLOx")), StringSearchCase::IgnoreCase) == false);
+ CHECK(String("hello HELLO").EndsWith(StringView(TEXT("xhello HELLO")), StringSearchCase::IgnoreCase) == false);
+ }
+ }
+}
+
+TEST_CASE("String Compare works") {
+ SECTION("String") {
+ SECTION("case sensitive") {
+ // Empty strings
+ CHECK(String("").Compare(String(TEXT("")), StringSearchCase::CaseSensitive) == 0);
+ CHECK(String("").Compare(String(TEXT("xxx")), StringSearchCase::CaseSensitive) < 0);
+ CHECK(String("xxx").Compare(String(TEXT("")), StringSearchCase::CaseSensitive) > 0);
+
+ // Equal lengths, difference at end
+ CHECK(String("xxx").Compare(String(TEXT("xxx")), StringSearchCase::CaseSensitive) == 0);
+ CHECK(String("abc").Compare(String(TEXT("abd")), StringSearchCase::CaseSensitive) < 0);
+ CHECK(String("abd").Compare(String(TEXT("abc")), StringSearchCase::CaseSensitive) > 0);
+
+ // Equal lengths, difference in the middle
+ CHECK(String("abcx").Compare(String(TEXT("abdx")), StringSearchCase::CaseSensitive) < 0);
+ CHECK(String("abdx").Compare(String(TEXT("abcx")), StringSearchCase::CaseSensitive) > 0);
+
+ // Different lengths, same prefix
+ CHECK(String("abcxx").Compare(String(TEXT("abc")), StringSearchCase::CaseSensitive) > 0);
+ CHECK(String("abc").Compare(String(TEXT("abcxx")), StringSearchCase::CaseSensitive) < 0);
+
+ // Different lengths, different prefix
+ CHECK(String("abcx").Compare(String(TEXT("abd")), StringSearchCase::CaseSensitive) < 0);
+ CHECK(String("abd").Compare(String(TEXT("abcx")), StringSearchCase::CaseSensitive) > 0);
+ CHECK(String("abc").Compare(String(TEXT("abdx")), StringSearchCase::CaseSensitive) < 0);
+ CHECK(String("abdx").Compare(String(TEXT("abc")), StringSearchCase::CaseSensitive) > 0);
+
+ // Case differences
+ CHECK(String("a").Compare(String(TEXT("A")), StringSearchCase::CaseSensitive) > 0);
+ CHECK(String("A").Compare(String(TEXT("a")), StringSearchCase::CaseSensitive) < 0);
+ }
+
+ SECTION("ignore case") {
+ // Empty strings
+ CHECK(String("").Compare(String(TEXT("")), StringSearchCase::IgnoreCase) == 0);
+ CHECK(String("").Compare(String(TEXT("xxx")), StringSearchCase::IgnoreCase) < 0);
+ CHECK(String("xxx").Compare(String(TEXT("")), StringSearchCase::IgnoreCase) > 0);
+
+ // Equal lengths, difference at end
+ CHECK(String("xxx").Compare(String(TEXT("xxx")), StringSearchCase::IgnoreCase) == 0);
+ CHECK(String("abc").Compare(String(TEXT("abd")), StringSearchCase::IgnoreCase) < 0);
+ CHECK(String("abd").Compare(String(TEXT("abc")), StringSearchCase::IgnoreCase) > 0);
+
+ // Equal lengths, difference in the middle
+ CHECK(String("abcx").Compare(String(TEXT("abdx")), StringSearchCase::IgnoreCase) < 0);
+ CHECK(String("abdx").Compare(String(TEXT("abcx")), StringSearchCase::IgnoreCase) > 0);
+
+ // Different lengths, same prefix
+ CHECK(String("abcxx").Compare(String(TEXT("abc")), StringSearchCase::IgnoreCase) > 0);
+ CHECK(String("abc").Compare(String(TEXT("abcxx")), StringSearchCase::IgnoreCase) < 0);
+
+ // Different lengths, different prefix
+ CHECK(String("abcx").Compare(String(TEXT("abd")), StringSearchCase::IgnoreCase) < 0);
+ CHECK(String("abd").Compare(String(TEXT("abcx")), StringSearchCase::IgnoreCase) > 0);
+ CHECK(String("abc").Compare(String(TEXT("abdx")), StringSearchCase::IgnoreCase) < 0);
+ CHECK(String("abdx").Compare(String(TEXT("abc")), StringSearchCase::IgnoreCase) > 0);
+
+ // Case differences
+ CHECK(String("a").Compare(String(TEXT("A")), StringSearchCase::IgnoreCase) == 0);
+ CHECK(String("A").Compare(String(TEXT("a")), StringSearchCase::IgnoreCase) == 0);
+ }
+ }
+
+ SECTION("StringView") {
+ SECTION("case sensitive") {
+ // Null string views
+ CHECK(StringView().Compare(StringView(), StringSearchCase::CaseSensitive) == 0);
+ CHECK(StringView().Compare(StringView(TEXT("xxx")), StringSearchCase::CaseSensitive) < 0);
+ CHECK(StringView(TEXT("xxx")).Compare(StringView(), StringSearchCase::CaseSensitive) > 0);
+
+ // Empty strings
+ CHECK(StringView(TEXT("")).Compare(StringView(TEXT("")), StringSearchCase::CaseSensitive) == 0);
+ CHECK(StringView(TEXT("")).Compare(StringView(TEXT("xxx")), StringSearchCase::CaseSensitive) < 0);
+ CHECK(StringView(TEXT("xxx")).Compare(StringView(TEXT("")), StringSearchCase::CaseSensitive) > 0);
+
+ // Equal lengths, difference at end
+ CHECK(StringView(TEXT("xxx")).Compare(StringView(TEXT("xxx")), StringSearchCase::CaseSensitive) == 0);
+ CHECK(StringView(TEXT("abc")).Compare(StringView(TEXT("abd")), StringSearchCase::CaseSensitive) < 0);
+ CHECK(StringView(TEXT("abd")).Compare(StringView(TEXT("abc")), StringSearchCase::CaseSensitive) > 0);
+
+ // Equal lengths, difference in the middle
+ CHECK(StringView(TEXT("abcx")).Compare(StringView(TEXT("abdx")), StringSearchCase::CaseSensitive) < 0);
+ CHECK(StringView(TEXT("abdx")).Compare(StringView(TEXT("abcx")), StringSearchCase::CaseSensitive) > 0);
+
+ // Different lengths, same prefix
+ CHECK(StringView(TEXT("abcxx")).Compare(StringView(TEXT("abc")), StringSearchCase::CaseSensitive) > 0);
+ CHECK(StringView(TEXT("abc")).Compare(StringView(TEXT("abcxx")), StringSearchCase::CaseSensitive) < 0);
+
+ // Different lengths, different prefix
+ CHECK(StringView(TEXT("abcx")).Compare(StringView(TEXT("abd")), StringSearchCase::CaseSensitive) < 0);
+ CHECK(StringView(TEXT("abd")).Compare(StringView(TEXT("abcx")), StringSearchCase::CaseSensitive) > 0);
+ CHECK(StringView(TEXT("abc")).Compare(StringView(TEXT("abdx")), StringSearchCase::CaseSensitive) < 0);
+ CHECK(StringView(TEXT("abdx")).Compare(StringView(TEXT("abc")), StringSearchCase::CaseSensitive) > 0);
+
+ // Case differences
+ CHECK(StringView(TEXT("a")).Compare(StringView(TEXT("A")), StringSearchCase::CaseSensitive) > 0);
+ CHECK(StringView(TEXT("A")).Compare(StringView(TEXT("a")), StringSearchCase::CaseSensitive) < 0);
+ }
+
+ SECTION("ignore case") {
+ //Null string views
+ CHECK(StringView().Compare(StringView(), StringSearchCase::IgnoreCase) == 0);
+ CHECK(StringView().Compare(StringView(TEXT("xxx")), StringSearchCase::IgnoreCase) < 0);
+ CHECK(StringView(TEXT("xxx")).Compare(StringView(), StringSearchCase::IgnoreCase) > 0);
+
+ // Empty strings
+ CHECK(StringView(TEXT("")).Compare(StringView(TEXT("")), StringSearchCase::IgnoreCase) == 0);
+ CHECK(StringView(TEXT("")).Compare(StringView(TEXT("xxx")), StringSearchCase::IgnoreCase) < 0);
+ CHECK(StringView(TEXT("xxx")).Compare(StringView(TEXT("")), StringSearchCase::IgnoreCase) > 0);
+
+ // Equal lengths, difference at end
+ CHECK(StringView(TEXT("xxx")).Compare(StringView(TEXT("xxx")), StringSearchCase::IgnoreCase) == 0);
+ CHECK(StringView(TEXT("abc")).Compare(StringView(TEXT("abd")), StringSearchCase::IgnoreCase) < 0);
+ CHECK(StringView(TEXT("abd")).Compare(StringView(TEXT("abc")), StringSearchCase::IgnoreCase) > 0);
+
+ // Equal lengths, difference in the middle
+ CHECK(StringView(TEXT("abcx")).Compare(StringView(TEXT("abdx")), StringSearchCase::IgnoreCase) < 0);
+ CHECK(StringView(TEXT("abdx")).Compare(StringView(TEXT("abcx")), StringSearchCase::IgnoreCase) > 0);
+
+ // Different lengths, same prefix
+ CHECK(StringView(TEXT("abcxx")).Compare(StringView(TEXT("abc")), StringSearchCase::IgnoreCase) > 0);
+ CHECK(StringView(TEXT("abc")).Compare(StringView(TEXT("abcxx")), StringSearchCase::IgnoreCase) < 0);
+
+ // Different lengths, different prefix
+ CHECK(StringView(TEXT("abcx")).Compare(StringView(TEXT("abd")), StringSearchCase::IgnoreCase) < 0);
+ CHECK(StringView(TEXT("abd")).Compare(StringView(TEXT("abcx")), StringSearchCase::IgnoreCase) > 0);
+ CHECK(StringView(TEXT("abc")).Compare(StringView(TEXT("abdx")), StringSearchCase::IgnoreCase) < 0);
+ CHECK(StringView(TEXT("abdx")).Compare(StringView(TEXT("abc")), StringSearchCase::IgnoreCase) > 0);
+
+ // Case differences
+ CHECK(StringView(TEXT("a")).Compare(StringView(TEXT("A")), StringSearchCase::IgnoreCase) == 0);
+ CHECK(StringView(TEXT("A")).Compare(StringView(TEXT("a")), StringSearchCase::IgnoreCase) == 0);
+ }
+ }
+}
diff --git a/Source/Engine/Tests/Tests.Build.cs b/Source/Engine/Tests/Tests.Build.cs
new file mode 100644
index 000000000..775388c5d
--- /dev/null
+++ b/Source/Engine/Tests/Tests.Build.cs
@@ -0,0 +1,21 @@
+// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
+
+using System.Collections.Generic;
+using Flax.Build;
+
+///
+/// Engine tests module.
+///
+public class Tests : EngineModule
+{
+ ///
+ public Tests()
+ {
+ Deploy = false;
+ }
+
+ ///
+ public override void GetFilesToDeploy(List files)
+ {
+ }
+}
diff --git a/Source/Engine/Threading/JobSystem.cpp b/Source/Engine/Threading/JobSystem.cpp
index 7b6651952..56d23901c 100644
--- a/Source/Engine/Threading/JobSystem.cpp
+++ b/Source/Engine/Threading/JobSystem.cpp
@@ -134,9 +134,13 @@ void JobSystemService::Dispose()
for (int32 i = 0; i < ThreadsCount; i++)
{
- if (Threads[i] && Threads[i]->IsRunning())
- Threads[i]->Kill(true);
- Threads[i] = nullptr;
+ if (Threads[i])
+ {
+ if (Threads[i]->IsRunning())
+ Threads[i]->Kill(true);
+ Delete(Threads[i]);
+ Threads[i] = nullptr;
+ }
}
}
diff --git a/Source/Engine/Utilities/MeshDataCache.cs b/Source/Engine/Utilities/MeshDataCache.cs
index 0978bdc40..c42b02865 100644
--- a/Source/Engine/Utilities/MeshDataCache.cs
+++ b/Source/Engine/Utilities/MeshDataCache.cs
@@ -20,7 +20,7 @@ namespace FlaxEngine.Utilities
///
/// The index buffer.
///
- public int[] IndexBuffer;
+ public uint[] IndexBuffer;
///
/// The vertex buffer.
diff --git a/Source/FlaxTests.Build.cs b/Source/FlaxTests.Build.cs
new file mode 100644
index 000000000..de951112d
--- /dev/null
+++ b/Source/FlaxTests.Build.cs
@@ -0,0 +1,65 @@
+// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
+
+using System;
+using System.IO;
+using System.Linq;
+using Flax.Build;
+using Flax.Build.NativeCpp;
+
+///
+/// Target that builds standalone, native tests.
+///
+public class FlaxTestsTarget : EngineTarget
+{
+ ///
+ public override void Init()
+ {
+ base.Init();
+
+ // Initialize
+ OutputName = "FlaxTests";
+ ConfigurationName = "Tests";
+ IsPreBuilt = false;
+ UseSymbolsExports = false;
+ Platforms = new[]
+ {
+ TargetPlatform.Windows,
+ TargetPlatform.Linux,
+ };
+ Architectures = new[]
+ {
+ TargetArchitecture.x64,
+ };
+ Configurations = new[]
+ {
+ TargetConfiguration.Development,
+ };
+
+ Modules.Remove("Main");
+ Modules.Add("Tests");
+ }
+
+ ///
+ public override void SetupTargetEnvironment(BuildOptions options)
+ {
+ base.SetupTargetEnvironment(options);
+
+ options.LinkEnv.LinkAsConsoleProgram = true;
+
+ // Setup output folder for Test binaries
+ var platformName = options.Platform.Target.ToString();
+ var architectureName = options.Architecture.ToString();
+ var configurationName = options.Configuration.ToString();
+ options.OutputFolder = Path.Combine(options.WorkingDirectory, "Binaries", "Tests", platformName, architectureName, configurationName);
+ }
+
+ ///
+ public override Target SelectReferencedTarget(ProjectInfo project, Target[] projectTargets)
+ {
+ var testTargetName = "FlaxNativeTests"; // Should this be added to .flaxproj, similarly as "GameTarget" and "EditorTarget"?
+ var result = projectTargets.FirstOrDefault(x => x.Name == testTargetName);
+ if (result == null)
+ throw new Exception(string.Format("Invalid or missing test target {0} specified in project {1} (referenced by project {2}).", testTargetName, project.Name, Project.Name));
+ return result;
+ }
+}
diff --git a/Source/ThirdParty/catch2/LICENSE.txt b/Source/ThirdParty/catch2/LICENSE.txt
new file mode 100644
index 000000000..36b7cd93c
--- /dev/null
+++ b/Source/ThirdParty/catch2/LICENSE.txt
@@ -0,0 +1,23 @@
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/Source/ThirdParty/catch2/catch.hpp b/Source/ThirdParty/catch2/catch.hpp
new file mode 100644
index 000000000..7e706f947
--- /dev/null
+++ b/Source/ThirdParty/catch2/catch.hpp
@@ -0,0 +1,17959 @@
+/*
+ * Catch v2.13.7
+ * Generated: 2021-07-28 20:29:27.753164
+ * ----------------------------------------------------------
+ * This file has been merged from multiple headers. Please don't edit it directly
+ * Copyright (c) 2021 Two Blue Cubes Ltd. All rights reserved.
+ *
+ * Distributed under the Boost Software License, Version 1.0. (See accompanying
+ * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+ */
+#ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
+#define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
+// start catch.hpp
+
+
+#define CATCH_VERSION_MAJOR 2
+#define CATCH_VERSION_MINOR 13
+#define CATCH_VERSION_PATCH 7
+
+#ifdef __clang__
+# pragma clang system_header
+#elif defined __GNUC__
+# pragma GCC system_header
+#endif
+
+// start catch_suppress_warnings.h
+
+#ifdef __clang__
+# ifdef __ICC // icpc defines the __clang__ macro
+# pragma warning(push)
+# pragma warning(disable: 161 1682)
+# else // __ICC
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Wpadded"
+# pragma clang diagnostic ignored "-Wswitch-enum"
+# pragma clang diagnostic ignored "-Wcovered-switch-default"
+# endif
+#elif defined __GNUC__
+ // Because REQUIREs trigger GCC's -Wparentheses, and because still
+ // supported version of g++ have only buggy support for _Pragmas,
+ // Wparentheses have to be suppressed globally.
+# pragma GCC diagnostic ignored "-Wparentheses" // See #674 for details
+
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wunused-variable"
+# pragma GCC diagnostic ignored "-Wpadded"
+#endif
+// end catch_suppress_warnings.h
+#if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER)
+# define CATCH_IMPL
+# define CATCH_CONFIG_ALL_PARTS
+#endif
+
+// In the impl file, we want to have access to all parts of the headers
+// Can also be used to sanely support PCHs
+#if defined(CATCH_CONFIG_ALL_PARTS)
+# define CATCH_CONFIG_EXTERNAL_INTERFACES
+# if defined(CATCH_CONFIG_DISABLE_MATCHERS)
+# undef CATCH_CONFIG_DISABLE_MATCHERS
+# endif
+# if !defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER)
+# define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER
+# endif
+#endif
+
+#if !defined(CATCH_CONFIG_IMPL_ONLY)
+// start catch_platform.h
+
+// See e.g.:
+// https://opensource.apple.com/source/CarbonHeaders/CarbonHeaders-18.1/TargetConditionals.h.auto.html
+#ifdef __APPLE__
+# include
+# if (defined(TARGET_OS_OSX) && TARGET_OS_OSX == 1) || \
+ (defined(TARGET_OS_MAC) && TARGET_OS_MAC == 1)
+# define CATCH_PLATFORM_MAC
+# elif (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1)
+# define CATCH_PLATFORM_IPHONE
+# endif
+
+#elif defined(linux) || defined(__linux) || defined(__linux__)
+# define CATCH_PLATFORM_LINUX
+
+#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) || defined(__MINGW32__)
+# define CATCH_PLATFORM_WINDOWS
+#endif
+
+// end catch_platform.h
+
+#ifdef CATCH_IMPL
+# ifndef CLARA_CONFIG_MAIN
+# define CLARA_CONFIG_MAIN_NOT_DEFINED
+# define CLARA_CONFIG_MAIN
+# endif
+#endif
+
+// start catch_user_interfaces.h
+
+namespace Catch {
+ unsigned int rngSeed();
+}
+
+// end catch_user_interfaces.h
+// start catch_tag_alias_autoregistrar.h
+
+// start catch_common.h
+
+// start catch_compiler_capabilities.h
+
+// Detect a number of compiler features - by compiler
+// The following features are defined:
+//
+// CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported?
+// CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported?
+// CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported?
+// CATCH_CONFIG_DISABLE_EXCEPTIONS : Are exceptions enabled?
+// ****************
+// Note to maintainers: if new toggles are added please document them
+// in configuration.md, too
+// ****************
+
+// In general each macro has a _NO_ form
+// (e.g. CATCH_CONFIG_NO_POSIX_SIGNALS) which disables the feature.
+// Many features, at point of detection, define an _INTERNAL_ macro, so they
+// can be combined, en-mass, with the _NO_ forms later.
+
+#ifdef __cplusplus
+
+# if (__cplusplus >= 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L)
+# define CATCH_CPP14_OR_GREATER
+# endif
+
+# if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
+# define CATCH_CPP17_OR_GREATER
+# endif
+
+#endif
+
+// Only GCC compiler should be used in this block, so other compilers trying to
+// mask themselves as GCC should be ignored.
+#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__) && !defined(__LCC__)
+# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" )
+# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic pop" )
+
+# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__)
+
+#endif
+
+#if defined(__clang__)
+
+# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic push" )
+# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic pop" )
+
+// As of this writing, IBM XL's implementation of __builtin_constant_p has a bug
+// which results in calls to destructors being emitted for each temporary,
+// without a matching initialization. In practice, this can result in something
+// like `std::string::~string` being called on an uninitialized value.
+//
+// For example, this code will likely segfault under IBM XL:
+// ```
+// REQUIRE(std::string("12") + "34" == "1234")
+// ```
+//
+// Therefore, `CATCH_INTERNAL_IGNORE_BUT_WARN` is not implemented.
+# if !defined(__ibmxl__) && !defined(__CUDACC__)
+# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) /* NOLINT(cppcoreguidelines-pro-type-vararg, hicpp-vararg) */
+# endif
+
+# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \
+ _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"")
+
+# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
+ _Pragma( "clang diagnostic ignored \"-Wparentheses\"" )
+
+# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \
+ _Pragma( "clang diagnostic ignored \"-Wunused-variable\"" )
+
+# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
+ _Pragma( "clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"" )
+
+# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
+ _Pragma( "clang diagnostic ignored \"-Wunused-template\"" )
+
+#endif // __clang__
+
+////////////////////////////////////////////////////////////////////////////////
+// Assume that non-Windows platforms support posix signals by default
+#if !defined(CATCH_PLATFORM_WINDOWS)
+ #define CATCH_INTERNAL_CONFIG_POSIX_SIGNALS
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// We know some environments not to support full POSIX signals
+#if defined(__CYGWIN__) || defined(__QNX__) || defined(__EMSCRIPTEN__) || defined(__DJGPP__)
+ #define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS
+#endif
+
+#ifdef __OS400__
+# define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS
+# define CATCH_CONFIG_COLOUR_NONE
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// Android somehow still does not support std::to_string
+#if defined(__ANDROID__)
+# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING
+# define CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// Not all Windows environments support SEH properly
+#if defined(__MINGW32__)
+# define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// PS4
+#if defined(__ORBIS__)
+# define CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// Cygwin
+#ifdef __CYGWIN__
+
+// Required for some versions of Cygwin to declare gettimeofday
+// see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin
+# define _BSD_SOURCE
+// some versions of cygwin (most) do not support std::to_string. Use the libstd check.
+// https://gcc.gnu.org/onlinedocs/gcc-4.8.2/libstdc++/api/a01053_source.html line 2812-2813
+# if !((__cplusplus >= 201103L) && defined(_GLIBCXX_USE_C99) \
+ && !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF))
+
+# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING
+
+# endif
+#endif // __CYGWIN__
+
+////////////////////////////////////////////////////////////////////////////////
+// Visual C++
+#if defined(_MSC_VER)
+
+# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION __pragma( warning(push) )
+# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION __pragma( warning(pop) )
+
+// Universal Windows platform does not support SEH
+// Or console colours (or console at all...)
+# if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP)
+# define CATCH_CONFIG_COLOUR_NONE
+# else
+# define CATCH_INTERNAL_CONFIG_WINDOWS_SEH
+# endif
+
+// MSVC traditional preprocessor needs some workaround for __VA_ARGS__
+// _MSVC_TRADITIONAL == 0 means new conformant preprocessor
+// _MSVC_TRADITIONAL == 1 means old traditional non-conformant preprocessor
+# if !defined(__clang__) // Handle Clang masquerading for msvc
+# if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL)
+# define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+# endif // MSVC_TRADITIONAL
+# endif // __clang__
+
+#endif // _MSC_VER
+
+#if defined(_REENTRANT) || defined(_MSC_VER)
+// Enable async processing, as -pthread is specified or no additional linking is required
+# define CATCH_INTERNAL_CONFIG_USE_ASYNC
+#endif // _MSC_VER
+
+////////////////////////////////////////////////////////////////////////////////
+// Check if we are compiled with -fno-exceptions or equivalent
+#if defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND)
+# define CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// DJGPP
+#ifdef __DJGPP__
+# define CATCH_INTERNAL_CONFIG_NO_WCHAR
+#endif // __DJGPP__
+
+////////////////////////////////////////////////////////////////////////////////
+// Embarcadero C++Build
+#if defined(__BORLANDC__)
+ #define CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+
+// Use of __COUNTER__ is suppressed during code analysis in
+// CLion/AppCode 2017.2.x and former, because __COUNTER__ is not properly
+// handled by it.
+// Otherwise all supported compilers support COUNTER macro,
+// but user still might want to turn it off
+#if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L )
+ #define CATCH_INTERNAL_CONFIG_COUNTER
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+
+// RTX is a special version of Windows that is real time.
+// This means that it is detected as Windows, but does not provide
+// the same set of capabilities as real Windows does.
+#if defined(UNDER_RTSS) || defined(RTX64_BUILD)
+ #define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH
+ #define CATCH_INTERNAL_CONFIG_NO_ASYNC
+ #define CATCH_CONFIG_COLOUR_NONE
+#endif
+
+#if !defined(_GLIBCXX_USE_C99_MATH_TR1)
+#define CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER
+#endif
+
+// Various stdlib support checks that require __has_include
+#if defined(__has_include)
+ // Check if string_view is available and usable
+ #if __has_include() && defined(CATCH_CPP17_OR_GREATER)
+ # define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW
+ #endif
+
+ // Check if optional is available and usable
+ # if __has_include() && defined(CATCH_CPP17_OR_GREATER)
+ # define CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL
+ # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER)
+
+ // Check if byte is available and usable
+ # if __has_include() && defined(CATCH_CPP17_OR_GREATER)
+ # include
+ # if defined(__cpp_lib_byte) && (__cpp_lib_byte > 0)
+ # define CATCH_INTERNAL_CONFIG_CPP17_BYTE
+ # endif
+ # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER)
+
+ // Check if variant is available and usable
+ # if __has_include() && defined(CATCH_CPP17_OR_GREATER)
+ # if defined(__clang__) && (__clang_major__ < 8)
+ // work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852
+ // fix should be in clang 8, workaround in libstdc++ 8.2
+ # include
+ # if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9)
+ # define CATCH_CONFIG_NO_CPP17_VARIANT
+ # else
+ # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT
+ # endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9)
+ # else
+ # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT
+ # endif // defined(__clang__) && (__clang_major__ < 8)
+ # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER)
+#endif // defined(__has_include)
+
+#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER)
+# define CATCH_CONFIG_COUNTER
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) && !defined(CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH)
+# define CATCH_CONFIG_WINDOWS_SEH
+#endif
+// This is set by default, because we assume that unix compilers are posix-signal-compatible by default.
+#if defined(CATCH_INTERNAL_CONFIG_POSIX_SIGNALS) && !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS)
+# define CATCH_CONFIG_POSIX_SIGNALS
+#endif
+// This is set by default, because we assume that compilers with no wchar_t support are just rare exceptions.
+#if !defined(CATCH_INTERNAL_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_WCHAR)
+# define CATCH_CONFIG_WCHAR
+#endif
+
+#if !defined(CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_CPP11_TO_STRING)
+# define CATCH_CONFIG_CPP11_TO_STRING
+#endif
+
+#if defined(CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_NO_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_CPP17_OPTIONAL)
+# define CATCH_CONFIG_CPP17_OPTIONAL
+#endif
+
+#if defined(CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_NO_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_CPP17_STRING_VIEW)
+# define CATCH_CONFIG_CPP17_STRING_VIEW
+#endif
+
+#if defined(CATCH_INTERNAL_CONFIG_CPP17_VARIANT) && !defined(CATCH_CONFIG_NO_CPP17_VARIANT) && !defined(CATCH_CONFIG_CPP17_VARIANT)
+# define CATCH_CONFIG_CPP17_VARIANT
+#endif
+
+#if defined(CATCH_INTERNAL_CONFIG_CPP17_BYTE) && !defined(CATCH_CONFIG_NO_CPP17_BYTE) && !defined(CATCH_CONFIG_CPP17_BYTE)
+# define CATCH_CONFIG_CPP17_BYTE
+#endif
+
+#if defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT)
+# define CATCH_INTERNAL_CONFIG_NEW_CAPTURE
+#endif
+
+#if defined(CATCH_INTERNAL_CONFIG_NEW_CAPTURE) && !defined(CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NEW_CAPTURE)
+# define CATCH_CONFIG_NEW_CAPTURE
+#endif
+
+#if !defined(CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+# define CATCH_CONFIG_DISABLE_EXCEPTIONS
+#endif
+
+#if defined(CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_NO_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_POLYFILL_ISNAN)
+# define CATCH_CONFIG_POLYFILL_ISNAN
+#endif
+
+#if defined(CATCH_INTERNAL_CONFIG_USE_ASYNC) && !defined(CATCH_INTERNAL_CONFIG_NO_ASYNC) && !defined(CATCH_CONFIG_NO_USE_ASYNC) && !defined(CATCH_CONFIG_USE_ASYNC)
+# define CATCH_CONFIG_USE_ASYNC
+#endif
+
+#if defined(CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_NO_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_ANDROID_LOGWRITE)
+# define CATCH_CONFIG_ANDROID_LOGWRITE
+#endif
+
+#if defined(CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_NO_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_GLOBAL_NEXTAFTER)
+# define CATCH_CONFIG_GLOBAL_NEXTAFTER
+#endif
+
+// Even if we do not think the compiler has that warning, we still have
+// to provide a macro that can be used by the code.
+#if !defined(CATCH_INTERNAL_START_WARNINGS_SUPPRESSION)
+# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION
+#endif
+#if !defined(CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION)
+# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
+#endif
+#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS)
+# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS
+#endif
+#if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS)
+# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS
+#endif
+#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS)
+# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS
+#endif
+#if !defined(CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS)
+# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS
+#endif
+
+// The goal of this macro is to avoid evaluation of the arguments, but
+// still have the compiler warn on problems inside...
+#if !defined(CATCH_INTERNAL_IGNORE_BUT_WARN)
+# define CATCH_INTERNAL_IGNORE_BUT_WARN(...)
+#endif
+
+#if defined(__APPLE__) && defined(__apple_build_version__) && (__clang_major__ < 10)
+# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS
+#elif defined(__clang__) && (__clang_major__ < 5)
+# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS
+#endif
+
+#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS)
+# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS
+#endif
+
+#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+#define CATCH_TRY if ((true))
+#define CATCH_CATCH_ALL if ((false))
+#define CATCH_CATCH_ANON(type) if ((false))
+#else
+#define CATCH_TRY try
+#define CATCH_CATCH_ALL catch (...)
+#define CATCH_CATCH_ANON(type) catch (type)
+#endif
+
+#if defined(CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_NO_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR)
+#define CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+#endif
+
+// end catch_compiler_capabilities.h
+#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line
+#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line )
+#ifdef CATCH_CONFIG_COUNTER
+# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ )
+#else
+# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ )
+#endif
+
+#include
+#include
+#include
+
+// We need a dummy global operator<< so we can bring it into Catch namespace later
+struct Catch_global_namespace_dummy {};
+std::ostream& operator<<(std::ostream&, Catch_global_namespace_dummy);
+
+namespace Catch {
+
+ struct CaseSensitive { enum Choice {
+ Yes,
+ No
+ }; };
+
+ class NonCopyable {
+ NonCopyable( NonCopyable const& ) = delete;
+ NonCopyable( NonCopyable && ) = delete;
+ NonCopyable& operator = ( NonCopyable const& ) = delete;
+ NonCopyable& operator = ( NonCopyable && ) = delete;
+
+ protected:
+ NonCopyable();
+ virtual ~NonCopyable();
+ };
+
+ struct SourceLineInfo {
+
+ SourceLineInfo() = delete;
+ SourceLineInfo( char const* _file, std::size_t _line ) noexcept
+ : file( _file ),
+ line( _line )
+ {}
+
+ SourceLineInfo( SourceLineInfo const& other ) = default;
+ SourceLineInfo& operator = ( SourceLineInfo const& ) = default;
+ SourceLineInfo( SourceLineInfo&& ) noexcept = default;
+ SourceLineInfo& operator = ( SourceLineInfo&& ) noexcept = default;
+
+ bool empty() const noexcept { return file[0] == '\0'; }
+ bool operator == ( SourceLineInfo const& other ) const noexcept;
+ bool operator < ( SourceLineInfo const& other ) const noexcept;
+
+ char const* file;
+ std::size_t line;
+ };
+
+ std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info );
+
+ // Bring in operator<< from global namespace into Catch namespace
+ // This is necessary because the overload of operator<< above makes
+ // lookup stop at namespace Catch
+ using ::operator<<;
+
+ // Use this in variadic streaming macros to allow
+ // >> +StreamEndStop
+ // as well as
+ // >> stuff +StreamEndStop
+ struct StreamEndStop {
+ std::string operator+() const;
+ };
+ template
+ T const& operator + ( T const& value, StreamEndStop ) {
+ return value;
+ }
+}
+
+#define CATCH_INTERNAL_LINEINFO \
+ ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) )
+
+// end catch_common.h
+namespace Catch {
+
+ struct RegistrarForTagAliases {
+ RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo );
+ };
+
+} // end namespace Catch
+
+#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) \
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
+
+// end catch_tag_alias_autoregistrar.h
+// start catch_test_registry.h
+
+// start catch_interfaces_testcase.h
+
+#include
+
+namespace Catch {
+
+ class TestSpec;
+
+ struct ITestInvoker {
+ virtual void invoke () const = 0;
+ virtual ~ITestInvoker();
+ };
+
+ class TestCase;
+ struct IConfig;
+
+ struct ITestCaseRegistry {
+ virtual ~ITestCaseRegistry();
+ virtual std::vector const& getAllTests() const = 0;
+ virtual std::vector const& getAllTestsSorted( IConfig const& config ) const = 0;
+ };
+
+ bool isThrowSafe( TestCase const& testCase, IConfig const& config );
+ bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config );
+ std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config );
+ std::vector const& getAllTestCasesSorted( IConfig const& config );
+
+}
+
+// end catch_interfaces_testcase.h
+// start catch_stringref.h
+
+#include
+#include
+#include
+#include
+
+namespace Catch {
+
+ /// A non-owning string class (similar to the forthcoming std::string_view)
+ /// Note that, because a StringRef may be a substring of another string,
+ /// it may not be null terminated.
+ class StringRef {
+ public:
+ using size_type = std::size_t;
+ using const_iterator = const char*;
+
+ private:
+ static constexpr char const* const s_empty = "";
+
+ char const* m_start = s_empty;
+ size_type m_size = 0;
+
+ public: // construction
+ constexpr StringRef() noexcept = default;
+
+ StringRef( char const* rawChars ) noexcept;
+
+ constexpr StringRef( char const* rawChars, size_type size ) noexcept
+ : m_start( rawChars ),
+ m_size( size )
+ {}
+
+ StringRef( std::string const& stdString ) noexcept
+ : m_start( stdString.c_str() ),
+ m_size( stdString.size() )
+ {}
+
+ explicit operator std::string() const {
+ return std::string(m_start, m_size);
+ }
+
+ public: // operators
+ auto operator == ( StringRef const& other ) const noexcept -> bool;
+ auto operator != (StringRef const& other) const noexcept -> bool {
+ return !(*this == other);
+ }
+
+ auto operator[] ( size_type index ) const noexcept -> char {
+ assert(index < m_size);
+ return m_start[index];
+ }
+
+ public: // named queries
+ constexpr auto empty() const noexcept -> bool {
+ return m_size == 0;
+ }
+ constexpr auto size() const noexcept -> size_type {
+ return m_size;
+ }
+
+ // Returns the current start pointer. If the StringRef is not
+ // null-terminated, throws std::domain_exception
+ auto c_str() const -> char const*;
+
+ public: // substrings and searches
+ // Returns a substring of [start, start + length).
+ // If start + length > size(), then the substring is [start, size()).
+ // If start > size(), then the substring is empty.
+ auto substr( size_type start, size_type length ) const noexcept -> StringRef;
+
+ // Returns the current start pointer. May not be null-terminated.
+ auto data() const noexcept -> char const*;
+
+ constexpr auto isNullTerminated() const noexcept -> bool {
+ return m_start[m_size] == '\0';
+ }
+
+ public: // iterators
+ constexpr const_iterator begin() const { return m_start; }
+ constexpr const_iterator end() const { return m_start + m_size; }
+ };
+
+ auto operator += ( std::string& lhs, StringRef const& sr ) -> std::string&;
+ auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&;
+
+ constexpr auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef {
+ return StringRef( rawChars, size );
+ }
+} // namespace Catch
+
+constexpr auto operator "" _catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef {
+ return Catch::StringRef( rawChars, size );
+}
+
+// end catch_stringref.h
+// start catch_preprocessor.hpp
+
+
+#define CATCH_RECURSION_LEVEL0(...) __VA_ARGS__
+#define CATCH_RECURSION_LEVEL1(...) CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(__VA_ARGS__)))
+#define CATCH_RECURSION_LEVEL2(...) CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(__VA_ARGS__)))
+#define CATCH_RECURSION_LEVEL3(...) CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(__VA_ARGS__)))
+#define CATCH_RECURSION_LEVEL4(...) CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(__VA_ARGS__)))
+#define CATCH_RECURSION_LEVEL5(...) CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(__VA_ARGS__)))
+
+#ifdef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+#define INTERNAL_CATCH_EXPAND_VARGS(...) __VA_ARGS__
+// MSVC needs more evaluations
+#define CATCH_RECURSION_LEVEL6(...) CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(__VA_ARGS__)))
+#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL6(CATCH_RECURSION_LEVEL6(__VA_ARGS__))
+#else
+#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL5(__VA_ARGS__)
+#endif
+
+#define CATCH_REC_END(...)
+#define CATCH_REC_OUT
+
+#define CATCH_EMPTY()
+#define CATCH_DEFER(id) id CATCH_EMPTY()
+
+#define CATCH_REC_GET_END2() 0, CATCH_REC_END
+#define CATCH_REC_GET_END1(...) CATCH_REC_GET_END2
+#define CATCH_REC_GET_END(...) CATCH_REC_GET_END1
+#define CATCH_REC_NEXT0(test, next, ...) next CATCH_REC_OUT
+#define CATCH_REC_NEXT1(test, next) CATCH_DEFER ( CATCH_REC_NEXT0 ) ( test, next, 0)
+#define CATCH_REC_NEXT(test, next) CATCH_REC_NEXT1(CATCH_REC_GET_END test, next)
+
+#define CATCH_REC_LIST0(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ )
+#define CATCH_REC_LIST1(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0) ) ( f, peek, __VA_ARGS__ )
+#define CATCH_REC_LIST2(f, x, peek, ...) f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ )
+
+#define CATCH_REC_LIST0_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ )
+#define CATCH_REC_LIST1_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0_UD) ) ( f, userdata, peek, __VA_ARGS__ )
+#define CATCH_REC_LIST2_UD(f, userdata, x, peek, ...) f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ )
+
+// Applies the function macro `f` to each of the remaining parameters, inserts commas between the results,
+// and passes userdata as the first parameter to each invocation,
+// e.g. CATCH_REC_LIST_UD(f, x, a, b, c) evaluates to f(x, a), f(x, b), f(x, c)
+#define CATCH_REC_LIST_UD(f, userdata, ...) CATCH_RECURSE(CATCH_REC_LIST2_UD(f, userdata, __VA_ARGS__, ()()(), ()()(), ()()(), 0))
+
+#define CATCH_REC_LIST(f, ...) CATCH_RECURSE(CATCH_REC_LIST2(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0))
+
+#define INTERNAL_CATCH_EXPAND1(param) INTERNAL_CATCH_EXPAND2(param)
+#define INTERNAL_CATCH_EXPAND2(...) INTERNAL_CATCH_NO## __VA_ARGS__
+#define INTERNAL_CATCH_DEF(...) INTERNAL_CATCH_DEF __VA_ARGS__
+#define INTERNAL_CATCH_NOINTERNAL_CATCH_DEF
+#define INTERNAL_CATCH_STRINGIZE(...) INTERNAL_CATCH_STRINGIZE2(__VA_ARGS__)
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+#define INTERNAL_CATCH_STRINGIZE2(...) #__VA_ARGS__
+#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param))
+#else
+// MSVC is adding extra space and needs another indirection to expand INTERNAL_CATCH_NOINTERNAL_CATCH_DEF
+#define INTERNAL_CATCH_STRINGIZE2(...) INTERNAL_CATCH_STRINGIZE3(__VA_ARGS__)
+#define INTERNAL_CATCH_STRINGIZE3(...) #__VA_ARGS__
+#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) (INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) + 1)
+#endif
+
+#define INTERNAL_CATCH_MAKE_NAMESPACE2(...) ns_##__VA_ARGS__
+#define INTERNAL_CATCH_MAKE_NAMESPACE(name) INTERNAL_CATCH_MAKE_NAMESPACE2(name)
+
+#define INTERNAL_CATCH_REMOVE_PARENS(...) INTERNAL_CATCH_EXPAND1(INTERNAL_CATCH_DEF __VA_ARGS__)
+
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) decltype(get_wrapper())
+#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))
+#else
+#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) INTERNAL_CATCH_EXPAND_VARGS(decltype(get_wrapper()))
+#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__)))
+#endif
+
+#define INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(...)\
+ CATCH_REC_LIST(INTERNAL_CATCH_MAKE_TYPE_LIST,__VA_ARGS__)
+
+#define INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_0) INTERNAL_CATCH_REMOVE_PARENS(_0)
+#define INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_0, _1) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_1)
+#define INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_0, _1, _2) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_1, _2)
+#define INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_0, _1, _2, _3) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_1, _2, _3)
+#define INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_0, _1, _2, _3, _4) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_1, _2, _3, _4)
+#define INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_0, _1, _2, _3, _4, _5) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_1, _2, _3, _4, _5)
+#define INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_0, _1, _2, _3, _4, _5, _6) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_1, _2, _3, _4, _5, _6)
+#define INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_0, _1, _2, _3, _4, _5, _6, _7) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_1, _2, _3, _4, _5, _6, _7)
+#define INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_1, _2, _3, _4, _5, _6, _7, _8)
+#define INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9)
+#define INTERNAL_CATCH_REMOVE_PARENS_11_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10)
+
+#define INTERNAL_CATCH_VA_NARGS_IMPL(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N
+
+#define INTERNAL_CATCH_TYPE_GEN\
+ template struct TypeList {};\
+ template\
+ constexpr auto get_wrapper() noexcept -> TypeList { return {}; }\
+ template class...> struct TemplateTypeList{};\
+ template class...Cs>\
+ constexpr auto get_wrapper() noexcept -> TemplateTypeList { return {}; }\
+ template\
+ struct append;\
+ template\
+ struct rewrap;\
+ template class, typename...>\
+ struct create;\
+ template class, typename>\
+ struct convert;\
+ \
+ template \
+ struct append { using type = T; };\
+ template< template class L1, typename...E1, template class L2, typename...E2, typename...Rest>\
+ struct append, L2, Rest...> { using type = typename append, Rest...>::type; };\
+ template< template class L1, typename...E1, typename...Rest>\
+ struct append, TypeList, Rest...> { using type = L1; };\
+ \
+ template< template class Container, template class List, typename...elems>\
+ struct rewrap, List> { using type = TypeList>; };\
+ template< template class Container, template class List, class...Elems, typename...Elements>\
+ struct rewrap, List, Elements...> { using type = typename append>, typename rewrap, Elements...>::type>::type; };\
+ \
+ template class Final, template< typename...> class...Containers, typename...Types>\
+ struct create, TypeList> { using type = typename append, typename rewrap, Types...>::type...>::type; };\
+ template class Final, template class List, typename...Ts>\
+ struct convert> { using type = typename append,TypeList...>::type; };
+
+#define INTERNAL_CATCH_NTTP_1(signature, ...)\
+ template struct Nttp{};\
+ template\
+ constexpr auto get_wrapper() noexcept -> Nttp<__VA_ARGS__> { return {}; } \
+ template class...> struct NttpTemplateTypeList{};\
+ template class...Cs>\
+ constexpr auto get_wrapper() noexcept -> NttpTemplateTypeList { return {}; } \
+ \
+ template< template class Container, template class List, INTERNAL_CATCH_REMOVE_PARENS(signature)>\
+ struct rewrap, List<__VA_ARGS__>> { using type = TypeList>; };\
+ template< template class Container, template class List, INTERNAL_CATCH_REMOVE_PARENS(signature), typename...Elements>\
+ struct rewrap, List<__VA_ARGS__>, Elements...> { using type = typename append>, typename rewrap, Elements...>::type>::type; };\
+ template class Final, template class...Containers, typename...Types>\
+ struct create, TypeList> { using type = typename append, typename rewrap, Types...>::type...>::type; };
+
+#define INTERNAL_CATCH_DECLARE_SIG_TEST0(TestName)
+#define INTERNAL_CATCH_DECLARE_SIG_TEST1(TestName, signature)\
+ template\
+ static void TestName()
+#define INTERNAL_CATCH_DECLARE_SIG_TEST_X(TestName, signature, ...)\
+ template\
+ static void TestName()
+
+#define INTERNAL_CATCH_DEFINE_SIG_TEST0(TestName)
+#define INTERNAL_CATCH_DEFINE_SIG_TEST1(TestName, signature)\
+ template\
+ static void TestName()
+#define INTERNAL_CATCH_DEFINE_SIG_TEST_X(TestName, signature,...)\
+ template\
+ static void TestName()
+
+#define INTERNAL_CATCH_NTTP_REGISTER0(TestFunc, signature)\
+ template\
+ void reg_test(TypeList, Catch::NameAndTags nameAndTags)\
+ {\
+ Catch::AutoReg( Catch::makeTestInvoker(&TestFunc), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), nameAndTags);\
+ }
+
+#define INTERNAL_CATCH_NTTP_REGISTER(TestFunc, signature, ...)\
+ template\
+ void reg_test(Nttp<__VA_ARGS__>, Catch::NameAndTags nameAndTags)\
+ {\
+ Catch::AutoReg( Catch::makeTestInvoker(&TestFunc<__VA_ARGS__>), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), nameAndTags);\
+ }
+
+#define INTERNAL_CATCH_NTTP_REGISTER_METHOD0(TestName, signature, ...)\
+ template\
+ void reg_test(TypeList, Catch::StringRef className, Catch::NameAndTags nameAndTags)\
+ {\
+ Catch::AutoReg( Catch::makeTestInvoker(&TestName::test), CATCH_INTERNAL_LINEINFO, className, nameAndTags);\
+ }
+
+#define INTERNAL_CATCH_NTTP_REGISTER_METHOD(TestName, signature, ...)\
+ template\
+ void reg_test(Nttp<__VA_ARGS__>, Catch::StringRef className, Catch::NameAndTags nameAndTags)\
+ {\
+ Catch::AutoReg( Catch::makeTestInvoker(&TestName<__VA_ARGS__>::test), CATCH_INTERNAL_LINEINFO, className, nameAndTags);\
+ }
+
+#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD0(TestName, ClassName)
+#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD1(TestName, ClassName, signature)\
+ template \
+ struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \
+ void test();\
+ }
+
+#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X(TestName, ClassName, signature, ...)\
+ template \
+ struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName)<__VA_ARGS__> { \
+ void test();\
+ }
+
+#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD0(TestName)
+#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD1(TestName, signature)\
+ template \
+ void INTERNAL_CATCH_MAKE_NAMESPACE(TestName)::TestName::test()
+#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X(TestName, signature, ...)\
+ template \
+ void INTERNAL_CATCH_MAKE_NAMESPACE(TestName)::TestName<__VA_ARGS__>::test()
+
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+#define INTERNAL_CATCH_NTTP_0
+#define INTERNAL_CATCH_NTTP_GEN(...) INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_1( __VA_ARGS__),INTERNAL_CATCH_NTTP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_0)
+#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD1, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD0)(TestName, __VA_ARGS__)
+#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD1, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD0)(TestName, ClassName, __VA_ARGS__)
+#define INTERNAL_CATCH_NTTP_REG_METHOD_GEN(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD0, INTERNAL_CATCH_NTTP_REGISTER_METHOD0)(TestName, __VA_ARGS__)
+#define INTERNAL_CATCH_NTTP_REG_GEN(TestFunc, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER0, INTERNAL_CATCH_NTTP_REGISTER0)(TestFunc, __VA_ARGS__)
+#define INTERNAL_CATCH_DEFINE_SIG_TEST(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DEFINE_SIG_TEST1, INTERNAL_CATCH_DEFINE_SIG_TEST0)(TestName, __VA_ARGS__)
+#define INTERNAL_CATCH_DECLARE_SIG_TEST(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST1, INTERNAL_CATCH_DECLARE_SIG_TEST0)(TestName, __VA_ARGS__)
+#define INTERNAL_CATCH_REMOVE_PARENS_GEN(...) INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_REMOVE_PARENS_11_ARG,INTERNAL_CATCH_REMOVE_PARENS_10_ARG,INTERNAL_CATCH_REMOVE_PARENS_9_ARG,INTERNAL_CATCH_REMOVE_PARENS_8_ARG,INTERNAL_CATCH_REMOVE_PARENS_7_ARG,INTERNAL_CATCH_REMOVE_PARENS_6_ARG,INTERNAL_CATCH_REMOVE_PARENS_5_ARG,INTERNAL_CATCH_REMOVE_PARENS_4_ARG,INTERNAL_CATCH_REMOVE_PARENS_3_ARG,INTERNAL_CATCH_REMOVE_PARENS_2_ARG,INTERNAL_CATCH_REMOVE_PARENS_1_ARG)(__VA_ARGS__)
+#else
+#define INTERNAL_CATCH_NTTP_0(signature)
+#define INTERNAL_CATCH_NTTP_GEN(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1,INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_0)( __VA_ARGS__))
+#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD1, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD0)(TestName, __VA_ARGS__))
+#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD1, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD0)(TestName, ClassName, __VA_ARGS__))
+#define INTERNAL_CATCH_NTTP_REG_METHOD_GEN(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD0, INTERNAL_CATCH_NTTP_REGISTER_METHOD0)(TestName, __VA_ARGS__))
+#define INTERNAL_CATCH_NTTP_REG_GEN(TestFunc, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER0, INTERNAL_CATCH_NTTP_REGISTER0)(TestFunc, __VA_ARGS__))
+#define INTERNAL_CATCH_DEFINE_SIG_TEST(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DEFINE_SIG_TEST1, INTERNAL_CATCH_DEFINE_SIG_TEST0)(TestName, __VA_ARGS__))
+#define INTERNAL_CATCH_DECLARE_SIG_TEST(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST1, INTERNAL_CATCH_DECLARE_SIG_TEST0)(TestName, __VA_ARGS__))
+#define INTERNAL_CATCH_REMOVE_PARENS_GEN(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_REMOVE_PARENS_11_ARG,INTERNAL_CATCH_REMOVE_PARENS_10_ARG,INTERNAL_CATCH_REMOVE_PARENS_9_ARG,INTERNAL_CATCH_REMOVE_PARENS_8_ARG,INTERNAL_CATCH_REMOVE_PARENS_7_ARG,INTERNAL_CATCH_REMOVE_PARENS_6_ARG,INTERNAL_CATCH_REMOVE_PARENS_5_ARG,INTERNAL_CATCH_REMOVE_PARENS_4_ARG,INTERNAL_CATCH_REMOVE_PARENS_3_ARG,INTERNAL_CATCH_REMOVE_PARENS_2_ARG,INTERNAL_CATCH_REMOVE_PARENS_1_ARG)(__VA_ARGS__))
+#endif
+
+// end catch_preprocessor.hpp
+// start catch_meta.hpp
+
+
+#include
+
+namespace Catch {
+ template
+ struct always_false : std::false_type {};
+
+ template struct true_given : std::true_type {};
+ struct is_callable_tester {
+ template
+ true_given()(std::declval()...))> static test(int);
+ template
+ std::false_type static test(...);
+ };
+
+ template
+ struct is_callable;
+
+ template
+ struct is_callable : decltype(is_callable_tester::test(0)) {};
+
+#if defined(__cpp_lib_is_invocable) && __cpp_lib_is_invocable >= 201703
+ // std::result_of is deprecated in C++17 and removed in C++20. Hence, it is
+ // replaced with std::invoke_result here.
+ template
+ using FunctionReturnType = std::remove_reference_t>>;
+#else
+ // Keep ::type here because we still support C++11
+ template
+ using FunctionReturnType = typename std::remove_reference::type>::type>::type;
+#endif
+
+} // namespace Catch
+
+namespace mpl_{
+ struct na;
+}
+
+// end catch_meta.hpp
+namespace Catch {
+
+template
+class TestInvokerAsMethod : public ITestInvoker {
+ void (C::*m_testAsMethod)();
+public:
+ TestInvokerAsMethod( void (C::*testAsMethod)() ) noexcept : m_testAsMethod( testAsMethod ) {}
+
+ void invoke() const override {
+ C obj;
+ (obj.*m_testAsMethod)();
+ }
+};
+
+auto makeTestInvoker( void(*testAsFunction)() ) noexcept -> ITestInvoker*;
+
+template
+auto makeTestInvoker( void (C::*testAsMethod)() ) noexcept -> ITestInvoker* {
+ return new(std::nothrow) TestInvokerAsMethod( testAsMethod );
+}
+
+struct NameAndTags {
+ NameAndTags( StringRef const& name_ = StringRef(), StringRef const& tags_ = StringRef() ) noexcept;
+ StringRef name;
+ StringRef tags;
+};
+
+struct AutoReg : NonCopyable {
+ AutoReg( ITestInvoker* invoker, SourceLineInfo const& lineInfo, StringRef const& classOrMethod, NameAndTags const& nameAndTags ) noexcept;
+ ~AutoReg();
+};
+
+} // end namespace Catch
+
+#if defined(CATCH_CONFIG_DISABLE)
+ #define INTERNAL_CATCH_TESTCASE_NO_REGISTRATION( TestName, ... ) \
+ static void TestName()
+ #define INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION( TestName, ClassName, ... ) \
+ namespace{ \
+ struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \
+ void test(); \
+ }; \
+ } \
+ void TestName::test()
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( TestName, TestFunc, Name, Tags, Signature, ... ) \
+ INTERNAL_CATCH_DEFINE_SIG_TEST(TestFunc, INTERNAL_CATCH_REMOVE_PARENS(Signature))
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( TestNameClass, TestName, ClassName, Name, Tags, Signature, ... ) \
+ namespace{ \
+ namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName) { \
+ INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, INTERNAL_CATCH_REMOVE_PARENS(Signature));\
+ } \
+ } \
+ INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, INTERNAL_CATCH_REMOVE_PARENS(Signature))
+
+ #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(Name, Tags, ...) \
+ INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, typename TestType, __VA_ARGS__ )
+ #else
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(Name, Tags, ...) \
+ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, typename TestType, __VA_ARGS__ ) )
+ #endif
+
+ #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(Name, Tags, Signature, ...) \
+ INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, Signature, __VA_ARGS__ )
+ #else
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(Name, Tags, Signature, ...) \
+ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, Signature, __VA_ARGS__ ) )
+ #endif
+
+ #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION( ClassName, Name, Tags,... ) \
+ INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ )
+ #else
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION( ClassName, Name, Tags,... ) \
+ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ ) )
+ #endif
+
+ #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION( ClassName, Name, Tags, Signature, ... ) \
+ INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ )
+ #else
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION( ClassName, Name, Tags, Signature, ... ) \
+ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ ) )
+ #endif
+#endif
+
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \
+ static void TestName(); \
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &TestName ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
+ static void TestName()
+ #define INTERNAL_CATCH_TESTCASE( ... ) \
+ INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), __VA_ARGS__ )
+
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &QualifiedMethod ), CATCH_INTERNAL_LINEINFO, "&" #QualifiedMethod, Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
+
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ namespace{ \
+ struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \
+ void test(); \
+ }; \
+ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \
+ } \
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
+ void TestName::test()
+ #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \
+ INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, __VA_ARGS__ )
+
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( Function ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
+
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_2(TestName, TestFunc, Name, Tags, Signature, ... )\
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
+ CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
+ INTERNAL_CATCH_DECLARE_SIG_TEST(TestFunc, INTERNAL_CATCH_REMOVE_PARENS(Signature));\
+ namespace {\
+ namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){\
+ INTERNAL_CATCH_TYPE_GEN\
+ INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature))\
+ INTERNAL_CATCH_NTTP_REG_GEN(TestFunc,INTERNAL_CATCH_REMOVE_PARENS(Signature))\
+ template \
+ struct TestName{\
+ TestName(){\
+ int index = 0; \
+ constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, __VA_ARGS__)};\
+ using expander = int[];\
+ (void)expander{(reg_test(Types{}, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index]), Tags } ), index++)... };/* NOLINT */ \
+ }\
+ };\
+ static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
+ TestName();\
+ return 0;\
+ }();\
+ }\
+ }\
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
+ INTERNAL_CATCH_DEFINE_SIG_TEST(TestFunc,INTERNAL_CATCH_REMOVE_PARENS(Signature))
+
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE(Name, Tags, ...) \
+ INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, typename TestType, __VA_ARGS__ )
+#else
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE(Name, Tags, ...) \
+ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, typename TestType, __VA_ARGS__ ) )
+#endif
+
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG(Name, Tags, Signature, ...) \
+ INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, Signature, __VA_ARGS__ )
+#else
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG(Name, Tags, Signature, ...) \
+ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, Signature, __VA_ARGS__ ) )
+#endif
+
+ #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(TestName, TestFuncName, Name, Tags, Signature, TmplTypes, TypesList) \
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
+ CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
+ template static void TestFuncName(); \
+ namespace {\
+ namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName) { \
+ INTERNAL_CATCH_TYPE_GEN \
+ INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature)) \
+ template \
+ struct TestName { \
+ void reg_tests() { \
+ int index = 0; \
+ using expander = int[]; \
+ constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TmplTypes))};\
+ constexpr char const* types_list[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TypesList))};\
+ constexpr auto num_types = sizeof(types_list) / sizeof(types_list[0]);\
+ (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFuncName ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index / num_types]) + "<" + std::string(types_list[index % num_types]) + ">", Tags } ), index++)... };/* NOLINT */\
+ } \
+ }; \
+ static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){ \
+ using TestInit = typename create()), TypeList>::type; \
+ TestInit t; \
+ t.reg_tests(); \
+ return 0; \
+ }(); \
+ } \
+ } \
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
+ template \
+ static void TestFuncName()
+
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+ #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE(Name, Tags, ...)\
+ INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, typename T,__VA_ARGS__)
+#else
+ #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE(Name, Tags, ...)\
+ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, typename T, __VA_ARGS__ ) )
+#endif
+
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+ #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG(Name, Tags, Signature, ...)\
+ INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, Signature, __VA_ARGS__)
+#else
+ #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG(Name, Tags, Signature, ...)\
+ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, Signature, __VA_ARGS__ ) )
+#endif
+
+ #define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_2(TestName, TestFunc, Name, Tags, TmplList)\
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
+ template static void TestFunc(); \
+ namespace {\
+ namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){\
+ INTERNAL_CATCH_TYPE_GEN\
+ template \
+ struct TestName { \
+ void reg_tests() { \
+ int index = 0; \
+ using expander = int[]; \
+ (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFunc ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ Name " - " + std::string(INTERNAL_CATCH_STRINGIZE(TmplList)) + " - " + std::to_string(index), Tags } ), index++)... };/* NOLINT */\
+ } \
+ };\
+ static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){ \
+ using TestInit = typename convert::type; \
+ TestInit t; \
+ t.reg_tests(); \
+ return 0; \
+ }(); \
+ }}\
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
+ template \
+ static void TestFunc()
+
+ #define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE(Name, Tags, TmplList) \
+ INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, TmplList )
+
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( TestNameClass, TestName, ClassName, Name, Tags, Signature, ... ) \
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
+ CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
+ namespace {\
+ namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){ \
+ INTERNAL_CATCH_TYPE_GEN\
+ INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature))\
+ INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, INTERNAL_CATCH_REMOVE_PARENS(Signature));\
+ INTERNAL_CATCH_NTTP_REG_METHOD_GEN(TestName, INTERNAL_CATCH_REMOVE_PARENS(Signature))\
+ template \
+ struct TestNameClass{\
+ TestNameClass(){\
+ int index = 0; \
+ constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, __VA_ARGS__)};\
+ using expander = int[];\
+ (void)expander{(reg_test(Types{}, #ClassName, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index]), Tags } ), index++)... };/* NOLINT */ \
+ }\
+ };\
+ static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
+ TestNameClass();\
+ return 0;\
+ }();\
+ }\
+ }\
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
+ INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, INTERNAL_CATCH_REMOVE_PARENS(Signature))
+
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( ClassName, Name, Tags,... ) \
+ INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ )
+#else
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( ClassName, Name, Tags,... ) \
+ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ ) )
+#endif
+
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( ClassName, Name, Tags, Signature, ... ) \
+ INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ )
+#else
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( ClassName, Name, Tags, Signature, ... ) \
+ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ ) )
+#endif
+
+ #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2(TestNameClass, TestName, ClassName, Name, Tags, Signature, TmplTypes, TypesList)\
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
+ CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
+ template \
+ struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName ) { \
+ void test();\
+ };\
+ namespace {\
+ namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestNameClass) {\
+ INTERNAL_CATCH_TYPE_GEN \
+ INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature))\
+ template\
+ struct TestNameClass{\
+ void reg_tests(){\
+ int index = 0;\
+ using expander = int[];\
+ constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TmplTypes))};\
+ constexpr char const* types_list[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TypesList))};\
+ constexpr auto num_types = sizeof(types_list) / sizeof(types_list[0]);\
+ (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index / num_types]) + "<" + std::string(types_list[index % num_types]) + ">", Tags } ), index++)... };/* NOLINT */ \
+ }\
+ };\
+ static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
+ using TestInit = typename create()), TypeList>::type;\
+ TestInit t;\
+ t.reg_tests();\
+ return 0;\
+ }(); \
+ }\
+ }\
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
+ template \
+ void TestName::test()
+
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+ #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( ClassName, Name, Tags, ... )\
+ INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), ClassName, Name, Tags, typename T, __VA_ARGS__ )
+#else
+ #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( ClassName, Name, Tags, ... )\
+ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), ClassName, Name, Tags, typename T,__VA_ARGS__ ) )
+#endif
+
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+ #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( ClassName, Name, Tags, Signature, ... )\
+ INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), ClassName, Name, Tags, Signature, __VA_ARGS__ )
+#else
+ #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( ClassName, Name, Tags, Signature, ... )\
+ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), ClassName, Name, Tags, Signature,__VA_ARGS__ ) )
+#endif
+
+ #define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD_2( TestNameClass, TestName, ClassName, Name, Tags, TmplList) \
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
+ template \
+ struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName ) { \
+ void test();\
+ };\
+ namespace {\
+ namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){ \
+ INTERNAL_CATCH_TYPE_GEN\
+ template\
+ struct TestNameClass{\
+ void reg_tests(){\
+ int index = 0;\
+ using expander = int[];\
+ (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ Name " - " + std::string(INTERNAL_CATCH_STRINGIZE(TmplList)) + " - " + std::to_string(index), Tags } ), index++)... };/* NOLINT */ \
+ }\
+ };\
+ static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
+ using TestInit = typename convert::type;\
+ TestInit t;\
+ t.reg_tests();\
+ return 0;\
+ }(); \
+ }}\
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
+ template \
+ void TestName::test()
+
+#define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD(ClassName, Name, Tags, TmplList) \
+ INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), ClassName, Name, Tags, TmplList )
+
+// end catch_test_registry.h
+// start catch_capture.hpp
+
+// start catch_assertionhandler.h
+
+// start catch_assertioninfo.h
+
+// start catch_result_type.h
+
+namespace Catch {
+
+ // ResultWas::OfType enum
+ struct ResultWas { enum OfType {
+ Unknown = -1,
+ Ok = 0,
+ Info = 1,
+ Warning = 2,
+
+ FailureBit = 0x10,
+
+ ExpressionFailed = FailureBit | 1,
+ ExplicitFailure = FailureBit | 2,
+
+ Exception = 0x100 | FailureBit,
+
+ ThrewException = Exception | 1,
+ DidntThrowException = Exception | 2,
+
+ FatalErrorCondition = 0x200 | FailureBit
+
+ }; };
+
+ bool isOk( ResultWas::OfType resultType );
+ bool isJustInfo( int flags );
+
+ // ResultDisposition::Flags enum
+ struct ResultDisposition { enum Flags {
+ Normal = 0x01,
+
+ ContinueOnFailure = 0x02, // Failures fail test, but execution continues
+ FalseTest = 0x04, // Prefix expression with !
+ SuppressFail = 0x08 // Failures are reported but do not fail the test
+ }; };
+
+ ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs );
+
+ bool shouldContinueOnFailure( int flags );
+ inline bool isFalseTest( int flags ) { return ( flags & ResultDisposition::FalseTest ) != 0; }
+ bool shouldSuppressFailure( int flags );
+
+} // end namespace Catch
+
+// end catch_result_type.h
+namespace Catch {
+
+ struct AssertionInfo
+ {
+ StringRef macroName;
+ SourceLineInfo lineInfo;
+ StringRef capturedExpression;
+ ResultDisposition::Flags resultDisposition;
+
+ // We want to delete this constructor but a compiler bug in 4.8 means
+ // the struct is then treated as non-aggregate
+ //AssertionInfo() = delete;
+ };
+
+} // end namespace Catch
+
+// end catch_assertioninfo.h
+// start catch_decomposer.h
+
+// start catch_tostring.h
+
+#include
+#include
+#include
+#include
+// start catch_stream.h
+
+#include
+#include
+#include
+
+namespace Catch {
+
+ std::ostream& cout();
+ std::ostream& cerr();
+ std::ostream& clog();
+
+ class StringRef;
+
+ struct IStream {
+ virtual ~IStream();
+ virtual std::ostream& stream() const = 0;
+ };
+
+ auto makeStream( StringRef const &filename ) -> IStream const*;
+
+ class ReusableStringStream : NonCopyable {
+ std::size_t m_index;
+ std::ostream* m_oss;
+ public:
+ ReusableStringStream();
+ ~ReusableStringStream();
+
+ auto str() const -> std::string;
+
+ template
+ auto operator << ( T const& value ) -> ReusableStringStream& {
+ *m_oss << value;
+ return *this;
+ }
+ auto get() -> std::ostream& { return *m_oss; }
+ };
+}
+
+// end catch_stream.h
+// start catch_interfaces_enum_values_registry.h
+
+#include
+
+namespace Catch {
+
+ namespace Detail {
+ struct EnumInfo {
+ StringRef m_name;
+ std::vector> m_values;
+
+ ~EnumInfo();
+
+ StringRef lookup( int value ) const;
+ };
+ } // namespace Detail
+
+ struct IMutableEnumValuesRegistry {
+ virtual ~IMutableEnumValuesRegistry();
+
+ virtual Detail::EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::vector const& values ) = 0;
+
+ template
+ Detail::EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::initializer_list values ) {
+ static_assert(sizeof(int) >= sizeof(E), "Cannot serialize enum to int");
+ std::vector intValues;
+ intValues.reserve( values.size() );
+ for( auto enumValue : values )
+ intValues.push_back( static_cast( enumValue ) );
+ return registerEnum( enumName, allEnums, intValues );
+ }
+ };
+
+} // Catch
+
+// end catch_interfaces_enum_values_registry.h
+
+#ifdef CATCH_CONFIG_CPP17_STRING_VIEW
+#include
+#endif
+
+#ifdef __OBJC__
+// start catch_objc_arc.hpp
+
+#import
+
+#ifdef __has_feature
+#define CATCH_ARC_ENABLED __has_feature(objc_arc)
+#else
+#define CATCH_ARC_ENABLED 0
+#endif
+
+void arcSafeRelease( NSObject* obj );
+id performOptionalSelector( id obj, SEL sel );
+
+#if !CATCH_ARC_ENABLED
+inline void arcSafeRelease( NSObject* obj ) {
+ [obj release];
+}
+inline id performOptionalSelector( id obj, SEL sel ) {
+ if( [obj respondsToSelector: sel] )
+ return [obj performSelector: sel];
+ return nil;
+}
+#define CATCH_UNSAFE_UNRETAINED
+#define CATCH_ARC_STRONG
+#else
+inline void arcSafeRelease( NSObject* ){}
+inline id performOptionalSelector( id obj, SEL sel ) {
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
+#endif
+ if( [obj respondsToSelector: sel] )
+ return [obj performSelector: sel];
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+ return nil;
+}
+#define CATCH_UNSAFE_UNRETAINED __unsafe_unretained
+#define CATCH_ARC_STRONG __strong
+#endif
+
+// end catch_objc_arc.hpp
+#endif
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable:4180) // We attempt to stream a function (address) by const&, which MSVC complains about but is harmless
+#endif
+
+namespace Catch {
+ namespace Detail {
+
+ extern const std::string unprintableString;
+
+ std::string rawMemoryToString( const void *object, std::size_t size );
+
+ template
+ std::string rawMemoryToString( const T& object ) {
+ return rawMemoryToString( &object, sizeof(object) );
+ }
+
+ template
+ class IsStreamInsertable {
+ template
+ static auto test(int)
+ -> decltype(std::declval() << std::declval(), std::true_type());
+
+ template
+ static auto test(...)->std::false_type;
+
+ public:
+ static const bool value = decltype(test(0))::value;
+ };
+
+ template
+ std::string convertUnknownEnumToString( E e );
+
+ template
+ typename std::enable_if<
+ !std::is_enum::value && !std::is_base_of::value,
+ std::string>::type convertUnstreamable( T const& ) {
+ return Detail::unprintableString;
+ }
+ template
+ typename std::enable_if<
+ !std::is_enum::value && std::is_base_of::value,
+ std::string>::type convertUnstreamable(T const& ex) {
+ return ex.what();
+ }
+
+ template
+ typename std::enable_if<
+ std::is_enum::value
+ , std::string>::type convertUnstreamable( T const& value ) {
+ return convertUnknownEnumToString( value );
+ }
+
+#if defined(_MANAGED)
+ //! Convert a CLR string to a utf8 std::string
+ template
+ std::string clrReferenceToString( T^ ref ) {
+ if (ref == nullptr)
+ return std::string("null");
+ auto bytes = System::Text::Encoding::UTF8->GetBytes(ref->ToString());
+ cli::pin_ptr p = &bytes[0];
+ return std::string(reinterpret_cast(p), bytes->Length);
+ }
+#endif
+
+ } // namespace Detail
+
+ // If we decide for C++14, change these to enable_if_ts
+ template
+ struct StringMaker {
+ template
+ static
+ typename std::enable_if<::Catch::Detail::IsStreamInsertable::value, std::string>::type
+ convert(const Fake& value) {
+ ReusableStringStream rss;
+ // NB: call using the function-like syntax to avoid ambiguity with
+ // user-defined templated operator<< under clang.
+ rss.operator<<(value);
+ return rss.str();
+ }
+
+ template
+ static
+ typename std::enable_if::value, std::string>::type
+ convert( const Fake& value ) {
+#if !defined(CATCH_CONFIG_FALLBACK_STRINGIFIER)
+ return Detail::convertUnstreamable(value);
+#else
+ return CATCH_CONFIG_FALLBACK_STRINGIFIER(value);
+#endif
+ }
+ };
+
+ namespace Detail {
+
+ // This function dispatches all stringification requests inside of Catch.
+ // Should be preferably called fully qualified, like ::Catch::Detail::stringify
+ template
+ std::string stringify(const T& e) {
+ return ::Catch::StringMaker::type>::type>::convert(e);
+ }
+
+ template
+ std::string convertUnknownEnumToString( E e ) {
+ return ::Catch::Detail::stringify(static_cast::type>(e));
+ }
+
+#if defined(_MANAGED)
+ template
+ std::string stringify( T^ e ) {
+ return ::Catch::StringMaker::convert(e);
+ }
+#endif
+
+ } // namespace Detail
+
+ // Some predefined specializations
+
+ template<>
+ struct StringMaker {
+ static std::string convert(const std::string& str);
+ };
+
+#ifdef CATCH_CONFIG_CPP17_STRING_VIEW
+ template<>
+ struct StringMaker {
+ static std::string convert(std::string_view str);
+ };
+#endif
+
+ template<>
+ struct StringMaker