作者热门文章
- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我想为一组派生类实现一种通用工厂机制,该机制使我不仅可以通用地实现一个工厂函数来创建该类的对象,而且还可以实现其他模板类的创建者,这些其他模板类的创建者将其中一个派生类作为模板参数。
理想情况下,解决方案仅使用C++ 17功能(不依赖)。
考虑这个例子
#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();
}
foo_factory
和
bar_factory
,这样,一旦我将
FooD
添加为其他派生类,就无需更新它们。理想情况下,不同的Foo衍生物将以某种方式“自我注册”,但将它们全部集中在一个中心位置也是可以接受的。
Foo
/BarInterface
的多态性,即它们不了解具体的派生类。另一方面,在Bar中,我们希望使用派生的Foo类的模板方法并促进内联,这就是为什么我们确实需要模板化的派生的Bar
类(而不是通过某些基类接口(interface)访问Foo对象)的原因。 BarInterface
和Bar
。因此,我们无法创建Bar的“构造器对象”,也无法像对foo_factory
那样将其保存在 map 中。我认为需要的是所有派生的Foo类型的某种“编译时映射”(或列表),以便在定义bar_factory时,编译器可以对其进行迭代,但是我不知道该怎么做。 ... SpecificFoo<double>::name
总是有效的。 @Julius的答案已得到扩展,以方便进行此操作。对于@Yakk来说,可以完成相同的操作(但需要花费一些时间来详细了解它)。 dynamic_cast
)。因此,一种解决方案允许在bar_factory的定义期间内联编写此代码,这对我来说似乎最容易理解。 @Julius的答案在这里非常有用,即使使用元组的循环代码有些冗长。 types
模板或使用元组)定义Foo类型(或模板)的列表,这已经很好了。但是,由于其他原因,我已经在同一中央位置列出了一个宏调用列表,每个foo调用一个,如DECLARE_FOO(FooA, "A") DECLARE_FOO(FooB, "B") ...
。可以在某种程度上利用FooTypes
的声明,因此我不必再次列出它们?我猜想这样的类型列表不能被迭代地声明(追加到已经存在的列表中),或者可以吗?在这种情况下,可能会有一些宏观魔术效果是可能的。也许总是在DECLARE_FOO
调用中重新定义并附加到预处理器列表,然后最终进行一些“迭代结束循环”来定义FooTypes
类型列表。 IIRC boost预处理器具有循环列表的功能(尽管我不希望boost依赖)。 context
,您可以想到不同的Foo及其模板参数,因为类似于
Eigen::Matrix<Scalar>
的类和Bar是与Ceres一起使用的成本仿函数。条工厂返回诸如
ceres::AutoDiffCostFunction<CostFunctor<SpecificFoo>, ...>
之类的对象作为
ceres::CostFunction*
指针。
bar_tmpl_factory
和
bar_ttmpl_factory
统一为一个函数(是这样吗?)。
bar_tmpl_factory
和bar_ttmpl_factory
Making the "single place" listing the Foos even simpler
types
模板替换了元组的使用(但是以一种方式可以在循环的所有foo类型的调用站点上内联地定义creator函数)。 最佳答案
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...
#include <cassert>
#include <tuple>
#include <utility>
#include "foo_and_bar_without_factories.hpp"
////////////////////////////////////////////////////////////////////////////////
template<std::size_t... indices, class LoopBody>
void loop_impl(std::index_sequence<indices...>, LoopBody&& loop_body) {
(loop_body(std::integral_constant<std::size_t, indices>{}), ...);
}
template<std::size_t N, class LoopBody>
void loop(LoopBody&& loop_body) {
loop_impl(std::make_index_sequence<N>{}, std::forward<LoopBody>(loop_body));
}
////////////////////////////////////////////////////////////////////////////////
using FooTypes = std::tuple<FooA, FooB, FooC>;// single registration
std::unique_ptr<Foo> foo_factory(const std::string& name) {
std::unique_ptr<Foo> ret{};
constexpr std::size_t foo_count = std::tuple_size<FooTypes>{};
loop<foo_count>([&] (auto i) {// `i` is an std::integral_constant
using SpecificFoo = std::tuple_element_t<i, FooTypes>;
if(name == SpecificFoo::name) {
assert(!ret && "TODO: check for unique names at compile time?");
ret = std::make_unique<SpecificFoo>();
}
});
return ret;
}
std::unique_ptr<BarInterface> bar_factory(const std::string& name) {
std::unique_ptr<BarInterface> ret{};
constexpr std::size_t foo_count = std::tuple_size<FooTypes>{};
loop<foo_count>([&] (auto i) {// `i` is an std::integral_constant
using SpecificFoo = std::tuple_element_t<i, FooTypes>;
if(name == SpecificFoo::name) {
assert(!ret && "TODO: check for unique names at compile time?");
ret = std::make_unique< Bar<SpecificFoo> >();
}
});
return ret;
}
关于c++ - C++ 17中的泛型工厂机制,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55045367/
我是一名优秀的程序员,十分优秀!