【C++】Enum::ParseとEnum::ToString
C#には Enum <-> 文字列の変換ができます
羨ましいですね~
C++でも似てるものが欲しい<(`^´)>
// こんな感じ #include <iostream> enum class Foo { X,Y,Z }; int main() { Foo foo = Enum::Parse<Foo>("Z"); std::string s = Enum::ToString(Foo::Z); }
書いてみた
// enum.h #pragma once #include <string> #include <boost/preprocessor.hpp> namespace Enum { // 宣言 template<class T> T Parse(const std::string&); template<class T> std::string ToString(T); } #define String2Enum(r,name,elem) if(in == BOOST_PP_STRINGIZE(elem)) out = name::elem; #define Enum2String(r,name,elem) case name::elem: return BOOST_PP_STRINGIZE(elem); #define REGIS_ENUM(name,seq)\ namespace Enum\ {\ inline void Conv(const std::string& in, name& out)\ {\ BOOST_PP_SEQ_FOR_EACH(String2Enum, name, seq)\ }\ inline std::string Conv(name in)\ {\ switch(in)\ {\ BOOST_PP_SEQ_FOR_EACH(Enum2String, name, seq)\ }\ return "";\ }\ /* 特殊化 */\ template<>\ name Parse(const std::string& in)\ {\ name r;\ Conv(in, r);\ return r;\ }\ template<>\ std::string ToString(name in)\ {\ return Conv(in);\ }\ }
使い方
#include "enum.h" enum class Foo { X,Y,Z }; REGIS_ENUM(Foo,(X)(Y)(Z));
【C#】列挙型の項目数取得(その2)
1年前に同じようなこと書きましたが
【C#】列挙型の項目数取得 - 浮遊島
今回はジェネリッククラスでstatic変数を持つバージョンの実装です
メリット:1回目だけ計算して、2回目以降のアクセスが早くなります
using System; public class EnumSize<T> where T : struct, IConvertible { public static int Count {get;} = Enum.GetValues(typeof(T)).Length; }
// Enum定義 enum Days {Sat, Sun, Mon, Tue, Wed, Thu, Fri}; // 使い方 EnumSize<Days>.Count; // Count:7
【C#】Enumの爆速ForEach
Enumをforeachで回すと遅いよと聞いて
自前で書いてみたら爆速ForEachができた
// 以下のenum定義を使用する enum Days {Sat=10, Sun=20, Mon, Tue, Wed, Thu, Fri};
まず結果(1,000,000回を回す)
// 1回目 foreach : 1328ms EnumExtension.ForEach : 1283ms EnumExtension.ForEachList : 482ms EnumForEach<T>.Exec : 56ms // 2回目 foreach : 1370ms EnumExtension.ForEach : 1357ms EnumExtension.ForEachList : 635ms EnumForEach<T>.Exec : 61ms // 3回目 foreach : 1342ms EnumExtension.ForEach : 1360ms EnumExtension.ForEachList : 488ms EnumForEach<T>.Exec : 62ms
実装詳細
foreach
普通のforeach
foreach(Days days in Enum.GetValues(typeof(Days))) { }
EnumExtension.ForEach
public static class EnumExtension { public static void ForEach<T>(Action<T> action) where T : struct, IConvertible { var type = typeof(T); foreach(T v in Enum.GetValues(type)) { action((T)v); } } }
// 使い方
EnumExtension.ForEach<Days>( days => {} );
速度がほぼforeachと変わらない
EnumExtension.ForEachList
public static class EnumExtension { public static void ForEachList<T>(Action<T> action) where T : struct, IConvertible { var type = typeof(T); var values = Enum.GetValues(type).Cast<T>().ToList(); var count = values.Count; for(int i = 0 ; i < count ; ++i) { action((T)values[i]); } } }
// 使い方
EnumExtension.ForEachList<Days>( days => {} );
Enum.GetValues(...)してListにキャストする方が遅いと思いきや
まさかかかった時間は半分以下に、ランダムアクセス早い!
EnumForEach.Exec
ジェネリッククラス版:static変数を持ち、反則のチートしてる
public class EnumForEach<T> where T : struct, IConvertible { static List<T> Values = Enum.GetValues(typeof(T)).Cast<T>().ToList(); static int Count = Values.Count; public static void Exec(Action<T> action) { for(int i = 0 ; i < Count ; ++i) { action((T)Values[i]); } } }
// 使い方
EnumForEach<Days>.Exec( days => {} );
爆速!かかった時間はforeachの20分の1以下
最後に
- 測定方法が間違ってないかしら?怖いorz
- 疑念点
- foreachみたいにループ中のbreak脱出はできない
- メモリ消費を考えてない(それほど心配ではないが)
- 俺は普通のforeachを使う(常識を考え百万回を回さないよね)
【C++】マジックインクリメント
PHPには文字列のインクリメントがあるらしく、C++で実装してみた
マジックインクリメントとは
(以下はネットで調べて理解したことです。間違ったら指摘していただければ幸いです)
英数字の文字列をインクリメントする
++"a" = "b" ++"0" = "1"
桁上げも可能です
++"az" = "ba" ++"19" = "20"
英数字だけの文字列だと
++"z9" = "aa0" ++"Z9" = "AA0" ++"9z" = "10a" 先頭の桁上げは最初の文字種類によって変わります。
英数字以外の文字を含む場合
++"Z!9" = "Z!0" 英数字以外の桁上げはしない
実装
#include <cctype> #include <string> std::string& operator++(std::string& s) { if(s.empty()) { s = "1"; return s; } auto begin = s.rbegin(); auto end = s.rend(); while(begin != end) { if(*begin == '9') { *begin = '0'; } else if(*begin == 'z') { *begin = 'a'; } else if(*begin == 'Z') { *begin = 'A'; } else if(std::isalnum(*begin)) { ++(*begin); return s; // 桁上げの必要ありません } else { return s; // 英数字以外は終了する } ++begin; } // 桁上げ if(std::isdigit(s.front())) { s = "1" + s; } else if(std::islower(s.front())) { s = "a" + s; } else if(std::isupper(s.front())) { s = "A" + s; } return s; } std::string operator++(std::string& s,int) { std::string tmp = s; ++s; return tmp; }
使い方
#include <iostream> int main() { std::string s = "a"; for(int i = 0 ; i < 26 ; i++) { std::cout << s++ << " "; } }
// 出力
a b c d e f g h i j k l m n o p q r s t u v w x y z
何かうれしいの?
連続の名前をアクセスしたいとき
#include <iostream> int main() { for(std::string s = "IMG_001a" ; s <= "IMG_001e" ; s++) { std::cout << s << std::endl; }
// 出力
IMG_001a
IMG_001b
IMG_001c
IMG_001d
IMG_001e
C++11のstd::iotaを使うとき(使うかしら...)
#include <iostream> #include <numeric> #include <vector> int main() { std::vector<std::string> v(5); std::iota(std::begin(v), std::end(v),std::string("IMG_001a")); for(const auto& s : v) { std::cout << s << std::endl; } }
// 出力
IMG_001a
IMG_001b
IMG_001c
IMG_001d
IMG_001e
【C++】UTF8
UTF8文字列処理のメモです。
いまだにUTF8を使ってサーバーとやり取りしてます。
たまに、表示文字数をカウントしたり、文字数が多すぎる場合途中から"..."を表示したり
結構困ります。。。
C++11からstd::u32stringを追加されて意外と便利です。
std::codecvt_utf8と組み合わせば、もう怖くない〜
例えば
文字数をカウントしたり
#include <iostream> #include <codecvt> size_t strlen(const std::string& s) { std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> conv; return conv.from_bytes(s).size(); } int main() { std::string utf8 = u8"2015年1月24日"; std::cout << "size:" << strlen(utf8) << std::endl; // size:10 }
文字列の一部分取得したり
#include <iostream> #include <codecvt> std::string substr(const std::string& s,size_t pos = 0, size_t len = std::string::npos) { std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> conv; auto u32string = conv.from_bytes(s); // UTF8 -> UTF32 auto sub = u32string.substr(pos,len); // 部分取得 return conv.to_bytes(sub); // UTF32 -> UTF8にして返す } int main() { std::string utf8 = u8"𠮷野家牛丼"; std::cout << substr(utf8,0,3) << std::endl; // 𠮷野家 }
サロゲートペアも無問題、めでたしめでたし〜
追記
2015/12/06
VisualStudio2015 では コンパイルエラーになると報告がありました
検証環境が用意できたので結果を報告します
結論から言うと、この記事のサンプルコードは問題ありません。
VS 2015 RC linker std::codecvt error
上記リンクによると、これはVisualStudio2015既知の不具合です
回避策として char32_t を int32_t に変更すればコンパイルが通ります
修正確認したらまた追記する