【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

Enum(自前)拡張メソッド

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

Enum(自前)拡張メソッド:Listキャスト版

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を使う(常識を考え百万回を回さないよね)

[Wandbox]三へ( へ՞ਊ ՞)へ ハッハッ

【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 に変更すればコンパイルが通ります

修正確認したらまた追記する