1
votes

Comment limiter certaines fonctions à certaines structures en C?

J'ai mon Struct configuré comme ceci:

void InitGraph(NVGcontext* vg, Graph* graph);
void DrawAxes(NVGcontext* vg, Axis* axisObject);

et les méthodes correspondantes comme celle-ci dans mon en-tête,

typedef struct Graph_
{
    Point2D position;
    double Size_X;
    double Size_Y;
    char* Label;
    NVGcolor Background_Color;
    bool Borders;
    bool Grid;
    int Grid_Scale;
    NVGcolor Grid_Color;
    float Border_Padding;
    void (*init)(NVGcontext* vg, struct Graph_ * graph);
    void (*drawAxes)(NVGcontext* vg, Axis * axis , struct Graph_* graph);
}Graph;

Je veux pour limiter lesdites méthodes à être appelées uniquement à partir de la structure parent et non directement. Y a-t-il une visibilité, une solution de contournement, en C? Excusez-vous s'il s'agit d'une question noob.

P.S. J'essaye d'utiliser la structure comme classe dans mon application.


3 commentaires

Vous voulez des classes et des méthodes privées. Utilisez C ++. Vous pouvez l'approcher en C en rendant les fonctions statiques des «méthodes» privées, mais elles ne seront appelables qu'à partir du fichier qui les définit.


Ouais, je suis d'accord mais je suis un peu coincé avec l'utilisation de c ici


Pourquoi la définition de struct Graph_ est-elle dans le fichier .h? Considérez uniquement typedef struct Graph_ Graph; dans le fichier .h. Masquer tous les membres de struct - mettre la position struct Graph_ {Point2D; .... void (* init) (NVGcontext * vg, struct Graph_ * graph); void (* drawAxes) (NVGcontext * vg, Axis * axis, struct Graph_ * graph); } dans le fichier .c.


3 Réponses :


3
votes

Il semble que vous vouliez empêcher les gens d'appeler ces fonctions directement, mais permettre aux gens de les appeler indirectement, via des pointeurs de fonction. C'est assez simple: ne déclarez pas du tout les fonctions dans votre fichier d'en-tête. Vous pouvez déclarer et définir les fonctions dans un fichier source .c, et dans ce même fichier, vous pouvez implémenter un moyen "d'obtenir" les pointeurs de fonction, soit littéralement en les renvoyant, soit en les définissant comme membres de la structure Graph. Seules ces fonctions getter / setter seront exposées dans votre fichier d'en-tête.

Par exemple, dans votre en-tête:

Graph graph;
SetupGraph(&graph);

NVGcontext vg;
graph.init(&vg, &graph);

Ensuite, l'implémentation dans votre fichier .c:

static void InitGraph(NVGcontext* vg, Graph* graph) {
    // ...
}

static void DrawAxes(NVGcontext* vg, Axis* axisObject) {
    // ...
}

// public
void SetupGraph(Graph* graph)
{
    graph->init = InitGraph;
    graph->drawAxes = DrawAxes;
}

Désormais, les utilisateurs externes de votre bibliothèque peuvent faire ceci:

void SetupGraph(Graph* graph);

Regardez, ils peuvent appeler InitGraph ( ) indirectement, via le pointeur de fonction qui est défini par SetupGraph () , mais ils ne peuvent jamais appeler InitGraph () directement car ils ne connaissent pas son nom .


2 commentaires

correct, je ne suis pas très bien, vous avez bien répondu à ma question. Disons que j'ai une fonction get définie dans mon fichier d'en-tête qui me donne le pointeur vers la fonction statique qui est dans l'image. Maintenant, je stocke cela en tant que membre dans la même structure. Je n'ai pas très bien compris comment appeler ladite fonction, surtout si elle a des arguments. Comment la fonction initiale sera-t-elle abstraite dans la structure spécifique? P.S. quelque chose comme une implémentation de classe


@AvidJoe: J'ai développé ma réponse, s'il vous plaît voir.



0
votes

Supprimez les déclarations de fonction du fichier d'en-tête et définissez les fonctions dans le fichier d'implémentation et ajoutez le keyord static devant la déclaration.


2 commentaires

Si je supprime la déclaration du fichier .h et que l'application cible utilise mon en-tête. Comment attribuer la référence à la fonction dans `` `` void ( init) (NVGcontext vg, struct Graph_ * graph); `` En ce moment, j'autorise les gens à attribuer la référence de fonction comme ceci où cet en-tête est inclus, '' `` // Top_Right_G est une structure définie localement. Top_Right_G.init = InitGraph; Top_Right_G.init (vg, & Top_Right_G); ''


@AvidJoe Je définirais une fonction d'initialisation (exportée) qui définit chaque champ de pointeur de fonction sur la fonction correcte.



1
votes

C'est, je pense, en partie la réponse que John a donnée, mais dérivée un peu plus abstraitement.

Je suppose que vous essayez d'imiter une classe C ++ avec des fonctions membres publiques et des fonctions membres privées en C.

Les "fonctions membres" publiques doivent être visibles par le "public", si vous excusez le jeu de mots, et elles ont besoin d'un pointeur vers la structure comme un paramètre qui prend le rôle du this implicite pointeur en C ++. Ils seront déclarés dans un en-tête public.

En C, vous n'avez pas de spécificateurs d'accessibilité, vous ne pouvez donc restreindre l'accès aux fonctions privées qu'en les cachant. C'est bon; seul le code qui implémente d'autres fonctions opérant sur votre structure a besoin de les voir.

Il existe deux façons de masquer les fonctions.

  1. Vous pouvez rendre statiques les fonctions (fichier) "privées". Ceci est propre car cela empêche le compilateur d'exporter complètement le symbole. Les fonctions statiques ne peuvent être utilisées que dans l'unité de traduction où elles sont définies. Comme ce sont des fichiers qui font partie de la "bibliothèque" de votre structure, le public ne peut tout simplement pas y accéder. L'éditeur de liens ne peut pas du tout lier à eux; pour les autres unités de traduction, elles n'existent tout simplement pas. Mais c'est aussi un inconvénient: pour les grandes "classes" avec de nombreuses "fonctions membres", toutes les fonctions utilisant les fonctions "privées" doivent être dans le même fichier source. (Sinon, vous pouvez écrire un fichier d'en-tête inhabituel contenant le code des fonctions statiques et l'inclure partout où vous avez besoin d'accéder à ces fonctions; mais cela augmentera la taille du code et, en tant que modèle général, n'est certainement pas recommandé.)

  2. Vous créez une collection de fonctions "privées", potentiellement réparties sur plusieurs fichiers sources. Ces fonctions ont un lien externe et peuvent être utilisées dans d'autres unités de traduction. Le mécanisme pour les cacher est de mettre leurs déclarations dans un en-tête qui n'est pas destiné à l'inclusion publique. Le public ne voit que les fonctions "publiques" publiées dans un en-tête séparé. Ce serait similaire à une interface / conception d'implémentation C ++.

    La question de savoir si ces fonctions privées peuvent théoriquement être appelées par le public (par exemple, simplement en devinant le nom et les paramètres, ou en trouvant l'en-tête "private" et en l'incluant) dépend de votre système de construction. Si vous distribuez une bibliothèque partagée pré-compilée, vous pouvez distinguer vos fonctions publiques en indiquant au compilateur d'une manière spécifique à la plate-forme de rendre le symbole visible de l'extérieur par __declspec (dllexport) (Visual C) ou __ attribut __ ((visibilité ("par défaut"))) (gcc). Les fonctions qui ne sont pas ainsi décorées ne sont pas visibles à l'extérieur de la bibliothèque et ne peuvent pas être liées. L'en-tête "privé" ne ferait même pas partie de la distribution.

    Si vous compilez et liez simplement un tas de fichiers source et objet alors que l'en-tête "private" doit être disponible pour la compilation, et que le public est libre de les utiliser s'ils l'incluent ou devinent les noms des fonctions. Si le public qui utilise votre structure est indiscipliné et aime les hacks, il est probable que du code émergera qui repose sur la fonction "privée".


0 commentaires