From f0862fb2464a7c96bcb72305142d7c5ed2d1d34a Mon Sep 17 00:00:00 2001 From: Dalton Messmer Date: Wed, 23 Nov 2022 18:33:43 -0500 Subject: [PATCH] Lazily initialize the Factory --- include/core/factory.h | 78 ++++++++++++++++++++--------------- include/core/options.h | 16 +++---- include/core/status.h | 2 +- include/dmf2mod.h | 7 ---- include/utils/stream_reader.h | 2 +- src/console/console.cpp | 2 - src/core/factory.cpp | 29 ++++++++++++- src/dmf2mod.cpp | 38 +---------------- src/webapp/webapp.cpp | 2 - 9 files changed, 83 insertions(+), 93 deletions(-) diff --git a/include/core/factory.h b/include/core/factory.h index c64d02c..5f6c173 100644 --- a/include/core/factory.h +++ b/include/core/factory.h @@ -34,8 +34,7 @@ namespace detail { template constexpr bool reflection_enabled_v = std::is_base_of_v; } // namespace detail - -template +template class BuilderBase { public: @@ -47,7 +46,7 @@ class BuilderBase }; // Builds an instance of a factory-enabled class; Can specialize this, but it must inherit from BuilderBase and Factory must have access to its members. -template +template class Builder : public BuilderBase { // NOTE: Derived must have a default constructor accessible by this class and also must have a public destructor. @@ -65,79 +64,90 @@ struct InfoBase }; // Static data for a factory-enabled class; Can specialize this, but it must inherit from InfoBase. -template +template struct Info : public InfoBase {}; -template +template class Factory { private: Factory() = delete; - virtual ~Factory() { Clear(); } + ~Factory() { Clear(); } Factory(const Factory&) = delete; Factory(Factory&&) = delete; Factory& operator=(const Factory&) = delete; Factory& operator=(Factory&&) = delete; - // Declaration. Factories will have to implement their own. - static void InitializeImpl(); - -public: - - static void Initialize() + struct InitializeImpl { - if (m_Initialized) - return; - + // Note: Factories will have to implement this. InitializeImpl(); - m_Initialized = true; + }; + + /* + * Initialize must be called at the start of every public Factory method to + * ensure lazy initialization of the Factory whenever it is first used. + */ + static bool Initialize() + { + if (!m_Initialized) + { + Clear(); + // This gets around static initialization ordering issues: + [[maybe_unused]] auto init = std::make_unique(); + m_Initialized = true; + } + return true; } +public: + static std::shared_ptr Create(TypeEnum classType) { - if (!m_Initialized) - throw std::runtime_error("Factory is not initialized for base class: " + std::string(typeid(Base).name())); + [[maybe_unused]] static bool init = Initialize(); if (m_Builders.find(classType) != m_Builders.end()) return m_Builders.at(classType)->Build(); - throw std::runtime_error("Factory is not initialized for TypeEnum '" + std::to_string(static_cast(classType)) + "."); + assert(false && "Factory is not initialized for Base."); return nullptr; } static Info const* GetInfo(TypeEnum classType) { - if (!m_Initialized) - throw std::runtime_error("Factory is not initialized for base class: " + std::string(typeid(Base).name())); + [[maybe_unused]] static bool init = Initialize(); if (m_Info.find(classType) != m_Info.end()) return m_Info.at(classType).get(); - throw std::runtime_error("Factory is not initialized for TypeEnum '" + std::to_string(static_cast(classType)) + "."); + assert(false && "Factory is not initialized for Base."); return nullptr; } - template >> + template, bool> = true> static std::shared_ptr Create() { - if (!m_Initialized) - throw std::runtime_error("Factory is not initialized for base class: " + std::string(typeid(Base).name())); + // Initialize() not needed here because GetEnumFromType calls it static TypeEnum classType = GetEnumFromType(); return std::static_pointer_cast(Create(classType)); } - template >> + template, bool> = true> static Info const* GetInfo() { - if (!m_Initialized) - throw std::runtime_error("Factory is not initialized for base class: " + std::string(typeid(Base).name())); + // Initialize() not needed here because GetEnumFromType calls it static TypeEnum classType = GetEnumFromType(); return GetInfo(classType); } - static const std::map>>& TypeInfo() { return m_Info; } + static const std::map>>& TypeInfo() + { + [[maybe_unused]] static bool init = Initialize(); + return m_Info; + } static std::vector GetInitializedTypes() { + [[maybe_unused]] static bool init = Initialize(); std::vector vec; for (const auto& mapPair : m_Builders) { @@ -146,15 +156,15 @@ class Factory return vec; } - template>> + template, bool> = true> static TypeEnum GetEnumFromType() { - if (!m_Initialized) - throw std::runtime_error("Factory is not initialized for base class: " + std::string(typeid(Base).name())); + [[maybe_unused]] static bool init = Initialize(); const auto& type = std::type_index(typeid(Type)); if (m_TypeToEnum.find(type) != m_TypeToEnum.end()) return m_TypeToEnum.at(type); - throw std::runtime_error("Factory is not initialized for Type '" + std::string(typeid(Type).name()) + "."); + assert(false && "Factory is not initialized for Type."); + return TypeInvalid; } private: @@ -248,7 +258,7 @@ struct ReflectionImpl : public Base // Inherit this class using CRTP to enable factory for any class -template +template struct EnableFactory : public detail::EnableFactoryBase, public std::conditional_t, ReflectionImpl, Base> // See note above { static_assert(std::is_base_of_v>, "Info must inherit from InfoBase"); diff --git a/include/core/options.h b/include/core/options.h index d8f9a84..1a8228c 100644 --- a/include/core/options.h +++ b/include/core/options.h @@ -52,7 +52,7 @@ class OptionDefinition OptionDefinition() : m_OptionType(OPTION), m_Id(-1), m_ValueType(Type::BOOL), m_Name(""), m_ShortName('\0'), m_DefaultValue(false) {} // OptionDefinition without accepted values; The value can be anything allowed by the variant - template{} || (std::is_enum_v && std::is_convertible_v, int>)>> + template{} || (std::is_enum_v && std::is_convertible_v, int>), bool> = true> OptionDefinition(OptionType type, T id, const std::string& name, char shortName, const value_t& defaultValue, const std::string& description) : m_OptionType(type), m_Id(static_cast(id)), m_Name(name), m_ShortName(shortName), m_DefaultValue(defaultValue), m_AcceptedValues({}), m_AcceptedValuesOrdered({}), m_Description(description) { @@ -69,8 +69,8 @@ class OptionDefinition // OptionDefinition with accepted values; Ensures that defaultValue and acceptedValues are the same type and are a valid variant alternative template && /* U must be a valid variant alternative */ - (std::is_integral_v || (std::is_enum_v && std::is_convertible_v, int>))>> /* T must be int or enum class with int underlying type */ + std::enable_if_t && /* U must be a valid variant alternative */ + (std::is_integral_v || (std::is_enum_v && std::is_convertible_v, int>)), bool> = true> /* T must be int or enum class with int underlying type */ OptionDefinition(OptionType type, T id, const std::string& name, char shortName, const U& defaultValue, const std::initializer_list& acceptedValues, const std::string& description) : m_OptionType(type), m_Id(static_cast(id)), m_Name(name), m_ShortName(shortName), m_DefaultValue(defaultValue), m_Description(description) { @@ -110,13 +110,13 @@ class OptionDefinition } // Allows the use of string literals, which are converted to std::string - template || (std::is_enum_v && std::is_convertible_v, int>)>> + template || (std::is_enum_v && std::is_convertible_v, int>), bool> = true> OptionDefinition(OptionType type, T id, const std::string& name, char shortName, const char* defaultValue, const std::initializer_list& acceptedValues, const std::string& description) : OptionDefinition(type, id, name, shortName, std::string(defaultValue), acceptedValues, description) {} // Allows custom accepted values text which is used when printing help for this option. m_AcceptedValues is empty. - template && /* U must be a valid variant alternative */ - (std::is_integral_v || (std::is_enum_v && std::is_convertible_v, int>))>> /* T must be int or enum class with int underlying type */ + template && /* U must be a valid variant alternative */ + (std::is_integral_v || (std::is_enum_v && std::is_convertible_v, int>)), bool> = true> /* T must be int or enum class with int underlying type */ OptionDefinition(OptionType type, T id, const std::string& name, char shortName, const U& defaultValue, const char* customAcceptedValuesText, const std::string& description) : OptionDefinition(type, id, name, shortName, defaultValue, description) { @@ -286,13 +286,13 @@ class OptionCollection const Option& GetOption(int id) const { return m_OptionsMap.at(id); } Option& GetOption(int id) { return m_OptionsMap[id]; } - template && std::is_convertible_v, int>>> + template && std::is_convertible_v, int>, bool> = true> const Option& GetOption(T id) const { return GetOption(static_cast(id)); } - template && std::is_convertible_v, int>>> + template && std::is_convertible_v, int>, bool> = true> Option& GetOption(T id) { return GetOption(static_cast(id)); diff --git a/include/core/status.h b/include/core/status.h index 79eb053..12df7f2 100644 --- a/include/core/status.h +++ b/include/core/status.h @@ -83,7 +83,7 @@ class ModuleException : public std::exception ModuleException& operator=(ModuleException& other) = default; // Construct using an enum for an error code - template && std::is_convertible_v, int>>> + template && std::is_convertible_v, int>, bool> = true> ModuleException(Category category, T errorCode, const std::string& errorMessage = "") : ModuleException(category, static_cast(errorCode), errorMessage) {} diff --git a/include/dmf2mod.h b/include/dmf2mod.h index 1ec7244..816f9ee 100644 --- a/include/dmf2mod.h +++ b/include/dmf2mod.h @@ -11,10 +11,3 @@ #include "dmf.h" #include "mod.h" - -namespace d2m { - -// Call this before using the dmf2mod library -void Initialize(); - -} // namespace d2m diff --git a/include/utils/stream_reader.h b/include/utils/stream_reader.h index 7d2604a..6654453 100644 --- a/include/utils/stream_reader.h +++ b/include/utils/stream_reader.h @@ -50,7 +50,7 @@ enum class Endianness { Unspecified, Little, Big }; Wrapper for std::istream and derived classes which provides convenient methods for reading strings and integers */ -template , IStream>>> +template , IStream>, bool> = true> class StreamReader { private: diff --git a/src/console/console.cpp b/src/console/console.cpp index 5aa4a03..2d3b20b 100644 --- a/src/console/console.cpp +++ b/src/console/console.cpp @@ -42,8 +42,6 @@ static void PrintHelp(const std::string& executable, ModuleType moduleType); int main(int argc, char *argv[]) { - Initialize(); - auto args = Utils::GetArgsAsVector(argc, argv); InputOutput io; diff --git a/src/core/factory.cpp b/src/core/factory.cpp index 38ca546..d84108e 100644 --- a/src/core/factory.cpp +++ b/src/core/factory.cpp @@ -2,9 +2,36 @@ factory.cpp Written by Dalton Messmer . - See factory.h + Implements InitializeImpl for each factory. */ #include "factory.h" +#include "dmf2mod.h" using namespace d2m; + +using MODOptionEnum = MODConversionOptions::OptionEnum; +static auto MODOptions = OptionDefinitionCollection +{ + /* Type / Option id / Full name / Short / Default / Possib. vals / Description */ + {OPTION, MODOptionEnum::AmigaFilter, "amiga", '\0', false, "Enables the Amiga filter"}, + {OPTION, MODOptionEnum::Arpeggio, "arp", '\0', false, "Allow arpeggio effects"}, + {OPTION, MODOptionEnum::Portamento, "port", '\0', false, "Allow portamento up/down effects"}, + {OPTION, MODOptionEnum::Port2Note, "port2note", '\0', false, "Allow portamento to note effects"}, + {OPTION, MODOptionEnum::Vibrato, "vib", '\0', false, "Allow vibrato effects"}, + {OPTION, MODOptionEnum::TempoType, "tempo", '\0', "accuracy", {"accuracy", "compat"}, "Prioritize tempo accuracy or compatibility with effects"}, +}; + +template<> +Factory::InitializeImpl::InitializeImpl() +{ + Register(); + Register(std::move(MODOptions)); +}; + +template<> +Factory::InitializeImpl::InitializeImpl() +{ + Register("Deflemask", "dmf"); + Register("ProTracker", "mod"); +}; diff --git a/src/dmf2mod.cpp b/src/dmf2mod.cpp index 474843e..a07b37e 100644 --- a/src/dmf2mod.cpp +++ b/src/dmf2mod.cpp @@ -2,45 +2,9 @@ dmf2mod.cpp Written by Dalton Messmer . - Implements initialize methods for each factory. + See dmf2mod.h */ #include "dmf2mod.h" using namespace d2m; - -using MODOptionEnum = MODConversionOptions::OptionEnum; -static auto MODOptions = OptionDefinitionCollection -{ - /* Type / Option id / Full name / Short / Default / Possib. vals / Description */ - {OPTION, MODOptionEnum::AmigaFilter, "amiga", '\0', false, "Enables the Amiga filter"}, - {OPTION, MODOptionEnum::Arpeggio, "arp", '\0', false, "Allow arpeggio effects"}, - {OPTION, MODOptionEnum::Portamento, "port", '\0', false, "Allow portamento up/down effects"}, - {OPTION, MODOptionEnum::Port2Note, "port2note", '\0', false, "Allow portamento to note effects"}, - {OPTION, MODOptionEnum::Vibrato, "vib", '\0', false, "Allow vibrato effects"}, - {OPTION, MODOptionEnum::TempoType, "tempo", '\0', "accuracy", {"accuracy", "compat"}, "Prioritize tempo accuracy or compatibility with effects"}, -}; - -template<> -void Factory::InitializeImpl() -{ - Clear(); - - Register(); - Register(std::move(MODOptions)); -} - -template<> -void Factory::InitializeImpl() -{ - Clear(); - - Register("Deflemask", "dmf"); - Register("ProTracker", "mod"); -} - -void d2m::Initialize() -{ - Factory::Initialize(); - Factory::Initialize(); -} diff --git a/src/webapp/webapp.cpp b/src/webapp/webapp.cpp index 6f9bb55..f84887f 100644 --- a/src/webapp/webapp.cpp +++ b/src/webapp/webapp.cpp @@ -43,8 +43,6 @@ static void SetStatusType(bool isError); int main() { - Initialize(); - // Initialize global options (for web app, user won't provide them) GlobalOptions::Get().GetOption(GlobalOptions::OptionEnum::Force).SetValue(true); GlobalOptions::Get().GetOption(GlobalOptions::OptionEnum::Verbose).SetValue(false);