web-dev-qa-db-ja.com

boost :: lexical_castはc ++ 11 stoi、stof、およびfamilyで冗長ですか?

boost::lexical_cast C++ 11が stoistof とファミリを導入したことで冗長になりましたが、それでもまだ使用する理由はありますか? (C++ 11コンパイラがないことを除いて)まったく同じ機能を提供しますか?

25
Ela782

boost::lexical_cast

  • ハンドル より多くの種類の変換 、イテレータのペア、配列、C文字列などを含みます。
  • 同じ汎用インターフェース(sto*はタイプごとに異なる名前を持っています)
  • ロケール依存(sto*/to_stringは一部のみです。 lexical_castは数千のセパレータを処理できますが、stoulは通常は処理できません)
21
Cubbi

boost::lexical_castは、一般的なコードで非常に重要であることが多いタイプ間で統一されたインターフェースを提供します。

一般に、同じ機能のタイプ間で一貫したインターフェイスを使用すると、汎用コードをより適切に使用できます。たとえば、文字列トークンからstd :: Tupleへの汎用パーサーとして次を使用できます。

template<typename T>
void fill(T& item, const std::string& token){
    item = boost::lexical_cast<T>(token)
} 

template<int N, typename ...Ts> 
void parse(std::integral_constant<int, N>, std::Tuple<Ts...>& info, std::vector<std::string>& tokens) {
    fill(std::get<N>(info), tokens[N]);
    parse(std::integral_constant<int, N - 1>, info, tokens); 
}

template<typename ...Ts> 
void parse(std::integral_constant<int, 0>, std::Tuple<Ts...>& info, std::vector<std::string>& tokens) {
    fill(std::get<0>(info), tokens[0]);
}

タプルの代わりに、私はしばしば boost fusion struct を使用して、トークン化された文字列を一般的な方法で直接構造体に直接逆シリアル化します。

19
OOO

パフォーマンスに関しては、次のコードを使用して比較できます(これは私の投稿のバリエーションです here)

#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include <chrono>
#include <random>
#include <exception>
#include <type_traits>
#include <boost/lexical_cast.hpp>

using namespace std;

// 1. A way to easily measure elapsed time -------------------
template<typename TimeT = std::chrono::milliseconds>
struct measure
{
    template<typename F>
    static typename TimeT::rep execution(F const &func)
    {
        auto start = std::chrono::system_clock::now();
        func();
        auto duration = std::chrono::duration_cast< TimeT>(
            std::chrono::system_clock::now() - start);
        return duration.count();
    }
};
// -----------------------------------------------------------

// 2. Define the convertion functions ========================
// A. Using boost::lexical_cast ------------------------------
template<typename Ret> 
Ret NumberFromString(string const &value) {
    return boost::lexical_cast<Ret>(value);
}

// B. Using c++11 stoi() -------------------------------------
int IntFromString(string const &value) { 
    return std::stoi(value);
}

// C. Using c++11 stof() -------------------------------------
float FloatFromString(string const &value) { 
    return std::stof(value);
}
// ===========================================================

// 3. A wrapper to measure the different executions ----------
template<typename T, typename F> long long 
MeasureExec(std::vector<string> const &v1, F const &func)
{
    return measure<>::execution([&]() {
        for (auto const &i : v1) {
            if (func(i) != NumberFromString<T>(i)) {
                throw std::runtime_error("FAIL");
            }
        }
    });
}
// -----------------------------------------------------------

// 4. Machinery to generate random numbers into a vector -----
template<typename T>
typename std::enable_if<std::is_integral<T>::value>::type
FillVec(vector<T> &v)
{
    mt19937 e2(1);
    uniform_int_distribution<> dist(3, 1440);
    generate(v.begin(), v.end(), [&]() { return dist(e2); });
}

template<typename T>
typename std::enable_if<!std::is_integral<T>::value>::type
FillVec(vector<T> &v)
{
    mt19937 e2(1);
    uniform_real_distribution<> dist(-1440., 1440.);
    generate(v.begin(), v.end(), [&]() { return dist(e2); });
}

template<typename T>
void FillVec(vector<T> const &vec, vector<string> *result)
{
    result->resize(vec.size());
    for (size_t i = 0; i < vec.size(); i++)
        result->at(i) = boost::lexical_cast<string>(vec[i]);
}
// -----------------------------------------------------------

int main()
{
    std::vector<int> vi(991908);
    FillVec(vi);
    std::vector<float> vf(991908);
    FillVec(vf);

    std::vector<string> vsi, vsf;
    FillVec(vi, &vsi);
    FillVec(vf, &vsf);

    cout << "C++ 11 stof function .. " <<
        MeasureExec<float>(vsf, FloatFromString) << endl;
    cout << "Lexical cast method ... " <<
        MeasureExec<float>(vsf, NumberFromString<float>) << endl;

    cout << endl << endl;

    cout << "C++ 11 stoi function .. " <<
        MeasureExec<int>(vsi, IntFromString) << endl;
    cout << "Lexical cast method ... " <<
        MeasureExec<int>(vsi, NumberFromString<int>) << endl;

    return 0;
}

実行される

g ++ -std = c ++ 11 -Ofast -march = native -Wall -pedantic main.cpp && ./a.out

結果は

C++ 11 stof関数.. 540

字句キャスト法... 559

C++ 11 stoi関数.. 117

字句キャスト法... 156

C++ 11に特化した関数は確かにパフォーマンスが良いようです。しかし、それらはまさにspecializedであり、そのため、抽象インターフェースの構築がlexical_castより困難になります。

5

boost :: lexical_castは、タイプの異なるセットに変換するだけではありません。

struct A {};
std::ostream& operator << (std::ostream& stream, const A&) {
    return stream;
}

struct B {};
std::istream& operator >> (std::istream& stream, B&) {
    return stream;
}

int main(){
    A a;
    B b = boost::lexical_cast<B>(a);
}

その長所と短所は、中間のstd :: stringstream(最適化されたアルゴリズムが適用される、または適用されない場合)を介した変換のタイプのペアを受け入れることです。

4
user2249683