(C#)Enumからstringへの変換
よくあるEnumからstringへの変換。世の中に大量に公開されている正解をツギハギして自分にちょうどいいものを探ってみた。
属性定義と拡張メソッドが必要だった。属性定義はDisplayNameAttributeがEnumメンバに付与できないので必要で、拡張メソッドは簡単にアクセスするために必要。DisplayName派生の方が無難だけど参考URL1つ目の思想も好きなので混ざっている。。
ソースコード
using System; using System.Linq; namespace EnumToString { // 独自Attribute [AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)] public sealed class AliasNameAttribute : Attribute { public string AliasName { get; private set; } public AliasNameAttribute(string aliasName) { AliasName = aliasName; } } // 独自Attribute用のEnum拡張 public static partial class EnumExtension { // どうしてもメソッドチェインを崩したくない人用 public static T ThrowIf<T>(this T value, Func<T, bool> predicate, Exception exception) { if (predicate(value)) throw exception; else return value; } // AliasNameAttribute.AliasNameを返す。定義されていなければ例外を発生させる。 public static string ToAliasName(this Enum value) { return value.GetType() .GetField(value.ToString()) .GetCustomAttributes(typeof(AliasNameAttribute), false) .Cast<AliasNameAttribute>() .FirstOrDefault() .ThrowIf(a => a == null, new ArgumentException("属性が設定されていません。")) .AliasName; } } // DisplayName継承Attribute // System.ComponentModel.DisplayNameAttributeと衝突する(こちらへ置換可能) [AttributeUsage(AttributeTargets.All, AllowMultiple = false)] public class DisplayNameAttribute : System.ComponentModel.DisplayNameAttribute { public DisplayNameAttribute(string displayName) : base(displayName) { } } // DisplayName継承Attribute用のEnum拡張 public static partial class EnumExtension { // DisplayNameAttribute.DisplayNameを返す。定義されていなければ定義名を返す。 public static string ToDisplayName(this Enum value) { var attribute = value.GetType() .GetField(value.ToString()) .GetCustomAttributes(typeof(DisplayNameAttribute), false) .Cast<DisplayNameAttribute>() .FirstOrDefault(); return (attribute != null) ? attribute.DisplayName : value.ToString(); } } // enum enum Fruit { [AliasName("ブドウ")] Grape, [DisplayName("あっぷる")] [AliasName("リンゴ")] Apple, [DisplayName("おれんじ")] [AliasName("オレンジ")] Orange, // 属性なし Dummy, } class Program { static void Main(string[] args) { // AliasName属性を使用して全要素を表示 foreach (Fruit f in Enum.GetValues(typeof(Fruit))) { try { Console.WriteLine(f.ToString()); Console.WriteLine($" {f.ToAliasName()}"); } catch (Exception e) { Console.WriteLine($" exception : {e.ToString()}"); } } Console.WriteLine(); // DisplayName属性を使用して全要素を表示 Console.WriteLine("================"); foreach (Fruit f in Enum.GetValues(typeof(Fruit))) { try { Console.WriteLine(f.ToString()); Console.WriteLine($" {f.ToDisplayName()}"); } catch (Exception e) { Console.WriteLine($" exception : {e.ToString()}"); } } Console.WriteLine(); // wait for key Console.ReadKey(); } } }
結果
Grape ブドウ Apple リンゴ Orange オレンジ Dummy exception : System.ArgumentException: 属性が設定されていません。 場所 EnumToString.EnumExtension.ThrowIf[T](T value, Func`2 predicate, Exception exception) 場所 ***\Program.cs:行 64 場所 EnumToString.EnumExtension.ToAliasName(Enum value) 場所 ***\Program.cs:行 71 場所 EnumToString.Program.Main(String[] args) 場所 ***\Program.cs:行 107 ================ Grape Grape Apple あっぷる Orange おれんじ Dummy Dummy
自前ヘキサダンプ関数
バイナリデータをバイナリエディタ風というかtcpdump風というかそんな感じに16進数で表示する関数。実装はC++11。やりたかったことはできた気がするけど気がしただけだった。ちょっと直す必要がある。あとソースコードが汚い。
ソースコード
#include <ctype.h> #include <string> #include <iomanip> #include <sstream> #include <iostream> static std::string GetDumpedString( void* targetData, int targetDataSize, bool showHead = true, bool showChar = true) { unsigned char* data = static_cast<unsigned char*>(targetData); std::stringstream ssDump; std::stringstream ssHead; std::stringstream ssLine; std::stringstream ssChar; ssLine.width( 2 ); ssLine.fill( '0' ); ssLine.flags( std::ios::hex | std::ios::uppercase ); for( int i = 0; i < targetDataSize; i++ ) { if( showHead && ( i % 16 == 0 ) ) { ssHead << std::setw(8) << std::setfill('0') << std::hex << i << ": " << std::flush; ssLine << ssHead.str(); } ssLine << (int)data[i] << ' '; ssChar << ((isprint(data[i]) != 0) ? (char)data[i] : '.'); if( (i+1) % 16 == 0 ) { if( showChar ) { ssLine << " " << ssChar.str(); } ssLine << std::endl; ssDump << ssLine.str(); ssHead.str(""); ssHead.clear(std::stringstream::goodbit); ssLine.str(""); ssLine.clear(std::stringstream::goodbit); ssChar.str(""); ssChar.clear(std::stringstream::goodbit); } } if( ssLine.str().size() > 0 ) { int spaceSize = 3*16 + (showHead ? 10 : 0) + (showChar ? 4 : 0) - ssLine.str().size(); ssLine << std::string(spaceSize, ' ') << ssChar.str() << std::endl; ssDump << ssLine.str(); } return ssDump.str(); } int main(void) { using namespace std; cout << " 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F" << endl; string str = "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "01234567890"; cout << GetDumpedString( (char*)str.data(), str.size() ) << endl; return 0; }
結果
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 00000000: 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F 70 abcdefghijklmnop 00000010: 71 72 73 74 75 76 77 78 79 7A 41 42 43 44 45 46 qrstuvwxyzABCDEF 00000020: 47 48 49 4A 4B 4C 4D 4E 4F 50 51 52 53 54 55 56 GHIJKLMNOPQRSTUV 00000030: 57 58 59 5A 30 31 32 33 34 35 36 37 38 39 30 WXYZ01234567890
vector<string>をexecv()に渡す
掲題のコード。正直いまいち分かってない。
ソース
#include <unistd.h> #include <vector> #include <string> using namespace std; int main(void) { vector<string> argList = { string("/bin/echo"), string("a"), string("b") }; const char **argv = new const char*[argList.size()+1]; for( size_t i = 0; i < argList.size(); i++ ) { argv[i] = argList.at(i).c_str(); } argv[argList.size()] = NULL; execv( argv[0], (char**)argv ); delete argv; return 0; }
boostでiniファイルを読み込んでmapに詰め込む
いつも地味に面倒なiniからの読み込みを簡単にするためにboost::property_treeでiniファイルを読み込んでmap<string,string>に詰め込むサンプル。
読み込むiniファイル(sample.ini)
[sectionA] key1 = val1 [section B] spaced key = spaced value [section C] # comment key1 = 1 # duped name key2 = あ ; japanee # comment ; comment
ソース(おためし)
まずは試しに表示するソースコード。普通にget_value()すると'='以降すべて取得できてしまうので、特定の文字以降をコメントとして扱いたい場合はsplit()してtrim()してあげる必要があるみたい。
#include <iostream> #include <string> #include <vector> #include <boost/property_tree/ptree.hpp> #include <boost/property_tree/ini_parser.hpp> #include <boost/algorithm/string.hpp> void printIni2(const char* filepath) { using boost::property_tree::ptree; ptree pt; read_ini( filepath, pt, std::locale("japanese") ); std::vector<std::string> valStrList; for (auto& section : pt) { std::cout << '[' << section.first << "]" << std::endl; for (auto& key : section.second) { std::string valStr = key.second.get_value<std::string>(); boost::split(valStrList, valStr, boost::is_any_of("#;")); std::cout << key.first << "=" << boost::algorithm::trim_copy(valStrList.at(0)) << std::endl; } } } void printIni(const char* filepath) { using boost::property_tree::ptree; ptree pt; read_ini( filepath, pt, std::locale("japanese") ); for (auto& section : pt) { // print part of "[section name]" std::cout << '[' << section.first << "]" << std::endl; // print part of "key = value" for (auto& key : section.second) { std::cout << key.first << "=" << key.second.get_value<std::string>() << std::endl; } } } int main(int argc, char* argv[]) { printIni("sample.ini"); std::cout << "--------------------------------" << std::endl; printIni2("sample.ini"); return 0; }
結果
[sectionA] key1=val1 [section B] spaced key=spaced value [section C] key1=1 # duped name key2=あ ; japanee -------------------------------- [sectionA] key1=val1 [section B] spaced key=spaced value [section C] key1=1 key2=あ
ソース(本題)
本題のソースコード。
#include <iostream> #include <string> #include <vector> #include <map> #include <boost/property_tree/ptree.hpp> #include <boost/property_tree/ini_parser.hpp> #include <boost/algorithm/string.hpp> #include <boost/lexical_cast.hpp> void ini2map(const char* filepath, std::map<std::string, std::string>& iniMap) { using boost::property_tree::ptree; ptree pt; read_ini( filepath, pt, std::locale("japanese") ); std::vector<std::string> valStrList; for (auto& section : pt) { std::string sectionName = section.first; for (auto& key : section.second) { std::string valStr = key.second.get_value<std::string>(); boost::split(valStrList, valStr, boost::is_any_of("#;")); std::string keyName = key.first; std::string val = boost::algorithm::trim_copy(valStrList.at(0)); std::cout << keyName << "=" << val << std::endl; iniMap[sectionName+"."+keyName] = val; } } } int main(int argc, char* argv[]) { std::map<std::string, std::string> iniMap; ini2map("sample.ini", iniMap); std::cout << iniMap.size() << std::endl; // print for( auto kvp : iniMap ) { std::cout << kvp.first << "=" << kvp.second << std::endl; } // get value as int std::cout << boost::lexical_cast<int>(iniMap.at("section C.key1")) << std::endl; // get value (failure) try { std::string valStr = iniMap.at(" undefined key "); } catch(std::exception& e) { std::cout << e.what() << std::endl; } return 0; }
結果
key1=val1 spaced key=spaced value key1=1 key2=あ 4 section B.spaced key=spaced value section C.key1=1 section C.key2=あ sectionA.key1=val1 1 map::at
ソース(おまけ)
ぶっちゃけ以下のようなコードで取得できるだろうけど、プロパティツリーを意識しないようにしたかった。あとこの場合は行内コメントできない。
ptree pt; read_ini("ini file", pt); string strValue = pt.get<string>("[section].item"); int intValue = pt.get<int>("[section].item");
メモ:
- forループの部分がこれで回るのはptreeが「map< string, map
> >」みたいな入れ子構造になっているかららしい。 - セクションがない項目は1階層目に入るので今回のコードだとうまく扱えない。
- せっかくなのでread_iniのロケールにjapanese指定してみたけど、実は指定しなくてもSJISのファイル読めた。逆にjapanese指定してもutf-8のファイル読めた。この辺はよくわからない。
- 環境:c++11 + boost 1.53
参考:
std::queue::pop()はデストラクタがあれば呼び出す
std::queue::pop()はデストラクタを呼び出す。しかしポインタ型を格納した場合は例外。
ポインタ型そのものがデストラクタを持っていないため、らしい。
逆に実体をpop()するとスコープ抜けたときとあわせて2回呼ばれることになる。
code.cpp
#include <queue> #include <iostream> using namespace std; class animal { string kind; public: animal(string kind) { this->kind = kind; cout << "animal ctor : " << kind << endl; } ~animal() { cout << "animal dtor : " << kind << endl; } }; int main(void) { queue<animal*> q; q.push( new animal("dog") ); auto dog = q.front(); q.pop(); delete dog; queue<animal> q2; animal cat = animal("cat"); q2.push( cat ); q2.pop(); // 1st dtor call by pop() cout << "popd" << endl; // 2nd dtor call by cat return 0; }
result
dog ctor : pochi dog dtor : pochi cat ctor : tama cat dtor : tama popd cat dtor : tama
参考
null文字のないchar[]の変換メモ
null文字のないchar[]の変換メモ。面倒
code.cpp
#include <iostream> #include <sstream> #include <iomanip> #include <boost/lexical_cast.hpp> using namespace std; using namespace boost; // print 'lexical_cast'ed cstr void casttest( const char* cstr ) { cout << "[" << cstr << "]" << endl; try { cout << " ->" << lexical_cast<int>(cstr) << endl; } catch(std::exception& e) { cout << " ->" << e.what() << endl; } } // int -> char[] void int2zerochars(int val, char* cstr, size_t size) { stringstream ss; ss.fill('0'); ss << setw(size) << val; ss.str().copy(cstr, size); } // string -> char[] void str2chars(string str, char* cstr, size_t size) { // string::copy(dst, size, start_pos = 0); str.copy(cstr, size, 0); } // char[] -> string string chars2str(char chars[], size_t size) { stringstream ss; for(size_t i = 0; i < size; i++) { ss << chars[i]; } return ss.str(); } int main(void) { char src[2] = { '9', '2' }; cout << chars2str( src, sizeof(src) ) << endl; int val = lexical_cast<int>(chars2str( src, sizeof(src) )); cout << val << endl; //------------------------------------------------------ casttest(" "); casttest(""); casttest("a"); casttest("0xDD"); casttest("000"); casttest("010"); casttest("-1"); casttest("--1"); casttest("1-1"); //------------------------------------------------------ char dst[4] = { 'x', 'x', 'x', 'x' }; string abc("abc"); string defg("defg"); string hijkl("hijkl"); // 3characters (size-1) str2chars(abc, dst, sizeof(dst)); cout << dst << endl; for(size_t i=0; i<sizeof(dst); i++){ cout<<" "<<i<<":"<<dst[i]; cout<<endl; } // 4characters (size) str2chars(defg, dst, sizeof(dst)); cout << dst << endl; for(size_t i=0; i<sizeof(dst); i++){ cout<<" "<<i<<":"<<dst[i]; cout<<endl; } // 5characters (size+1) str2chars(hijkl, dst, sizeof(dst)); cout << dst << endl; for(size_t i=0; i<sizeof(dst); i++){ cout<<" "<<i<<":"<<dst[i]; cout<<endl; } //------------------------------------------------------ char xxxx[4] = { 'x', 'x', 'x', 'x' }; int2zerochars( 2, xxxx, sizeof(xxxx) ); cout << xxxx << endl; cout.flush() << endl; int2zerochars( 888, xxxx, sizeof(xxxx) ); cout << xxxx << endl; cout.flush() << endl; int2zerochars( 9999, xxxx, sizeof(xxxx) ); cout << xxxx << endl; cout.flush() << endl; int2zerochars( 77777, xxxx, sizeof(xxxx) ); cout << xxxx << endl; cout.flush() << endl; cout << 9 << endl; return 0; }
result
92 92 [ ] ->bad lexical cast: source type value could not be interpreted as target [] ->bad lexical cast: source type value could not be interpreted as target [a] ->bad lexical cast: source type value could not be interpreted as target [0xDD] ->bad lexical cast: source type value could not be interpreted as target [000] ->0 [010] ->10 [-1] ->-1 [--1] ->bad lexical cast: source type value could not be interpreted as target [1-1] ->bad lexical cast: source type value could not be interpreted as target abcx 0:a 1:b 2:c 3:x defg 0:d 1:e 2:f 3:g hijk 0:h 1:i 2:j 3:k 0002< 0888< 9999< 7777< 9