Zserio is a framework for serializing structured data, known for its compact efficiency and extremely low overhead. By defining data structures using a dedicated Zserio language, it can generate code in multiple programming languages (such as C++, Java, Python), facilitating data serialization (writing) and deserialization (reading) across different platforms. The Zserio C++ Runtime Library provides the necessary runtime support for C++ developers to handle Zserio serialized data.
Below, we will introduce how to use the Zserio C++ Runtime Library through a specific sensor data collection case.
🔧 Defining Data Structures
First, we define a simple sensor data structure using the Zserio language. Save the following content as sensor_data.zs:
package sensors;
struct SensorData {
uint16 sensorId; // Sensor ID
string name; // Sensor name
float32 reading; // Reading
uint8 status; // Status code
timestamp lastCalibrated; // Last calibrated time
};
This SensorData structure includes the sensor ID, name, reading, status code, and last calibrated time.
⚙️ Generating C++ Code and Integrating into the Project
Use the Zserio compiler to generate C++ code. Run the following command in the terminal:
zserio -cpp generated_code -src sensors_data.zs
This will generate the corresponding C++ header and source files in the generated_code directory.
A simple CMakeLists.txt project configuration file is as follows:
cmake_minimum_required(VERSION 3.10)
project(SensorDataExample)
# Set C++ standard
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Find Zserio Runtime, ensure it is installed
find_package(PkgConfig REQUIRED)
pkg_check_modules(ZSERIO_RUNTIME REQUIRED zserio_runtime)
# Add executable
add_executable(sensor_example
src/main.cpp
generated_code/sensors/SensorData.cpp
generated_code/sensors/SensorData.h
)
# Include header directories
target_include_directories(sensor_example PRIVATE
${ZSERIO_RUNTIME_INCLUDE_DIRS}
generated_code
)
# Link libraries
target_link_libraries(sensor_example ${ZSERIO_RUNTIME_LIBRARIES})
💻 Writing C++ Serialization and Deserialization Code
In src/main.cpp, we use the generated SensorData object for serialization and deserialization:
#include <iostream>
#include <fstream>
#include <chrono>
// Include generated header files and runtime header files
#include "sensors/SensorData.h"
#include "zserio/SerializeUtil.h"
int main() {
// Create a SensorData instance and set data
sensors::SensorData sensor;
sensor.setSensorId(1001);
sensor.setName("Temperature Sensor");
sensor.setReading(23.5f);
sensor.setStatus(1);
// Get current timestamp for last calibrated time
const auto now = std::chrono::system_clock::now();
const auto timestamp = std::chrono::duration_cast<std::chrono::seconds>(
now.time_since_epoch()).count();
sensor.setLastCalibrated(timestamp);
// Serialize data to file
const char* filename = "sensor_data.bin";
try {
zserio::serializeToFile(sensor, filename);
std::cout << "Data serialized successfully to file: " << filename << std::endl;
} catch (const zserio::CppRuntimeException& e) {
std::cerr << "Serialization failed: " << e.what() << std::endl;
return 1;
}
// Deserialize data from file
try {
auto deserializedSensor = zserio::deserializeFromFile<sensors::SensorData>(filename);
std::cout << "\nDeserialized data:" << std::endl;
std::cout << "Sensor ID: " << deserializedSensor.getSensorId() << std::endl;
std::cout << "Sensor Name: " << deserializedSensor.getName() << std::endl;
std::cout << "Reading: " << deserializedSensor.getReading() << std::endl;
std::cout << "Status: " << static_cast<int>(deserializedSensor.getStatus()) << std::endl;
std::cout << "Last Calibrated Time: " << deserializedSensor.getLastCalibrated() << std::endl;
} catch (const zserio::CppRuntimeException& e) {
std::cerr << "Deserialization failed: " << e.what() << std::endl;
return 1;
}
return 0;
}
🚀 Compiling and Running
Compile and run the program:
mkdir build
cd build
cmake ..
make
./sensor_example
After the program runs successfully, you will see prompts indicating that the data has been successfully serialized and deserialized, along with the sensor data read and restored from the binary file.
💎 Handling Complex and Optional Data
Zserio supports more complex data structures, such as optional fields and constraints. Below is an example of ExtendedSensorData with optional fields and constraints that can be added to sensor_data.zs:
struct ExtendedSensorData {
SensorData basicData; // Embedded basic sensor data
string location?; // Optional field: location information
uint8 accuracy; // Accuracy value
const uint8 MAX_ACCURACY = 100; // Constant definition
constraint accuracy <= MAX_ACCURACY; // Constraint: accuracy cannot exceed maximum value
// Optional field: only record calibrator information if accuracy is above 95
string calibratedBy? if accuracy > 95;
};
Using this structure with constraints and optional fields in C++ code:
// Create ExtendedSensorData instance
sensors::ExtendedSensorData extendedSensor;
extendedSensor.setBasicData(sensor); // Use previously created sensor
extendedSensor.setAccuracy(97);
// Since accuracy > 95, must set calibratedBy
extendedSensor.setCalibratedBy("John Doe");
// Serialize
zserio::serializeToFile(extendedSensor, "extended_sensor_data.bin");
// Deserialize
auto deserializedExtended = zserio::deserializeFromFile<sensors::ExtendedSensorData>(
"extended_sensor_data.bin");
// Check and access optional fields
if (deserializedExtended.isLocationSet()) {
std::cout << "Location: " << deserializedExtended.getLocation() << std::endl;
}
if (deserializedExtended.isCalibratedBySet()) {
std::cout << "Calibrated By: " << deserializedExtended.getCalibratedBy() << std::endl;
}
🔄 Real-World Application Scenarios
The Zserio C++ Runtime Library performs excellently in real-world applications:
- Embedded Systems: Optimize data storage space using its compactness and low overhead.
- Cross-Platform Data Exchange: Applications on different platforms (such as C++, Java, Python) can easily exchange data through a unified Zserio schema definition.
- Real-Time Data Processing: Efficient serialization and deserialization capabilities reduce data processing time, enhancing system real-time performance.
💡 Development Considerations
When using the Zserio C++ Runtime Library, be aware of the following:
- Environment Configuration: Ensure the Zserio compiler and C++ runtime library are correctly installed.
- Version Compatibility: Ensure that the code generated by the Zserio compiler is compatible with the runtime library version.
- Memory Management: Although the runtime library encapsulates low-level memory operations, be mindful of memory usage when handling large amounts of data.
- Error Handling: As shown in the example, properly catch and handle
zserio::CppRuntimeExceptionexceptions.
💎 Conclusion
The Zserio C++ Runtime Library provides a powerful and efficient data serialization solution. With simple schema definitions, combined with its generated type-safe C++ code and runtime support, developers can easily achieve compact data exchange across platforms, making it particularly suitable for resource-constrained and performance-critical scenarios.