Updated: October 28, 2024 |
Find the next control for an interface
#include <aoi.h> const AOICtrl_t *AoIterate(const char *iname, const int32_t version, int32_t* const cookie);
This function iterates through all available AOI controls, returning each one that has an interface with the given name and minimum version. When you first call this function, you should set cookie to point to a zero value. You can then keep calling this function until it returns the control you're looking for, or it returns NULL, which means there are no more controls.
If name is NULL, AoIterate() returns the next control in the global list, without considering the name or version of any of its interfaces.
For the first call, a pointer to an AOICtrl_t structure for the first control containing an interface that matches the name and version values, or NULL if there are no addons to return. Subsequent calls return the next control with such an interface, until there are no more controls, at which point the function returns NULL.
Suppose you want to write an application that translates text between natural languages. Your application will allow end users to pick the source (input) and destination (output) languages, and then will pick an addon to do the actual translation (e.g., English to French). Each addon can support one or more source languages and one or more destination languages. The application can iterate over the available controls and for each that contains the translator interface, check if the addon supports the requested source and destination languages and if so, obtain a rating for it. Then, after it's finished iterating over the controls, the application can use the highest rated addon to do the translation.
typedef struct AcmeTranslatorInterface { int (*RateLanguages)(const char *srclang, const char *dstlang); struct AcmeTranslatorHandle *(*Create)(const char *srclang, const char *dstlang); void (*Destroy)(struct AcmeTranslatorHandle *trh); size_t (*Translate) (struct AcmeTranslatorHandle *trh, const char *srctxt, char *dstbuf, size_t dstsize); } AcmeTranslatorInterface; // Strings; their values must be lists of language codes (e.g., "en", "fr"). #define ACME_TRANSLATOR_SRCLANGS_STRING "AcmeTranslatorSrcLanguages" #define ACME_TRANSLATOR_DSTLANGS_STRING "AcmeTranslatorDstLanguages"
static AcmeTranslatorInterface translator_interface = { my_RateLanguages, my_CreateTranslator, my_Destroy, my_Translate }; static const char srclanguages[] = ACME_TRANSLATOR_SRCLANGS_STRING "=fr,en", dstlanguages[] = ACME_TRANSLATOR_DSTLANGS_STRING "=fr,en", *strings_interface[] = { srclanguages, dstlanguages, NULL }; AOInterface_t interfaces[] = { { "Name", 1, "my_acme_translator" }, { "Strings", 1, strings_interface }, { "AcmeTranslatorInterface", ACME_TRANSLATOR_VERSION, &translator_interface }, { 0,0,0 } };
// Check if the string named by "stringname" mentions the language "lang". static inline bool findlang(const char *const *strings, const char *stringname, const char *lang, size_t langlen) { const char *list; if ((list = AoFindString(strings, stringname)) == NULL) { return false; } else { return AoSearchString(list, lang, langlen, AO_STRING_IGNORE_CASE) > 0; } }
In this example, we provide a function called AcmeTranslatorCreate() that finds an addon to translate from one language to another. Your application would call this function after the user specifies the source (input) and destination (output) languages.
The rating could depend on the specific combination of languages; for instance, an addon could list Spanish and English as its inputs and French and English as its outputs, but support translating only between English and the other language, and not from Spanish to French. It could depend also on the language variants; for instance, the addon could support Canadian French better than Belgium French.
AcmeTranslator_t *AcmeTranslatorCreate(const char *srclang, const char *dstlang) { // Allocate memory for a translator structure, which is a private data type defined elsewhere AcmeTranslator_t *tr; if ( (tr = calloc(1, sizeof(*tr))) == NULL ) { AO_LOG(AO_LOG_ERROR, "AcmeTranslatorCreate: out of memory"); } else { size_t srcllen = strcspn(srclang, "/"); // Count language code only, ignore country suffix size_t dstllen = strcspn(dstlang, "/"); // Count language code only, ignore country suffix int bestrating = 0; int32_t cookie = 0; const AOICtrl_t *ctrl; // 1. Find the next control containing the translator interface while ( (ctrl = AoIterate( "AcmeTranslatorInterface", ACME_TRANSLATOR_VERSION, &cookie) ) != NULL) { // 2. We have an addon. Does it support the languages we want? const char *const *strings = AoGetStrings(ctrl); bool tryit = findlang(strings, ACME_TRANSLATOR_SRCLANGS_STRING, srclang, srcllen) && findlang(strings, ACME_TRANSLATOR_DSTLANGS_STRING, dstlang, dstllen); // We don't need this pointer to the strings anymore AoUngetStrings(strings); if (tryit) { // 3a. Hold the control. if ( AoHold(ctrl) ) { AO_LOG(AO_LOG_WARNING, "AcmeTranslatorCreate: AoHold failed, skipping this addon"); } else { // 3b. Get the addon's name for logging purposes. const char *name; const AcmeTranslatorInterface *iface; if ( (name = AoGetInterface( ctrl, "Name", 0, 0 )) == NULL ) { name = "<unnamed>"; } // 3c. Get the addon's rating for the source and destination languages. if ( (iface = AoGetInterface(ctrl, "AcmeTranslatorInterface", ACME_TRANSLATOR_VERSION, 0)) == NULL ) { // AoIterate() was wrong about this DLL's interfaces -- // maybe it had old info (old config file or DLL has just been updated) AO_LOG(AO_LOG_WARNING, "AcmeTranslatorCreate: interface has disappeared from addon %s, ignoring", name); } else { int rating = iface->RateLanguages(srclang, dstlang); if (rating < bestrating) { AO_LOG(AO_LOG_DEBUG1, "We got a rating of %d from %s, out best so far is %d", rating, name, bestrating); } else { // 4a. The current addon has the highest rating so far, so we note its // name and rating. AO_LOG(AO_LOG_DEBUG1, "We got a rating of %d from %s, this is our new best", rating, name); if (bestrating > 0) { AoRelease(tr->aoictrl); } bestrating = rating; tr->aoictrl = ctrl; tr->iface = iface; tr->aoiname = name; continue; } } // 4b. If we haven't taken the "continue" above, the current addon won't // be used, so we can release its control. AoRelease(ctrl); } } } if (bestrating == 0) { // 5a. The highest rating is 0, so we must report that no suitable addon was found AO_LOG(AO_LOG_ERROR, "AcmeTranslatorCreate: we have not found an addon that supports %s to %s", srclang, dstlang); } else { // 5b. Try to create a translator handle. If successful, report the name of the addon // chosen for the translation; otherwise, report the failure and release the control. if ((tr->trh = tr->iface->Create(srclang, dstlang)) != NULL) { AO_LOG(AO_LOG_DEBUG1, "AcmeTranslatorCreate: successfully created instance of %s for %s to %s translation", tr->aoiname, srclang, dstlang); return tr; } AO_LOG(AO_LOG_ERROR, "AcmeTranslatorCreate: Create() call of %s failed!", tr->aoiname); AoRelease(tr->aoictrl); } free(tr); } return NULL; }
QNX Neutrino