New Research: Supply Chain Attack on Axios Pulls Malicious Dependency from npm.Details
Socket
Book a DemoSign in
Socket

soia-cc-gen

Package Overview
Dependencies
Maintainers
1
Versions
11
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

soia-cc-gen

[![npm](https://img.shields.io/npm/v/soia-cc-gen)](https://www.npmjs.com/package/soia-cc-gen) [![build](https://github.com/gepheum/soia-cc-gen/workflows/Build/badge.svg)](https://github.com/gepheum/soia-cc-gen/actions)

latest
Source
npmnpm
Version
1.1.15
Version published
Weekly downloads
9
-10%
Maintainers
1
Weekly downloads
 
Created
Source

npm build

Soia's C++ Code Generator

Official plugin for generating C++ code from .soia files.

Targets C++17 and higher.

Installation

From your project's root directory, run npm i --save-dev soia-cc-gen.

In your soia.yml file, add the following snippet under generators:

  - mod: soia-cc-gen
    config:
      writeGoogleTestHeaders: true

The npm run soiac command will now generate C++ code within the soiagen directory.

For more information, see this C++ project example.

C++ generated code guide

The examples below are for the code generated from this .soia file.

Referring to generated symbols

Every generated symbol lives in a namespace called soiagen_${path}, where ${path} is the path to the .soia file relative from the root of the soia source directory, with the ".soia" extension removed, and slashes replaced with underscores.

#include "soiagen/user.h"

using ::soiagen_user::User;
using ::soiagen_user::UserRegistry;

Constructing structs

// You can construct a struct like this:
User john;
john.user_id = 42;
john.name = "John Doe";

// Or you can use the designated initialized syntax:
User jane = {
    // Keep fields in alphabetical order
    .name = "Jane Doe",
    .pets = {{
                  .name = "Fluffy",
                  .picture = "cat",
              },
              {
                  .name = "Rex",
                  .picture = "dog",
              }},
    .subscription_status = soiagen::kPremium,
    .user_id = 43,
};

// ${StructName}::whole forces you to initialize all the fields of the struct.
// You will get a compile-time error if you miss one.
User lyla = User::whole{
    .name = "Lyla Doe",
    .pets =
        {
            User::Pet::whole{
                .height_in_meters = 0.05f,
                .name = "Tiny",
                .picture = "🐁",
            },
        },
    .quote = "This is Lyla's world, you just live in it",
    .subscription_status = soiagen::kFree,
    .user_id = 44,
};

Constructing enums


// Use soiagen::${kFieldName} for constant variants.
User::SubscriptionStatus john_status = soiagen::kFree;
User::SubscriptionStatus jane_status = soiagen::kPremium;

// Compilation error: MONDAY is not a field of the SubscriptionStatus enum.
// User::SubscriptionStatus sara_status = soiagen::kMonday;

// Use soiagen::wrap_${field_name} for data variants.
User::SubscriptionStatus jade_status =
    soiagen::wrap_trial_start_time(absl::FromUnixMillis(1743682787000));

// The ${kFieldName} and wrap_${field_name} symbols are also defined in the
// generated class.
User::SubscriptionStatus lara_status = User::SubscriptionStatus::kFree;

Conditions on enums

if (john_status == soiagen::kFree) {
  std::cout << "John, would you like to upgrade to premium?\n";
}

// Call is_${field_name}() to check if the enum holds a data variant.
if (jade_status.is_trial_start_time()) {
  // as_${field_name}() returns the value held by the enum
  const absl::Time trial_start_time = jade_status.as_trial_start_time();
  std::cout << "Jade's trial started on " << trial_start_time << "\n";
}

// One way to do an exhaustive switch on an enum.
switch (lara_status.kind()) {
  case User::SubscriptionStatus::kind_type::kConstUnknown:
    // UNKNOWN is the default value for an uninitialized SubscriptionStatus.
    // ...
    break;
  case User::SubscriptionStatus::kind_type::kConstFree:
    // ...
    break;
  case User::SubscriptionStatus::kind_type::kConstPremium:
    // ...
    break;
  case User::SubscriptionStatus::kind_type::kValTrialStartTime: {
    const absl::Time& trial_start_time = lara_status.as_trial_start_time();
    std::cout << "Lara's trial started on " << trial_start_time << "\n";
  }
}

// Another way to do an exhaustive switch using the visitor pattern.
struct Visitor {
  void operator()(soiagen::k_unknown) const {
    std::cout << "Lara's subscription status is UNKNOWN\n";
  }
  void operator()(soiagen::k_free) const {
    std::cout << "Lara's subscription status is FREE\n";
  }
  void operator()(soiagen::k_premium) const {
    std::cout << "Lara's subscription status is PREMIUM\n";
  }
  void operator()(
      User::SubscriptionStatus::wrap_trial_start_time_type& w) const {
    const absl::Time& trial_start_time = w.value;
    std::cout << "Lara's trial started on " << trial_start_time << "\n";
  }
};
lara_status.visit(Visitor());

Serialization

Use ToDenseJson, ToReadableJson or ToBytes to serialize a soia value.

// Serialize a soia value to JSON with ToDenseJson or ToReadableJson.
std::cout << soia::ToDenseJson(john) << "\n";
// [42,"John Doe"]

std::cout << soia::ToReadableJson(john) << "\n";
// {
//   "user_id": 42,
//   "name": "John Doe"
// }

// The dense flavor is the flavor you should pick if you intend to
// deserialize the value in the future. Soia allows fields to be renamed, and
// because fields names are not part of the dense JSON, renaming a field does
// not prevent you from deserializing the value.
// You should pick the readable flavor mostly for debugging purposes.

// The binary format is not human readable, but it is a bit more compact than
// JSON, and serialization/deserialization can be a bit faster.
// Only use it when this small performance gain is likely to matter, which
// should be rare.
std::cout << soia::ToBytes(john).as_string() << "\n";
// soia�+Jane Doe����Fluffy�cat��Rex�dog

Deserialization

Use Parse to deserialize a soia value from JSON or binary format.

absl::StatusOr<User> maybe_john = soia::Parse<User>(soia::ToDenseJson(john));
assert(maybe_john.ok() && *maybe_john == john);

Keyed arrays

A keyed_items<T, get_key> is a container that stores items of type T and allows for fast lookups by key using a hash table.

UserRegistry user_registry;
soia::keyed_items<User, soiagen::get_user_id<>>& users = user_registry.users;
users.push_back(john);
users.push_back(jane);
users.push_back(lyla);

assert(users.size() == 3);
assert(users[0] == john);

const User* maybe_jane = users.find_or_null(43);
assert(maybe_jane != nullptr && *maybe_jane == jane);

assert(users.find_or_default(44).name == "Lyla Doe");
assert(users.find_or_default(45).name == "");

Equality and hashing

Soia structs and enums are equality comparable and hashable.

absl::flat_hash_set<User> user_set;
user_set.insert(john);
user_set.insert(jane);
user_set.insert(jane);
user_set.insert(lyla);

assert(user_set.size() == 3);

Constants

const User& tarzan = soiagen_user::k_tarzan();
assert(tarzan.name == "Tarzan");

Soia services

Starting a soia service on an HTTP server

Full example here.

Sending RPCs to a soia service

Full example here.

Dynamic reflection

using ::soia::reflection::GetTypeDescriptor;
using ::soia::reflection::TypeDescriptor;

// A TypeDescriptor describes a soia type. It contains the definition of all
// the structs and enums referenced from the type.
const TypeDescriptor& user_descriptor = GetTypeDescriptor<User>();

// TypeDescriptor can be serialized/deserialized to/from JSON.

absl::StatusOr<TypeDescriptor> reserialized_type_descriptor =
    TypeDescriptor::FromJson(user_descriptor.AsJson());
assert(reserialized_type_descriptor.ok());

Static reflection

Static reflection allows you to inspect and modify values of generated soia types in a typesafe maneer.

See string_capitalizer.h.

User tarzan_copy = soiagen_user::k_tarzan();
// CapitalizeStrings recursively capitalizes all the strings found within a
// soia value.
CapitalizeStrings(tarzan_copy);

std::cout << tarzan_copy << "\n";
// {
//   .user_id: 123,
//   .name: "TARZAN",
//   .quote: "AAAAAAAAAAYAAAAAAAAAAYAAAAAAAAAA",
//   .pets: {
//     {
//       .name: "CHEETA",
//       .height_in_meters: 1.67,
//       .picture: "🐒",
//     },
//   },
//   .subscription_status: ::soiagen::wrap_trial_start_time(absl::FromUnixMillis(1743592409000 /* 2025-04-02T11:13:29+00:00 */)),
// }

// ...

Writing unit tests with GoogleTest

Full example here.

Struct matchers

const User john = {
    .name = "John Doe",
    .pets =
        {
            {.height_in_meters = 1.67, .name = "Cheeta", .picture = "🐒"},
        },
    .quote = "Life is like a box of chocolates.",
    .user_id = 42,
};

EXPECT_THAT(john, (StructIs<User>{
                      // Only the specified fields are tested
                      .pets = testing::ElementsAre(StructIs<User::Pet>{
                          .height_in_meters = testing::FloatNear(1.7, 0.1),
                      }),
                      .quote = testing::StartsWith("Life is"),
                      .user_id = 42,
                  }));

Enum matchers

User::SubscriptionStatus john_status = soiagen::kFree;

EXPECT_THAT(john_status, testing::Eq(soiagen::kFree));

User::SubscriptionStatus jade_status =
    soiagen::wrap_trial_start_time(absl::FromUnixMillis(1743682787000));

EXPECT_THAT(jade_status, IsTrialStartTime());
EXPECT_THAT(jade_status, IsTrialStartTime(testing::Gt(absl::UnixEpoch())));

FAQs

Package last updated on 21 Nov 2025

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts