Skip to content

[BUG]: Wrong c++ code generation #2685

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
o3wiz opened this issue Feb 17, 2025 · 2 comments
Open

[BUG]: Wrong c++ code generation #2685

o3wiz opened this issue Feb 17, 2025 · 2 comments
Labels

Comments

@o3wiz
Copy link

o3wiz commented Feb 17, 2025

Incorrect c++ code generation

Issue Type

quicktype output via cli is incorrect was output a hpp (c++) files.

Context

Windows 11,

Input Format: json schema
Output Language: c++

CLI
Version: 23.0.171

Description

I'm encountering unexpected output when running the following quicktype CLI command:

quicktype `
	--out "Student.hpp" `
	--lang c++ `
	--top-level Student `
	--src "student.schema.json" `
	--code-format with-struct `
	--type-style camel-case `
	--member-style camel-case `
	--enumerator-style pascal-case `
	--no-boost

Input Data

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "properties": {
    "firstName": { "type": "string" },
    "lastName": { "type": "string" },
    "age": { "type": "integer" },
    "gender": { "enum": ["Male", "Female"] }
  },
  "required": ["firstName", "lastName", "age"]
}

Current Output

#pragma once

#include "json.hpp"

namespace quicktype {
    using nlohmann::json;

    #ifndef NLOHMANN_UNTYPED_quicktype_HELPER
    #define NLOHMANN_UNTYPED_quicktype_HELPER
    inline json get_untyped(const json & j, const char * property) {
        if (j.find(property) != j.end()) {
            return j.at(property).get<json>();
        }
        return json();
    }

    inline json get_untyped(const json & j, std::string property) {
        return get_untyped(j, property.data());
    }
    #endif

    struct age {
        std::string type;
    };

    struct gender {
        std::vector<std::string> genderEnum;
    };

    struct properties { // what is this??
        age firstName;
        age lastName;
        age age;
        gender gender;
    };

    struct student {
        std::string schema;
        std::string type;
        properties properties;
        std::vector<std::string> required;
    };
}
...

Issues Observed:

Inconsistent CLI vs Web App Output:

The generated C++ code differs between the CLI tool and the web app.
Incorrect Schema Interpretation:

The age and gender fields are not being correctly represented as expected.
The properties struct is structured strangely.
Constraint Handling Issues:

Adding constraints (e.g., minimum and maximum for age) does not work as expected.
Any insights into why this is happening and how to resolve it?

@o3wiz o3wiz added the bug label Feb 17, 2025
@SimonCahill
Copy link
Contributor

SimonCahill commented Feb 20, 2025

While we're on the topic of stange C++ code being generated, Quicktype is generating a single struct from two separate objects, which are referenced by a oneOf directive.

Here's my schema:

// in another object
"credentials": {
    "type": "object",
    "oneOf": [
        {
            "$ref": "#/definitions/MqttUserCredentials"
        },
        {
            "$ref": "#/definitions/MqttCertCredentials"
        },
        {
            "type": "null"
        }
    ]
},

// more definitions
"MqttUserCredentials": {
    "type": "object",
    "properties": {
        "user_name": {
            "type": "string",
            "description": "The username for the client certificate"
        },
        "user_password": {
            "type": "string",
            "description": "The password for the client certificate"
        }
    },
    "required": [ "user_name", "user_password" ]
},
"MqttCertCredentials": {
    "type": "object",
    "properties": {
        "cert_path": {
            "type": "string",
            "description": "The path to the client certificate"
        },
        "key_path": {
            "type": "string",
            "description": "The path to the client private key"
        },
        "key_password": {
            "type": "string",
            "description": "The password for the client private key"
        }
    },
    "required": [ "cert_path", "key_path", "key_password" ]
},

And here is the resulting C++ code:

struct MqttCredentials {
    /**
    * The username for the client certificate
    */
    std::optional<std::string> user_name;
    /**
    * The password for the client certificate
    */
    std::optional<std::string> user_password;
    /**
    * The path to the client certificate
    */
    std::optional<std::string> cert_path;
    /**
    * The password for the client private key
    */
    std::optional<std::string> key_password;
    /**
    * The path to the client private key
    */
    std::optional<std::string> key_path;
};

struct MqttConfig {
    //<snip>
    std::optional<MqttCredentials> credentials;
    //<snip>
};

The expected behaviour is, especially considering the option --no-combine-classes is passed, that Quicktype generates an std::variant<MqttUserCredentials, MqttCertCredentials, std::nullptr_t> credentials field, instead of the nonsense it generates currently.

For reference, here's my CMake/Quicktype invocation:

####################################
##  CONFIG GENERATION             ##
####################################

# Define the input and output files
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/config/config.schema.json" "${CMAKE_CURRENT_BINARY_DIR}/cmake/config/config.schema.json" @ONLY)
set(INPUT_FILE "${CMAKE_CURRENT_BINARY_DIR}/cmake/config/config.schema.json")
set(OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/include/config")
set(OUTPUT_FILE "${OUTPUT_DIR}/MyConfig.hpp")

# Define the command to run
set(COMMAND "quicktype")
list(APPEND ARGUMENTS
    "--src-lang" "schema"
    "--lang" "c++"
    "--const-style" "west-const"
    "--namespace" "myapp::config"
    "--source-style" "multi-source"
    "--include-location" "global-include"
    "--type-style" "pascal-case"
    "--member-style" "underscore-case"
    "--enumerator-style" "upper-underscore-case"
    "--no-boost"
    "--code-format" "with-struct"
    "--no-combine-classes"
    "--out" "${OUTPUT_FILE}"
    "${INPUT_FILE}"
)

# Create the output directory
file(MAKE_DIRECTORY ${OUTPUT_DIR})

# Run the command
message(STATUS "Generating C++ code from JSON schema...")
message(STATUS "Command: ${COMMAND} ${ARGUMENTS}")
execute_process(COMMAND ${COMMAND} ${ARGUMENTS})

@SimonCahill
Copy link
Contributor

This behaviour is also present on the web app.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants