// My library API
int foo(const char *path);
int bar(void);
int baz(double a, double b);
int foo(const char *path);
Version 1
int foo(const char *path);
int foo_2(const char *path, int flags);
Version 2
API function signatures do not change. Library upgrade is seamless, customer's build does not break.
API gets polluted with the foo_2-like functions, e.g. WinAPI CreateProcessEx, POSIX gethostbyname_r, etc
int foo(const char *path);
Version 1
int foo(const char *path, int flags);
Version 2
API function signatures change! Library upgrade requires re-integration.
API stays clean and non-polluted.
Is it possible to keep backward compatibility, while not polluting the API by introducing dozen of function "variants" ?
def foo(path, flags=None, otherFutureParam='whatever'):
print path, flags
struct foo_args {
const char *path;
};
int foo(struct foo_args args);
Version 1
Version 2
struct foo_args {
const char *path;
int flags;
};
int foo(struct foo_args args);
Works! For binary compatibilty, include version marker in the structure (WinApi often uses structure size)
INT_PTR WINAPI DialogBoxIndirect(
_In_opt_ HINSTANCE hInstance,
_In_ LPCDLGTEMPLATE lpTemplate, // <-- a structure
_In_opt_ HWND hWndParent,
_In_opt_ DLGPROC lpDialogFunc
);
There is also DialogBox(), DialogBoxParam(), and DialogBoxIndirectParam().
DLGTEMPLATE dialog_template = { ... };
DialogBoxIndirect(NULL, &dialog_template, NULL, DlgProc);
But C does not have keyword arguments!
...Or does it ?
C90 initialization
struct params p1 = { .flags = 1, .path = "my_file.txt" };
struct params p2 = { .flags = 1 }; // path is NULL
struct params p3 = { .path = "my_file.txt" }; // flags is 0
// Structure declaration
struct params {
int flags;
const char *path;
};
C99 initialization: designated initializers
struct params p1 = { 1, "my_file.txt" };
struct params p2 = { 1 }; // path is NULL
// Structure declaration
struct params {
int flags;
const char *path;
};
struct params p = { .flags = 1, .path = "my_file.txt" };
Declaration and initialization at the same time
Declaration and assigment in different places: using compound literal to make assignment
// Structure declaration
struct params {
int flags;
const char *path;
};
struct params p;
.... // Some code
p = ((struct params) { .path = "my_file.txt" });
struct foo_args {
int flags;
const char *path;
};
#define foo(...) foo_impl((struct foo_args) { __VA_ARGS__ })
void foo_impl(struct foo_args);
lib_foo.h
void foo_impl(struct foo_args args) {
if (args.path != NULL) {
...
}
}
lib_foo.c
foo(.path = "my_file.txt"); // flags initialies to 0
// Expands to:
// foo_impl((struct foo_args) { .path = "my_file.txt" });
user_code.c
contact me at
sergey.lyubka@cesanta.com