7

この質問は、 「文字列を列挙型にマップする方法」の n 回目の反復である可能性が非常に高いです。

私の要件はもう少し進んでthrow、有効な入力の範囲内にキーが見つからない場合に特定の例外が必要です。だから私はこれのこの実装を持っています(定義EnumMapのためにブーストが必要です):const std::map

#include <map>
#include <string>
#include <sstream>
#include <stdexcept>
#include <boost/assign.hpp>

typedef enum colors {
  RED,
  GREEN,
} colors;
// boost::assign::map_list_of
const std::map<std::string,int> colorsMap  = boost::assign::map_list_of
                                            ("red",   RED)
                                            ("green", GREEN);
//-----------------------------------------------------------------------------
// wrapper for a map std::string --> enum
class EnumMap {
 private:
  std::map<std::string,int> m_map;
  // print the map to a string
  std::string toString() const {
    std::string ostr;
    for(auto x : m_map) {
      ostr += x.first + ", ";
    }
    return ostr;
  }
 public:
  // constructor
  EnumMap(const std::map<std::string,int> &m) : m_map(m) { }
  // access
  int at(const std::string &str_type) {
    try{
      return m_map.at(str_type);
    }
    catch(std::out_of_range) {
      throw(str_type + " is not a valid input, try : " + toString());
    }
    catch(...) {
      throw("Unknown exception");
    }
  }
};
//-----------------------------------------------------------------------------
int main()
{
  EnumMap aColorMap(colorsMap);
  try {
    aColorMap.at("red");    // ok
    aColorMap.at("yellow"); // exception : "yellow is not a valid input ..."
  }
  catch(std::string &ex) {
    std::cout << ex << std::endl;
  }
  return 0;
}

これはうまく機能し、必要なことを行います。ここで、特定の のすべての要素がコンストラクターにenum渡されること、および のすべての要素が対応する文字列と一致することをコンパイル時に認識できるようにしたいと考えています。EnumMapenum

std::initializer_liststatic_assertを試してみましたが、VC2010 はまだサポートしていないようですstd::initializer_list(こちらを参照)。

これをどのように実装することが可能になるかについて、誰かが考えを持っていますか? おそらくテンプレートを使用するか、独自の Enum クラスを実装しますか?

4

3 に答える 3

4
typedef enum colors {
  MIN_COLOR,
  RED = MIN_COLOR,
  GREEN,
  MAX_COLOR
} colors;

template< colors C >
struct MapEntry {
  std::string s;
  MapEntry(std::string s_):s(s_) {}
};
void do_in_order() {}
template<typename F0, typename... Fs>
void do_in_order(F0&& f0, Fs&&... fs) {
  std::forward<F0>(f0)();
  do_in_order( std::forward<Fs>(fs)... );
}
struct MapInit {
  std::map< std::string, color > retval;
  operator std::map< std::string, color >() {
    return std::move(retval);
  }
  template<colors C>
  void AddToMap( MapEntry<C>&& ent ) {
    retval.insert( std::make_pair( std::move(end.s), C ) );
  }
  template< typename... Entries >
  MapInit( Entries&& entries ) {
    do_in_order([&](){ AddToMap(entries); }...);
  } 
};
template<typename... Entries>
MapInit mapInit( Entries&&... entries ) {
  return MapInit( std::forward<Entries>(entries)... );
}
const std::map<std::string, colors> = mapInit( MapEntry<RED>("red"), MapEntry<GREEN>("green") );

std::mapこれにより、コンパイル時colorおよび実行時のデータからC++11 を構築する方法が提供されstringます。

次に、"list of MapEntry<colors>to list of colors" メタ関数を投入します。

template<colors... Cs>
struct color_list {};
template<typename... Ts>
struct type_list {};
template<typename MapEnt>
struct extract_color;
template<colors C>
struct extract_color<MapEntry<C>> {
  enum {value=C};
};
template<typename Entries>
struct extract_colors;
template<typename... MapEntries>
struct extract_colors<type_list<MapEntries...>> {
  typedef color_list< ( (colors)extract_colors<MapEntries>::value)... > type;
};

そのリストを並べ替えます。重複を検出します -- もしあれば、あなたは台無しです。

コンパイル時の並べ替えは、100 行以上のコードであり、残りの部分よりも難しくなります。よほど気にならない方はお譲りします! これは、比較的単純な適応で機能するスタック オーバーフローの質問に答えるために過去に書いたコンパイル時のマージ ソートです(値を使用して型をソートします。この場合、コンパイル時の値のリストを直接ソートしています)。

// takes a sorted list of type L<T...>, returns true if there are adjacent equal
// elements:
template<typename clist, typename=void>
struct any_duplicates:std::false_type {};
template<typename T, template<T...>class L, T t0, T t1, T... ts>
struct any_duplicates< L<t0, t1, ts...>, typename std::enable_if<t0==t1>::type>:
  std::true_type {};
template<typename T, template<T...>class L, T t0, T t1, T... ts>
struct any_duplicates< L<t0, t1, ts...>, typename std::enable_if<t0!=t1>::type>:
  any_duplicates< L<t1, ts...> > {};

の有効範囲外の要素を検出しますcolors(つまり、 <MIN_COLORまたは>=MAX_COLOR)。もしそうなら、あなたはめちゃくちゃです。

 template<typename List>
 struct min_max;
 template<typename T, template<T...>class L, T t0>
 struct min_max {
   enum {
     min = t0,
     max = t1,
   };
 };
 template<typename T, template<T...>class L, T t0, T t1, T... ts>
 struct min_max {
   typedef min_max<L<t1, ts...>> rest_of_list;
   enum {
     rest_min = rest_of_list::min,
     rest_max = rest_of_list::max,
     min = (rest_min < t0):rest_min:t0,
     max = (rest_max > t0):rest_max:t0,
   };
 };
 template< typename T, T min, T max, typename List >
 struct bounded: std::integral_constant< bool,
   (min_max<List>::min >= min) && (min_max<List>::max < max)
 > {};

要素の数を数えます。要素があるはずMAX_COLORです。そうでなければ、あなたはめちゃくちゃです。

 template<typename List>
 struct element_count;
 template<typename T, template<T...>L, T... ts>
 struct element_count<L<ts...>>:std::integral_constant< std::size_t, sizeof...(ts) > {};

これらのいずれも発生しなかった場合は、pigeonhole によってそれぞれを初期化したに違いありません。

唯一欠けているのはstring、2 つの値に同じものを使用できたということです。コンパイル時間stringは面倒なので、実行時にこれを確認してください( のエントリ数が初期化後mapの の数と等しいことを確認colorsしてください)。

C++03 でこれを行うのは難しくなります。さまざまなテンプレートがないため、それらを偽造する必要があります。これは苦痛です。 mplそこでお役に立てるかもしれません。

Variardic テンプレートは、2012 年 11 月の MSVC CTP コンパイラの更新で利用できます。

これは、重複チェックと境界チェックなしのおもちゃの例です (マップエントリの数が一致することをチェックするだけです)。

#include <cstddef>
#include <utility>
#include <string>
#include <map>

enum TestEnum {
   BeginVal = 0,
   One = BeginVal,
   Two,
   Three,
   EndVal
};

template<TestEnum e>
struct MapEntry {
  enum { val = e };
  std::string s;
  MapEntry( std::string s_ ):s(s_) {}
};

void do_in_order() {}
template<typename F0, typename... Fs>
void do_in_order(F0&& f0, Fs&&... fs) {
  std::forward<F0>(f0)();
  do_in_order( std::forward<Fs>(fs)... );
}

template<typename... MapEntries>
struct count_entries:std::integral_constant< std::size_t, sizeof...(MapEntries) > {};

// should also detect duplicates and check the range of the values:
template<typename... MapEntries>
struct caught_them_all:
  std::integral_constant<
    bool,
    count_entries<MapEntries...>::value == (TestEnum::EndVal-TestEnum::BeginVal)
  >
{};

struct BuildMap {
  typedef std::map<std::string, TestEnum> result_map;
  mutable result_map val;
  operator result_map() const {
    return std::move(val);
  }
  template<typename... MapEntries>
  BuildMap( MapEntries&&... entries ) {
    static_assert( caught_them_all<MapEntries...>::value, "Missing enum value" );
    bool _[] = { ( (val[ entries.s ] = TestEnum(MapEntries::val)), false )... };
  }
};

std::map< std::string, TestEnum > bob = BuildMap(
  MapEntry<One>("One")
  ,MapEntry<Two>("Two")
#if 0
  ,MapEntry<Three>("Three")
#endif
);

int main() {}

を に置き換えて、#if 0コンパイル#if 1を監視します。 プレイしたい場合はライブリンク。

于 2013-04-16T21:11:40.607 に答える
2

これをどのように実装することが可能になるかについて、誰かが考えを持っていますか? おそらくテンプレートを使用するか、独自の Enum クラスを実装しますか?

できません。std::map ではなく、テンプレートのメタプログラミングでもありません。

于 2013-04-16T20:15:05.697 に答える