Factory and FactoryRegistrators initialized in wrong order

Hi everyone,
I have the following weird problem (working on a library):

I have an abstract class ExportFilter, that has a static element Factory<ExportFilter>. The intention is that the derived classes are registered there so you can instantiate them.

export.h:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    class ExportFilter
    {
      public:
        static Factory<ExportFilter> ExportFilterFactory;
        ...
    };

    class ExportFilterDOT : public ExportFilter
    {
        ...
    };

    class ExportFilterGASTEX : public ExportFilter
    {
        ...
    };

    ... (more derived classes)


now to register them, I (globally) instantiate FactoryRegistrator<ExportFilter> objects. these are "dummy"-objects, that are only there so that their constructor is executed - the constructor registers the class at the factory.

So I can enforce the RegisterClass calls within the library and don't have to worry whether some initialization-code is called from a program using my library.

export.cpp
1
2
3
4
    #include "export.h"
    ...
    Factory<ExportFilter> ExportFilter::ExportFilterFactory;
    ...


dot.cpp
1
2
3
4
5
    #include "export.h"
    ...
    FactoryRegistrator<ExportFilter> ExportFilterDotRegistrator(
      &ExportFilter::ExportFilterFactory, "dot", new DefaultInstantiator<...>(...));
    ...



gastex.cpp
1
2
3
4
5
    #include "export.h"
    ...
    FactoryRegistrator<ExportFilter> ExportFilterGastexRegistrator(
      &ExportFilter::ExportFilterFactory, "gastex", new DefaultInstantiator<...>(...));
    ...


When I run a program using the library, I get a segmentation fault.
If I comment out the FactoryRegistrator for ExportFilterDot, it works (I dont't have to comment out any other Registrator) but then I cannot instantiate ExportFilterDOT using the factory...

The problem seems to be, that the ExportFilterDotRegistrator is initialized before the ExportFilter::ExportFilterFactory is initialized, so passing the Factorys address to the FactoryRegistrator's constructor causes the segmentation fault...
I guess that's because dot.cpp lexicographically comes before export.cpp, so it's objects are initialized first (gastex.cpp and all others come after export.cpp)

Shouldn't the linker get this right? I mean dot.cpp includes export.h, where the Factory is declared. shouldn't the linker see that the factory must be initialized first, because (by inclusion of export.h) dot.cpp says that it expects to be able to use the factory?

And more importantly, how can I circumvent this problem? I don't really want a tinkered solution like giving dot.cpp a less self-explanatory filename...

Thanks in advance for any help :-)
cya

Viktor
Last edited on
Initialization order has nothing to do with the order of your include files. Base classes are always initialized before derived classes in case the hierarchy calls for two classes which use the same base (Each class is only constructed once).
Virtual base classes are constructed foremost, going from left to right in the derivation list, then other base classes in the same format, and finally the class that was actually created.
Last edited on
Just construct the factory on the first use, rather than play russian roulette with static initialization across different TUs: http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.15
Thank you for your help :-)

@BlackSheep: that sounds promising, I'll try turning the registrators into static members of their respective ExportFilter :-)

@Cubbi: The article is interesting, but I don't like the method described there. Of course the OS takes back the memory at program termination, but I like to write my code as clean as possible and it doesn't seem very clean to leave stuff in memory and let the OS kick it out... At least the pointer should be a static member of the class (not static inside a method) so you can delete the object. But then you have to work with some guard-object that frees memory upon leaving its scope (global -> at program termination). This however would put you in the same initialization/deinitialization problem as before... :-/
@BlackSheep: that sounds promising, I'll try turning the registrators into static members of their respective ExportFilter :-)


What BlackSheep said has nothing to do with your problem, which is construction order of different static objects in different translation units (and not the order of initialization of the base classes for one object.)

I think I'd rather go with the the not so 'very clean' option than one that doesn't work.

Of course, static initialization order is well defined if all of the objects you are initializing are in the same translation unit.
> but I like to write my code as clean as possible
> and it doesn't seem very clean to leave stuff in memory and let the OS kick it out...
> But then you have to work with some guard-object that frees memory

std::shared_ptr<> is such a guard object. http://en.cppreference.com/w/cpp/memory/shared_ptr

1
2
3
4
5
6
7
8
#include <memory>

std::shared_ptr< Factory<ExportFilter> > export_filter_factory()
{
    static std::shared_ptr< Factory<ExportFilter> > factory =
                             std::make_shared< Factory<ExportFilter> >() ;
    return factory ;
}


This is not required (the OS does clean up on program termination) on most implementations.
Good news, everyone </professor farnsworth>

I found this article
http://publib.boulder.ibm.com/infocenter/comphelp/v8v101/topic/com.ibm.xlcpp8a.doc/proguide/ref/tushrlib.htm

which directed me here
http://publib.boulder.ibm.com/infocenter/comphelp/v8v101/topic/com.ibm.xlcpp8a.doc/compiler/ref/rnpgprio.htm#rnpgprio

assigning higher priorities to objects that have to be initialized earlier, using #pragma priority, didn't work
Warning: #pragma priority i ignored [-Wunknown-pragmas]

but they also mention assigning different priorities to different source files using the compiler option -qpriority and indeed, in codeblocks you can configure individual .cpp files. I didn't even have to force -qpriority into the commandline manually - the configuration dialog has an option for the priority :-)

I knew there had to be a clean solution!
#pragma priority and the commandline option -qpriority are the non-standard compiler extensions offered by the IBM XL C/C++ compiler, which happens to be my primary compiler at work. But since my code has to be portable across several other platforms, I don't use this extension.

A non-standard non-portable extension is never a "clean solution"
Topic archived. No new replies allowed.