I would like to implement a generic factory mechanism for a set of derived classes that allows me to generically implement not only a factory function to create objects of that class, but also creators of other template classes which take as template arguments one of the derived classes.
Ideally a solution would only use C++17 features (no dependencies).
Consider this example
#include <iostream>
#include <string>
#include <memory>
struct Foo {
virtual ~Foo() = default;
virtual void hello() = 0;
};
struct FooA: Foo {
static constexpr char const* name = "A";
void hello() override { std::cout << "Hello " << name << std::endl; }
};
struct FooB: Foo {
static constexpr char const* name = "B";
void hello() override { std::cout << "Hello " << name << std::endl; }
};
struct FooC: Foo {
static constexpr char const* name = "C";
void hello() override { std::cout << "Hello " << name << std::endl; }
};
struct BarInterface {
virtual ~BarInterface() = default;
virtual void world() = 0;
};
template <class T>
struct Bar: BarInterface {
void world() { std::cout << "World " << T::name << std::endl; }
};
std::unique_ptr<Foo> foo_factory(const std::string& name) {
if (name == FooA::name) {
return std::make_unique<FooA>();
} else if (name == FooB::name) {
return std::make_unique<FooB>();
} else if (name == FooC::name) {
return std::make_unique<FooC>();
} else {
return {};
}
}
std::unique_ptr<BarInterface> bar_factory(const std::string& foo_name) {
if (foo_name == FooA::name) {
return std::make_unique<Bar<FooA>>();
} else if (foo_name == FooB::name) {
return std::make_unique<Bar<FooB>>();
} else if (foo_name == FooC::name) {
return std::make_unique<Bar<FooC>>();
} else {
return {};
}
}
int main()
{
auto foo = foo_factory("A");
foo->hello();
auto bar = bar_factory("C");
bar->world();
}
I am looking for a mechanism that would allow me to implement both foo_factory
and bar_factory
without listing all classes, such that they do not need to be updated once I add for example FooD
as an additional derived class. Ideally, the different Foo derivatives would somehow "self-register", but listing them all in one central place is also acceptable.
Edit:
Some clarifications based on comments / answers:
- It is necessary in my case to invoke the factories with (something like) a string, since the callers of the factories use polymorphism with
Foo
/BarInterface
, i.e. they don't know about the concrete derived classes. On the other hand in Bar we want to use template methods of the derived Foo classes and facilitate inlining, that's why we really need the templated derivedBar
classes (rather than accessing Foo objects through some base-class interface). - We can assume that all derived Foo classes are defined in one place (and a manual registration where we list them all once in the same place is therefore acceptable, if necessary). However, they do not know about the existence of Bar, and in fact we have multiple different classes like
BarInterface
andBar
. So we cannot create "constructor objects" of Bar and save them in a map the same way we can do it for afoo_factory
. What I think is needed is some kind of "compile-time map" (or list) of all the derived Foo types, such that when defining the bar_factory, the compiler can iterate over them, but I don't know how to do that...
Edit2:
Additional constraints that proofed to be relevant during discussion:
- Templates and template templates: The Foo are actually templates (with a single class argument) and the Bar are template templates taking a concrete Foo as template argument. The Foo templates have no specializations and all have the same "name", so querying any concrete type is fine. In particular
SpecificFoo<double>::name
is always valid. @Julius' answer has been extended to facilitate this already. For @Yakk's the same can probably be done (but it will take me some time for figure it out in detail). - Flexible bar factory code: The factory for Bar does a little more than just call the constructor. It also passes some arguments and does some type casting (in particular, it may have Foo references that should be
dynamic_cast
to the corresponding concrete derived Foo). Therefore a solution that allows to write this code inline during definition of the bar_factory seems most readable to me. @Julius' answer works great here, even if the loop code with tuples is a little verbose. - Making the "single place" listing the Foos even simpler: From the answers so far I believe the way to go for me is having a compile-time list of foo types and a way to iterate over them. There are two answers that define a list of Foo types (or templates) in one central place (either with a
types
template or with tuples), which is already great. However, for other reasons I already have in the same central place a list of macro calls, one for each foo, likeDECLARE_FOO(FooA, "A") DECLARE_FOO(FooB, "B") ...
. Can the declaration ofFooTypes
be somehow take advantage of that, so I don't have to list them again? I guess such type lists cannot be declared iteratively (appending to an already existing list), or can it? In the absence of that, probably with some macro magic it would be possible. Maybe always redefining and thus appending to a preprocessor list in theDECLARE_FOO
calls, and then finally some "iterate over loop" to define theFooTypes
type list. IIRC boost preprocessor has facilities to loop over lists (although I don't want a boost dependency).
For some more context
, you can think of the different Foo and it's template argument as classes similar to Eigen::Matrix<Scalar>
and the Bar are cost functors to be used with Ceres. The bar factory returns objects like ceres::AutoDiffCostFunction<CostFunctor<SpecificFoo>, ...>
as ceres::CostFunction*
pointers.
Edit3:
Based on @Julius' answer I created a solution that works with Bars that are templates as well as template templates. I suspect one could unify bar_tmpl_factory
and bar_ttmpl_factory
into one function using variadic variadic template templates (is that a thing?).
TODO:
combinebar_tmpl_factory
andbar_ttmpl_factory
the pointMaking the "single place" listing the Foos even simpler
from abovemaybe replacing the use of tuples with @Yakk'stypes
template (but in a way such that the creator function can be defined inline at the call site of the loop over all foo types).
I consider the question answered and if anything the above points should be separate questions.
Write a generic factory like the following that allows registration at the class site:
And then use it like:
and
Note: there is no way to guarantee that the class would be registered. The compiler might decide not to include the translation unit, if there are no other dependencies. It is probably better to simply register all classes in one central place. Also note that the self-registering implementation depends on inline variables (C++17). It is not a strong dependence, and it is possible to get rid of it by declaring the booleans in the header and defining them in the CPP (which makes self-registering uglier and more prone to failing to register).
edit
The examples above assume that the definition of
Bar<T>
is moved aboveFoo
. If that is impossible, then the registration can be done in an initialization function, in a cpp:Here is one basic option:
In C++17, we can apply the fold expression to simplify the storing process of generating functions
std::make_unique<FooA>()
,std::make_unique<FooB>()
, and so on into the factory class in this case.To begin with, for convenience, let us define the following type alias
Generator
which describes the type of each generating function[](){ return std::make_unique<T>(); }
:Next, we define the following rather generic functor
createFactory
which returns each factory as a hash mapstd::unordered_map
. Here I apply the fold expression with the comma operators. For instance,createFactory<BarInterface, Bar, std::tuple<FooA, FooB, FooC>>()()
returns the hash map corresponding to your functionbar_factory
:This functor enables us to list
FooA, FooB, FooC, ...
all in one central place as follows:DEMO (where I added virtusl destructors in base classes)
that lets us work with bundles of types without the overhead of a tuple.
this lets us work with types as values.
Now a type tag map is a function that takes a type tag, and returns another type tag.
this takes a template type map and makes it into a tag map.
that lets us test a condition on a bunch of types, and run an operation on the one that "succeeds".
now we want to make shared pointers of some template class.
so that makes shared pointers given a type.
now we can compose function objects at compile time.
Next wire it up.
and Done.
The original design was actually c++20 with lambdas instead of those
struct
s forshared_ptr_maker
and the like.Both
make_foos
andmake_bars
have zero runtime state.