library for c++ c++11 boost nimrod nim

c++ - for - boost library ubuntu



¿Cómo puedo crear un nuevo tipo primitivo utilizando typedefs fuertes de estilo C++ 11? (5)

Estoy tratando de emular en C ++ un tipo distinto del lenguaje de programación Nim . El siguiente ejemplo no compilará en Nim porque el compilador captura las variables e y d tienen diferentes tipos ( Error: type mismatch: got (Euros, float) ) a pesar de que ambos son flotantes en el nivel binario:

type Euros = distinct float when isMainModule: var e = Euros(12.34) d = 23.3 echo (e + d)

Una forma de hacer esto en C ++ sería escribir una clase contenedora para flotadores. Pero esto no funciona bien con las API que exportan el tipo porque el tamaño no será el mismo que el flotante. O incluso si el tamaño de una clase coincide con la longitud de almacenamiento de un flotador, nunca coincidirá con el tamaño de un tipo char. Eso funcionará si también implementa todos los operadores posibles para operaciones como la suma, la resta, etc., pero requiere mucha escritura y duplicación de código.

Las preguntas más antiguas como Crear un nuevo tipo primitivo tienen como respuesta aceptada el uso de typedef fuerte de boost. Sin embargo, typedef parece funcionar solo para firmas de tipo de función, typedef no evitará que se agreguen dos tipos heredados de flotación y su tipo se cambie por completo (bueno, porque existe la ilusión de un nuevo tipo):

#include <boost/serialization/strong_typedef.hpp> #include <stdio.h> BOOST_STRONG_TYPEDEF(float, money); void test(money a, float b) { int t = a + b; printf("value is %d", t); } int main() { money a(5.5); int euros(5); // This is not caught! int dollars = a + euros; printf("dollars %d/n", dollars); // But the compiler catches this misuse. test(euros, a); }

Pero eso es casi todo, la llamada test() no funcionará porque la firma no coincide, pero el lenguaje todavía permite que otras operaciones manipulen tipos a voluntad.

Esa misma respuesta menciona a C ++ 0x para generar fuertes typedefs, así que busqué este nuevo soporte y descubrí que el propio Bjarne Stroustrup dio una nota de apertura al estilo de C ++ 11 en 2012 . Alrededor del minuto 21 comienza a hablar de estas nuevas definiciones de tipos fuertes. Si solo descarga las diapositivas, la página 19 comienza a hablar sobre las Unidades SI y más adelante las páginas 22 y 23 mencionan cómo se haría esto. Sin embargo, no he podido hacer que los ejemplos funcionen. Aquí está el mosaico que logré inventar:

template<int M, int K, int S> struct Unit { // a unit in the MKS system enum { m=M, kg=K, s=S }; }; template<typename Unit> // a magnitude with a unit struct Value { double val; // the magnitude explicit Value(double d) : val(d) {} // construct a Value from a double }; using Meter = Unit<1,0,0>; // unit: meter using Second = Unit<0,0,1>; // unit: sec using Speed = Value< Unit<1,0,-1> >; // meters/second type constexpr Value<Second> operator "" _s(long double d) // a f-p literal suffixed by ‘_s’ { return Value<Second> (d); } constexpr Value<Meter> operator "" _m(long double d) // a f-p literal suffixed by ‘_m’ { return Value<Meter> (d); } int main(void) { Speed sp1 = 100_m / 9.8_s; return 42; }

Estoy tratando de compilar esto bajo MacOSX con el último Xcode 5.1.1 con la línea de comando:

$ g++ unit.cpp -std=c++11 unit.cpp:13:25: error: constexpr function''s return type ''Value<Second>'' is not a literal type constexpr Value<Second> operator "" _s(long double d) ^ unit.cpp:5:8: note: ''Value<Unit<0, 0, 1> >'' is not literal because it is not an aggregate and has no constexpr constructors other than copy or move constructors struct Value { ^ unit.cpp:18:24: error: constexpr function''s return type ''Value<Meter>'' is not a literal type constexpr Value<Meter> operator "" _m(long double d) ^ unit.cpp:5:8: note: ''Value<Unit<1, 0, 0> >'' is not literal because it is not an aggregate and has no constexpr constructors other than copy or move constructors struct Value { ^ unit.cpp:26:20: error: no matching literal operator for call to ''operator "" _m'' with argument of type ''unsigned long long'' or ''const char *'', and no matching literal operator template Speed sp1 = 100_m / 9.8_s; ^ unit.cpp:26:28: error: no matching literal operator for call to ''operator "" _s'' with argument of type ''long double'' or ''const char *'', and no matching literal operator template Speed sp1 = 100_m / 9.8_s; ^ 4 errors generated.

Tal vez los ejemplos dados en las diapositivas y me falta algún código más? ¿Alguien tiene un ejemplo completo de lo que Bjarne estaba tratando de demostrar?


Hay varias maneras de resolver esto, pero como estaba buscando una solución al código de Bjarne en las diapositivas de la presentación, estoy aceptando esta respuesta que @ robson3.14 dejó en los comentarios a la pregunta:

#include <iostream> template<int M, int K, int S> struct Unit { // a unit in the MKS system enum { m=M, kg=K, s=S }; }; template<typename Unit> // a magnitude with a unit struct Value { double val; // the magnitude // construct a Value from a double constexpr explicit Value(double d) : val(d) {} }; using Meter = Unit<1,0,0>; // unit: meter using Second = Unit<0,0,1>; // unit: sec using Speed = Value<Unit<1,0,-1>>; // meters/second type // a f-p literal suffixed by ‘_s’ constexpr Value<Second> operator "" _s(long double d) { return Value<Second> (d); } // a f-p literal suffixed by ‘_m’ constexpr Value<Meter> operator "" _m(long double d) { return Value<Meter> (d); } // an integral literal suffixed by ‘_m’ constexpr Value<Meter> operator "" _m(unsigned long long d) { return Value<Meter> (d); } template<int m1, int k1, int s1, int m2, int k2, int s2> Value<Unit<m1 - m2, k1 - k2, s1 - s2>> operator / (Value<Unit<m1, k1, s1>> a, Value<Unit<m2, k2, s2>> b) { return Value<Unit<m1 - m2, k1 - k2, s1 - s2>>(a.val / b.val); } int main() { Speed sp1 = 100_m / 9.8_s; std::cout << sp1.val; }


Los compiladores de C ++ generalmente esperan que la opción de línea de comando -std=c++11 (o -std=c++0x para los más antiguos, respectivamente) active el soporte de C ++ 11.

no soporta el estilo C ++ 11 en absoluto.

No, perfectamente lo hace. El soporte de GCC 4.7.2 se puede consultar aquí . Para activar algunas funciones experimentales, pase -std=gnu++11 .

Y Clang 3.4 en realidad soporta casi todo en C ++ 11 y ya está fuera de C ++ 1y .


No estoy seguro de que esto sea lo que quieres, es feo, pero funciona :) Puedes envolver el tipo en una clase de plantilla,

template <typename T, int N> // N is used for tagging struct strong_typedef { using strong_type = strong_typedef<T,N>; // typedef for the strong type using type = T; // the wrapped type T value; // the wrapped value strong_typedef(T val): value(val){}; // constructor strong_typedef(){value={};}; // default, zero-initialization // operator overloading, basic example: strong_type& operator+(const strong_type& rhs) { value+=rhs.value; return *this; } // display it friend ostream& operator<<(ostream & lhs, const strong_typedef& rhs) { lhs << rhs.value; return lhs; } };

entonces utilízalo como

// these are all different types strong_typedef<double, 0> x = 1.1; strong_typedef<double, 1> y = 2.2; strong_typedef<double, 2> z = 3.3; std::cout << x + x << std::endl; // outputs 2.2, can add x and x // cout << x + y << endl; // compile-time ERROR, different types

x , y y z son 3 tipos diferentes ahora, debido a los diferentes N -s utilizados en la plantilla. Puede acceder al tipo y valor utilizando los campos type y value , como x::value (será el doble 1.1). Por supuesto, si struct_typedef::type directamente el struct_typedef::type , struct_typedef::type al punto de struct_typedef::type , ya que estás perdiendo el tipo strong . Básicamente, su tipo debe ser strong_typedef y no strong_typedef::type .


No hay typedefs fuertes en C ++ 11. Hay soporte para unidades con <chrono> pero eso es una cosa totalmente diferente. Nadie puede estar de acuerdo con el comportamiento que deben tener los tipos de letra fuertes, exactamente, por lo que nunca ha habido una propuesta para ellos que llegue a ninguna parte, por lo que no solo están en C ++ 11 ni en C ++ 14, sino que no hay una perspectiva realista. momento en que entrarán en cualquier futuro estándar.


Una alternativa que no implica typedef y no es impuesta por el compilador, pero dificulta al programador cometer errores al codificar la unidad en cuestión como miembro de la estructura.

Para mi proyecto arduino tengo tipos como

template <typename T> struct millisecond { T millisecond; static constexpr const struct millisecond<T> zero = { 0 }; }; template <typename T> struct microsecond { T microsecond; static constexpr const struct microsecond<T> zero = { 0 }; };

y usar como

auto time_diff = millisecond<unsigned long>::zero; time_diff.millisecond = nowMilliseconds() - s_lastPollTime.millisecond;

Entonces, con esta estrategia, el compilador no le impide mezclar unidades, pero si lo hace, el error siempre le gritará :

total_expenses.euros = expence1.euros + expence2.dollars;