vendredi 12 mai 2023

Auto registration of types with C++ ODR use

I'm trying to get a demo code working to show auto registration with the help of ODR use (I've only learned about this in the last day so forgive me if I'm not using that correctly).

This is the interesting bits of the code

// Interface for the class.
class ICompressionMethod {
 public:
  ICompressionMethod() = default;
  virtual ~ICompressionMethod() = default;

  virtual void Compress() = 0;
};

// Helper class holding static methods for factory
class CompressionMethodFactory {
 public:
  using TCreateMethod = std::unique_ptr<ICompressionMethod> (*)();

 public:
  CompressionMethodFactory() = delete;

  static bool Register(const std::string name, TCreateMethod createFunc) {
    if (auto it = s_methods.find(name); it == s_methods.end()) {
      s_methods[name] = createFunc;
      std::cout << name << " registered\n";
      return true;
    }
    return false;
  }

  static std::unique_ptr<ICompressionMethod> Create(const std::string& name) {
    if (auto it = s_methods.find(name); it != s_methods.end())
      return it->second();

    return nullptr;
  }

 private:
  static std::map<std::string, TCreateMethod> s_methods;
};


std::map<std::string, CompressionMethodFactory::TCreateMethod>
    CompressionMethodFactory::s_methods;



template <bool*>
struct OdrUse {};

template <typename T>
class RegisteredInFactory {
 protected:
  static bool s_bRegistered;
  static constexpr OdrUse<&s_bRegistered> odr{};
};


template <typename T>
bool RegisteredInFactory<T>::s_bRegistered =
    (CompressionMethodFactory::Register(T::kFactoryName, T::CreateMethod),
     true);


class ZipCompression : public ICompressionMethod,
                       public RegisteredInFactory<ZipCompression> {
 public:
  static constexpr const char* kFactoryName = "ZIP";
  void Compress() override {
    // if (s_bRegistered) std::cout << "registered\n";
    std::cout << "in compress\n";
  }

  static std::unique_ptr<ICompressionMethod> CreateMethod() {
    return std::make_unique<ZipCompression>();
  }
  static std::string GetFactoryName() { return "ZIP"; }
};

int main(int argc, char* argv[]) {

  std::cout << "main starts...\n";
  auto pMethod = CompressionMethodFactory::Create("ZIP");
  if (pMethod != nullptr) {
    pMethod->Compress();
  } else {
    std::cout << "factory creation failed\n";
  }
  std::cout << "end" << "\n";
  return 0;
}

Lot of the code has been copied from https://www.cppstories.com/2018/02/factory-selfregister/

I'm getting this error when compiling

$ g++ test_odr.cc
test_odr.cc: In instantiation of ‘bool RegisteredInFactory<ZipCompression>::s_bRegistered’:
test_odr.cc:50:27:   required from ‘class RegisteredInFactory<ZipCompression>’
test_odr.cc:59:31:   required from here
test_odr.cc:55:44: error: incomplete type ‘ZipCompression’ used in nested name specifier
   55 |     (CompressionMethodFactory::Register(T::kFactoryName, T::CreateMethod),
      |                                            ^~~~~~~~~~~~
test_odr.cc:55:61: error: incomplete type ‘ZipCompression’ used in nested name specifier
   55 |     (CompressionMethodFactory::Register(T::kFactoryName, T::CreateMethod),
      |                                                             ^~~~~~~~~~~~

Looking at the error, why does the compiler need RegisteredInFactory<ZipCompression> fully defined to access kFactoryName ?

Aucun commentaire:

Enregistrer un commentaire