Habillage idiomatique de l'API de type de modèle C ++ en C

9

Je travaille sur l'encapsulation d'une API C ++ qui donne accès à un magasin de données (Hazelcast) dans les fonctions C, afin que le magasin de données soit également accessible à partir de code C uniquement.

L'API Hazelcast C ++ pour la structure de données de la carte ressemble à ceci:

auto map = hazelcastClient->client->getMap<int, string>(mapName);
map.put(key, value);

Il utilise des types de modèles keyet des valueparamètres. Comme il n'y a pas de modèles disponibles en C, j'ai pensé à créer une fonction wrapper pour chaque spécialisation de la getMap<T, U>méthode. Autrement dit, pour chaque type C. Même si je suis conscient qu'il existe signedet unsignedversions de types C, je suis très bien avec la limitation de l'API pour soutenir que int, double, float, char *pour keyet value.

J'ai donc écrit un petit script, qui génère automatiquement toutes les combinaisons. Les fonctions exportées ressemblent à ceci:

int Hazelcast_Map_put_int_string(
    Hazelcast_Client_t *hazelcastClient,
    const char *mapName,
    int key,
    char *value,
    char** errptr
);

int Hazelcast_Map_put_int_int(
    Hazelcast_Client_t *hazelcastClient,
    const char *mapName,
    int key,
    int value,
    char** errptr
);

...

Génération d' une fonction pour get, set, containsavec toutes les combinaisons possibles keyet valuetypes augmente la quantité de code tout à fait beaucoup, et même si je pense que la génération du code est une bonne idée, il ajoute une complexité supplémentaire d'avoir à créer une sorte d'infrastructures génératrices de code.

Une autre idée que je peux imaginer est une fonction générique en C, comme ceci:

int Hazelcast_Map_put(
    Hazelcast_Client_t *hazelcastClient,
    const char *mapName,

    const void *key,
    API_TYPE key_type,

    const void *value,
    API_TYPE value_type,

    char** errptr
);

Qui peut être utilisé comme ceci:

Hazelcast_Map_put(client, mapName, "key", API_TYPE_STR, "val", API_TYPE_STR, &err);

Cela rend un peu plus facile pour l'appelant, car cela déplace la charge d'obtenir la spécialisation correcte sur mon code, mais il perd la sécurité du type et nécessite des transtypages. De plus, pour passer un int, comme void *c'est maintenant le type de keyet value, un cast comme (void *) (intptr_t) intValserait nécessaire du côté des appelants, ce qui n'est pas très agréable à lire et à maintenir.

  • Y a-t-il une troisième option que je ne peux pas reconnaître?
  • Quelle version serait préférée par les développeurs C?

Je suis surtout enclin à générer automatiquement toutes les combinaisons de types et à créer une fonction pour chacune, bien que le fichier d'en-tête devienne assez énorme, je suppose.

Max
la source
Beaucoup de votes positifs, pas encore d'avis. Je suppose que c'est un problème commun comment encapsuler les méthodes tapées par modèle en C?
Max
Je ne suis pas sûr que ce soit courant. J'ai voté positivement parce que je trouve le problème intéressant.
MetaFight
Liés, mais pas vraiment utiles ici: stackoverflow.com/questions/1588788/…
Martin Ba

Réponses:

1

Générer pour toutes les possibilités ne me semblait pas être une très bonne solution. La clé et les valeurs peuvent également être des objets. Par conséquent, les possibilités sont infinies :(

Avez-vous regardé la classe IMapImpl? Cette classe n'utilise pas de types mais les données binaires (qui sont fournies après la sérialisation). Par conséquent, une autre solution serait d'écrire une API qui imite cette interface + fournissant un utilitaire de sérialisation qui convertit tout type donné en binaire dont cette interface a besoin.

Par exemple

API:

struct Binary {
   byte *data;
   size_t length;
   int32_t dataType;
};
Binary *hazelcast_map_put(const Binary *key, const Binary *value);

Utilitaire de sérialisation:

int hazelcast_binary_to_int(const Binary *data);

Vous devrez peut-être écrire ces fonctions d'assistance pour les types d'objets que vous souhaitez prendre en charge. Cela peut être une interface viable. Il y a des choses à considérer comme la gestion de la mémoire.

La sérialisation est un sujet complexe, mais vous pouvez sûrement commencer par prendre en charge les types primitifs en premier. Voir http://docs.hazelcast.org/docs/3.6/manual/html-single/index.html#serialization et https://github.com/hazelcast/hazelcast/blob/master/hazelcast/src/main/java /com/hazelcast/internal/serialization/impl/ConstantSerializers.java pour les détails de sérialisation.

ihsan demir
la source
Je pense que c'est la voie à suivre pour mon cas. Pour les personnes hors de la boucle, j'ai également posé la même question dans un PR au client Hazelcast C ++ github.com/hazelcast/hazelcast-cpp-client/pull/127 et ihsan , le mainteneur de ce client C ++, était si gentil que pour répondre à ma question ici sur SO, aussi.
Max