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>() {}
TL;DR The code has undefined behaviour (surprise!). Don't write this code.
The undefined behaviour results from a little accident due to your demonstration. Non-inline variables with static storage duration that is a specialization, in your case isRegistrated, has unordered dynamic initialization. Since its initialization is necessarily dynamic, it has unordered initialization. std::cout is only guaranteed to be initialized before ordered initialization, so you are using an uninitialized std::cout, which is UB.
Suppose we modify your snippet so we avoid std::cout in the initialization.
template <typename Base>
struct Factory
{
static auto& list()
{
static std::vector<std::string> v;
return v;
}
template <typename Derived>
static void subscribe(std::string name)
{
list().push_back(std::move(name));
}
};
What you expect is each specialization of isRegistered is initialized because we implicitly instantiated Registrable, which in turn causes subscribe to be called. That is not the case.
Each MyFactory is a class, whose definition causes the implicit instantiation of its base class template Registrable. The implicit instantiation of a class template instantiates the declarations but not the definitions of its member functions and member variables. Importantly, isRegistered will not be defined just because we inherited from Registrable, thus its initialization won't ever happen.
MyFactory2 defines a default constructor, which calls and instantiates Registrable(). Registrable() in turn uses and instantiates isRegistered. Therefore you see MyFactory2 being registered.
MyFactory1 declares a explicitly-defaulted constructor within the class. Such a declaration is called a non-user-provided defaulted function, which is only defined after its odr-use or when needed in constant evaluation. To force MyFactory1 to be registered, you simply need to use its constructor somewhere.
But we're still not done yet. Even if isRegistered is instantiated, it is not guaranteed to be initialized. Since isRegistered is dynamically initialized, it may be deferred until just before the first odr-use of any static or thread-local variable from the same translation unit that is not part of initialization. Given that your main is empty, it is possible that no initialization happens regardless.
Related
How do I specialize initialize() (see below) where the type isn't based on the method argument, just the overall class template parameter?
template<class STREAM_TYPE>
class MyClass
{
struct MyStruct
{
STREAM_TYPE* _ifs{nullptr};
}
public:
// Use this when STREAM_TYPE = std::ifstream
void initialize()
{
for(MyStruct& ms : _myStructs)
{
ms._ifs = new std::ifstream("");
}
}
// Use this when STREAM_TYPE = std::stringstream
void initialize()
{
}
private:
std::array<MyStruct, 10> _myStructs;
};
Non-template members of class template are themselves independent templates. You can specialize them independently. In your case - by using explicit specialization
// Main template
template<class STREAM_TYPE>
class MyClass
{
void initialize()
{
}
};
// Specialization, declared outside the main template definition
template<>
void MyClass<std::ifstream>::initialize()
{
for(MyStruct& ms : _myStructs)
{
ms._ifs = new std::ifstream("");
}
}
It is up to you to decide which version of the method is the "default" version and which is the "specialized" version. Or maybe you want to declare both versions as specializations.
For example, you might decide to consider both versions as specializations, while defining the main version as deleted
// Main template
template<class STREAM_TYPE>
class MyClass
{
void initialize() = delete;
};
// Specialization, declared outside the main template definition
template<>
void MyClass<std::ifstream>::initialize()
{
for(MyStruct& ms : _myStructs)
{
ms._ifs = new std::ifstream("");
}
}
template<>
void MyClass<std::stringstream>::initialize()
{
}
Just keep in mind that an explicit specialization is no longer a template. It obeys ODR as an ordinary function. Even if your template class is defined in a header file (as template classes usually are), the definition of the above specialization(s) have to go to a .cpp file. The header file should contain mere declarations for your specializations
// header file declarations
template<> void MyClass<std::ifstream>::initialize();
template<> void MyClass<std::stringstream>::initialize();
while the definitions should go to a .cpp file.
What about using SFINAE to enable only the correct version?
template <typename ST = STREAM_TYPE>
std::enable_if_t<std::is_same<ST, std::ifstream>::value> initialize ()
{
std::cout << "ifstream case" << std::endl;
for (MyStruct & ms : _myStructs)
ms._ifs = new std::ifstream("");
}
template <typename ST = STREAM_TYPE>
std::enable_if_t<std::is_same<ST, std::stringstream>::value> initialize ()
{
std::cout << "stringstream case" << std::endl;
}
I have a base class template which has 2 parameters, T is the derived class, flag means I want to activate some feature, default as false:
template
<
typename T,
bool flag
>
class SomeBase
{
public:
static Info& GetInfo()
{
static Info& instance = CreateInfo<T>(T::ClassName());
static bool inited = false;
if (!inited)
{
Test<flag>(instance);
inited = true;
}
return instance;
}
private:
template<bool enable>
static void Test(Info& instance)
{
return;
}
template<>
static void Test<true>(Info& instance)
{
T::Add(fields);
}
};
and to use this base:
class /*dllexport*/ MyClass : public SomeBase<MyClass, false>
{
public:
// ...
};
The flag template parameter is set to false, so according to my specialization, it should compiles the upper empty function, and the compiler does it, which is fine.
But, if I add dllexport to MyClass, then the compiler is giving C2039, which says 'Add' is not a member of MyClass, which doesnt make sense, because I am using SomeBase as flag == false.
Why does adding dllexport makes compiler try to compile the wrong specialization?
////////////////////////////////////////
Edit 1:
////////////////////////////////////////
According to this link:
http://msdn.microsoft.com/en-us/library/twa2aw10%28v=vs.100%29.aspx
Is the statement when one or more of the base classes is a specialization of a class template talking about SomeBase<MyClass, false>?
If so, the compiler implicitly applies dllexport to the specializations of class templates means the compiler is adding dllexport to SomeBase<MyClass, false>.
And, since I've already fully specialized static void Test(Info& instance), the compiler should choose the correct version of Test(), which is Test<false>().
So how come it is choosing(or compiling) the wrong version (Test<true>())?
Thanks!
Without dllexport, you will get the same error when you invoke MyClass::GetInfo from main.
In this case, compiler is expanding and compiling only part code that is invoked.
But with dllexport, it expands and compiles everything.
You can validate with this
template <typename T>
class SomeBase
{
public:
void test()
{
dafsaf;
}
private:
};
class /*__declspec(dllexport)*/ MyClass : public SomeBase<MyClass>
{
public:
// ...
};
int main()
{
MyClass o;
//o.test();
return 1;
}
I was trying to integrate the boost::share_ptr into a pair of templated classes that were originally derived from a boost::asio example I found. When I define a type within one class which is a shared::ptr of that class. I can't seem to reference the type in another templated class. If I remove templates from the code, it all compiles.
This won't compile:
#include <iostream>
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
using namespace std;
template <typename TSomething1>
class SomeTemplateT : public boost::enable_shared_from_this<SomeTemplateT<TSomething1> >
{
public:
typedef boost::shared_ptr<SomeTemplateT<TSomething1> > Ptr;
static Ptr Create()
{
return Ptr(new SomeTemplateT<TSomething1>());
}
SomeTemplateT()
{
cout << "SomeTemplateT created" << endl;
}
};
template <typename TSomething>
class OtherTemplateT
{
public:
OtherTemplateT()
{
// COMPILATION ERROR HERE
SomeTemplateT<TSomething>::Ptr someTemplate = SomeTemplateT<TSomething>::Create();
}
private:
};
The code above yields the following compilation error:
src\Templates\main.cpp: In constructor 'OtherTemplateT<TSomething>::OtherTemplateT()':
src\comps\oamp\src\Templates\main.cpp:30: error: expected ';' before 'someTemplate'
Taking virtually the same code without templates compiles without difficulty:
class SomeTemplateT : public boost::enable_shared_from_this<SomeTemplateT>
{
public:
typedef boost::shared_ptr<SomeTemplateT> Ptr;
static Ptr Create()
{
return Ptr(new SomeTemplateT());
}
SomeTemplateT()
{
cout << "SomeTemplateT created" << endl;
}
};
class OtherTemplateT
{
public:
OtherTemplateT()
{
SomeTemplateT::Ptr someTemplate = SomeTemplateT::Create();
}
private:
};
Platform information:
I'm using gcc4.4.0 from MinGW on windows XP (Code:Blocks IDE).
Am I doing something wrong?
EDIT:
I forgot to mention that if I replace the use of the Ptr typedef with the full declaration of the shared ptr:
boost::shared_ptr
Everything compiles fine.
Also, I can use the type in code outside the of the template.
SomeTemplateT<TSomething>::Ptr is a dependent name; that is, its definition depends on the template parameter. The compiler can't assume that it's a type name unless you say so:
typename SomeTemplateT<TSomething>::Ptr someTemplate = SomeTemplateT<TSomething>::Create();
^^^^^^^^
You need to use typename:
typename SomeTemplateT<TSomething>::Ptr someTemplate = SomeTemplateT<TSomething>::Create();
This is required to make parsing possible without semantic analysis. Whether SomeTemplateT<TSomething>::Ptr is a type or a member is not known until SomeTemplateT<TSomething> has been compiled.
A example taken from the C++11 Standard (n3290) that demonstrate why the keyword typename (in this context) is useful.
( 14.6 Name resolution [temp.res] )
struct A
{
struct X { };
int X;
};
struct B
{
struct X { };
};
template<class T> void f(T t)
{
typename T::X x;
}
void foo()
{
A a;
B b;
f(b); // OK: T::X refers to B::X
f(a); // error: T::X refers to the data member A::X not the struct A::X
}
I need to write template which generates some code depending on whether template parameter is instance of of some class. The template can be generated for all classes but only in case the class is subclass of other class the code should be executed.
The problem is that function that should be implemented does not receive any instance of the class, so the only thing known is class name. So it is impossible to achieve this with dynamic_cast as it demands instance of the object
template<T>
class A
{
void somefunction(void)
{
if (T instanceof Foo) then ...
else ...
}
}
adding some explanation
class X: public Foo {};
class Y {};
class A<X> {} // special logic is generated
class A<Y> {} // special logic is NOT generated
You can use template specialization or boost::is_base_of from the boost traits library
Or of course write your own traits, but shouldn't, for you have not mastered templates yet.
Using specialization, you could
template<T>
class A
{
void somefunction() {
// generic version
}
};
template<>
class A <Foo>
{
void somefunction() {
// foo version
}
};
As always, let me recommend Vandevoorde/Josuttis "C++ Templates: The Complete Guide".
If you fear code bloat because only one memberfunction out of many needs to be specialized, you can still outsource that function:
template <typename T> struct somefunction_helper {
static void thatfunction () {
// generic form
}
};
template <> struct somefunction_helper<Foo> {
static void thatfunction () {
// Foo form
}
};
template<T>
class A
{
void somefunction() {
somefunction_helper<T>::thatfunction();
}
};
This is what template specializations are for. They're more difficult to write, but that's what you do with them. For example:
template<T> class A
{
void somefunction(void) {
...//default for any object type.
}
};
template<> class A<Foo>
{
void somefunction(void) {
...//specific to the type Foo.
}
};
Yes, it requires an extra bit of work. There are some template metaprogramming ways to do this the way you want, but someone else will have to explain those.
Either specialize A or delegate the work of the someFunction() member function template to some free function template which you can (fully) specializes on T.
Since specialization won't work since the template parameter will only ever be derived from Foo, use what another answer said: is_base_of, either from Boost or from the standard library if it already supports parts of C++0x:
#include <type_traits> // C++0x
class Foo{};
template<class T>
class A{
void specialOnFoo(){
// dispatch the call
specialOnFoo(std::is_base_of<T, Foo>::type());
}
void specialOnFoo(std::true_type){
// handle Foo case
}
void specialOnFoo(std::false_type){
// non-Foo case
}
};
Edit #2 - see working example. Please note that this the run-time equivalent of template specialization (which happens at compile-time) and requires RTTI enabled.
#include <iostream>
#include <typeinfo>
using namespace std;
class Foo {};
class X: public Foo {};
class Y {};
template<typename T> class A {
public:
void somefunction()
{
if (typeid(T) == typeid(X)) {
cout << "X specific logic happening" << endl;
}
else {
cout << "Default behavior" << endl;
}
}
};
int main() {
A<X> ax;
A<Y> ay;
ax.somefunction(); // prints "X specific logic happening"
ay.somefunction(); // prints "Default behavior"
}
typeid can be used with templates to extract the type of the template parameter -- as described below:
// expre_typeid_Operator_3.cpp
// compile with: /c
#include <typeinfo>
template < typename T >
T max( T arg1, T arg2 ) {
cout << typeid( T ).name() << "s compared." << endl;
return ( arg1 > arg2 ? arg1 : arg2 );
}
Taken from: http://msdn.microsoft.com/en-us/library/fyf39xec(v=vs.80).aspx
Note that the value of name() is implementation defined.
Why does following code raise an exception (in createObjects call to map::at)
alternativly the code (and its output) can be viewed here
intererestingly the code works as expected if the commented lines are uncommented with both microsoft and gcc compiler (see here), this even works with initMap as ordinary static variable instead of static getter.
The only reason for this i can think of is that the order of initialization of the static registerHelper_ object (factory_helper_)and the std::map object (initMap) are wrong, however i cant see how that could happen, because the map object is constructed on first usage and thats in factory_helper_ constructor, so everything should be alright shouldnt it ?
I am even more suprised that those doNothing() lines fix the issue, because that call to doNothing() would happen after the critical section (which currently fails) is passed anyway.
EDIT: debugging showed, that without the call to factory_helper_.doNothing(), the constructor of factory_helper_ is never called.
#include <iostream>
#include <string>
#include <map>
#define FACTORY_CLASS(classtype) \
extern const char classtype##_name_[] = #classtype; \
class classtype : FactoryBase<classtype,classtype##_name_>
namespace detail_
{
class registerHelperBase
{
public:
registerHelperBase(){}
protected:
static std::map<std::string, void * (*)(void)>& getInitMap() {
static std::map<std::string, void * (*)(void)>* initMap = 0;
if(!initMap)
initMap= new std::map<std::string, void * (*)(void)>();
return *initMap;
}
};
template<class TParent, const char* ClassName>
class registerHelper_ : registerHelperBase {
static registerHelper_ help_;
public:
//void doNothing(){}
registerHelper_(){
getInitMap()[std::string(ClassName)]=&TParent::factory_init_;
}
};
template<class TParent, const char* ClassName>
registerHelper_<TParent,ClassName> registerHelper_<TParent,ClassName>::help_;
}
class Factory : detail_::registerHelperBase
{
private:
Factory();
public:
static void* createObject(const std::string& objclassname) {
return getInitMap().at(objclassname)();
}
};
template <class TClass, const char* ClassName>
class FactoryBase {
private:
static detail_::registerHelper_<FactoryBase<TClass,ClassName>,ClassName> factory_helper_;
static void* factory_init_(){ return new TClass();}
public:
friend class detail_::registerHelper_<FactoryBase<TClass,ClassName>,ClassName>;
FactoryBase(){
//factory_helper_.doNothing();
}
virtual ~FactoryBase(){};
};
template <class TClass, const char* ClassName>
detail_::registerHelper_<FactoryBase<TClass,ClassName>,ClassName> FactoryBase<TClass,ClassName>::factory_helper_;
FACTORY_CLASS(Test) {
public:
Test(){}
};
int main(int argc, char** argv) {
try {
Test* test = (Test*) Factory::createObject("Test");
}
catch(const std::exception& ex) {
std::cerr << "caught std::exception: "<< ex.what() << std::endl;
}
#ifdef _MSC_VER
system("pause");
#endif
return 0;
}
The problem is not related to initialization order, but rather to template instantiation.
Templated code is instantiated on demand, that is, the compiler will not instantiate any templated code that is not used in your program. In particular, in your case the static class member FactoryBase<>::factory_helper_ is not being instantiated and thus it does not exist in the final binary, it does not register itself... (you can check this with 'nm' from the gnu toolchain, that will show the list of symbols present in your executable)
Try changing the FactoryBase constructor to this:
template <class TClass, const char* ClassName>
class FactoryBase {
//...
FactoryBase(){
factory_helper_;
}
//...
};
This will force the compiler into actually instantiating the static member in the binary and you should be set. There is no need to create an empty method and calling it.
EDIT: As an answer to the comment, towards the end of paragraph ยง14.7.1[temp.inst]/1 in the current standard:
Unless a member of a class template or
a member template has been explicitly
instantiated or explicitly
specialized, the specialization of the
member is implicitly instantiated when
the specialization is referenced in a
context that requires the member
definition to exist; in particular,
the initialization (and any associated
side-effects) of a static data member
does not occur unless the static data
member is itself used in a way that
requires the definition of the static
data member to exist.
#define FACTORY_CLASS(classtype) \
class classtype; \
extern const char classtype##_name_[] = #classtype; \
template detail_::registerHelper_<FactoryBase<classtype,classtype##_name_>,classtype##_name_> FactoryBase<classtype,classtype##_name_>::factory_helper_; \
class classtype : FactoryBase<classtype,classtype##_name_>
explicitly instantiating factory_helper_ fixed the issue.