【问题标题】:Choose multiple template based on multiple run-time string in C++在 C++ 中基于多个运行时字符串选择多个模板
【发布时间】:2017-10-24 13:42:55
【问题描述】:

我的问题类似于this post 期望我有多个模板参数和字符串。因此,设置是

class base_class; // no template args

template<typename T, typename U, typename V>
class child_class : public base_class;

TUV 的实现类型数量有限,我想在运行时选择三个字符串。因此,作为引用帖子中的问题,我可以做类似的事情

std::unique_ptr<base_class> choose_arg1(
    std::string T_str, std::string U_str, std::string v_str){
  if(T_str == "int"){
    return(choose_arg2<int>(U_str, V_str));

  } else if(T_str == "char"){
    return(choose_arg2<char>(U_str, V_str));

  } // ...
}

template<typename T>
std::unique_ptr<base_class> choose_arg2(std::string U_str, std::string v_str){
  if(U_str == "int"){
    return(choose_arg3<T, int>(V_str));

  } else if(U_str == "char"){
    return(choose_arg3<T, char>(V_str));

  } // ...
}

template<typename T, typename U>
std::unique_ptr<base_class> choose_arg3(std::string v_str){
  if(v_str == "int"){
    return(std::make_unique<child_class<T, U, int>>());

  } else if(v_str == "char"){
    return(std::make_unique<child_class<T, U, char>>());

  } // ...
}

但是有更好的方法吗?我记录的组合少于 5^3 个。

【问题讨论】:

  • 这应该是可移植的还是您只是在玩弄您的计算机和特定的编译器 (GCC...)?
  • 应该是便携的。
  • 你可以在每个函数中有一个静态的std::map&lt;std::string, std::function&lt;std::unique_ptr&lt;base_class&gt;(std::string const &amp;)&gt;&gt;,然后在其中搜索参数字符串。
  • @Darhuuk 就像this 在我的问题中链接到的帖子中回答?
  • @BenjaminChristoffersen 啊,是的。好像我浪费时间为您的代码输入示例:)。

标签: c++ templates c++14


【解决方案1】:

我建议开发一个带有几个静态 func() 方法的模板帮助器结构

template <typename ... Ts>
struct choose_args_h
 {
   using retT = std::unique_ptr<base_class>;

   template <typename ... Args>
   static retT func (std::string const & s, Args const & ... args)
    {
      if ( s == "int" )
         return choose_args_h<Ts..., int>::func(args...);
      else if ( s == "char" )
         return choose_args_h<Ts..., char>::func(args...);
      // else ...
    }

   static retT func ()
    { return std::make_unique<child_class<Ts...>>(); }
 };

所以你可以简单地写一个choose_args() func,如下所示

template <typename ... Args>
std::unique_ptr<base_class> choose_args (Args const & ... args)
 { return choose_args_h<>::func(args...); }

以下是一个完整的工作示例

#include <string>
#include <memory>

class base_class
 { };

template <typename, typename, typename>
class child_class : public base_class
 { };

template <typename ... Ts>
struct choose_args_h
 {
   using retT = std::unique_ptr<base_class>;

   template <typename ... Args>
   static retT func (std::string const & s, Args const & ... args)
    {
      if ( s == "int" )
         return choose_args_h<Ts..., int>::func(args...);
      else if ( s == "char" )
         return choose_args_h<Ts..., char>::func(args...);
      // else ...
    }

   static retT func ()
    { return std::make_unique<child_class<Ts...>>(); }
 };

template <typename ... Args>
std::unique_ptr<base_class> choose_args (Args const & ... args)
 { return choose_args_h<>::func(args...); }

int main ()
 {
   auto p0 = choose_args("int", "char", "int");
   auto p1 = choose_args("int", "char", "char");
 }

【讨论】:

  • 除非对每个模板参数允许的类型有特定限制,否则我认为这是我建议的更好版本:)。
  • 是的,很遗憾我有限制,但我同意。
  • @BenjaminChristoffersen - 抱歉:从您的示例中,我了解到每个位置都有相同的字符串参数列表和相同的模板选择;如果不是,我怀疑您的示例(每个级别的函数)是一个很好的解决方案;不一定是最好的,但很好。
  • @max66 我同意,我的例子在这方面并不清楚。我应该更具体。
【解决方案2】:

本文展示的是一个 C++17 解决方案,通过类型 Argmaps 对允许的类型和对应的键进行编译时配置。查找由编译时循环完成。

C++11 不支持此处使用的编译时循环所需的通用 lambda。相反,可以使用“索引技巧”(如this online demo)通过模板元编程来执行查找,但这感觉太复杂了,无论如何我更喜欢the std::map approach。请注意,如果键不唯一,我的链接 C++11 尝试可能会 call the constructor twice

#include <iostream>
#include <memory>
#include <string>

#include "loop.hpp"

template<class... Ts> struct Types {
  static constexpr size_t size = sizeof...(Ts);

  template<size_t i>
  using At = std::tuple_element_t<i, std::tuple<Ts...>>;
};

template<class... Ts> constexpr Types<Ts...> to_types(Ts...) { return {}; }

template<auto... cs> struct Str {
  operator std::string() const {
    constexpr auto list = std::initializer_list<char>{cs...};
    return std::string{list.begin(), list.end()};
  }
};

template<class Char, Char... cs>
constexpr auto operator""_c() {
  return Str<cs...>{};
}

//////////////////////////////////////////////////////////////////////////////

struct Base {
  virtual void identify() const = 0;
};

template<class... Ts>
struct Derived : Base {
  virtual void identify() const override {
    std::cout << __PRETTY_FUNCTION__ << std::endl;
  }
};

using Ptr = std::unique_ptr<Base>;

//////////////////////////////////////////////////////////////////////////////

template<class Argmaps, class Args=Types<>>
struct choose_impl;

template<class Map0, class... Maps, class... Args>
struct choose_impl<Types<Map0, Maps...>, Types<Args...>> {
  static constexpr size_t pos = sizeof...(Args);

  template<class S0, class... Ss>
  static Ptr get(S0 s0, Ss... ss) {
    Ptr ret{nullptr};

    using namespace Loop;
    loop(less<Map0::size>, [&] (auto i) {
      using Argmapping = typename Map0::template At<i>;
      using Key = typename Argmapping::template At<0>;
      using Arg = typename Argmapping::template At<1>;
      using Recursion = choose_impl<Types<Maps...>, Types<Args..., Arg>>;
      if(std::string(Key{}) == s0) ret = Recursion::get(ss...);
    });

    if(!ret) {
      std::cerr << "NOT MAPPED AT POS " << pos << ": " << s0 << std::endl;
      std::terminate();
    }

    return ret;
  }
};

template<class... Args>// all Args are resolved
struct choose_impl<Types<>, Types<Args...>> {
  static Ptr get() {
    return std::make_unique<Derived<Args...>>();
  }
};

template<class Argmaps, class... Ss>
Ptr choose(Ss... ss) {
  static_assert(Argmaps::size == sizeof...(Ss));
  return choose_impl<Argmaps>::get(std::string(ss)...);
}

template<class V, class K>
auto make_argmapping(K) {
  return Types<K, V>{};
}

//////////////////////////////////////////////////////////////////////////////

int main() {
  using Argmaps = decltype(
    to_types(
      to_types(// first template parameter
        make_argmapping<int>("int"_c),
        make_argmapping<char>("char"_c),
        make_argmapping<bool>("bool"_c)
      ),
      to_types(// ... second ...
        make_argmapping<double>("double"_c),
        make_argmapping<long>("long"_c)
      ),
      to_types(// ... third
        make_argmapping<bool>("bool"_c)
      )
    )
  );

  choose<Argmaps>("int", "double", "bool")->identify();
  choose<Argmaps>("int", "long", "bool")->identify();
  choose<Argmaps>("char", "double", "bool")->identify();
  choose<Argmaps>("char", "long", "bool")->identify();
  choose<Argmaps>("bool", "double", "bool")->identify();
  choose<Argmaps>("bool", "long", "bool")->identify();

// bad choice:
  choose<Argmaps>("int", "int", "bool")->identify();

  return 0;
}

来自this unread answer的loop.hpp:

#ifndef LOOP_HPP
#define LOOP_HPP

namespace Loop {

template<auto v> using Val = std::integral_constant<decltype(v), v>;

template<auto i> struct From : Val<i> {};
template<auto i> static constexpr From<i> from{};

template<auto i> struct Less : Val<i> {};
template<auto i> static constexpr Less<i> less{};

// `to<i>` implies `less<i+1>`
template<auto i> struct To : Less<i+decltype(i)(1)> {};
template<auto i> static constexpr To<i> to{};

template<auto i> struct By : Val<i> {};
template<auto i> static constexpr By<i> by{};

template<auto i, auto N, auto delta, class F>
constexpr void loop(From<i>, Less<N>, By<delta>, F f) noexcept {
  if constexpr(i<N) {
    f(Val<i>{});
    loop(from<i+delta>, less<N>, by<delta>, f);
  }
}

// overload with two arguments (defaulting `by<1>`)
template<auto i, auto N, class F>
constexpr void loop(From<i>, Less<N>, F f) noexcept {
  loop(from<i>, less<N>, by<decltype(i)(1)>, f);
}

// overload with two arguments (defaulting `from<0>`)
template<auto N, auto delta, class F>
constexpr void loop(Less<N>, By<delta>, F f) noexcept {
  loop(from<decltype(N)(0)>, less<N>, by<delta>, f);
}

// overload with one argument (defaulting `from<0>`, `by<1>`)
template<auto N, class F>
constexpr void loop(Less<N>, F f) noexcept {
  using Ind = decltype(N);
  loop(from<Ind(0)>, less<N>, by<Ind(1)>, f);
}

} // namespace Loop

#endif

http://coliru.stacked-crooked.com/a/5ce61617497c3bbe

【讨论】:

  • 酷,我稍后会看看这个。
  • 看起来很整洁。不过,我需要它的代码才能使用仅支持 C++11 的编译器进行构建。这可能吗?
  • 查看我的编辑,其中我链接了一个不完整的 C++11 尝试。
【解决方案3】:

正如我在评论中指出的,您可以使用字符串的静态映射来运行。

对于您的示例代码(稍微简化为 2 个模板参数以使其更短一些),这将变为:

#include <iostream>
#include <string>
#include <map>
#include <functional>
#include <memory>

class base_class { }; // no template args

template<typename T, typename U>
class child_class : public base_class { };

using ptr_type = std::unique_ptr<base_class>;

// Declarations
std::unique_ptr<base_class> choose_arg1 (std::string const & T_str,
    std::string const & U_str);

template<typename T>
std::unique_ptr<base_class> choose_arg2 (std::string const & U_str);

// Definitions
std::unique_ptr<base_class> choose_arg1 (std::string const & T_str,
    std::string const & U_str) {
  using function_type = std::function<ptr_type(std::string const &)>;
  using map_type = std::map<std::string, function_type>;

  static const map_type ptrMap = {
    {"int",  choose_arg2<int>  },
    {"char", choose_arg2<char> }
  };

  auto ptrIter = ptrMap.find(T_str);
  return (ptrIter != ptrMap.end()) ? ptrIter->second(U_str) : nullptr;
}

template<typename T>
std::unique_ptr<base_class> choose_arg2 (std::string const & U_str) {
  using function_type = std::function<ptr_type()>;
  using map_type = std::map<std::string, function_type>;

  static const map_type ptrMap = {
    {"int",  []{ return std::make_unique<child_class<T, int>>();  } },
    {"char", []{ return std::make_unique<child_class<T, char>>(); } }
  };

  auto ptrIter = ptrMap.find(U_str);
  return (ptrIter != ptrMap.end()) ? ptrIter->second() : nullptr;
}

int main () {
  std::cout << typeid(choose_arg1("int", "char")).name() << "\n";
  std::cout << "[Done]\n";
}

【讨论】:

  • 非常好。简短的元问题:如果我更喜欢你的答案,删除我自己的答案是一种好习惯吗?
  • @Julius 谢谢!我会留下你的答案。今晚晚些时候我想看看它:)。
  • 我不清楚我用这种方法而不是我建议的if-else if 方法赢得了什么?
  • 这种带有std::map 的方法在我看来非常强大。我想它可以用于创建像mine 这样的可配置/可变参数版本,但没有那些丑陋的类型并且更具可读性。
  • @BenjaminChristoffersen 对我来说,与if-else 方法相比的优势在于您不必一直重复if-statement,并且在 1 个位置清楚地拥有所有可能的选择(构造函数静态地图)。换句话说,我觉得它更具可读性。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-12-03
  • 2016-07-12
  • 2015-05-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-11
相关资源
最近更新 更多