在编译时或运行时将常量字符 * 映射到鸭型 T
Mapping const char * to duck-typed T at compile-time or run-time
我有许多类A,B,C,D等是鸭型的,因此具有相同的方法和接口,但不是从同一类继承的。
例如
class A {
public:
void foo();
void bar();
}
class B {
public:
void foo();
void bar();
}
class C {
public:
void foo();
void bar();
}
我想在运行时将const char *
映射到这些类之一的相应实例,例如
"A"
-> A a
"B"
-> B b
这里a
是类A
的某个实例。
OR 在编译时将"const char *"映射到相应的类型,例如
"A"
-> A
我需要在其他一些功能调用(即调用foo()
或bar()
)中使用对象的实例,但 API 只能接受 const char *,因为底层对象被抽象掉了。
我正在一个大型的代码生成代码库中工作,因此更改范式是不切实际的。
使用适配器接口和一堆实现该接口的具体适配器执行类型擦除;适配器可以是类模板的实例。
struct IFooBar {
virtual ~IFooBar() {}
virtual void foo() = 0;
virtual void bar() = 0;
};
template<class T> struct FooBarAdaptor : IFooBar {
T* t;
FooBarAdaptor(T* t) : t{t} {} ~FooBarAdaptor() {}
void foo() override { return t->foo(); }
void bar() override { return t->bar(); }
};
// ...
A a;
B b;
C c;
std::map<std::string, std::unique_ptr<IFooBar>> m;
m["a"] = std::make_unique<FooBarAdaptor<A>>(&a);
m["b"] = std::make_unique<FooBarAdaptor<B>>(&b);
m["c"] = std::make_unique<FooBarAdaptor<C>>(&c);
Fatal 允许您使用编译时字符串、类型映射和字符串查找结构轻松解决问题的编译时版本。
让我们首先从我们将使用的标头开始:
// type_map so we can associated one type to another
#include <fatal/type/map.h>
// for efficient compile-time built string lookup structures
#include <fatal/type/string_lookup.h>
// for compile-time string
#include <fatal/type/sequence.h>
在此示例中,我们基本上希望将字符串与操作相关联,两者都由类型表示。
struct foo_action {
// FATAL_STR creates a compile-time string, equivalent to
// `using name = fatal::constant_sequence<char, 'f', 'o', 'o'>;`
FATAL_STR(name, "foo");
static void DOIT() { std::cout << "FOO-ACTION"; }
};
struct bar_action {
FATAL_STR(name, "bar");
static void DOIT() { std::cout << "BAR-ACTION"; }
};
struct baz_action {
FATAL_STR(name, "baz");
static void DOIT() { std::cout << "BAZ-ACTION"; }
};
现在我们创建从编译时字符串到关联类型的映射:
using my_map = fatal::build_type_map<
foo_action::name, foo_action,
bar_action::name, bar_action,
baz_action::name, baz_action
>;
为了在运行时执行高效的字符串查找,让我们在编译时创建一些查找结构,因为我们已经有可供编译器使用的字符串。实际结构是实现定义的,但它通常使用前缀树或完美哈希:
using my_lookup = my_map::keys::apply<fatal::string_lookup>;
现在,我们需要一个访问者,每当查找中有匹配项时,都会调用该访问者。
访问者将收到编译时字符串作为其第一个参数,包装在类型标记中以确保它是一个空实例。
您可以接收任意数量的附加参数。在此示例中,我们将接收a1
和a2
作为用于演示目的的额外参数。它们不是强制性的,可以安全地删除:
struct lookup_visitor {
// note that we don't give the type_tag parameter a name
// since we're only interested in the types themselves
template <typename Key>
void operator ()(fatal::type_tag<Key>, int a1, std::string const &a2) const {
// Key is the compile-time string that matched
// now let's lookup the associated type in the map:
using type = typename my_map::template get<Key>;
// at this point you have `type`, which is the type associated
// to `Key` in `my_map`
// TODO: at this point you can do whatever you like with the mapped type
// and the extra arguments. Here we simply print stuff and call a method from
// the mapped type:
std::cout << "performing action from type '" << typeid(type).name()
<< "' (additional args from the call to exact: a1="
<< a1 << ", a2='" << a2 << "'):";
// call the static method `DOIT` from the mapped type
type::DOIT();
std::cout << std::endl;
}
};
现在剩下要做的就是在查找结构中查找字符串,并在找到匹配项时调用访问者。
在下面的代码中,我们从标准输入中读取运行时字符串in
,并在编译时生成的查找结构中查找它。
如上所述,我们还将两个额外的参数传递给 exact()
.这些论点不是由exact()
检查的,而是完美地转发给访问者。它们是完全可选的,它们在这里只是为了演示将其他状态传递给访问者是多么方便。
在此示例中,其他参数56
和"test"
:
int main() {
for (std::string in; std::cout << "lookup: ", std::cin >> in; ) {
// exact() calls the visitor and returns true if a match is found
// when there's no match, the visitor is not called and false is returned
bool const found = my_lookup::match<>::exact(
in.begin(), in.end(), lookup_visitor(),
56, "test"
);
if (!found) {
std::cout << "no match was found for string '" << in << '''
<< std::endl;
}
}
return 0;
}
下面是此代码的示例输出:
$ clang++ -Wall -std=c++11 -I path/to/fatal sample.cpp -o sample && ./sample
lookup: somestring
no match was found for string 'somestring'
lookup: test
no match was found for string 'test'
lookup: foo
performing action from type '10foo_action' (additional args from the call to exact: a1=56, a2='test'): FOO-ACTION
lookup: bar
performing action from type '10bar_action' (additional args from the call to exact: a1=56, a2='test'): BAR-ACTION
lookup: ^D
$
关于上述代码最有趣的部分是,为了支持更多映射,您需要做的就是向my_map
添加另一个条目。编译器将找出其余部分。
更新:更改了代码以反映最新的上游 Fatal。
注意:由于您添加了在运行时添加某些内容的要求,因此无法使用下面的解决方案。我也确信您对C++中鸭子类型的含义感到困惑,因为运行时和鸭子类型不能一起工作。
使用工厂模板和一堆专用项:
template<int type> void create_ducky();
template<> A create_ducky<'A'>() { return A(); }
依此类推,然后像
create_ducky<'A'>().foo();
但是,这完全是胡说八道,因为与其编写'A'
,不如将A
编写为模板参数要容易得多。我相对确定你想要的不是一个好主意,或者你至少在走一条徒劳的道路,实现你真正想要达到的那个(尚未命名的)目标。
也许是一个返回 boost 变体实例的函数,如下所示:
using V = boost::variant< A, B, C >;
V MyFactory(const char * m)
{
if (std::string(m) == "A") return V(A());
...
}
您还可以实例化 std::map<std::string,>。
Fusion 和 Boost MPL 可以帮助:
#include<boost/fusion/container/generation/make_map.hpp>
#include<boost/mpl/char.hpp>
#include<cassert>
#include<boost/fusion/container/generation/make_map.hpp>
#include <boost/fusion/sequence/intrinsic/at_key.hpp>
#include<boost/mpl/char.hpp>
#include<cassert>
struct A{std::string call(){return "a";}};
struct B{std::string call(){return "b";}};
struct C{std::string call(){return "c";}};
int main(){
namespace fus = boost::fusion;
namespace mpl = boost::mpl;
A a{};
B b{};
C c{};
auto mymap = fus::make_map<
mpl::char_<'a'>,
mpl::char_<'b'>,
mpl::char_<'c'>
>(
a, // or use
b,
c
);
// which is of type
// fus::map<
// fus::pair<mpl::char_<'a'>, A>,
// fus::pair<mpl::char_<'b'>, B>,
// fus::pair<mpl::char_<'c'>, char>
// >
// and it is used as:
assert( fus::at_key<mpl::char_<'b'>>(mymap).call() == "b" );
}
(运行代码:http://coliru.stacked-crooked.com/a/aee0daa07510427e)
这是否有帮助取决于是需要运行时多态性还是编译时多态性(此解决方案)。可以使boost::fusion::map
周围的包装器直接接受文字字符。还可以使用编译时字符串。
在你的问题中,你说"编译时或运行时"。但是,如果你两者都需要,它基本上将是"运行时",这意味着某种形式的固有和指针(在某种程度上)。例如,std::map<char, BaseofABC*>
.
- 删除一个线程上有数百万个字符串的大型哈希映射会影响另一个线程的性能
- C++映射:具有自定义类的运算符[]不起作用(总是返回0)
- 将函数类成员映射到类本身内部
- 如何在 C# 中映射双 C 结构指针?
- 如何在C++中使用结构生成映射
- 使用std::函数映射对象方法
- 如何加载(或映射)文件部分的最大大小,但适合在Windows上的RAM
- C++映射分割错误(核心转储)
- 如何防止 c++ 在从浮点型转换为双精度型(不适用于 IO)时添加额外的小数?
- 内联映射初始化的动态atexit析构函数崩溃
- 使用"std::unordereded_map"映射到"std::list"对象
- 错误处理.将系统错误代码映射到泛型
- 将整型常量映射到类型
- 在 stl 映射和列表 (c++) 上进行迭代的泛型循环
- 如何在c++中使用模板制作泛型映射
- 将字符串映射到整型CPP -输出在执行期间挂起
- 迭代整型和对象的映射
- 编写泛型包装器:有条件地将模板参数中的不同类型映射到单个类内部类型
- 在编译时或运行时将常量字符 * 映射到鸭型 T
- 数据映射:从"any"类到泛型键值映射(C++)