scrap book

( ..)φメモメモ

(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