Skip to content

Instantly share code, notes, and snippets.

@vpodzime
Last active April 6, 2016 10:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vpodzime/9df0eede735c383de9d956b74342bc49 to your computer and use it in GitHub Desktop.
Save vpodzime/9df0eede735c383de9d956b74342bc49 to your computer and use it in GitHub Desktop.

Problems

  1. Many functions in libblockdev utilities that take lots of extra options and arguments. Adding separate parameters for all these options would complicate things a lot for the majority of use cases that don't need to specify them. And having multiple functions for the same thing just with different sets of arguments would be basically the same.
  2. Adding extra arguments to functions means API breakage. The only solution keeping the API stable is to add extra functions that take the old arguments as well as the extra ones and use those from the code that requires the new stuff.
  3. libblockdev is written in C which sucks a lot in this area. It has no support for anything like **kwargs in Python and the support for varying number of arguments is far from being ideal and really useful, especially for this use case. Last but not least, C doesn't support function overloading, so it's not possible to have multiple functions with the same name and different numbers/types/... of arguments or return values.

    (Of course everything in this item is just a matter of how crazy/dirty/hacky things one is willing to do.)

Solution(s)

libblockdev clearly needs a mechanism for working with functions' extra arguments. It looks like adding this mechanism for (almost) all functions could be useful. Some of the functions would just declare (in the documentation) that the extra arguments will be passed down to the utilities/library/... being used, other functions would declare which extra arguments are supported (could be none). Exceptions should be functions that clearly won't ever need anything more than the original set of arguments (if there are such functions).

There are multiple ways how to pass/take such extra arguments in C. Some are faster, some are easier to use from C code, some are easier to use via the GObject introspection. Here is the list of methods that could be used:

  1. GHashTable *extra - this looks like a natural choice, doesn't it? A hash table is what python calls dictionary so it effectively means the same as **kwargs in Python. However, it has two quite unfortunate shortcomings:
    • it seems to be impossible to create a working GHashTable instance in Python and pass it as an argument to some function (I haven't found a way) because it is a generic pointer-to-pointer mapping,
    • sometimes, it's necessary to pass some argument twice (or multiple times in general) -- remember LVM and its famous "--force --force --yes"?
  2. gchar *extra - extra arguments could be passed as a single string which would later be run through shlex parsing. This is a great way for Python code passing extra arguments to such functions. However, constructing such string in C and then parsing and processing it would be quite crazy (tons of memory allocations and free's).
  3. StringStringTable *extra - our own type usable from C as well as Python using GHashTable internally, but really only working with strings (gchar *) as keys and values. The limitation here is again no way to specify some argument twice.
  4. ExtraArg **extra - our own type, but this time just a struct {gchar *opt; gchar* val;}; with extra being a NULL-terminated array of such structs. It would be easy to construct such arrays in C (even by initial static values) as well as in Python (just a list of ExtraArg(opt, val) objects. By not using any hash table, this also doesn't bring in the limitation with things being needed twice or more times.
  5. GVariant *extra - making use of the variable-type mechanism that GLib provides and which is used by e.g. DBus. Basically it's a data structure put together with the description of its type/structure. One of the possible types/structures is also 'a{sv}' (array of string->variant mappings) which is what's usually being used on DBus for extra parameters. This would allow us to accept extra parameters of various types (just with the keys being strings), basically like Python's **kwargs. However, creating the GVariant instances is not really easy and simple (the lvm-dbus plugin does this all the time on hundreds of lines of C code). And while this provides the greatest flexibility of all the choices here, it may be a too heavy hammer for our quite simple and restricted use case.
@vpodzime
Copy link
Author

vpodzime commented Apr 6, 2016

Added the fifth option.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment