Más utilidades de Boost

Aprenda cómo funciona Boost en segundo plano

Las bibliotecas de C++ de Boost facilitan la escritura de un buen código. Aprenda sobre los dispositivos de los archivos de cabecera de Boost y descubra prácticas utilidades como pares comprimidos y clases no copiables, así como la forma de manejar afirmaciones fallidas.

Arpan Sen, Independent author, Mentor Graphics

Arpan Sen es un ingeniero en jefe trabajando en el desarrollo de software en la industria de automatización del diseño electrónico. Ha trabajado en varios proyectos de UNIX, incluyendo Solaris, SunOS, HP-UX e IRIX así como Linux y Microsoft Windows durante muchos años. Tiene un gran interés en las técnicas de optimización del rendimiento del software, en la teoría del gráfico y en la computación paralela. Arpan tiene un posgrado en sistemas de software. Puede contactarlo escribiendo a arpansen@gmail.com.


Nivel de autor contribuyente en developerWorks

26-03-2012

Introducción

Las bibliotecas de C++ de Boost tienen muchos dispositivos de utilidades que le ayudan a escribir un código mejor y más efectivo. Este artículo examina algunas de las prácticas utilidades, como par comprimido y rasgo de tipo así como dispositivos generales en Boost, que rápidamente ayudan a hacer una clase no copiable o permitir que una función específica devuelva la llamada cuando falla una afirmación. Más aún, el artículo habla de los orígenes de Boost (principalmente de los archivos de cabecera) para explicar la magia que está sucediendo en segundo plano.

Todo el código en este artículo fue compilado usando gcc-4.3.4 y probado usando la biblioteca de Boost versión 1.45. Todos los dispositivos de utilidades discutidos en este artículo son sólo de cabecera, incluyendo las cabeceras apropiadas (normalmente en el formato de boost/su_cabecera.hpp), y especificar la ruta incluida (con la opción –I si está usando GNU Compiler Collection o GCC) durante el tiempo de compilación funciona muy bien.

Necesita entender las plantillas con algo de detalle para seguir este artículo. En particular, el conocimiento de la especialización parcial de plantilla es invaluable. Si necesita ayuda con las plantillas, consulte la sección Recursos .


Par comprimido

La Biblioteca de Plantillas Estándar (STL) define el pair en la utilidad de cabecera. pair es un tipo heterogéneo y contiene un objeto de tipo T1 y otro de tipo T2. El Listado 1 muestra cómo un par sería típicamente implementado.

Listado 1. Una implementación típica de std::pair
template<class _T1, class _T2>
struct pair
  {	// store a pair of values
       pair() : first(_T1()), second(_T2())
      { }     // construct from defaults
     
      pair(const _T1& _Val1, const _T2& _Val2)
          : first(_Val1), second(_Val2)
     { } 	// construct from specified values

      // … more stuff follows 
      _T1 first;	// the first stored value
      _T2 second;	// the second stored value
  };

Esto está bien, pero no es precisamente lo óptimo. ¿Qué pasa si, digamos, una de las clases no tiene miembros? El compilador aún tiene que asignar espacio para la clase vacía, ¿cierto? Vea el Listado 2.

Listado 2. El tamaño de una clase vacía no es 0
#include <iostream>
using namespace std;
 
class A { };
 
int main()
{
  A _a;
  cout << sizeof(_a) << endl;
}

La salida del Listado 2 es 1. El compilador asigna 1 byte por cada objeto de tipo A. Esto significa que si uno de los tipos es un entero y el otro una clase vacía, el tamaño del par correspondiente se vuelve 4 (normalmente, el tamaño del entero en la plataforma x86) + 1 (el tamaño de un objeto vacío) + el desplazamiento para alinear el objeto en límites de 4 bytes—esto es, 8 bytes. El Listado 3 demuestra el punto.

Listado 3. Las clases vacías usadas en par requieren más memoria
#include <iostream>
using namespace std;
class A { };
 
int main()
{
  A _a;
  std::pair<A, int> _b;
 
  cout << sizeof(_a) << endl; // prints 1 
  cout << sizeof(_b) << endl; // prints 8 
}

En lugar de un par, ahora use boost::compressed_pair, definido en la cabecera, compressed_pair.hpp. El Listado 4 muestra el código usando compressed_pair en lugar de un par de STL.

Listado 4. Usando resultados de compressed_pair en un ejecutable de memoria eficiente
#include <iostream>
#include <boost/compressed_pair.hpp>
using namespace std;
class A { };

int main()
{
  A _a;
  std::pair<A, int> _b;
  boost::compressed_pair<A, int> _c;

  cout << sizeof(_a) << endl;
  cout << sizeof(_b) << endl;
  cout << sizeof(_c) << endl;
}

La salida del Listado 4 es 1 8 4.

El tamaño del objeto de par comprimido es 4 bytes—la mitad de lo que requiere std::pair . ¿Entonces cuál es el secreto detrás de esta reducción de memoria? Este es el truco: en lugar de contener la clase vacía como un miembro, la estructura del par en Boost se deriva desde una clase vacía. El compilador optimiza dicha derivación, y los objetos generados para la clase del par son sólo del tamaño de la clase no vacía. El Listado 5 muestra el código que prueba el punto sobre los compiladores optimizando las clases base vacías.

Listado 5. Optimizando la estructura del par al derivar desde una clase vacía
#include <iostream>
using namespace std;
class A { };

struct modified_pair : public A { 
   int n;
}; 
 
int main()
{
  A _a;
  std::pair<A, int> _b;
  modified_pair _c; 
 
  cout << sizeof(_a) << endl;  // prints 1
  cout << sizeof(_b) << endl;  // prints 8 
  cout << sizeof(_c) << endl;  // prints 4 
}

Examinemos las cabeceras de Boost para la definición compressed_pair . Los componentes clave de compressed_pair son compressed_pair_switch y compressed_pair_imp. El Listado 6 muestra las declaraciones para lo mismo.

Listado 6. Componentes de compressed_pair seccionado
template <class T1, class T2, bool IsSame, bool FirstEmpty, bool SecondEmpty>
struct compressed_pair_switch;

template <class T1, class T2, int Version> 
class compressed_pair_imp;

// Let's consider specific partial specializations
template <class T1, class T2>
struct compressed_pair_switch<T1, T2, false, true, false>
{static const int value = 1;};

template <class T1, class T2>
struct compressed_pair_switch<T1, T2, false, false, true>
{static const int value = 2;};

template <class T1, class T2>
class compressed_pair_imp<T1, T2, 1> : protected ::boost::remove_cv<T1>::type
{
  typedef T1                                                 first_type;
  typedef T2                                                 second_type;
   // … 
   private: 
      second_type second_;  // Only the second element is a class member 
};

El compressed_pair_switch y compressed_pair_imp son elementos "de tipo plantilla". Boost define sólo las especializaciones parciales seleccionadas de estas plantillas. El Listado 6 mencionó únicamente compressed_pair_imp<T1, T2, 1>, pero existen otras especializaciones. Cuando el segundo elemento no está vacío y el primer elemento está vacío, compressed_pair (como verá pronto) es derivado desde compressed_pair_imp<T1, T2, 2>.

Observe que, como es esperado, el compressed_pair_imp es derivado desde la clase vacía. Ahora observemos la definición de compressed_pair, que es derivado desde compressed_pair_imp (vea el Listado 7).

Listado 7. Declaración de compressed_pair
template <class T1, class T2>
class compressed_pair
   : private ::boost::details::compressed_pair_imp<T1, T2,
             ::boost::details::compressed_pair_switch<
                    T1,
                    T2,
                    ::boost::is_same<typename remove_cv<T1>::type, 
                    	typename remove_cv<T2>::type>::value,
                    ::boost::is_empty<T1>::value,
                    ::boost::is_empty<T2>::value> ::value>
{ 
// … code for the class follows
};

Si el primer elemento del par está vacío y el segundo elemento no lo está, entonces la clase compressed_pair instanciada tendría a compressed_pair_imp<T1, T2, 1> como la clase base. El tercer elemento de la clase base es usado para elegir qué especialización de plantilla específica usar. El valor para el tercer elemento es proporcionado por:

struct compressed_pair_switch<T1, T2, false, true, false>::value

Observe la definición de compressed_pair_imp<T1, T2, 1>: Esto sólo define la clase para tener el segundo elemento como un miembro. Igualmente, compressed_pair_imp<T1, T2, 2> como se define en compressed_pair.hpp tiene sólo el primer elemento como un miembro.

Los métodos first ( ) y second ( ) son delegados de la clase compressed_pair para la clase compressed_pair_imp . Las definiciones para la misma en compressed_pair_imp son mostradas en el Listado 8.

Listado 8. Los métodos first y second para compressed_pair
typedef typename call_traits<first_type>::reference        first_reference;
typedef typename call_traits<second_type>::reference   second_reference;
      
typedef typename call_traits<first_type>::const_reference      first_const_reference;
typedef typename call_traits<second_type>::const_reference second_const_reference;

first_reference            first()       {return *this;}
first_const_reference first() const {return *this;}

second_reference       second()       {return second_;}
second_const_reference second() const {return second_;}

Observe que cuando el primer elemento es una clase vacía, compressed_pair_imp retorna *esto.


¿Cómo puede saber si una clase está vacía?

Para determinar si una clase está vacía, si T es el tipo de clase, simplemente use boost::is_empty<T>::value, disponible en boost/type_traits/is_empty.hpp, que es lo que compressed_pair usó. Si el valor equivale a 1, la clase está vacía; de lo contrario, debe ver 0. ¿Cómo implementa esto Boost? El Listado 9 proporciona una implementación básica.

Listado 9. Código para averiguar si una clase está vacía
#include <iostream>
using namespace std;
 
template <typename T>
struct is_empty<int>
{
    static const int value = 0;
};
 
class A { };
class B { double d; };
 
int main()
{
  cout << is_empty<A>::value << endl;
  cout << is_empty<B>::value << endl;
}

El Listado 9 está basado en el supuesto de que el compilador optimiza las clases base vacías. Ahora, veamos la categoría general de utilidades como is_empty que proporciona Boost. Estas utilidades forman la biblioteca Boost Type Traits descrita a continuación.


Aprendiendo la Biblioteca Boost Type Traits

¿Entonces, qué es exactamente la Biblioteca Boost Type Traits? El nombre mismo de la biblioteca es un buen lugar para comenzar. Type traits se refiere a información sobre un tipo. Una parte de la información típica que tal vez quiera averiguar sobre un tipo incluye si es un tipo fundamental, un tipo enumerado, una unión o un tipo de clase o referencia; si es un constructor y destructor trivial; etc. Los rasgos de tipo atienden tres propósitos fundamentales:

  • Determinar información sobre un tipo—por ejemplo, is_pointer, is_void, is_array.
  • Probar la relación entre dos tipos—por ejemplo, is_same<T1, T2>, is_base_and_derived<T1, T2>.
  • Transformar un tipo en otro—por ejemplo, remove_const<T>::type crearía el mismo tipo que T pero con el calificador const eliminado, remove_cv<T>::type crearía el mismo tipo que T pero con los calificadores const y volatile eliminados.

Para usar la Biblioteca Type Traits, debe incluir type_traits.hpp en su código de aplicación. El Listado 10 muestra algunas de las funcionalidades que los rasgos de tipo proporcionan.

Listado 10. Algunos de los rasgos de tipo que proporciona Boost
template <typename T>
struct is_empty;

template <typename T>
struct is_array;

template <typename T>
struct is_class;

template <typename T>
struct is_floating_point;

template <typename T>
struct is_enum;

template <typename T>
struct is_function;

¿Por qué desea usar la Biblioteca Type Traits? La respuesta está en el hecho de que frecuentemente necesita crear bibliotecas genéricas y, para algunos tipos específicos, desea evitar el comportamiento genérico y tener una implementación especializada. La Biblioteca Type Traits puede ayudarle con esto. Este artículo no ahonda en las cabeceras de Boost directamente para los rasgos de tipo—la implementación está demasiado involucrada como para explicarla en un solo artículo—pero discute algunos usos y pensamientos en estrategias de implementación típicas. El Listado 11 ofrece una implementación potencial para el rasgo is_array .

Listado 11. Implementación de is_array<T> típica
template<class T>
struct is_array{
  static const bool value = false;
};

template<class T, std::size_t N>
struct is_array< T (&)[N] >{
  static const bool value = true;
};

Eso fue simple; una especialización para el tipo de matriz le ayudó con esto. Usted usaría array<T>::value en su código y actuaría en consecuencia. Tome en cuenta que esta no es exactamente la forma en que lo hace Boost. Las plantillas de rasgos de tipo son derivadas de un true_type o false_type. Entonces, en el Listado 11, la versión especializada para las matrices debe ser derivada de true-type, mientras que la versión genérica debe venir de false-type. Considere un ejemplo usando las cabeceras de Boost reales (Listado 12).

Listado 12. Usando rasgos de is_array<T> e is_pointer<T> en su código
#include <iostream>
#include <boost/type_traits.hpp>

using namespace std;

int main() 
{
    cout << boost::is_array<int[10]>::value << endl;  // outputs 1
    cout << boost::is_array<int[ ]>::value << endl;  // outputs 1
    cout << boost::is_array<int*>::value << endl;  // outputs 0
    cout << boost::is_pointer<int[ ]>::value << endl;  // outputs 0
    cout << boost::is_pointer<float*>::value << endl; // outputs 1
}

Ese fue is_pointer<T>; El Listado 13 muestra cómo is_pointer<T> puede ser implementado. (La especialización parcial para punteros es la ruta esperada.)

Listado 13. Implementación de is_pointer<T> típica
template <typename T> 
struct is_pointer : public false_type{};

template <typename T> 
struct is_pointer<T*> : public true_type{};

Para algunas de las utilidades como is_enum, no hay una forma fácil para hacer esto, y la implementación debe depender de orígenes específicos de compilador para conseguir el resultado deseado. Eso hace el código específico de plataforma, así que consulte la documentación de Boost para obtener más detalles.


Haciendo una clase no copiable, al estilo de Boost

Si necesita hacer su clase no copiable, la forma típica es hacer el constructor de copia y el operador de asignación de la clase privados o protegidos. Si ninguno de los dos es definido, el compilador proporciona una versión implícita que es una función de miembro público. Boost proporciona una forma más fácil para hacer esto al proporcionarle una clase noncopyable definida en la cabecera noncopyable.hpp. Si desea hacer su propia clase no copiable, simplemente derive desde esta clase. No importa si la derivación es pública, protegida o privada: su clase siempre es noncopyable. El Listado 14 muestra cómo debe hacerse la derivación.

Listado 14. Derivando desde la clase noncopyable
#include <boost/noncopyable.hpp>
#include <iostream>

class A : public boost::noncopyable { 
public: 
   A( ) { std::cout << “In A\n” << std::endl; }
};

Ahora, intente usar una construcción de copia y una asignación de operador para la clase A, declarada en el Listado 15.

Listado 15. Usando un constructor de copia y una asignación de operador para un objeto no copiable
int main()
{
    A object1;
    A object2(object1);
    object1 = object2;
    return 0;
}

El Listado 16 muestra el registro de error.

Listado 16. Registro de Error al compilar el código en el Listado 14
/usr/include/boost/noncopyable.hpp: In copy constructor 
	‘<unnamed>::DontTreadOnMe::DontTreadOnMe
	(const<unnamed>::DontTreadOnMe&)’:
/usr/include/boost/noncopyable.hpp:27: error: 
	‘boost::noncopyable_::noncopyable::noncopyable
	(const boost::noncopyable_::noncopyable&)’ 
	is private

/usr/include/boost/noncopyable.hpp: In member function 
	‘<unnamed>::DontTreadOnMe&<unnamed>::
	DontTreadOnMe::operator=(const<unnamed>::DontTreadOnMe&)’:
/usr/include/boost/noncopyable.hpp:28: error: 
	‘const boost::noncopyable_::noncopyable& 
	boost::noncopyable_::noncopyable::operator=
	(const boost::noncopyable_::noncopyable&)’ 
	is private

La definición de clase noncopyable no presenta ninguna sorpresa, ya que copy constructor y operator= son declarados privados. El Listado 17 muestra la declaración de clase.

Listado 17. La declaración de la clase noncopyable
class noncopyable
  {
   protected:
      noncopyable() {}
      ~noncopyable() {}
   private:  // emphasize the following members are private
      noncopyable( const noncopyable& );
      const noncopyable& operator=( const noncopyable& );
  };

Lo otro que hay que tener en cuenta en el Listado 17 es que las definiciones para los métodos copy constructor y operator= no son proporcionados. Si hubieran sido implementados, ¡hubiera sido técnicamente posible copiar la clase noncopyable dentro de sus propios métodos privados! Con esta implementación, usted obtiene mensajes claros de error de tiempo de compilación.


Función de devolución de llamada cuando una afirmación falla

La programación defensiva se trata de tener afirmaciones en el lugar correcto en su código. ¿Pero qué pasa si una afirmación falla? Normalmente, usted puede saber dónde falló la afirmación (nombre de archivo o número de línea) y quizá vea algún mensaje opcional que imprimió el código. Viene en Boost, que proporciona un buen mecanismo de devolución de llamada. Si su expresión evalúa como falso y por ello desencadena una falla de afirmación, una rutina predefinida llamada assertion_failed declarada en el archivo de cabecera assert.hpp es ejecutada. El Listado 18 proporciona un código de muestra usando assertion_failed.

Listado 18. Usando assertion_failed para definir comportamiento de programa cuando las afirmaciones fallan
#include <iostream>
using namespace std;
 
#define BOOST_ENABLE_ASSERT_HANDLER
#include <boost/assert.hpp>
 
namespace boost { 
void assertion_failed(char const * expr, char const * function, 
                             char const * file, long line)
{
   cout << expr << endl;  // the offending expression 
   cout << function << endl;  // calling function 
   cout << file << endl;   // file which contains the assertion
   cout << line << endl;  // line number where assert failed
}
}
 
int main( )
{ 
   BOOST_ASSERT(2 > 3);
}

La función assertion_failed es declarada en la cabecera assert.hpp pero no definida. Usted debe proporcionar una definición para esta función. También, el macro BOOST_ENABLE_ASSERT_HANDLER debe ser definido antes de incluir la cabecera de assert.hpp en el código de aplicación. La salida del Listado 18 se explica a sí misma: assertion_failed es llamado en el momento en que BOOST_ASSERT falla:

2 > 3
int main()
prog.cpp
20

Si BOOST_ENABLE_ASSERT_HANDLER no es definido, entonces el comportamiento de BOOST_ASSERT es el mismo que la normal afirmación.


Utilidad de Boost para intercambiar dos variables

Intercambiar variables es una tarea diaria en la vida de todo programador. La función de plantilla template<class T> void swap (T& left, T& right) disponible en la cabecera boost/swap.hpp le permite intercambiar los valores de dos variables. ¿Entonces por qué es boost::swap interesante cuando STL ya proporcionastd::swap? El comportamiento de std::swap es equivalente a:

template <class T> void swap ( T& a, T& b )
{
  T c(a); 
  a=b; 
  b=c;
}

Ahora, para clases que almacenan grandes cantidades de datos, este método tal vez no sea la forma más eficiente de intercambiar datos, ya que swap involucra un copy construction y dos asignaciones. También, para clases que tengan razones de diseño para tener private o no copy constructor, este estilo de intercambio no funcionará. Esto es lo que boost::swap le da:

  • Puede intercambiar matrices de tipo T, std::swap no puede.
  • boost::swap puede invocar una función con firma swap(T&, T&) si la misma existe en lugar de la predeterminada copy constructor más dos opciones de asignación.
  • boost::swap puede invocar una especialización de plantilla of std::swap.
  • Si ni la segunda ni la tercera de las opciones anteriores son válidas, T debe ser construible para copia y asignable.

El Listado 19 muestra boost::swap siendo usado para intercambiar dos matrices.

Listado 19. Usando boost::swap para intercambiar dos matrices
#include <boost/swap.hpp>
#include <boost/foreach.hpp>
#include <iostream>
using namespace std;
 
int main()
{
  int a[] = {10, 20, 30, 40};
  int b[] = {4, 3, 2, 1};
 
  boost::swap(a, b); // using std::swap here won't work 
 
  BOOST_FOREACH(int t, a) { cout << t << endl; }
  BOOST_FOREACH(int t, a) { cout << t << endl; }
}

Un ejemplo de boost::swap invocando su rutina de intercambio personalizada es mostrado en el Listado 20.

Listado 20. Usando boost::swap para implementar un intercambio personalizado
#include <boost/swap.hpp>
#include <iostream>
using namespace std;
 
typedef struct T { 
  int m_data;
  T(int data) : m_data(data) { }
} T;
 
void swap(T& a, T& b) // custom swap routine that boost ::swap calls
{
  cout << "In custom swap" << endl;
  a.m_data ^= b.m_data;
  b.m_data ^= a.m_data;
  a.m_data ^= b.m_data;
}
 
int main()
{
  T a(30), b(10);
  boost::swap(a, b);
  cout << a.m_data << endl;
  cout << b.m_data << endl;
}

Finalmente, la versión especializada de plantilla es mostrada en el Listado 21.

Listado 21. Usando una versión especializada de plantilla de std::swap
#include <boost/swap.hpp>
#include <iostream>
using namespace std;
 
typedef struct T { 
  int m_data;
  T(int data) : m_data(data) { }
} T;
 
namespace std { 
template<
void swap<T> (T& a, T& b) 
{
  cout << "In template-specialized swap" << endl;
  a.m_data ^= b.m_data;
  b.m_data ^= a.m_data;
  a.m_data ^= b.m_data;
}
}
 
int main()
{
  T a(30), b(10);
  boost::swap(a, b);
  cout << a.m_data << endl;
  cout << b.m_data << endl;
}

Ahora, veamos las partes internas de cómo usted implementa boost::swap. Es de interés específico cómo las matrices swap...for son definidas. El Listado 22 muestra el código, copiado de boost/swap.hpp.

Listado 22. Código de origen para boost::swap
#include <algorithm> //for std::swap
#include <cstddef> //for std::size_t

namespace boost_swap_impl
{
  template<class T>
  void swap_impl(T& left, T& right)
  {
    using namespace std;//use std::swap if argument dependent lookup fails
    swap(left,right);
  }

template<class T, std::size_t N>
  void swap_impl(T (& left)[N], T (& right)[N])
  {
    for (std::size_t i = 0; i < N; ++i)
    {
      ::boost_swap_impl::swap_impl(left[i], right[i]);
    }
  }
} 

namespace boost
{
  template<class T1, class T2>
  void swap(T1& left, T2& right)
  {
    ::boost_swap_impl::swap_impl(left, right);
  }
}

Para matrices, llamar boost::swap finalmente resulta en una llamada para evitar swap_impl(T (& left)[N], T (& right)[N]), porque lo mismo es especializado para matrices. Observe la declaración swap_impl(T (& left)[N], T (& right)[N]); aquí, left y right son referencias a la matriz de tipo T y tamaño N. Ambas matrices deben ser del mismo tamaño, o de lo contrario obtendrá un error de compilación. Para los demás casos, swap_impl(T& left, T& right) es llamado. Observando la definición de swap_impl(T& left, T& right), usted ve que hace una llamada a la rutina swap . Si tiene su versión especializada de plantilla de std::swap (consulte el Listado 21) o una rutina swap global (consulte el Listado 20), lo mismo será llamado. De lo contrario, std::swap es llamado.

Finalmente, observe que la declaración de swap tiene template<typename T1, typename T2>, aunque sería suficiente usar T1. Esto es intencional, porque esta declaración lo hace menos especializado que std::swap. Si boost::swap y std::swap están disponibles en el mismo ámbito, una llamada a swap daría prioridad a std::swap.


Conclusión

Aquí terminamos con este artículo. Observamos algunas utilidades interesantes—pares comprimidos, rasgos de tipo, clases no copiables, manejo de afirmación personalizada e intercambio personalizado—desde el punto de vista del uso y también intentamos entender las partes internas cuando fue posible. Estrictamente hablando, entender las partes internas de las cabeceras de Boost no es requerido para usar estas utilidades, pero conocerlas hace las cosas mucho más fáciles en una variedad de planes. Más notablemente, los trucos que Boost despliega generan un desarrollo de código y biblioteca de rendimiento eficiente.

Recursos

Aprender

Obtener los productos y tecnologías

  • Pruebe el software de IBM gratis. Descargue una versión de prueba, regístrese en una prueba online, trabaje con un producto en un entorno de recinto de seguridad o accédalo a través de la nube. Elija de entre más de 100 productos de prueba de IBM.
  • Aprenda más sobre las bibliotecas de Boost C++.

Comentar

Comentarios

developerWorks: Ingrese

Los campos obligatorios están marcados con un asterisco (*).


¿Necesita un IBM ID?
¿Olvidó su IBM ID?


¿Olvidó su Password?
Cambie su Password

Al hacer clic en Enviar, usted está de acuerdo con los términos y condiciones de developerWorks.

 


La primera vez que inicie sesión en developerWorks, se creará un perfil para usted. La información en su propio perfil (nombre, país/región y nombre de la empresa) se muestra al público y acompañará a cualquier contenido que publique, a menos que opte por la opción de ocultar el nombre de su empresa. Puede actualizar su cuenta de IBM en cualquier momento.

Toda la información enviada es segura.

Elija su nombre para mostrar



La primera vez que inicia sesión en developerWorks se crea un perfil para usted, teniendo que elegir un nombre para mostrar en el mismo. Este nombre acompañará el contenido que usted publique en developerWorks.

Por favor elija un nombre de 3 - 31 caracteres. Su nombre de usuario debe ser único en la comunidad developerWorks y debe ser distinto a su dirección de email por motivos de privacidad.

Los campos obligatorios están marcados con un asterisco (*).

(Por favor elija un nombre de 3 - 31 caracteres.)

Al hacer clic en Enviar, usted está de acuerdo con los términos y condiciones de developerWorks.

 


Toda la información enviada es segura.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=90
Zone=Linux
ArticleID=806618
ArticleTitle=Más utilidades de Boost
publish-date=03262012