Match const char * with duck type T at compile time or run time

I have many classes A, B, C, D, etc., which are printed by duck and thus have the same methods and interface, but are not inherited from the same class.

eg.

class A {
public:
  void foo();
  void bar();
}
class B {
public:
  void foo();
  void bar();
}
class C {
public:
  void foo();
  void bar();
}

I want to either map const char *to the appropriate instance of one of these classes at runtime, for example.

"A" β†’ A a

"B" β†’ B b

Where here ais some instance of the class a.

OR map 'const char *' to the corresponding type at compile time, for example.

"A" β†’ a

I need to use an instance of an object in some other functional call (i.e. a call to foo()or bar()), but the API can only accept const char *, since the underlying objects are abstracted.

, .

+4
5

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 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 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. .

: , .

+2

, ; .

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);
+4

. - , . , ++, .


factory :

template<int type> void create_ducky();
template<> A create_ducky<'A'>() { return A(); }

..,

create_ducky<'A'>().foo();

, 'A' A . , , , , , ( ) , .

+1

, 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 > .

0

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)

Whether this helps or not depends on whether you need run-time polymorphism or compiletime polymorphism (this is a solution). A wrap around boost::fusion::mapcan be done to directly accept alphabetic characters. It is also possible to use compiletime strings.

In your question, you say "compiletime or runtime". But if you need both, it will be mainly "runtime", which means some form of inheritance and pointers (on some level). For example, std::map<char, BaseofABC*>.

0
source

All Articles