Protocol Buffers es una API que usa Google internamente para codificar data. Podría parecerse a xml en que es data estructurada y multi-plataforma, pero es mucho más eficiente que éste, ya que la data va serializada. Sin embargo, a diferencia de la serialización propia de los lenguajes, define un protocolo común para todos los lenguajes. Éste es el aspecto que nos interesa, de hecho, ya que podría permitirnos comunicar data entre C++ y Java.
instalación C++
Descargamos el tarball desde acá >>, lo descomprimimos y dentro del directorio generado ejecutamos
./configure make make check sudo make install
con lo que preparamos los makefiles, compilamos, corremos los tests e instalamos la librería compartida.
Nótese que en Linux (Debian y RedHat >> por lo menos) ejecutar el compilador protoc da el siguiente error
protoc: error while loading shared libraries: libprotobuf.so.7: cannot open shared object file: No such file or directory
ésto se debe a que la librería compartida por defecto queda en /usr/local, el cual no está incluído en el LD_LIBRARY_PATH. En vez de cambiarlo, podemos ejecutar lo anterior con el siguiente flag
./configure --prefix=/usr
para que lo instale en /usr. (Si lo estamos ejecutando por segunda vez, hay que hacer antes un make clean)
.proto
Lo siguiente es definir la data que estaremos serializando. Ésto se hace con un archivo .proto. Aquí intentamos modelar una matriz para pasarla de un lenguaje a otro:
package mi.paquete; //namespace evita conflictos entre proyectos option optimize_for = SPEED; //optimiza serialización message Matrix{ repeated Vector vec = 1; message Vector{ repeated double cell = 1 [packed=true]; //ineficiente sino } }
nótense las opciones packed=true, que es una optimización en la serialización de listas de primitivos, y SPEED, que optimiza el código generado para las opciones de serialización, lectura, etc. en detrimento del tamaño del código.
compilación
Finalmente para compilar este prototipo, usamos el comando protoc con la opción – -cpp_out para que nos genere los archivos C++
protoc --cpp_out=/ruta/salida matrix.proto
Ésto nos genera un header matrix.pb.h y una implementación matrix.pb.cpp.
Para ver qué es lo que contienen estos archivos, RTFM! >>
uso
Como dejé las clases dentro de mi proyecto, para poder usarlas
#include<matrix.pb.h>
estoy teniendo que poner mi workspace en el include path al compilador
-I"/mi/workspace/"
¿ésto estará bien?¿O en general se exportará a /usr/local?
Además, hay que poner la librería compartida en el linker con -lprotobuf
Para dar un ejemplo de cómo estoy llenando esta ‘matriz’, digamos que leo de un .csv los valores como en el siguiente pseudocódigo (el original es propietario…)
#include<matrix.pb.h> //... mi::paquete::Matrix matrix; virtual void metodo(){ std::ifstream archivo; archivo.open("/ruta/a/test.csv"); if(archivo.is_open()){ std::string linea; while(archivo.good()){ std::getline(archivo, linea); char *chars, *token;//csv chars = new char[linea.size()+1]; strcpy (chars, linea.c_str()); token=strtok(chars,";"); //creo fila mi::paquete::Matrix_Vector* vector = matrix.add_vec(); while (token!=NULL){ double numero; std::istringstream buffer(token); buffer >> numero; vector->add_cell(numero);//creo celda token=strtok(NULL,";"); } delete[] chars; } archivo.close(); } }
En seguida, puedo serializar este objeto en un binario como sigue
#include<fstream> //... std::fstream output("testprotobuff.bin", std::ios::out | std::ios::trunc | std::ios::binary); matrix.SerializeToOstream(&output);
El cual luego podemos leer así
#include<fstream> //... std::fstream input("testprotobuff.bin", std::ios::in | std::ios::binary); matrix.ParseFromIstream(&input);