Skip to content

Language Examples

This page shows how to use generated code in each language with the standardized serialize/deserialize API.

Proto Definition

Examples use this proto file:

package example;

message Status {
  option msgid = 1;
  uint32 id = 1;
  float value = 2;
}

Generate code:

python -m struct_frame status.proto --build_c --build_cpp --build_ts --build_py --build_js --build_csharp

Using Frame Profiles

Frame profiles provide complete framing and parsing functionality. This is the recommended approach for most applications.

Python with Profiles

from generated.example import ExampleStatus
from generated.frame_profiles import (
    ProfileStandardWriter, 
    ProfileStandardAccumulatingReader
)

# Create and serialize a message
msg = ExampleStatus(id=42, value=3.14)

# Encode with frame profile
writer = ProfileStandardWriter(1024)
bytes_written = writer.write(msg)
frame_data = writer.data()

# Parse received frames
reader = ProfileStandardAccumulatingReader()
reader.add_data(frame_data)

# Deserialize using FrameMsgInfo
result = reader.next()
if result:
    decoded_msg = ExampleStatus.deserialize(result)  # Pass FrameMsgInfo directly
    print(f"ID: {decoded_msg.id}, Value: {decoded_msg.value}")

TypeScript with Profiles

import { ExampleStatus } from './generated/example.structframe';
import { 
    ProfileStandardWriter, 
    ProfileStandardAccumulatingReader 
} from './generated/frame-profiles';

// Create and serialize a message
const msg = new ExampleStatus();
msg.id = 42;
msg.value = 3.14;

// Encode with frame profile
const writer = new ProfileStandardWriter(1024);
writer.write(msg);
const frameData = writer.data();

// Parse received frames
const reader = new ProfileStandardAccumulatingReader();
reader.addData(frameData);

// Deserialize using FrameMsgInfo
const result = reader.next();
if (result) {
    const decodedMsg = ExampleStatus.deserialize(result);  // Pass FrameMsgInfo directly
    console.log(`ID: ${decodedMsg.id}, Value: ${decodedMsg.value}`);
}

C++ with Profiles

#include "example.structframe.hpp"
#include "FrameProfiles.hpp"

// Create and serialize a message
ExampleStatus msg;
msg.id = 42;
msg.value = 3.14f;

// Encode with frame profile
uint8_t buffer[1024];
FrameParsers::ProfileStandardWriter writer(buffer, sizeof(buffer));
writer.write(msg);

// Parse received frames
FrameParsers::ProfileStandardAccumulatingReader reader;
reader.add_data(buffer, writer.size());

// Deserialize using FrameMsgInfo
if (auto result = reader.next()) {
    ExampleStatus decoded_msg;
    decoded_msg.deserialize(result);  // Pass FrameMsgInfo directly
    std::cout << "ID: " << decoded_msg.id << ", Value: " << decoded_msg.value << std::endl;
}

C# with Profiles

using StructFrame;

// Create and serialize a message
var msg = new ExampleStatus {
    Id = 42,
    Value = 3.14f
};

// Encode with frame profile
var writer = new ProfileStandardWriter(1024);
writer.Write(msg);
var frameData = writer.Data();

// Parse received frames
var reader = new ProfileStandardAccumulatingReader();
reader.AddData(frameData);

// Deserialize using FrameMsgInfo
var result = reader.Next();
if (result != null) {
    var decodedMsg = ExampleStatus.Deserialize(result);  // Pass FrameMsgInfo directly
    Console.WriteLine($"ID: {decodedMsg.Id}, Value: {decodedMsg.Value}");
}

JavaScript with Profiles

const { ExampleStatus } = require('./generated/example.structframe');
const { 
    ProfileStandardWriter, 
    ProfileStandardAccumulatingReader 
} = require('./generated/frame-profiles');

// Create and serialize a message
const msg = new ExampleStatus();
msg.id = 42;
msg.value = 3.14;

// Encode with frame profile
const writer = new ProfileStandardWriter(1024);
writer.write(msg);
const frameData = writer.data();

// Parse received frames
const reader = new ProfileStandardAccumulatingReader();
reader.addData(frameData);

// Deserialize using FrameMsgInfo
const result = reader.next();
if (result) {
    const decodedMsg = ExampleStatus.deserialize(result);  // Pass FrameMsgInfo directly
    console.log(`ID: ${decodedMsg.id}, Value: ${decodedMsg.value}`);
}

Direct Message Serialization

For cases where you need direct access to message bytes without framing:

Python

from generated.example import ExampleStatus

# Create a message
msg = ExampleStatus(id=42, value=3.14)

# Serialize to bytes
data = msg.serialize()

# Send data over serial, network, etc.
# ...

# Deserialize from bytes
received = ExampleStatus.deserialize(data)
print(f"ID: {received.id}, Value: {received.value}")

TypeScript

import { ExampleStatus } from './generated/example.structframe';

// Create a message
const msg = new ExampleStatus();
msg.id = 42;
msg.value = 3.14;

// Serialize to buffer
const data = msg.serialize();

// Send data over network, etc.
// ...

// Deserialize from buffer
const received = ExampleStatus.deserialize(data);
console.log(`ID: ${received.id}, Value: ${received.value}`);

JavaScript

const { ExampleStatus } = require('./generated/example.structframe');

// Create a message
const msg = new ExampleStatus();
msg.id = 42;
msg.value = 3.14;

// Serialize to buffer
const data = msg.serialize();

// Send data over network, etc.
// ...

// Deserialize from buffer
const received = ExampleStatus.deserialize(data);
console.log(`ID: ${received.id}, Value: ${received.value}`);

C++

#include "example.structframe.hpp"
#include <iostream>

int main() {
    // Create a message
    ExampleStatus msg;
    msg.id = 42;
    msg.value = 3.14f;

    // Serialize to buffer
    uint8_t buffer[1024];
    msg.serialize(buffer);
    size_t size = sizeof(ExampleStatus);

    // Send data over serial, network, etc.
    // ...

    // Deserialize from buffer
    ExampleStatus received;
    received.deserialize(buffer, size);
    std::cout << "ID: " << received.id << ", Value: " << received.value << std::endl;

    return 0;
}

C

using StructFrame;

class Program {
    static void Main() {
        // Create a message
        var msg = new ExampleStatus {
            Id = 42,
            Value = 3.14f
        };

        // Serialize to bytes
        byte[] data = msg.Serialize();

        // Send data over network, etc.
        // ...

        // Deserialize from bytes
        var received = ExampleStatus.Deserialize(data);
        Console.WriteLine($"ID: {received.Id}, Value: {received.Value}");
    }
}

Communication Examples

Serial Communication (Python)

import serial
from generated.example import ExampleStatus
from generated.frame_profiles import ProfileStandardAccumulatingReader

# Setup serial connection
ser = serial.Serial('/dev/ttyUSB0', 115200)
reader = ProfileStandardAccumulatingReader()

while True:
    if ser.in_waiting:
        # Read available data
        data = ser.read(ser.in_waiting)
        reader.add_data(data)

        # Process all complete frames
        while True:
            result = reader.next()
            if result is None or not result.valid:
                break

            # Deserialize using FrameMsgInfo
            msg = ExampleStatus.deserialize(result)
            print(f"Received: ID={msg.id}, Value={msg.value}")

Streaming Parser (C++)

#include "example.structframe.hpp"
#include "FrameProfiles.hpp"

// Byte-by-byte streaming parser for UART/serial
FrameParsers::ProfileStandardAccumulatingReader reader;

void on_byte_received(uint8_t byte) {
    if (auto result = reader.push_byte(byte)) {
        // Complete message received - result is valid due to operator bool()
        ExampleStatus msg;
        msg.deserialize(result);
        std::cout << "ID: " << msg.id << std::endl;
    }
}

TCP Socket (TypeScript)

import * as net from 'net';
import { ExampleStatus } from './generated/example.structframe';
import { ProfileStandardAccumulatingReader } from './generated/frame-profiles';

const client = net.createConnection({ port: 8080 });
const reader = new ProfileStandardAccumulatingReader();

client.on('data', (data: Buffer) => {
    reader.addData(data);

    let result;
    while ((result = reader.next()) && result.valid) {
        const msg = ExampleStatus.deserialize(result);
        console.log(`ID: ${msg.id}, Value: ${msg.value}`);
    }
});

WebSocket (JavaScript)

const { ExampleStatus } = require('./generated/example.structframe');
const { ProfileStandardAccumulatingReader } = require('./generated/frame-profiles');
const WebSocket = require('ws');

const ws = new WebSocket('ws://localhost:8080');
const reader = new ProfileStandardAccumulatingReader();

ws.on('message', (data) => {
    reader.addData(Buffer.from(data));

    let result;
    while ((result = reader.next()) && result.valid) {
        const msg = ExampleStatus.deserialize(result);
        console.log(`ID: ${msg.id}, Value: ${msg.value}`);
    }
});

Frame Profiles

Different profiles for different use cases:

  • ProfileStandard: General purpose (Basic header + Default payload)
  • ProfileSensor: Low-bandwidth sensors (Tiny header + Minimal payload)
  • ProfileIPC: Trusted inter-process (No header + Minimal payload)
  • ProfileBulk: Large data transfers (Basic header + Extended payload)
  • ProfileNetwork: Multi-system networks (Basic header + Extended Multi-System Stream payload)

All profiles support the same API - just change the class name!

Arrays

Example with arrays:

message SensorData {
  option msgid = 2;
  repeated float readings = 1 [max_size=10];
}

Creating messages with arrays works the same way across languages:

ExampleSensorData data;
data.readings_count = 3;
data.readings[0] = 1.1f;
data.readings[1] = 2.2f;
data.readings[2] = 3.3f;

// Serialize/deserialize works the same as simple messages
// Just use msg.serialize() / deserialize(data)
ExampleSensorData data;
data.readings_count = 3;
data.readings[0] = 1.1f;
data.readings[1] = 2.2f;
data.readings[2] = 3.3f;

// Serialize/deserialize works the same as simple messages
// Just use msg.serialize() / deserialize(data)
data = ExampleSensorData()
data.readings_count = 3
data.readings[0] = 1.1
data.readings[1] = 2.2
data.readings[2] = 3.3

# Serialize/deserialize works the same as simple messages
# Just use msg.serialize() / deserialize(data)
const data = new ExampleSensorData();
data.readings_count = 3;
data.readings[0] = 1.1;
data.readings[1] = 2.2;
data.readings[2] = 3.3;

// Serialize/deserialize works the same as simple messages
// Just use msg.serialize() / deserialize(data)
var data = new ExampleSensorData {
    ReadingsCount = 3
};
data.Readings[0] = 1.1f;
data.Readings[1] = 2.2f;
data.Readings[2] = 3.3f;

// Serialize/deserialize works the same as simple messages
// Just use msg.Serialize() / Deserialize(data)

Nested Messages

Example with nested messages:

message Position {
  double lat = 1;
  double lon = 2;
}

message Vehicle {
  option msgid = 3;
  uint32 id = 1;
  Position pos = 2;
}

Nested messages are accessed naturally in all languages:

ExampleVehicle v;
v.id = 1;
v.pos.lat = 37.7749;
v.pos.lon = -122.4194;

// Serialize/deserialize works the same - nested messages are handled automatically
ExampleVehicle v;
v.id = 1;
v.pos.lat = 37.7749;
v.pos.lon = -122.4194;

// Serialize/deserialize works the same - nested messages are handled automatically
v = ExampleVehicle(id=1)
v.pos.lat = 37.7749
v.pos.lon = -122.4194

# Serialize/deserialize works the same - nested messages are handled automatically
const v = new ExampleVehicle();
v.id = 1;
v.pos.lat = 37.7749;
v.pos.lon = -122.4194;

// Serialize/deserialize works the same - nested messages are handled automatically
var v = new ExampleVehicle {
    Id = 1,
    Pos = new ExamplePosition {
        Lat = 37.7749,
        Lon = -122.4194
    }
};

// Serialize/deserialize works the same - nested messages are handled automatically

Key Takeaways

  1. Consistent API: All languages use serialize() / deserialize() (case varies by language convention)
  2. Frame Profiles: Use ProfileStandardWriter/Reader for complete framing and parsing
  3. FrameMsgInfo: Pass frame parser results directly to deserialize() - no manual extraction needed
  4. Variable Messages: Transparent handling - same API for fixed and variable-length messages
  5. Arrays & Nested Messages: Automatically handled by serialize/deserialize