繁体   English   中英

在编译时或运行时将const char *映射到duck-typed 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

这里的aAA一些实例。

或者在编译时将'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>;

现在,我们需要一个访问者,只要在查找中匹配就会调用它。

访问者将接收编译时字符串作为其第一个参数,包装在类型标记中以确保它是一个空实例。

您可以接收任意数量的其他参数。 在这个例子中,我们接收a1a2作为额外的参数用于演示目的。 它们不是强制性的,可以安全地删除:

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添加另一个条目。 编译器将弄清楚其余部分。

更新 :更改代码以反映最新的上游致命因素。

注意 :由于您添加了在运行时添加某些内容的要求,因此您无法使用下面的解决方案。 我也相信你对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,V>。

Boost 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*>

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM