Fix a few issues with the C generator (part 1 version 2) (#14434)

* C: add a template for an empty any_type.h header

Some generated C apis fail to build because the source files get
'#include "any_type.h"' lines, but no such header gets generated. As a
simple fix, add a new template for an empty file with that name. This is
enough to fix the problem for us, because all the generic type stuff is
handled by object_t.

* C: fix enums

I'm guessing that enums have not been used much with the C generator
before, because they always seem to produce code that doesn't build, or
that tries to free them after use. This patch fixes all the problems
we've encountered so far, except for those that need checking the return
type. I'll come back to that later.

* C: fix confusion of 'classname'/'classFilename'

* C: fix issues with returned enums

Currently, the C templates never check if a function returns an enum
inside mustache, so when that happens the generated code has broken
return types and doesn't build. I originally tried to fix this by
extending CodegenOperation to implement a 'returnTypeIsEnum' check, but
William Cheng suggested[1] that I use the existing 'returnProperty'
instead:

  https://github.com/OpenAPITools/openapi-generator/pull/14379#discussion_r1064636735

So do that.

* C: update the samples

As required for a pull request, run the generate-samples.sh script and
commit the changes.

* update samples

---------

Co-authored-by: William Cheng <wing328hk@gmail.com>
This commit is contained in:
Ernesto Fernández 2024-11-27 07:16:10 -03:00 committed by GitHub
parent 8a94fc667e
commit 47665aaa97
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 73 additions and 13 deletions

View File

@ -368,6 +368,7 @@ public class CLibcurlClientCodegen extends DefaultCodegen implements CodegenConf
// Object files in model folder
supportingFiles.add(new SupportingFile("object-body.mustache", "model", "object.c"));
supportingFiles.add(new SupportingFile("object-header.mustache", "model", "object.h"));
supportingFiles.add(new SupportingFile("any_type-header.mustache", "model", "any_type.h"));
}
@Override

View File

@ -76,6 +76,7 @@ set(HDRS
include/keyValuePair.h
external/cJSON.h
model/object.h
model/any_type.h
{{#models}}
{{#model}}
model/{{classFilename}}.h

View File

@ -0,0 +1,5 @@
/*
* any_type.h
*
* A placeholder for now, this type isn't really needed.
*/

View File

@ -98,7 +98,7 @@ end:
// {{{.}}}
//
{{/notes}}
{{#returnType}}{{#returnTypeIsPrimitive}}{{#returnSimpleType}}{{{.}}}*{{/returnSimpleType}}{{^returnSimpleType}}{{#isArray}}{{{.}}}_t*{{/isArray}}{{#isMap}}{{{.}}}{{/isMap}}{{/returnSimpleType}}{{/returnTypeIsPrimitive}}{{^returnTypeIsPrimitive}}{{{.}}}_t*{{/returnTypeIsPrimitive}}{{/returnType}}{{^returnType}}void{{/returnType}}
{{#returnType}}{{#returnTypeIsPrimitive}}{{#returnSimpleType}}{{{.}}}*{{/returnSimpleType}}{{^returnSimpleType}}{{#isArray}}{{{.}}}_t*{{/isArray}}{{#isMap}}{{{.}}}{{/isMap}}{{/returnSimpleType}}{{/returnTypeIsPrimitive}}{{^returnTypeIsPrimitive}}{{#returnProperty}}{{^isEnum}}{{{returnType}}}_t*{{/isEnum}}{{#isEnum}}{{projectName}}_{{{returnType}}}_{{returnEnumName}}_e{{/isEnum}}{{/returnProperty}}{{/returnTypeIsPrimitive}}{{/returnType}}{{^returnType}}void{{/returnType}}
{{{classname}}}_{{{operationId}}}(apiClient_t *apiClient{{#allParams}}, {{#isPrimitiveType}}{{#isNumber}}{{{dataType}}} {{/isNumber}}{{#isLong}}{{{dataType}}} {{/isLong}}{{#isInteger}}{{{dataType}}} *{{/isInteger}}{{#isDouble}}{{{dataType}}} {{/isDouble}}{{#isFloat}}{{{dataType}}} {{/isFloat}}{{#isBoolean}}{{dataType}} *{{/isBoolean}}{{#isEnum}}{{#isString}}{{projectName}}_{{operationId}}_{{baseName}}_e {{/isString}}{{/isEnum}}{{^isEnum}}{{#isString}}{{{dataType}}} *{{/isString}}{{/isEnum}}{{#isByteArray}}{{{dataType}}} *{{/isByteArray}}{{#isDate}}{{{dataType}}} {{/isDate}}{{#isDateTime}}{{{dataType}}} {{/isDateTime}}{{#isFile}}{{{dataType}}} {{/isFile}}{{#isFreeFormObject}}{{dataType}}_t *{{/isFreeFormObject}}{{/isPrimitiveType}}{{^isArray}}{{^isPrimitiveType}}{{#isModel}}{{#isEnum}}{{datatypeWithEnum}}_e {{/isEnum}}{{^isEnum}}{{{dataType}}}_t *{{/isEnum}}{{/isModel}}{{^isModel}}{{#isEnum}}{{datatypeWithEnum}}_e {{/isEnum}}{{/isModel}}{{#isUuid}}{{dataType}} *{{/isUuid}}{{#isEmail}}{{dataType}} {{/isEmail}}{{/isPrimitiveType}}{{/isArray}}{{#isContainer}}{{#isArray}}{{dataType}}_t *{{/isArray}}{{#isMap}}{{dataType}} {{/isMap}}{{/isContainer}}{{{paramName}}}{{/allParams}})
{
list_t *localVarQueryParameters = {{#hasQueryParams}}list_createList();{{/hasQueryParams}}{{^hasQueryParams}}NULL;{{/hasQueryParams}}
@ -263,7 +263,7 @@ end:
{{#isPrimitiveType}}{{#isNumber}}{{{dataType}}}{{/isNumber}}{{#isLong}}{{{dataType}}}{{/isLong}}{{#isInteger}}{{{dataType}}}{{/isInteger}}{{#isDouble}}{{{dataType}}}{{/isDouble}}{{#isFloat}}{{{dataType}}}{{/isFloat}}{{#isBoolean}}{{dataType}}{{/isBoolean}}{{#isEnum}}{{#isString}}{{projectName}}_{{operationId}}_{{baseName}}_e{{/isString}}{{/isEnum}}{{^isEnum}}{{#isString}}{{{dataType}}} *{{/isString}}{{/isEnum}}{{#isByteArray}}{{{dataType}}} *{{/isByteArray}}{{#isDate}}{{{dataType}}}{{/isDate}}{{#isDateTime}}{{{dataType}}}{{/isDateTime}}{{#isFile}}{{{dataType}}}{{/isFile}}{{/isPrimitiveType}}{{^isPrimitiveType}}{{#isModel}}{{#isEnum}}{{datatypeWithEnum}}_e{{/isEnum}}{{^isEnum}}{{{dataType}}}_t *{{/isEnum}}{{/isModel}}{{^isModel}}{{#isEnum}}{{datatypeWithEnum}}_e{{/isEnum}}{{/isModel}}{{#isUuid}}{{dataType}} *{{/isUuid}}{{#isEmail}}{{dataType}}{{/isEmail}}{{/isPrimitiveType}} valueForm_{{paramName}} = 0;
keyValuePair_t *keyPairForm_{{paramName}} = 0;
{{/isFile}}
if ({{paramName}} != NULL)
if ({{paramName}} != {{^isEnum}}NULL{{/isEnum}}{{#isEnum}}0{{/isEnum}})
{
{{#isFile}}
keyForm_{{paramName}} = strdup("{{{baseName}}}");
@ -399,9 +399,9 @@ end:
{{^returnContainer}}
//nonprimitive not container
cJSON *{{classname}}localVarJSON = cJSON_Parse(apiClient->dataReceived);
{{{returnBaseType}}}_t *elementToReturn = {{{returnBaseType}}}_parseFromJSON({{classname}}localVarJSON);
{{#returnProperty}}{{^isEnum}}{{{returnBaseType}}}_t *{{/isEnum}}{{#isEnum}}{{projectName}}_{{{returnType}}}_{{returnEnumName}}_e {{/isEnum}}{{/returnProperty}}elementToReturn = {{{returnBaseType}}}_parseFromJSON({{classname}}localVarJSON);
cJSON_Delete({{classname}}localVarJSON);
if(elementToReturn == NULL) {
if(elementToReturn == {{#returnProperty}}{{^isEnum}}NULL{{/isEnum}}{{#isEnum}}0{{/isEnum}}{{/returnProperty}}) {
// return 0;
}
@ -524,8 +524,10 @@ end:
keyForm_{{{paramName}}} = NULL;
}
if (valueForm_{{{paramName}}}) {
{{^isEnum}}
free(valueForm_{{{paramName}}});
valueForm_{{{paramName}}} = NULL;
{{/isEnum}}
valueForm_{{{paramName}}} = {{^isEnum}}NULL{{/isEnum}}{{#isEnum}}0{{/isEnum}};
}
free(keyPairForm_{{paramName}});
{{/isString}}
@ -541,7 +543,7 @@ end:
return elementToReturn;
end:
free(localVarPath);
return NULL;
return {{#returnProperty}}{{^isEnum}}NULL{{/isEnum}}{{#isEnum}}0{{/isEnum}}{{/returnProperty}};
{{/returnType}}
{{^returnType}}
//No return type

View File

@ -32,7 +32,7 @@ typedef enum { {{projectName}}_{{operationId}}_{{enumName}}_NULL = 0{{#enumVars
// {{{.}}}
//
{{/notes}}
{{#returnType}}{{#returnTypeIsPrimitive}}{{#returnSimpleType}}{{{.}}}*{{/returnSimpleType}}{{^returnSimpleType}}{{#isArray}}{{{.}}}_t*{{/isArray}}{{#isMap}}{{{.}}}{{/isMap}}{{/returnSimpleType}}{{/returnTypeIsPrimitive}}{{^returnTypeIsPrimitive}}{{{.}}}_t*{{/returnTypeIsPrimitive}}{{/returnType}}{{^returnType}}void{{/returnType}}
{{#returnType}}{{#returnTypeIsPrimitive}}{{#returnSimpleType}}{{{.}}}*{{/returnSimpleType}}{{^returnSimpleType}}{{#isArray}}{{{.}}}_t*{{/isArray}}{{#isMap}}{{{.}}}{{/isMap}}{{/returnSimpleType}}{{/returnTypeIsPrimitive}}{{^returnTypeIsPrimitive}}{{#returnProperty}}{{^isEnum}}{{{returnType}}}_t*{{/isEnum}}{{#isEnum}}{{projectName}}_{{{returnType}}}_{{returnEnumName}}_e{{/isEnum}}{{/returnProperty}}{{/returnTypeIsPrimitive}}{{/returnType}}{{^returnType}}void{{/returnType}}
{{{classname}}}_{{{operationId}}}(apiClient_t *apiClient{{#allParams}}, {{#isPrimitiveType}}{{#isNumber}}{{{dataType}}} {{/isNumber}}{{#isLong}}{{{dataType}}} {{/isLong}}{{#isInteger}}{{{dataType}}} *{{/isInteger}}{{#isDouble}}{{{dataType}}} {{/isDouble}}{{#isFloat}}{{{dataType}}} {{/isFloat}}{{#isBoolean}}{{dataType}} *{{/isBoolean}}{{#isEnum}}{{#isString}}{{projectName}}_{{operationId}}_{{baseName}}_e {{/isString}}{{/isEnum}}{{^isEnum}}{{#isString}}{{{dataType}}} *{{/isString}}{{/isEnum}}{{#isByteArray}}{{{dataType}}} *{{/isByteArray}}{{#isDate}}{{{dataType}}} {{/isDate}}{{#isDateTime}}{{{dataType}}} {{/isDateTime}}{{#isFile}}{{{dataType}}} {{/isFile}}{{#isFreeFormObject}}{{dataType}}_t *{{/isFreeFormObject}}{{/isPrimitiveType}}{{^isArray}}{{^isPrimitiveType}}{{#isModel}}{{#isEnum}}{{datatypeWithEnum}}_e {{/isEnum}}{{^isEnum}}{{{dataType}}}_t *{{/isEnum}}{{/isModel}}{{^isModel}}{{#isEnum}}{{datatypeWithEnum}}_e {{/isEnum}}{{/isModel}}{{#isUuid}}{{dataType}} *{{/isUuid}}{{#isEmail}}{{dataType}} {{/isEmail}}{{/isPrimitiveType}}{{/isArray}}{{#isContainer}}{{#isArray}}{{dataType}}_t *{{/isArray}}{{#isMap}}{{dataType}} {{/isMap}}{{/isContainer}}{{{paramName}}}{{/allParams}});

View File

@ -25,7 +25,7 @@ char* {{classFilename}}_{{classname}}_ToString({{projectName}}_{{classVarName}}_
return 0;
}
cJSON *{{classFilename}}_{{classname}}_convertToJSON({{projectName}}_{{classVarName}}_{{enumName}}_e {{classname}}) {
cJSON *{{classname}}_convertToJSON({{projectName}}_{{classVarName}}_{{enumName}}_e {{classname}}) {
cJSON *item = cJSON_CreateObject();
{{#isString}}
if(cJSON_AddStringToObject(item, "{{{classname}}}", {{classFilename}}_{{{classname}}}_ToString({{{classname}}})) == NULL) {
@ -48,7 +48,7 @@ fail:
return NULL;
}
{{projectName}}_{{classVarName}}_{{enumName}}_e {{classFilename}}_{{classname}}_parseFromJSON(cJSON *{{classname}}JSON) {
{{projectName}}_{{classVarName}}_{{enumName}}_e {{classname}}_parseFromJSON(cJSON *{{classname}}JSON) {
{{projectName}}_{{classVarName}}_{{enumName}}_e *{{classname}} = NULL;
{{#isEnum}}
{{#isNumeric}}
@ -75,6 +75,7 @@ end:
{{^isEnum}}
{{#vars}}
{{^isContainer}}
{{#isPrimitiveType}}
{{^isModel}}
{{#isEnum}}
char* {{classname}}_{{name}}_ToString({{projectName}}_{{classVarName}}_{{enumName}}_e {{name}}) {
@ -96,6 +97,7 @@ char* {{classname}}_{{name}}_ToString({{projectName}}_{{classVarName}}_{{enumNam
}
{{/isEnum}}
{{/isModel}}
{{/isPrimitiveType}}
{{/isContainer}}
{{#isContainer}}
{{#items}}
@ -138,7 +140,12 @@ char* {{classname}}_{{name}}_ToString({{projectName}}_{{classVarName}}_{{enumNam
{{/isModel}}
{{^isModel}}
{{^isFreeFormObject}}
{{^isEnum}}
{{datatype}}_t *{{name}}{{^-last}},{{/-last}}
{{/isEnum}}
{{#isEnum}}
{{projectName}}_{{dataType}}_{{enumName}}_e {{name}}{{^-last}},{{/-last}}
{{/isEnum}}
{{/isFreeFormObject}}
{{/isModel}}
{{#isUuid}}
@ -227,10 +234,12 @@ void {{classname}}_free({{classname}}_t *{{classname}}) {
{{/isModel}}
{{^isModel}}
{{^isFreeFormObject}}
{{^isEnum}}
if ({{{classname}}}->{{{name}}}) {
{{{complexType}}}_free({{{classname}}}->{{{name}}});
{{classname}}->{{name}} = NULL;
}
{{/isEnum}}
{{/isFreeFormObject}}
{{/isModel}}
{{#isUuid}}
@ -337,7 +346,12 @@ cJSON *{{classname}}_convertToJSON({{classname}}_t *{{classname}}) {
}
{{/isEnum}}
{{#isEnum}}
{{#isPrimitiveType}}
if ({{projectName}}_{{classVarName}}_{{enumName}}_NULL == {{{classname}}}->{{{name}}}) {
{{/isPrimitiveType}}
{{^isPrimitiveType}}
if ({{projectName}}_{{dataType}}_{{enumName}}_NULL == {{{classname}}}->{{{name}}}) {
{{/isPrimitiveType}}
goto fail;
}
{{/isEnum}}
@ -347,7 +361,12 @@ cJSON *{{classname}}_convertToJSON({{classname}}_t *{{classname}}) {
if({{{classname}}}->{{{name}}}) {
{{/isEnum}}
{{#isEnum}}
{{#isPrimitiveType}}
if({{{classname}}}->{{{name}}} != {{projectName}}_{{classVarName}}_{{enumName}}_NULL) {
{{/isPrimitiveType}}
{{^isPrimitiveType}}
if({{{classname}}}->{{{name}}} != {{projectName}}_{{dataType}}_{{enumName}}_NULL) {
{{/isPrimitiveType}}
{{/isEnum}}
{{/required}}
{{^isContainer}}
@ -589,7 +608,12 @@ fail:
{{^isModel}}
{{^isFreeFormObject}}
// define the local variable for {{{classname}}}->{{{name}}}
{{^isEnum}}
{{complexType}}_t *{{name}}_local_nonprim = NULL;
{{/isEnum}}
{{#isEnum}}
{{projectName}}_{{dataType}}_{{enumName}}_e {{name}}_local_nonprim = 0;
{{/isEnum}}
{{/isFreeFormObject}}
{{/isModel}}
@ -853,7 +877,7 @@ fail:
{{/isModel}}
{{^isModel}}
{{^isFreeFormObject}}
{{^required}}{{{name}}} ? {{/required}}{{{name}}}_local_nonprim{{^required}} : NULL{{/required}}{{^-last}},{{/-last}}
{{^required}}{{{name}}} ? {{/required}}{{{name}}}_local_nonprim{{^required}} : {{^isEnum}}NULL{{/isEnum}}{{#isEnum}}0{{/isEnum}}{{/required}}{{^-last}},{{/-last}}
{{/isFreeFormObject}}
{{/isModel}}
{{#isUuid}}
@ -929,8 +953,10 @@ end:
{{^isModel}}
{{^isFreeFormObject}}
if ({{{name}}}_local_nonprim) {
{{^isEnum}}
{{complexType}}_free({{{name}}}_local_nonprim);
{{{name}}}_local_nonprim = NULL;
{{/isEnum}}
{{{name}}}_local_nonprim = {{^isEnum}}NULL{{/isEnum}}{{#isEnum}}0{{/isEnum}};
}
{{/isFreeFormObject}}
{{/isModel}}

View File

@ -30,14 +30,15 @@ char* {{classFilename}}_{{classname}}_ToString({{projectName}}_{{classVarName}}_
{{projectName}}_{{classVarName}}_{{enumName}}_e {{classFilename}}_{{classname}}_FromString(char* {{classname}});
//cJSON *{{classFilename}}_{{classname}}_convertToJSON({{projectName}}_{{classVarName}}_{{enumName}}_e {{classname}});
cJSON *{{classname}}_convertToJSON({{projectName}}_{{classVarName}}_{{enumName}}_e {{classname}});
//{{projectName}}_{{classVarName}}_{{enumName}}_e {{classFilename}}_{{classname}}_parseFromJSON(cJSON *{{classname}}JSON);
{{projectName}}_{{classVarName}}_{{enumName}}_e {{classname}}_parseFromJSON(cJSON *{{classname}}JSON);
{{/isEnum}}
{{^isEnum}}
{{#vars}}
{{^isContainer}}
{{#isPrimitiveType}}
{{^isModel}}
{{#isEnum}}
// Enum {{enumName}} for {{classVarName}}
@ -52,6 +53,7 @@ char* {{classFilename}}_{{name}}_ToString({{projectName}}_{{classVarName}}_{{enu
{{/isEnum}}
{{/isModel}}
{{/isPrimitiveType}}
{{/isContainer}}
{{#isContainer}}
{{#items}}
@ -88,7 +90,12 @@ typedef struct {{classname}}_t {
{{/isModel}}
{{^isModel}}
{{^isFreeFormObject}}
{{^isEnum}}
{{datatype}}_t *{{name}}; // custom
{{/isEnum}}
{{#isEnum}}
{{projectName}}_{{dataType}}_{{enumName}}_e {{name}}; //referenced enum
{{/isEnum}}
{{/isFreeFormObject}}
{{/isModel}}
{{#isUuid}}
@ -163,7 +170,12 @@ typedef struct {{classname}}_t {
{{/isModel}}
{{^isModel}}
{{^isFreeFormObject}}
{{^isEnum}}
{{datatype}}_t *{{name}}{{^-last}},{{/-last}}
{{/isEnum}}
{{#isEnum}}
{{projectName}}_{{dataType}}_{{enumName}}_e {{name}}{{^-last}},{{/-last}}
{{/isEnum}}
{{/isFreeFormObject}}
{{/isModel}}
{{#isUuid}}

View File

@ -26,6 +26,7 @@ include/binary.h
include/keyValuePair.h
include/list.h
libcurl.licence
model/any_type.h
model/api_response.c
model/api_response.h
model/category.c

View File

@ -75,6 +75,7 @@ set(HDRS
include/keyValuePair.h
external/cJSON.h
model/object.h
model/any_type.h
model/mapped_model.h
model/api_response.h
model/category.h

View File

@ -0,0 +1,5 @@
/*
* any_type.h
*
* A placeholder for now, this type isn't really needed.
*/

View File

@ -25,6 +25,7 @@ include/binary.h
include/keyValuePair.h
include/list.h
libcurl.licence
model/any_type.h
model/api_response.c
model/api_response.h
model/category.c

View File

@ -0,0 +1,5 @@
/*
* any_type.h
*
* A placeholder for now, this type isn't really needed.
*/