C++について、知っていそうで知らないこと、素朴な疑問など。(新着順)

enumをフラグとしてつかうためのユーティリティー

template <typename Enum, typename Integer = uint32_t>
struct EnumFlags {
using Type = Enum;
    Integer value_;
    EnumFlags(Integer v = 0) : value_(v) {
        static_assert(std::is_integral<Integer>::value, "Integer must be some int class");
    }
    EnumFlags(const EnumFlags& e) : value_(e.value_) {}
    EnumFlags(Type e) : value_(get(e)) {}
    bool any_of(const EnumFlags& a) const { return (value_ & a.value_) != 0; }
    bool all_of(const EnumFlags& a) const { return (value_ & a.value_) == a.value_; }
    bool check(Type e) const { return (value_ & get(e)) != 0; }
    Integer get(Type e) const { return static_cast<Integer>( 1 << static_cast<uint32_t>(e) ); }

    EnumFlags operator | (Type e) const {
        return EnumFlags(value_ | get(e));        
    }
    EnumFlags& operator |= (Type e) {
        value_ |= get(e);
        return *this;
    }
};

template <typename E>
EnumFlags<E> operator | (E v1, E v2) {
      EnumFlags<E> e(v1);
      return e |= v2;
}

使用例

#include <iostream>
#include <type_traits>

enum class EHoge {
    Hoge ,
    Fuga ,
    Piyo ,
    Boo  ,
    Honya 
};
int main() {
    using namespace std;
    EnumFlags<EHoge> e1 = EHoge::Hoge | EHoge::Piyo;
    e1 |= EHoge::Fuga;
     
    cout << (e1.check(EHoge::Fuga) ? "has Fuga" : "no Fuga") << endl;
    cout << (e1.any_of(EHoge::Hoge | EHoge::Piyo) ? "has HogePiyo" : "no HogePiyo") << endl;
    cout << (e1.all_of(EHoge::Hoge | EHoge::Piyo) ? "has HogePiyo" : "no HogePiyo") << endl;
    
}

http://melpon.org/wandbox/permlink/u5b5tLUl49YUxLmW

一時オブジェクトの寿命についての注意

一時オブジェクトは、次の行にいくと寿命が尽きてそのオブジェクトへの参照は未定義になります。
実装の具合や最適化オプション、コンパイラの種類によっては一見正常に動いてしまうので、注意が必要です。

#include <iostream>
using namespace std;

struct Hoge {
    const char* char_ = nullptr;
    Hoge(const char* c) : char_(c) {}
};

struct Piyo {
    const Hoge* hoge_;
    Piyo(const Hoge& hoge) : hoge_(&hoge) {
        //cout << "Piyo(hoge)" << endl; // このコメントを活かすとなぜかうごく
    }
};

int main() {
    Piyo piyo = Piyo(Hoge("hogehoge"));
    // 上のHogeの寿命は尽きているので、piyo.hoge_ は未定義な値になる?
    // 何のエラーもなく動いてしまうのが怖い。
    // Piyoのコンストラクタに実装を書くか、最適化を外すと期待通りに動いてしまう。
    if (piyo.hoge_->char_ == nullptr) {
        cout << "Error!" << endl;
    }
    else {
        cout << piyo.hoge_->char_;
    }
}

http://melpon.org/wandbox/permlink/3x1ZGslfSFw7IGFG

指定した値で初期化するenumクラス

enumで定義した変数は、暗黙のコンストラクタが呼ばれるとゼロが設定されます。
特定の値(たとえば、-1など)で初期化したい場合に便利なenumのwrapperクラスを作ってみました。

#include <iostream>
#include <map>
using namespace std;
enum Hoges {
    Invalid = -1,
    Hoge = 0,
    Fuga = 1,
    Piyo = 2,
    Hogera = 3,
    PiyoPiyo = 4,
};

// このmapは、値の初期値はHogeになる
map <int, Hoges> hoges1;

// 初期値をテンプレートパラメータで指定するenumクラス
template <typename EnumType, EnumType InitialValue>
struct initialized_enum {
    EnumType value_;
    initialized_enum(EnumType v = InitialValue) : value_(v) {}
    operator EnumType () const { return value_; }
};

// このmapは、初期値がInvalid(-1)になる
map <int, initialized_enum<Hoges, Invalid>> hoges2;

int main() {
    // Hogesの初期値は、Invalidにしたいけど、Hogeになる
    cout << hoges1[0] << endl;
    // initialized_enumだと、初期値を入れてくれる
    cout << hoges2[0] << endl;
}

http://melpon.org/wandbox/permlink/DSQ21IThska1JTQk

テンプレートクラスのクラス変数(staticなインスタンス)を宣言する方法

通常のクラス変数は、cppなどで明示的に宣言しないとインスタンスが生成されませんが、
テンプレートクラスの場合は、ヘッダファイルで宣言するだけでクラス変数のインスタンスが生成されます。

意外と知られていないですが、便利な機能です。

http://melpon.org/wandbox/permlink/kKGoK7YiFfmkJyyM

VisualStudioで、文字リテラルをUTF-8にする

VisualStudioでは、ソースファイルの文字形式にかかわらず、コンパイル時に文字列リテラルはすべてシフトJISに変換されてしまいます。

たとえば、printf("漢字")というプログラムで、ソースコードがUTF-8でも実行時にはシフトJISで表示されます。

コンパイル時の文字コード変換機能をOFFにするやり方はわかりませんが、強制的にUTF-8にするには、以下の方法で可能です。

#if defined(_MSC_VER)
// 文字リテラルをUTF-8として処理する
#pragma execution_character_set("utf-8")
#endif

boost regex の使い方メモ

XMLの文字エスケープのサンプル

#include <iostream>                                                                                                                                                            
#include <string>                                                                                                                                                              
#include <boost/regex.hpp>                                                                                                                                                     
                                                                                                                                                                               
namespace  {                                                                                                                                                                   
  using std::string;                                                                                                                                                           
  using boost::regex;                                                                                                                                                          
  using boost::regex_replace;                                                                                                                                                  
                                                                                                                                                                               
  string escape(const string& src) {                                                                                                                                           
    regex reg("(<)|(>)|(&)|(\\\")|(')");                                                                                                                                       
    string fmt("(?1&lt;)(?2&gt;)(?3&amp;)(?4&quot;)(?5&apos;)");                                                                                                               
    return regex_replace(src, reg, fmt, boost::match_default|boost::format_all);                                                                                               
  }                                                                                                                                                                            
  string unescape(const string& src) {                                                                                                                                         
    regex reg("(&lt;)|(&gt;)|(&amp;)|(&quot;)|(&apos;)");                                                                                                                      
    string fmt("(?1<)(?2>)(?3&)(?4\\\")(?5')");                                                                                                                                
    return regex_replace(src, reg, fmt, boost::match_default|boost::format_all);                                                                                               
  }                                                                                                                                                                            
}                                                                                                                                                                              
                                                                                                                                                                               
int main()                                                                                                                                                                     
{                                                                                                                                                                              
  using namespace std;                                                                                                                                                         
  string src = "hoge<hoge> fuga<'fuga'> &>_<\" <o'o> &lt; &amp;\n";                                                                                                            
  string result = escape(src);                                                                                                                                                 
  cout << src << result << unescape(result);                                                                                                                                   
}                                                                                                                                                                              

output

                                               
hoge<hoge> fuga<'fuga'> &>_<" <o'o> &lt; &amp;
hoge&lt;hoge&gt; fuga&lt;&apos;fuga&apos;&gt; &amp;&gt;_&lt;&quot; &lt;o&apos;o&gt; &amp;lt; &amp;amp; 
hoge<hoge> fuga<'fuga'> &>_<" <o'o> &lt; &amp;

複数の引数を必要とするオブジェクトのemplace (C++11)

便利で高速なemplaceですが、mapやunordered_mapのように、pair型として保持するコンテナの場合、emplaceするときに困った問題があります。

1つの値から生成できるkey, valueの場合は問題ないのですが、

 map<int,int> hoge;
 hoge.emplace(1,2); // OK

生成に2つの値が必要なクラスを格納する場合、問題があります。

 struct Point {
   Point(int x, int y);
 };
 map<int, Point> hoge;
 hoge.emplace(1,2,3); // コンパイルエラー

この書式では、(1,2,3)から、pair<int(1), Point(2,3)> を推測してくれません。 こんな時は、"piecewise_construct"という呪文を使えば、pairを召喚できます。

#include <vector>
#include <map>
#include <iostream>

struct Point {
  int x_;
  int y_;
  Point(int x,int y) : x_(x), y_(y) {}
};

int main(int ac, char* av[]) {
  using namespace std;

  // vectorなら問題 ない//
  vector<Point> hoge;
  hoge.emplace_back(1,2);

  // mapの場合は、key,valueでOK!
  map<string, int> intMap;
  intMap.emplace("hoge", 1);

  // valueのコンストラクタの引数が2個以上の場合は厄介!
  map<string, Point> hogeMap;
  // hogeMap.emplace("hoge", 1, 2); // compile error!
  hogeMap.emplace(piecewise_construct          // pairのコンストラクタを召喚する呪文!
                  , forward_as_tuple("hoge")   // tuple型の生成。make_tupleだとコピーされるので、
                  , forward_as_tuple(1,2));    // forward_as_tupleを使う
}

参考URL

定数の定義と静的構造体テーブルを同時に作るマクロ

定数の定義をソースコード上で行う場合、定数の定義と値の定義を同時に行いたい場合があります。

通常はこのように書きます。

 enum EnumHoge { hoge, fuga, piyo };
 struct HogeHoge {
   EnumHoge id;
   float value;
   const char* name;
 } const table[] = { 
  { hoge, 1.0f, "hoge" },
  { fuga, 2.0f, "fuga" },
  { piyo, 3.0f, "piyo" },
 };

enumの定義と構造体の定義が冗長で、名前の文字列の定義も冗長です。
しかし、魂を悪魔(#define)に売り渡せば、一発で記述できます。

 // ここで定数を定義する(冗長でない単一の記載で済ませる)
 #define LIST \
 DEF(hoge, 1.0f), \
 DEF(fuga, 2.0f), \
 DEF(piyo, 3.0f)
 
 // enumの生成
 #undef DEF
 #define DEF(a, b) a
 enum EnumHoge {  LIST };
 
 // 構造体の生成
 #undef DEF
 #define DEF(a, b) {a, b, #a}
 struct HogeHoge {
   EnumHoge id;
   float    value;
   const char* name;
 } const table[] = { LIST };

使用例

 #include <stdio.h>
 int main(int ac, char*av []) {
   for (size_t n = 0; n < sizeof(table)/sizeof(HogeHoge); ++n) {
     printf("table[%d] = id:%d value:%f name='%s'\n", n, table[n].id,   table[n].value, table[n].name);
   }
 }

結果

table[0] = id:0 value:1.000000 name='hoge'
table[1] = id:1 value:2.000000 name='gufa'
table[2] = id:2 value:3.000000 name='piyo'

ideone: http://ideone.com/HEIsAa#view_edit_box

コンテナに対して、pushとpush_backを自動判別して追加するUtility (C++11)

  // decltypeを使ってコンテナに要素を追加する
  // push_back()を持っている場合                                                                                                                                  
  template <typename CT, typename VT>
  inline auto push_to_container(CT& container, VT&& val, int=0) -> decltype(container.push_back(val))
  {
    return container.push_back(val);
  }
  // push()がある場合                                                                                                                                  
  template <typename CT, typename VT>
  inline auto push_to_container(CT& container, VT&& val) -> decltype(container.push(val))
  {
    return container.push(val);
  }

使用例

 std::vector<int> hoge;
 std::priority_queue<int> fuga;
 ... 
 push_to_container(hoge, 1); // hoge.push_back(1)が呼ばれる
 push_to_container(fuga, 1); // fuga.push(1)が呼ばれる

参考にしたページ

メンバー関数の存在を確かめる方法

VC++でイテレーターを高速化する

VC++では、イテレーターの範囲チェックが行われており、安全な反面、若干遅いです。
以下の定義を行うことで、範囲チェックを無効にできます。(C++ポケットリファレンスより引用)

#define _SECURE_SCL 0

ちなみに、以下の設定で範囲チェックが有効になります。

#define _SECURE_SCL_1
#define _HAS_ITERATOR_DEBUGGING 1

boost mpl map for_each覚え書き

boost::mplのvectorやmapをfor_eachするサンプル。 mapのkeyだけ回すmap_keysのようなフィルタの例も作ってみました。

 #include <iostream>
 #include <vector>
 #include <string>
 #include <typeinfo>
 #include <boost/mpl/list.hpp>
 #include <boost/mpl/map.hpp>
 #include <boost/mpl/for_each.hpp>
 #include <boost/mpl/key_type.hpp>
 
 using namespace boost;
 using namespace std;
    
 struct Disp {
     template <class Type>
     void operator()(const Type&) const {
         cout << typeid(Type).name() << endl;
     }
     template <typename K, typename V>
     void operator()(const mpl::pair<K,V>&) const {
         cout << typeid(K).name() << "," << typeid(V).name() << endl;
     }
 } disp;
  
  
 int main()
 {
     typedef mpl::list<int, std::string, char, std::vector<int> > tl;
     typedef mpl::map<mpl::pair<int,char>, mpl::pair<int,int>, mpl::pair<char, char> > t2;
  
     cout << "mpl list test" << endl;
     mpl::for_each<tl>(disp);
     cout << "mpl  map test" << endl;
     mpl::for_each<t2>(disp);
     cout << "mpl  map key_type test" << endl;
     mpl::for_each<t2, mpl::lambda<mpl::key_type<t2, mpl::_1> > >(disp);
     cout << "finish" << endl;
  
     return 0;
 }

cpp_akiraさんのページにあったサンプルにmapを足してみました。

http://ideone.com/rrk1ZY


トップ   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS