Home > Blockchain >  Why c static member was not initialized in this case?
Why c static member was not initialized in this case?

Time:07-23

registerT is not called and the function was not registered in the map. I have no clue. This is the code.

//factory.h

#include <iostream>
#include <memory>
#include <string>
#include <unordered_map>
#include <cstdlib>
#include <cxxabi.h>
template <typename BaseType, typename... Args>
class Factory {
public:
    static std::shared_ptr<BaseType> Make(const std::string &name) {
        return constructMap().at(name)();
    }
public:
    template <typename T>
    class Registrar : public BaseType {
        public:
            friend T;
            static bool registerT() {
                const auto name = "Derive";
                // add constuct function to funcMap
                // ...
                return true;
            }
            static bool registered;
    };

    private:
        // declare funcMap

};

//base.h

#include "factory.h"

class Base : public Factory<Base> {
public:
    Base() {}
};

//derive.h

#include <string>
#include "base.h"

class Derive : public Base::Registrar<Derive> {
public:
    Derive();
};

//derive.cpp

#include "derive.h"

Derive::Derive() {}

//main.cpp

#include "base.h"

int main() {
  std::string name = "Derive";
  auto b = Base::Make(name);
  return 0;
}

Use g -o mian ./main.cpp -Lderive.o to get exe file. But the function registerT was not called when exec ./mian

It works when using g -o mian ./main.cpp derive.o to get exe file. Thank you.@user17732522

The problem is solved. Thank you.

CodePudding user response:

First of all, the compilation command is wrong. The -L option expects a path to a directory containing libraries which are specified later. I am not sure what you are attempting by using it here, but as a result you are not linking the derive translation unit. .o files should simply be listed like source files, e.g.

g   -o mian main.cpp derive.o

(This part doesn't apply anymore after the question was edited in response.)

After fixing that.

The program has undefined behavior.

This is because there is no guarantee that Derive::name is initialized before Factory<Base>::Registrar<Derive>::registered. In particular the latter is a specialization of a template and therefore has so-called unordered dynamic initialization, meaning that there is no ordering guarantee for it with any other global static storage duration object.

However, the initialization of the latter calls registerT() which accesses the former in const auto name = T::name;. If name has not been initialized at this point, accessing it causes undefined behavior.

The usual workaround is to not rely on inter-dependent dynamically-initialized static members, but instead use static functions with local static variables as you are already doing for constructMap. E.g. here you could use the same for name.


Additionally, even if there was no ordering issue with the static storage duration objects you defined yourself, there is still the issue that the standard streams need to be initialized before std::cout may be used. They are initialized through construction of an object of type std::ios_base::Init. Including <iostream> behaves as if it defined a global static storage duration object of that type, which usually guarantees it is initialized before you use it. But again, here registered is unordered with it.

So you must create an object of type std::ios_base::Init yourself in registerT before trying to write to std::cout.

  • Related