I was trying an auto-registering CRTP factory class just like crtp-registering. But I've got a curious problem here with static template member initialization. Here is the test code:
#include <string>
#include <unordered_map>
#include <iostream>
#include <memory>
#include <functional>
#include <string_view>
template <typename Base>
class Factory
{
template <typename, typename>
friend class Registrable;
private:
static std::unordered_map<std::string, std::shared_ptr<Base>> &map()
{
static std::unordered_map<std::string, std::shared_ptr<Base>> map;
return map;
}
template <typename Derived>
static void subscribe(std::string name)
{
// insert already-exist-check here
std::cout << "registered: " << name << std::endl;
map().emplace(std::move(name), std::static_pointer_cast<Base>(std::make_shared<Derived>(Derived())));
}
};
template <typename T>
constexpr auto type_name() noexcept
{
std::string_view name, prefix, suffix;
#ifdef __clang__
name = __PRETTY_FUNCTION__;
prefix = "auto type_name() [T = ";
suffix = "]";
#elif defined(__GNUC__)
name = __PRETTY_FUNCTION__;
prefix = "constexpr auto type_name() [with T = ";
suffix = "]";
#endif
name.remove_prefix(prefix.size());
name.remove_suffix(suffix.size());
return name;
}
template <typename Base, typename Derived>
class Registrable
{
protected:
Registrable()
{
isRegistered = true;
}
~Registrable() = default;
static bool init()
{
Factory<Base>::template subscribe<Derived>(std::string(type_name<Derived>()));
return true;
}
private:
static bool isRegistered;
};
template <typename Base, typename Derived>
bool Registrable<Base, Derived>::isRegistered = Registrable<Base, Derived>::init();
struct MyFactoryBase
{
virtual ~MyFactoryBase() = default;
virtual void method() const = 0;
};
struct MyFactory1 : public MyFactoryBase, public Registrable<MyFactoryBase, MyFactory1>
{
void method() const override { std::cout << "yay Class1" << std::endl; }
MyFactory1() = default;
};
struct MyFactory2 : public MyFactoryBase, public Registrable<MyFactoryBase, MyFactory2>
{
void method() const override { std::cout << "yay Class1" << std::endl; }
MyFactory2() : MyFactoryBase(), Registrable<MyFactoryBase, MyFactory2>() {}
};
int main()
{
return 0;
}
my gcc version : gcc (GCC) 8.3.1 20191121 (Red Hat 8.3.1-5)
the code's output is :
registered: MyFactory2
why MyFactory2 can auto reigst while MyFactory1 cannot, what's the difference between the default constructor and the almost-default constructor
MyFactory2() : MyFactoryBase(), Registrable<MyFactoryBase, MyFactory2>() {}