Skip to content

Commit 9e9498f

Browse files
authored
feat: C helper with short-circuit evaluation code generation (#12)
* feat: C type-Specific helper with short-circuit evaluation code generation * build: update generated vips package * feat: C type-Specific helper with short-circuit evaluation code generation * build: update generated vips package * feat: C type-Specific helper with short-circuit evaluation code generation * build: update generated vips package * feat: C type-Specific helper with short-circuit evaluation code generation * build: update generated vips package * feat: C type-Specific helper with short-circuit evaluation code generation * build: update generated vips package
1 parent ffd7f90 commit 9e9498f

File tree

5 files changed

+2517
-1398
lines changed

5 files changed

+2517
-1398
lines changed

internal/generator/templatefunc.go

Lines changed: 116 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1297,6 +1297,7 @@ func generateCFunctionDeclaration(op introspection.Operation) string {
12971297
// generateCFunctionImplementation generates C implementations for vips operations
12981298
func generateCFunctionImplementation(op introspection.Operation) string {
12991299
var result strings.Builder
1300+
13001301
// Handle basic function (no options)
13011302
if len(op.Arguments) == 0 {
13021303
result.WriteString(fmt.Sprintf("int vipsgen_%s() {\n", op.Name))
@@ -1359,89 +1360,147 @@ func generateCFunctionImplementation(op introspection.Operation) string {
13591360
result.WriteString(fmt.Sprintf(" VipsOperation *operation = vips_operation_new(\"%s\");\n", op.Name))
13601361
result.WriteString(" if (!operation) return 1;\n")
13611362

1362-
// Handle required arguments first
1363+
// Create VipsArray objects for array inputs
1364+
for _, opt := range op.OptionalInputs {
1365+
if strings.HasPrefix(opt.GoType, "[]") {
1366+
arrayType := getArrayType(opt.GoType)
1367+
if arrayType == "double" {
1368+
result.WriteString(fmt.Sprintf(" VipsArrayDouble *%s_array = NULL;\n", opt.Name))
1369+
result.WriteString(fmt.Sprintf(" if (%s != NULL && %s_n > 0) { %s_array = vips_array_double_new(%s, %s_n); }\n", opt.Name, opt.Name, opt.Name, opt.Name, opt.Name))
1370+
} else if arrayType == "int" {
1371+
result.WriteString(fmt.Sprintf(" VipsArrayInt *%s_array = NULL;\n", opt.Name))
1372+
result.WriteString(fmt.Sprintf(" if (%s != NULL && %s_n > 0) { %s_array = vips_array_int_new(%s, %s_n); }\n", opt.Name, opt.Name, opt.Name, opt.Name, opt.Name))
1373+
} else if arrayType == "image" {
1374+
result.WriteString(fmt.Sprintf(" VipsArrayImage *%s_array = NULL;\n", opt.Name))
1375+
result.WriteString(fmt.Sprintf(" if (%s != NULL && %s_n > 0) { %s_array = vips_array_image_new(%s, %s_n); }\n", opt.Name, opt.Name, opt.Name, opt.Name, opt.Name))
1376+
}
1377+
}
1378+
}
1379+
1380+
// Combine required and optional parameters in a single condition
1381+
var allParamsList []string
1382+
1383+
// Add required parameters first
13631384
for _, arg := range op.Arguments {
13641385
if arg.IsOutput {
13651386
continue // Skip output arguments, they'll be handled after build
13661387
}
13671388

13681389
// Special handling for different types of arguments
13691390
if arg.IsSource {
1370-
result.WriteString(fmt.Sprintf(" if (vips_object_set(VIPS_OBJECT(operation), \"%s\", (VipsSource*)%s, NULL)) { g_object_unref(operation); return 1; }\n", arg.Name, arg.Name))
1391+
allParamsList = append(allParamsList,
1392+
fmt.Sprintf("vips_object_set(VIPS_OBJECT(operation), \"%s\", (VipsSource*)%s, NULL)", arg.Name, arg.Name))
13711393
} else if arg.Name == "buf" || arg.Name == "buffer" {
1372-
// Handle buffer and its length together
1373-
result.WriteString(fmt.Sprintf(" if (vips_object_set(VIPS_OBJECT(operation), \"%s\", %s, NULL)) { g_object_unref(operation); return 1; }\n", arg.Name, arg.Name))
1374-
for _, lenArg := range op.Arguments {
1375-
if lenArg.Name == "len" {
1376-
result.WriteString(fmt.Sprintf(" if (vips_object_set(VIPS_OBJECT(operation), \"%s\", %s, NULL)) { g_object_unref(operation); return 1; }\n", lenArg.Name, lenArg.Name))
1394+
// Handle buffer parameter
1395+
allParamsList = append(allParamsList,
1396+
fmt.Sprintf("vips_object_set(VIPS_OBJECT(operation), \"%s\", %s, NULL)", arg.Name, arg.Name))
1397+
} else if arg.Name == "len" {
1398+
// Handle len parameter (check if associated with buffer)
1399+
var isBufferLen bool
1400+
for _, bArg := range op.Arguments {
1401+
if bArg.Name == "buf" || bArg.Name == "buffer" {
1402+
isBufferLen = true
13771403
break
13781404
}
13791405
}
1380-
} else if arg.Name != "len" { // Skip "len" as it's handled with buffer
1381-
result.WriteString(fmt.Sprintf(" if (vips_object_set(VIPS_OBJECT(operation), \"%s\", %s, NULL)) { g_object_unref(operation); return 1; }\n", arg.Name, arg.Name))
1406+
if isBufferLen {
1407+
allParamsList = append(allParamsList,
1408+
fmt.Sprintf("vips_object_set(VIPS_OBJECT(operation), \"%s\", %s, NULL)", arg.Name, arg.Name))
1409+
}
1410+
} else if arg.GoType == "string" {
1411+
// String parameter
1412+
allParamsList = append(allParamsList,
1413+
fmt.Sprintf("vips_object_set(VIPS_OBJECT(operation), \"%s\", %s, NULL)", arg.Name, arg.Name))
1414+
} else if arg.GoType == "*C.VipsImage" {
1415+
// Image parameter
1416+
allParamsList = append(allParamsList,
1417+
fmt.Sprintf("vips_object_set(VIPS_OBJECT(operation), \"%s\", %s, NULL)", arg.Name, arg.Name))
13821418
} else {
1383-
continue // Skip len parameter, already handled
1419+
// Other scalar parameters
1420+
allParamsList = append(allParamsList,
1421+
fmt.Sprintf("vips_object_set(VIPS_OBJECT(operation), \"%s\", %s, NULL)", arg.Name, arg.Name))
13841422
}
13851423
}
13861424

1387-
// Create VipsArray objects for array inputs
1425+
// Add optional parameters using type-specific setter functions
13881426
for _, opt := range op.OptionalInputs {
13891427
if strings.HasPrefix(opt.GoType, "[]") {
13901428
arrayType := getArrayType(opt.GoType)
13911429
if arrayType == "double" {
1392-
result.WriteString(fmt.Sprintf(" VipsArrayDouble *%s_array = NULL;\n", opt.Name))
1393-
result.WriteString(fmt.Sprintf(" if (%s != NULL && %s_n > 0) { %s_array = vips_array_double_new(%s, %s_n); }\n", opt.Name, opt.Name, opt.Name, opt.Name, opt.Name))
1430+
allParamsList = append(allParamsList,
1431+
fmt.Sprintf("vipsgen_set_array_double(operation, \"%s\", %s_array)", opt.Name, opt.Name))
13941432
} else if arrayType == "int" {
1395-
result.WriteString(fmt.Sprintf(" VipsArrayInt *%s_array = NULL;\n", opt.Name))
1396-
result.WriteString(fmt.Sprintf(" if (%s != NULL && %s_n > 0) { %s_array = vips_array_int_new(%s, %s_n); }\n", opt.Name, opt.Name, opt.Name, opt.Name, opt.Name))
1433+
allParamsList = append(allParamsList,
1434+
fmt.Sprintf("vipsgen_set_array_int(operation, \"%s\", %s_array)", opt.Name, opt.Name))
13971435
} else if arrayType == "image" {
1398-
result.WriteString(fmt.Sprintf(" VipsArrayImage *%s_array = NULL;\n", opt.Name))
1399-
result.WriteString(fmt.Sprintf(" if (%s != NULL && %s_n > 0) { %s_array = vips_array_image_new(%s, %s_n); }\n", opt.Name, opt.Name, opt.Name, opt.Name, opt.Name))
1436+
allParamsList = append(allParamsList,
1437+
fmt.Sprintf("vipsgen_set_array_image(operation, \"%s\", %s_array)", opt.Name, opt.Name))
14001438
}
1439+
} else if opt.GoType == "bool" {
1440+
allParamsList = append(allParamsList,
1441+
fmt.Sprintf("vipsgen_set_bool(operation, \"%s\", %s)", opt.Name, opt.Name))
1442+
} else if opt.GoType == "string" {
1443+
allParamsList = append(allParamsList,
1444+
fmt.Sprintf("vipsgen_set_string(operation, \"%s\", %s)", opt.Name, opt.Name))
1445+
} else if opt.IsEnum {
1446+
allParamsList = append(allParamsList,
1447+
fmt.Sprintf("vipsgen_set_int(operation, \"%s\", %s)", opt.Name, opt.Name))
1448+
} else if opt.GoType == "*C.VipsImage" {
1449+
allParamsList = append(allParamsList,
1450+
fmt.Sprintf("vipsgen_set_image(operation, \"%s\", %s)", opt.Name, opt.Name))
1451+
} else if opt.GoType == "*Interpolate" || opt.GoType == "*C.VipsInterpolate" {
1452+
// Handle interpolate parameters
1453+
allParamsList = append(allParamsList,
1454+
fmt.Sprintf("vipsgen_set_interpolate(operation, \"%s\", %s)", opt.Name, opt.Name))
1455+
} else if opt.IsSource {
1456+
// Handle source parameters
1457+
allParamsList = append(allParamsList,
1458+
fmt.Sprintf("vipsgen_set_source(operation, \"%s\", %s)", opt.Name, opt.Name))
1459+
} else if opt.GoType == "int" {
1460+
allParamsList = append(allParamsList,
1461+
fmt.Sprintf("vipsgen_set_int(operation, \"%s\", %s)", opt.Name, opt.Name))
1462+
} else if opt.GoType == "float64" {
1463+
allParamsList = append(allParamsList,
1464+
fmt.Sprintf("vipsgen_set_double(operation, \"%s\", %s)", opt.Name, opt.Name))
1465+
} else if strings.Contains(opt.CType, "guint64") {
1466+
// Handle guint64 parameters
1467+
allParamsList = append(allParamsList,
1468+
fmt.Sprintf("vipsgen_set_guint64(operation, \"%s\", %s)", opt.Name, opt.Name))
1469+
} else if strings.Contains(opt.CType, "unsigned int") || strings.Contains(opt.CType, "guint") {
1470+
// Handle unsigned int parameters
1471+
allParamsList = append(allParamsList,
1472+
fmt.Sprintf("set_uint_param(operation, \"%s\", %s)", opt.Name, opt.Name))
1473+
} else if strings.Contains(opt.CType, "*") || strings.Contains(opt.GoType, "*") {
1474+
// This is a pointer type - use general pointer handler
1475+
allParamsList = append(allParamsList,
1476+
fmt.Sprintf("set_pointer_param(operation, \"%s\", %s)", opt.Name, opt.Name))
1477+
} else {
1478+
// For any other non-pointer scalar types, default to int
1479+
allParamsList = append(allParamsList,
1480+
fmt.Sprintf("vipsgen_set_int(operation, \"%s\", %s)", opt.Name, opt.Name))
14011481
}
14021482
}
14031483

1404-
// Handle optional arguments - only set if they have non-default values
1405-
for _, opt := range op.OptionalInputs {
1406-
// Create a cleanup function for error cases
1407-
cleanupCode := "g_object_unref(operation); "
1484+
// Join all parameters with the || operator for short-circuit evaluation
1485+
if len(allParamsList) > 0 {
1486+
result.WriteString(" if (\n ")
1487+
result.WriteString(strings.Join(allParamsList, " ||\n "))
1488+
result.WriteString("\n ) {\n")
1489+
result.WriteString(" g_object_unref(operation);\n")
1490+
1491+
// Free all array resources on error
14081492
for _, cleanupOpt := range op.OptionalInputs {
14091493
if strings.HasPrefix(cleanupOpt.GoType, "[]") {
14101494
arrayType := getArrayType(cleanupOpt.GoType)
14111495
if arrayType != "unknown" {
1412-
cleanupCode += fmt.Sprintf("if (%s_array != NULL) { vips_area_unref(VIPS_AREA(%s_array)); } ", cleanupOpt.Name, cleanupOpt.Name)
1496+
result.WriteString(fmt.Sprintf(" if (%s_array != NULL) { vips_area_unref(VIPS_AREA(%s_array)); }\n", cleanupOpt.Name, cleanupOpt.Name))
14131497
}
14141498
}
14151499
}
1416-
cleanupCode += "return 1;"
14171500

1418-
// Different handling for different types of optional arguments
1419-
if strings.HasPrefix(opt.GoType, "[]") {
1420-
arrayType := getArrayType(opt.GoType)
1421-
if arrayType != "unknown" {
1422-
result.WriteString(fmt.Sprintf(" if (%s_array != NULL) { if (vips_object_set(VIPS_OBJECT(operation), \"%s\", %s_array, NULL)) { %s } }\n",
1423-
opt.Name, opt.Name, opt.Name, cleanupCode))
1424-
}
1425-
} else if opt.GoType == "bool" {
1426-
result.WriteString(fmt.Sprintf(" if (%s) { if (vips_object_set(VIPS_OBJECT(operation), \"%s\", %s, NULL)) { %s } }\n",
1427-
opt.Name, opt.Name, opt.Name, cleanupCode))
1428-
} else if opt.GoType == "string" {
1429-
result.WriteString(fmt.Sprintf(" if (%s != NULL && strlen(%s) > 0) { if (vips_object_set(VIPS_OBJECT(operation), \"%s\", %s, NULL)) { %s } }\n",
1430-
opt.Name, opt.Name, opt.Name, opt.Name, cleanupCode))
1431-
} else if opt.IsEnum {
1432-
result.WriteString(fmt.Sprintf(" if (%s != 0) { if (vips_object_set(VIPS_OBJECT(operation), \"%s\", %s, NULL)) { %s } }\n",
1433-
opt.Name, opt.Name, opt.Name, cleanupCode))
1434-
} else if opt.GoType == "*C.VipsImage" {
1435-
result.WriteString(fmt.Sprintf(" if (%s != NULL) { if (vips_object_set(VIPS_OBJECT(operation), \"%s\", %s, NULL)) { %s } }\n",
1436-
opt.Name, opt.Name, opt.Name, cleanupCode))
1437-
} else if opt.GoType == "int" || opt.GoType == "float64" {
1438-
result.WriteString(fmt.Sprintf(" if (%s != 0) { if (vips_object_set(VIPS_OBJECT(operation), \"%s\", %s, NULL)) { %s } }\n",
1439-
opt.Name, opt.Name, opt.Name, cleanupCode))
1440-
}
1501+
result.WriteString(" return 1;\n }\n\n")
14411502
}
14421503

1443-
result.WriteString("\n")
1444-
14451504
// Collect the output parameters
14461505
var outputParams []string
14471506
for _, arg := range op.Arguments {
@@ -1465,26 +1524,23 @@ func generateCFunctionImplementation(op introspection.Operation) string {
14651524
// Add NULL terminator
14661525
outputParams = append(outputParams, "NULL")
14671526

1468-
// Clean up array objects in case of error
1469-
cleanupArraysCode := ""
1527+
// Generate the call to the helper function
1528+
result.WriteString(fmt.Sprintf(" int result = vipsgen_operation_execute(&operation, %s);\n", strings.Join(outputParams, ", ")))
1529+
1530+
// Clean up array objects
1531+
hasArrays := false
14701532
for _, opt := range op.OptionalInputs {
14711533
if strings.HasPrefix(opt.GoType, "[]") {
14721534
arrayType := getArrayType(opt.GoType)
14731535
if arrayType != "unknown" {
1474-
cleanupArraysCode += fmt.Sprintf(" if (%s_array != NULL) { vips_area_unref(VIPS_AREA(%s_array)); }\n", opt.Name, opt.Name)
1536+
if !hasArrays {
1537+
hasArrays = true
1538+
}
1539+
result.WriteString(fmt.Sprintf(" if (%s_array != NULL) { vips_area_unref(VIPS_AREA(%s_array)); }\n", opt.Name, opt.Name))
14751540
}
14761541
}
14771542
}
14781543

1479-
// Generate the call to the helper function
1480-
result.WriteString(fmt.Sprintf(" int result = vipsgen_operation_execute(&operation, %s);\n", strings.Join(outputParams, ", ")))
1481-
1482-
// Clean up array objects
1483-
if cleanupArraysCode != "" {
1484-
result.WriteString("\n")
1485-
result.WriteString(cleanupArraysCode)
1486-
}
1487-
14881544
result.WriteString(" return result;\n}")
14891545
}
14901546

internal/templates/vips.c.tmpl

Lines changed: 62 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,42 +3,92 @@
33
#include "vips.h"
44
#include <unistd.h>
55

6-
// Prerequisite function to build, get outputs and cleanup a vips operation
6+
// Prerequisites to build, get outputs and cleanup a vips operation
7+
78
int vipsgen_operation_execute(VipsOperation **operation, ...) {
89
va_list ap;
9-
int result = 0;
10-
11-
// Build the operation
1210
if (vips_cache_operation_buildp(operation)) {
1311
vips_object_unref_outputs(VIPS_OBJECT(*operation));
1412
g_object_unref(*operation);
1513
return 1;
1614
}
17-
18-
// Get output parameters from operation
1915
va_start(ap, operation);
20-
21-
// First, get the param name
2216
const char *name;
2317
while ((name = va_arg(ap, const char *)) != NULL) {
24-
// Get the corresponding value pointer
2518
void *value = va_arg(ap, void *);
2619
if (value != NULL) {
2720
g_object_get(VIPS_OBJECT(*operation), name, value, NULL);
2821
}
2922
}
3023
va_end(ap);
31-
32-
// Cleanup
3324
vips_object_unref_outputs(VIPS_OBJECT(*operation));
3425
g_object_unref(*operation);
3526
return 0;
3627
}
3728

29+
int vipsgen_set_int(VipsOperation *operation, const char *name, int value) {
30+
if (value != 0) { return vips_object_set(VIPS_OBJECT(operation), name, value, NULL); }
31+
return 0;
32+
}
33+
34+
int vipsgen_set_bool(VipsOperation *operation, const char *name, gboolean value) {
35+
if (value) { return vips_object_set(VIPS_OBJECT(operation), name, value, NULL); }
36+
return 0;
37+
}
38+
39+
int vipsgen_set_double(VipsOperation *operation, const char *name, double value) {
40+
if (value != 0.0) { return vips_object_set(VIPS_OBJECT(operation), name, value, NULL); }
41+
return 0;
42+
}
43+
44+
int vipsgen_set_guint64(VipsOperation *operation, const char *name, guint64 value) {
45+
if (value != 0) { return vips_object_set(VIPS_OBJECT(operation), name, value, NULL); }
46+
return 0;
47+
}
48+
49+
int vipsgen_set_string(VipsOperation *operation, const char *name, const char *value) {
50+
if (value != NULL && strlen(value) > 0) { return vips_object_set(VIPS_OBJECT(operation), name, value, NULL); }
51+
return 0;
52+
}
53+
54+
int vipsgen_set_image(VipsOperation *operation, const char *name, VipsImage *value) {
55+
if (value != NULL) { return vips_object_set(VIPS_OBJECT(operation), name, value, NULL); }
56+
return 0;
57+
}
58+
59+
int vipsgen_set_array_double(VipsOperation *operation, const char *name, VipsArrayDouble *value) {
60+
if (value != NULL) { return vips_object_set(VIPS_OBJECT(operation), name, value, NULL); }
61+
return 0;
62+
}
63+
64+
int vipsgen_set_array_int(VipsOperation *operation, const char *name, VipsArrayInt *value) {
65+
if (value != NULL) { return vips_object_set(VIPS_OBJECT(operation), name, value, NULL); }
66+
return 0;
67+
}
68+
69+
int vipsgen_set_array_image(VipsOperation *operation, const char *name, VipsArrayImage *value) {
70+
if (value != NULL) { return vips_object_set(VIPS_OBJECT(operation), name, value, NULL); }
71+
return 0;
72+
}
73+
74+
int vipsgen_set_interpolate(VipsOperation *operation, const char *name, VipsInterpolate *value) {
75+
if (value != NULL) { return vips_object_set(VIPS_OBJECT(operation), name, value, NULL); }
76+
return 0;
77+
}
78+
79+
int vipsgen_set_source(VipsOperation *operation, const char *name, VipsSource *value) {
80+
if (value != NULL) { return vips_object_set(VIPS_OBJECT(operation), name, value, NULL); }
81+
return 0;
82+
}
83+
84+
// Generated operations
85+
3886
{{range .Operations}}
3987
{{generateCFunctionImplementation .}}
4088
{{end}}
4189

90+
// Custom operations
91+
4292
int image_new_from_source(VipsSourceCustom *source, VipsImage **out) {
4393
*out = vips_image_new_from_source((VipsSource*) source, "", NULL);
4494
if (!*out) return 1;
@@ -349,3 +399,4 @@ int remove_exif(VipsImage *in, VipsImage **out) {
349399
g_strfreev(fields);
350400
return 0;
351401
}
402+

internal/templates/vips.h.tmpl

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,33 @@
44
#include <vips/vips.h>
55
#include <vips/vector.h>
66

7-
int vipsgen_operation_execute(VipsOperation **operation, ...);
7+
// Prerequisites to build, get outputs and cleanup a vips operation
88

9+
int vipsgen_operation_execute(VipsOperation **operation, ...);
10+
int vipsgen_set_source(VipsOperation *operation, const char *name, VipsSource *value);
11+
int vipsgen_set_int(VipsOperation *operation, const char *name, int value);
12+
int vipsgen_set_bool(VipsOperation *operation, const char *name, gboolean value);
13+
int vipsgen_set_double(VipsOperation *operation, const char *name, double value);
14+
int vipsgen_set_guint64(VipsOperation *operation, const char *name, guint64 value);
15+
int vipsgen_set_string(VipsOperation *operation, const char *name, const char *value);
16+
int vipsgen_set_image(VipsOperation *operation, const char *name, VipsImage *value);
17+
int vipsgen_set_array_double(VipsOperation *operation, const char *name, VipsArrayDouble *value);
18+
int vipsgen_set_array_int(VipsOperation *operation, const char *name, VipsArrayInt *value);
19+
int vipsgen_set_array_image(VipsOperation *operation, const char *name, VipsArrayImage *value);
20+
int vipsgen_set_interpolate(VipsOperation *operation, const char *name, VipsInterpolate *value);
21+
22+
// Generated operations
923
{{range .Operations}}
1024
{{generateCFunctionDeclaration .}}
1125
{{end}}
1226

13-
int image_new_from_source(VipsSourceCustom *source, VipsImage **out);
27+
// Custom operations
1428

29+
int image_new_from_source(VipsSourceCustom *source, VipsImage **out);
1530
int image_new_from_source_with_option(VipsSourceCustom *source, VipsImage **out, const char *option_string);
16-
1731
int image_new_from_file(const char *name, VipsImage **out);
18-
1932
int image_new_from_buffer(const void *buf, size_t len, VipsImage **out);
20-
2133
int image_new_from_buffer_with_option(const void *buf, size_t len, VipsImage **out, const char *option_string);
22-
2334
int image_new_from_memory(const void *buf, size_t len, int width, int height, int bands, VipsImage **out);
2435

2536
int has_alpha_channel(VipsImage *image);
@@ -58,3 +69,4 @@ void set_image_delay(VipsImage *in, const int *array, int n);
5869
const char * get_meta_string(const VipsImage *image, const char *name);
5970

6071
int remove_exif(VipsImage *in, VipsImage **out);
72+

0 commit comments

Comments
 (0)