C has supported struct returns since at least C89:
/* Compile with "gcc -std=c89 -pedantic -Wall -Wextra -Werror". */
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
typedef struct {
double value;
enum { NO_ERROR = 0, PARSE_ERROR, TOO_BIG } error;
} result_t;
static result_t parse_double(char const *str) {
char *endptr;
double parsed;
result_t result;
errno = 0;
parsed = strtod(str, &endptr);
if (endptr == str) {
result.value = 0.0;
result.error = PARSE_ERROR;
} else if (errno == ERANGE) {
result.value = 0.0;
result.error = TOO_BIG;
} else {
result.value = parsed;
result.error = NO_ERROR;
}
return result;
}
int main(void) {
result_t r;
r = parse_double("1.23");
printf("value: %f, error: %d\n", r.value, r.error);
r = parse_double("abc");
printf("value: %f, error: %d\n", r.value, r.error);
r = parse_double("1e5000");
printf("value: %f, error: %d\n", r.value, r.error);
return 0;
}
Needing to typedef a unique struct for every possible function return type isn't very ergonomic, but you can work around that with C23: // Compile with "gcc -std=c23 -pedantic -Wall -Wextra -Werror".
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
typedef enum {
NO_ERROR = false,
PARSE_ERROR,
TOO_BIG,
} errors;
static struct { double value; errors error; } parse_double(char const *str) {
errno = 0;
char *endptr = nullptr;
double parsed = strtod(str, &endptr);
typeof(parse_double(str)) result;
if (endptr == str) {
result.value = 0.0;
result.error = PARSE_ERROR;
} else if (errno == ERANGE) {
result.value = 0.0;
result.error = TOO_BIG;
} else {
result.value = parsed;
result.error = NO_ERROR;
}
return result;
}
int main() {
auto r = parse_double("1.23");
printf("value: %f, error: %d\n", r.value, r.error);
r = parse_double("abc");
printf("value: %f, error: %d\n", r.value, r.error);
r = parse_double("1e5000");
printf("value: %f, error: %d\n", r.value, r.error);
return 0;
}