c++ - Clang links to different locations when referring a templated static variable from multiple compilation units -


in attempt compile existing (gcc developed) code base clang, we're facing interesting problem. result clang-compiled executable creates multiple instances of singletons. not sure if our usage , understanding in accordance standard, or if gcc an/or clang or c++ standard library , toolchain on linux have problem.

  • we're using factory create singleton instances
  • the actual creation delegated policy template
  • at places, we're using variation of singleton factory, actual type of singleton can configured @ definition site, without necessity reveal concrete type client accessing singleton. client knows interface type
  • the problem shows when referring "same" static variable through inlined function used different compilation units

the following excerpt, omitting locking, lifecycle issues, initialisation , clean-up


file-1: clang-static-init.hpp

#include <iostream> using std::cout;  namespace test {    /* === layer-1: singleton factory based on templated static variable === */    template<typename                     ///< interface of product type           ,template <class> class fac     ///< policy: actual factory create instance           >   struct holder     {       static i* instance;        i&       get()         {           if (!instance)             {               cout << "singleton factory: invoke fabrication ---> address of static instance variable: "<<&instance<<"...\n";                instance = fac<i>::create();             }           return *instance;         }     };    /**    * allocate storage per-type shared    * (static) variable hold singleton instance    */   template<typename           ,template <class> class f           >   i* holder<i,f>::instance;       template<typename c>   struct factory     {       static c*       create()         {           return new c();         }     };        /* === layer-2: configurable product type === */    template<typename i>   struct adapter     {       typedef i* factoryfunction (void);        static factoryfunction* factoryfunction;         template<typename c>       static i*       concretefactoryfunction()         {           return static_cast<i*> (factory<c>::create());         }         template<typename x>       struct adaptedconfigurablefactory         {           static x*           create()             {               return (*factoryfunction)();             }         };     };    /** storage per-type shared function pointer concrete factory */   template<typename i>   typename adapter<i>::factoryfunction*  adapter<i>::factoryfunction;      template<typename c>   struct typeinfo { };      /**    * singleton factory ability configure actual product type c    * @ \em definition site. users see interface type t    */   template<typename t>   struct configurableholder     : holder<t, adapter<t>::template adaptedconfigurablefactory>     {       /** define actual product type */       template<typename c>       configurableholder (typeinfo<c>)         {           adapter<t>::factoryfunction = &adapter<t>::template concretefactoryfunction<c>;         }     };        /* === actual usage: test case fabricating subject instances === */    struct subject     {       static int creationcount;        subject();      };    typedef configurableholder<subject> accesspoint;    /** singleton factory instance */   extern accesspoint fab;     subject& fabricate();  } // namespace test 

file-2: clang-static-init-1.cpp

#include "clang-static-init.hpp"   test::subject& localfunction() {   return test::fab.get(); }   int main (int, char**)   {     cout <<  "\nstart testcase: invoking 2 instances of configurable singleton factory...\n\n";      test::subject& ref1 = test::fab.get();     test::subject& sub2 = test::fabricate();  ///note: invoking get() within compilation unit reveales problem     test::subject& sub3 = localfunction();      cout << "sub1="  << &ref1          << "\nsub2="<< &sub2          << "\nsub3="<< &sub3          << "\n";       return 0;   } 

file-3: clang-static-init-2.cpp

#include "clang-static-init.hpp"    namespace test {     int subject::creationcount = 0;    subject::subject()     {       ++creationcount;       std::cout << "subject("<<creationcount<<")\n";     }      namespace {       typeinfo<subject> shall_build_a_subject_instance;   }    /**    * instance of singleton factory    * @note example we're using \em 1    *       shared instance of factory.    *       yet still, 2 (inlined) calls get() function might    *       access different addresses embedded singleton instance    */   accesspoint fab(shall_build_a_subject_instance);     subject&   fabricate()   {     return fab.get();   }   } // namespace test 

notable points

  • we're using single instance of accesspoint
  • yet still, different compilation units using (inlined) function holder<t,f>::get(), see different locations static variable instance
  • while actual ctor call configurableholder templated concrete type of singleton create, specific type info erased; should not bear relevance type of adapter or configurableholder
  • if understanding correct, usages of get() should see same type of holder , same location of static variable embedded in holder
  • but in fact clang compiled executable invokes factory again sub2, called aonther compilation unit, while sub1 , sub3 share same singleton instance expected

interestingly, symbol table of executable built clang-3.0 shows static variable has been linked twice (the behaviour same when using clang-3.2)

10: 0000000000000000     0 file    local  default  abs research/clang-static-init-1.cpp 11: 0000000000400cd0    11 func    local  default   14 global constructors keyed 12: 0000000000400b70   114 func    local  default   14 test::holder<test::subject, test::adapter<test::subject>::adaptedconfigurablefactory>::get() 13: 00000000004027e0     8 object  local  default   28 test::holder<test::subject, test::adapter<test::subject>::adaptedconfigurablefactory>::instance 14: 00000000004027d8     1 object  local  default   28 std::__ioinit 15: 0000000000400b10    62 func    local  default   14 __cxx_global_var_init 16: 0000000000000000     0 file    local  default  abs research/clang-static-init-2.cpp 17: 00000000004010e8     0 notype  local  default   17 gcc_except_table9 18: 0000000000400e60    16 func    local  default   14 global constructors keyed 19: 00000000004027f9     1 object  local  default   28 test::(anonymous namespace)::shall_build_a_subject_instance 20: 0000000000400de0   114 func    local  default   14 test::holder<test::subject, test::adapter<test::subject>::adaptedconfigurablefactory>::get() 21: 0000000000402800     8 object  local  default   28 test::holder<test::subject, test::adapter<test::subject>::adaptedconfigurablefactory>::instance 

...while relevant section of gcc-4.7.2 compiled executable reads expected

44: 0000000000400b8c    16 func    global default   14 localfunction() 45: 00000000004026dc     1 object  global default   28 test::fab 46: 0000000000400c96    86 func    weak   default   14 test::holder<test::subject, test::adapter<test::subject>::adaptedconfigurablefactory>::get() 47: 00000000004026e0   272 object  global default   28 std::cout 48: 0000000000000000     0 func    global default  und std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(st 49: 0000000000400d4b    16 func    global default   14 test::fabricate() 50: 0000000000000000     0 func    global default  und std::basic_ostream<char, std::char_traits<char> >::operator<<(void const*) 51: 00000000004026d0     8 object  unique default   28 test::holder<test::subject, test::adapter<test::subject>::adaptedconfigurablefactory>::instance 52: 0000000000400cec    15 func    weak   default   14 test::adapter<test::subject>::adaptedconfigurablefactory<test::subject>::create() 53: 00000000004026c8     8 object  unique default   28 test::adapter<test::subject>::factoryfunction 

we're using debian/stable 64bit (gcc-4.7 , clang-3.0) , debian/testing 32bit (clang-3.2) build

the fix declare singleton template class extern, , explicitly instantiate singleton in single compilation unit.

if compilation units in separate (shared) libraries, clang behaving way because can.

when code compiled, compiler instantiates singleton template every time specified. @ link time, 1 instantiation discarded. happens if have shared libraries in project, , there several link-times? each shared object have 1 instantiation of template. gcc ensures there 1 surviving template instantiation in final executable (maybe using vague linkage?), evidently clang not.


Comments

Popular posts from this blog

java.util.scanner - How to read and add only numbers to array from a text file -

rewrite - Trouble with Wordpress multiple custom querystrings -