Factory, unique_ptr and static_cast

Consider polymorphic classes with a base object, a derived interface, and a target:

// base object struct object { virtual ~object() = default; }; // interfaces derived from base object struct interface1 : object { virtual void print_hello() const = 0; template<typename T> static void on_destruction(object* /*ptr*/) { std::cout << "interface1::on_destruction" << std::endl; } }; // final object struct derived1 : interface1 { virtual void print_hello() const override { std::cout << "hello" << std::endl; } static std::string get_type_name() { return "derived1"; } }; 

In real use, end objects are defined through a plug-in system, but this is not so. Please note that I want to be able to call on_destruction when the object is destroyed (see register_object below). I want to use these classes as follows:

 int main() { // register derived1 as an instantiable object, // may be called in a plugin register_object<derived1>(); // create an instance using the factory system auto instance = create_unique<interface1>("derived1"); instance->print_hello(); return 0; } 

Using std :: unique_ptr to manage objects, I got the following code for register_object :

 template<typename T> using unique = std::unique_ptr< T, std::function<void(object*)> // object deleter >; namespace { std::map< std::string, std::function<unique<object>(void)> > factory_map; } template<typename T> void register_object() { factory_map.emplace( T::get_type_name(), []() { unique<T> instance{ new T, [](object* ptr) { T::on_destruction<T>(ptr); delete ptr; } }; return static_move_cast<object>( std::move(instance) ); } ); } 

And the create * functions:

 unique<object> create_unique_object(const std::string& type_name) { auto f = factory_map.at(type_name); return f(); } template<typename T> unique<T> create_unique(const std::string& type_name) { return static_move_cast<T>( create_unique_object(type_name) ); } 

You notice in register_object and create_unique call to static_move_cast , which is declared as:

 template<typename U, typename T, typename D> std::unique_ptr<U, D> static_move_cast ( std::unique_ptr<T, D>&& to_move_cast ) { auto deleter = to_move_cast.get_deleter(); return std::unique_ptr<U, D>{ static_cast<U*>( to_move_cast.release() ), deleter }; } 

The purpose of static_move_cast is to enable static_cast on std :: unique_ptr when moving the deleter during translation. The code works, but I want to hack std :: unique_ptr. Is there a way to reorganize the code to avoid my static_move_cast ?

+6
source share
2 answers

static_move_cast not required in register_object , since you can simply use the conversion constructor unique_ptr template< class U, class E > unique_ptr( unique_ptr<U, E>&& u ) :

  unique<T> instance{ new T, // ... }; return instance; 

Or, even simpler, build and return a unique<object> directly, since T* can be converted to object* :

  return unique<object>{ new T, // ... }; 

However, for create_unique using static_move_cast inevitable, since the unique_ptr conversion unique_ptr will not work for downcasts.

Note that shared_ptr has a static_pointer_cast that performs downcasts, but there is no appropriate tool for unique_ptr , presumably because it is considered simple and correct to do the throw yourself.

+1
source

I would say that this is a good solution, given the requirements. You transfer responsibility to the caller create_unique . It should give the correct combination of type and string and string, which is in the registry.

 auto instance = create_unique<interface1>("derived1"); // ^^^^^^^^^^ ^^^^^^^^ // what if those two don't match? 

You can improve it a bit by changing static_cast to dynamic_cast . And the create_unique should always verify that it received a non-zero pointer before calling anything on it.

Or at least use dynamic_cast with assert in debug mode, so you find inconsistencies during development.


Alternative refactoring: Separate the factory for each existing interface.

+1
source

All Articles