To preface, I do not have a complete minimum functional example here to define the factory because I don't have access to the definitions of the dynamic library I'm using.
What I'm looking for right now is suggestions for reading, unless my issue is clearly a general one and doesn't require knowledge of the function definitions to explain.
Suppose we have some variation of a factory pattern which allows the following to compile and operate as intended, per the documentation that came with the DLL:
int main(){
std::cout << "Start" << std::endl;
Base* ptr = Base::Create();
ptr->ConcreteMemberDoSomething();
std::cout << "End" << std::endl;
}
With output:
Start
/-- Concrete Member... Output --/
End
With that in mind, why might this compile but (consistently) cause the program to hang indefinitely when run?:
class Init{
public:
Base* ptr;
Init(){
std::cout << "Ctor start" << std::endl;
ptr = Base::Create();
std::cout << "Ctor end" << std::endl;
};
~Init(){
std::cout << "Dtor" << std::endl;
};
}
Init obj;
int main(){
std::cout << "Start" << std::endl;
obj.ptr->ConcreteMemberDoSomething();
std::cout << "End" << std::endl;
}
With output:
Ctor start
I expect that I have more debugging to do with my main()
, but I don't understand why the Init
constructor freezes. I'm worried it's something to do with the initialization order, since I've been reading about the static initialization order problem, but I don't know what I could try to fix it since I don't have access to any of the definitions compiled in the dynamic library.
This is a probable case of static initialization order fiasco. Although other sorts of problems are probable, this speculation is based solely on the provided snippet code. Generally speaking, creating none-function-local static objects with dynamic initialization is considered bad programming practice and if it is indeed inevitable (e.g std::cout
), carefully designed ad-hoc workarounds must be considered.
One way to get around this problem is known as Scott Mayer's Singleton. User code must not store any pointers or references to the singleton object and always use singleton_t::instance()
. Some recommend wrapping the Mayer's singleton in a monostate type:
struct mono{
single* const operator ->() const{
static single ret{/*TODO:init stuff here*/};
return std::addressof(ret);
};
};
//...
{
//In Some code scope:
mono x;
use(x->y);
};
In the context of OP, assuming the existence of a destroy
counterpart for create
we can use a unique_ptr
:
struct mono{
auto const operator ->() const{
static std::unique_ptr
<Base,
decltype([](Base *const ptr)
{Base::Destroy(ptr);})>
ret {Base::Create();};
return ret;
};
};
If the such Destroy
exists and the object is not supposed to be delete
d at all, then instead of a smart pointer, a raw pointer can be used.