Internal API protocol and operation

Name

gg_api_ops : Internal API protocol and operation

Synopsis

#include <ggi/gg-api.h>

typedef int  (ggfunc_api_op_init)(struct gg_api *);

typedef void (ggfunc_api_op_exit)(struct gg_api *);

typedef int  (ggfunc_api_op_attach)(struct gg_api *, struct gg_stem *);

typedef void (ggfunc_api_op_detach)(struct gg_api *, struct gg_stem *);

typedef const char* (ggfunc_api_op_env)(struct gg_api *, const char *);

typedef int (ggfunc_api_op_plug)(struct gg_api *,
                                 struct gg_module *,
                                 struct gg_stem *,
                                 const char *,
                                 void *,
                                 struct gg_plugin **);

typedef int (ggfunc_api_op_unplug)(struct gg_api *, struct gg_module *);


struct gg_api_ops {
      ggfunc_api_op_init *init;
      ggfunc_api_op_exit *exit;
      ggfunc_api_op_attach *attach;
      ggfunc_api_op_detach *detach;
      ggfunc_api_op_getenv *getenv;
      ggfunc_api_op_plug *plug;
      ggfunc_api_op_unplug *unplug;
};

Description

The semi-internal gg_api_ops structure contains all the operations that a gg_api(3) can provide and on which libgg relies to operate porperly. It is only interresting when developping a new API for the libgg-api framework. See the section Interface Implementation below for details on how to implement such an API.

Interface Implementation

This section describes the method and requirements for writing a LibGG compatible API that defines one (or more) dynamic interfaces.

First you must choose a canonical name for your API. A canonical name is a name that is 'well-known' to every one, and that publicly and uniquely identifies this specific API. For example, it is a bad idea to use "libgii", because it would confuse other libraries into thinking that they are talking to the official "libgii".

Your source must define a gg_api(3) singleton that will represent your API for libgg and other libraries. It can be static and you must initialize it correctly at compile time, so that it can be safely registered to libgg at runtime. Supposing that we write an API called "foo", the source code will look like:

static struct gg_api_ops foo_ops = {
    foo_init,
    foo_exit,
    foo_attach,
    foo_detach,
    foo_getenv,
    foo_plug,
    foo_unplug,
}

static struct gg_api foo = GG_API_INIT("foo", 1, 0, &foo_ops);
struct gg_api *libfoo = &foo;

This will define the API named "foo", with version number '1.0' and with the 'foo_*' ops. None of these ops is required, but your interface is probably useless if there is no way to attach it to a stem. If your API supports the 'plug' op, it must also provide an 'unplug' call.

'init'

Called the first time the interface is initialized. 'api->id' will be initialized with the unique API runtime identifier, that is used to add/get private data on stems. It is a good idea to define a macro for accessing them:

#define FOO_PRIV(stem) STEM_API_PRIV(stem, (&foo))
#define FOO_DATA(stem) STEM_API_DATA(stem, (&foo), struct foo_data*)

The former accesses it directly as 'void*' (useful for setting the value), whereas the latter does the typecast.

The init op must is responsible for allocating and initializing all the necessary resources, including loading its configuration files.

It must returns 'GGI_OK' on success, or an error code on failure. In this case case, all resources partially allocated must be freed there, because 'exit' will not be called.

'exit'

Called when the interface not used anymore. It is supposed to free all resources allocated in init. When called, it is guaranteed that this API is not attached on any stem.

'attach'

Called when the API is attached on the stem for the first time. It will mostly allocate a private data structure for it, using the macros defined above:

if((FOO_PRIV(stem) = malloc(sizeof(struct foo_data))) == NULL) {
      return GGI_ENOMEM;
}

/*  initialize with FOO_DATA(stem)->...  */

return GGI_OK;

Returns 'GGI_OK' on success or an error code.

'detach'

Called to unbind this API from the stem.

'getenv'

Return the value of the API environment variables, or NULL if it is not known. The call is really a placeholder for things that are not worth abstracting more. Typical variable names are:

  • 'CONFDIR' : absolute path to the library configurtion directory.
  • 'DLSYMBOL' : symbol to use for retrieving entries initialization function in dynamic library.
'plug'

Request a new plugin (implemented by the given module) to be created and "installed" (whatever that could mean for this specific API) on the given stem.

This call is supposed to setup a structure deriving (in the C sense) from 'struct gg_plugin' as required by the given module. What this means and how it works is entirely depending on the API design.

The resulting plugin instance is returned through the last argument. On failure, it must return an error code.

'unplug'

Closes and frees a plugin.

See also