parse - ¿Booster:: lexical_cast es redundante con c++ 11 stoi, stof y family?
stoi in c++ (4)
En cuanto a rendimiento, puedes hacer la comparación usando el siguiente código (es una variación de mi publicación 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;
}
Cuando se executed con
g ++ -std = c ++ 11 -Desnudo -march = native -Wall -pedantic main.cpp && ./a.out
Los resultados son
Función C ++ 11 stof .. 540
Método de lanzamiento léxico ... 559
C ++ 11 función stoi .. 117
Método de lanzamiento léxico ... 156
Las funciones especializadas C ++ 11 ciertamente parecen funcionar mejor. Pero son exactamente eso, especializados , y como tales hacen que la construcción de interfaces abstractas sea más difícil que el lexical_cast
¿ boost::lexical_cast
redundante ahora que C ++ 11 introduce stoi
, stof
y family, o hay alguna razón para seguir utilizándolo? (además de no tener un compilador C ++ 11) ¿Ofrecen exactamente la misma funcionalidad?
boost :: lexical_cast es más que convertir a un conjunto distinto de tipos:
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);
}
Su punto fuerte y su debilidad es la aceptación de cualquier par de tipos para la conversión a través de un estándar std :: stringstream (donde se aplica o no un algoritmo optimizado).
boost::lexical_cast
- maneja más tipos de conversión , incluidos pares de iteradores, matrices, cadenas C, etc.
- ofrece la misma interfaz genérica (
sto*
tiene diferentes nombres para diferentes tipos) - es sensible a la configuración regional (
sto*
/to_string
son solo en parte, por ejemplo,lexical_cast
puede procesar separadores de miles, mientras questoul
no suele)
boost::lexical_cast
le ofrece una interfaz uniforme entre los tipos, que a menudo es muy importante en el código genérico.
En general, una interfaz coherente entre los tipos para la misma funcionalidad permite un mejor código genérico. Por ejemplo, follow se puede usar como analizador genérico desde tokens de cadena a 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]);
}
En lugar de tupla, a menudo utilizo boost fusion struct para deserializar algunas cadenas tokenizadas directamente en una estructura de una manera genérica.