7
votes

STD :: Fonction / Lié comme Type-Erasure sans norme C ++ Bibliothèque C ++

Je développe une application simple événementielle dans C ++ 11 basée sur le modèle de publication / souscription. Les classes ont un ou plusieurs Onketvereventent () Code> Invoqué par la boucle d'événement (inversion du contrôle). Étant donné que l'application est en fait un micrologiciel, où la taille du code est critique et la flexibilité n'est pas haute priorité, la partie "Souscrire" est une table simple avec les identifiants d'événement et les gestionnaires associés.

Voici un code très simplifié de l'idée: P>

typedef void (*Pfn)(void*, int);
Pfn pfn1 = reinterpret_cast<Pfn>(&Button::onTick);
Pfn pfn2 = reinterpret_cast<Pfn>(&Menu::onButtonPressed);


4 commentaires

Stimule une option? Il y a boost :: fonction et boost :: lid dedans.


Honnêtement, je n'ai pas essayé. En regardant Boost :: Fonction et boost :: Les en-tons liés (tonnes d'autres incluses), ce n'est pas clair à quel point ils dépendent des en-têtes STDC ++ et de la bibliothèque C ++ Runtime. Je vais vérifier rapidement si le projet compile du tout avec une en-tête de boost incluse.


Notez qu'un appel de fonction virtuelle n'est pas plus lent qu'un appel via un STD :: Fonction , si quelque chose, je m'attends à ce qu'elle soit plus rapide.


@Quentin: Vous avez probablement raison. Je me demandais simplement si cela peut être réduit dans un appel direct avec le piratage de style C dans l'option 3, peut-être que quelque chose de similaire est réalisable en C ++. Mais j'ai l'impression qu'au moins 1 indirection est nécessaire pour récupérer les informations de type pour l'appel de la méthode.


3 Réponses :


2
votes

Votre 1ère idée est votre solution typique objet orientée au problème. C'est parfaitement bien, mais un peu lourd - pas aussi utile que std :: fonction . Votre 3ème idée est un comportement indéfini. Nope Nope Nope.

Votre 2e idée - maintenant, il y a quelque chose que nous pouvons travailler avec! Ceci est proche de la façon dont std :: fonction est réellement implémenté. Nous pouvons écrire une classe qui peut prendre n'importe quel objet appelable avec int et renvoie vide : xxx

avec ça , vous avez essentiellement std :: Fonction d'une manière utilisable, générique.

Maintenant une idée 4ème pourrait être de prolonger votre 3ème idée à quelque chose d'utilisable. Utilisez effectivement des pointeurs de fonction: xxx

puis utilisez lambdas pour faire des choses telles que: xxx

mais vous devez tenir sur les contextes en quelque sorte - qui ajoute un travail supplémentaire.


2 commentaires

Votre suggestion encapsule élégamment un concept similaire à ce que j'avais à l'esprit (en option 2) dans un seul foncteur. Mais quelle est la valeur ajoutée de la mise en capture de Lambda dans le foncteur dérivé intérieur ( STR STRUTER ), au lieu de mettre l'instance et les pointeurs de méthode. Il ajoute une autre couche d'indirection. La 4ème idée semble également bonne pour un microcontrôleur.


@Gyorgyszekely, ce n'est pas vraiment plus indirect. Cela prend également en charge tout foncteur capturé (foncteur, non lambda), au lieu d'appeler simplement des fonctions membres sur un objet.



14
votes

Un solide, efficace, std :: fonction ne doit pas être difficile à écrire.

Comme nous sommes intégrés, nous voulons éviter l'allocation de mémoire . J'écrirais donc un small_task . Il crée un tampon de taille sz et alignement algn dans lequel il stocke ses objets effacés.

Il stocke également un déménageur, un destructeur et un Pointeur de fonction Invoker. Ces pointeurs peuvent être localement dans le small_task (localité maximale) ou dans un manuel structure {/*...*/} const * Table . xxx

exemple en direct .

Une autre chose manquante est une qualité de haute qualité Void (args ...) qui ne se soucie pas si le passage apportable a une valeur de retour.

la tâche ci-dessus Prend en charge le déplacement, mais pas copier. Ajout de copie signifie que tout stocké doit être copié et nécessite une autre fonction dans la machine à livrer (avec une implémentation similaire à Move , sauf src est const et non std :: déplacer ).

une petite quantité de C ++ 14 a été utilisé, à savoir le activer_if_t et décrypy_t alias et similaire. Ils peuvent être facilement écrits en C ++ 11 ou remplacés par typename std :: activation_if :: Type .

Bind est meilleur remplacé par Lambdas, honnêtement. Je ne l'utilise pas même sur des systèmes non intégrés.

Une autre amélioration serait d'apprendre à traiter comment traiter small_task s plus petit / moins aligné en stockant leur < Code> VTable POINTER plutôt que de la copier dans la mémoire tampon DATA dans un autre Tablevisible . Cela encouragerait à utiliser small_tasks suffisamment grand pour votre problème de problème.


Conversion des fonctions de l'élément à la fonction Les pointeurs sont non seulement un comportement non défini, souvent la convention appelante de Une fonction est différente d'une fonction membre. En particulier, ceci est transmis dans un registre particulier sous certaines conventions d'appel.

de telles différences peuvent être subtiles et peuvent atteindre lorsque vous changez de code non liée ou que la version du compilateur change , ou quoi que ce soit d'autre. J'éviterais donc que si vous avez peu d'autre choix.


Comme indiqué, la plate-forme manque de bibliothèques. Chaque utilisation de std ci-dessus est minuscule, donc je vais simplement les écrire: xxx

déplacer xxx

Transférer xxx

décroissance xxx

is_convertible xxx < H1> Activer_if xxx

résultat_of xxx

aligné_storage xxx

is_same xxx

exemple en direct , à propos d'une douzaine de lignes par std Modèle de bibliothèque dont j'avais besoin.

Je mettrais cette "réimplémentation de la bibliothèque STD" dans Nomspace NotStd pour préciser ce qui est clairement Aller sur.

Si vous le pouvez, utilisez un lien qui plie des fonctions identiques ensemble, comme la lieur d'or. Le métaprogramming de modèle peut provoquer un gonflement binaire sans une liaison solide pour la bande.


6 commentaires

Wow, c'est un peu cohéros C ++, je pense que je dois grandir plus de muscle de modèle pour comprendre toutes les parties de celle-ci. Malheureusement, il repose fortement sur les en-têtes STDC ++ qui ne sont pas disponibles sur la plate-forme cible, je ne peux donc pas l'utiliser directement. +1 pour la 2e partie mentionnant les différences de niveau abi possibles.


@gyor Move, Transférer, Stockage aligné, Decom, IS_Convertable, Activer_if, Résultat_of - Je manque tout? La plupart sont 3-4 lignes chacune et facile à écrire. aligné_storage: Quelles sont les exigences d'alignement de votre plate-forme?


C'est un UC de 8 bits avec 2 kmbytes de RAM et 32kbytes de Flash -> Aucune exigence d'alignement. :)


@Gyorgyszekely ok, fait - pas d'en-têtes utilisés avant mon petit_task , tout écrit de Nus C ++ 11. Je pense que tout est compatible C ++ 14 (Sfinae supporté le résultat_of_t, etc.).


@Yakk, j'aimerais utiliser votre code dans un projet. Quelle est la licence pour cela? Quelque chose comme mit, cc-0 ou wtfpl serait parfait :-)


@Yakk, vient de trouver meta.stackexchange.com/questions/271080/... qui dit que le code est autorisé sous la CC-BY-SA, mais cette licence n'est pas vraiment adapté au code et à la problématique dans mon projet actuel. Souhaitez-vous reliciter cela sous la licence MIT, comme les nouvelles contributions de code sur Stackoverflow?



0
votes

Avant d'essayer d'écrire toutes les choses STL à la main, j'essaie d'utiliser la STL que j'ai déjà du compilateur lui-même. Étant donné que la majeure partie du code STL que vous utilisez est l'en-tête seulement, je l'incluais simplement et faites quelques hacks mineurs pour les obtenir compilés. En fait, ID a fait 10 minutes pour le préparer!

J'ai utilisé la version AVR-GCC-5.2.0 sans aucun problème pour la tâche. Je n'ai aucune installation ancienne et je pense qu'il est plus facile d'installer la version actuelle en quelques minutes au lieu de résoudre les problèmes de l'ancien.

Après avoir compilé votre code d'exemple pour AVR I GEA IT GOING ERREURS: xxx

écrivez simplement votre propre __ thort_bad_function_call et supprimez-vous du problème de lien "

pour moi, cela n'a vraiment aucun sens d'écrire un propre Mise en œuvre STL. Ici, j'ai simplement utilisé les en-têtes qui proviennent de l'installation du compilateur (GCC 5.2.0).


4 commentaires

C'est bon à savoir! Les en-têtes STDC ++ semblaient tellement emmêlés, que je pensais qu'ils tirent de nombreuses dépendances d'exécution.


@Gyorgyszekely bien sûr, la mise en œuvre de la STL semble un peu difficile à comprendre, mais il n'ya généralement rien qui nécessite une sorte de code de bibliothèque. Comme déjà dit, la plupart du temps, le code est en tête-tête seulement. Et pour le reste, il est souvent possible de compiler une source unique si la mise en œuvre de la bibliothèque. std :: string et std :: vecteur fonctionne sur AVR comme prévu. Mais oui, il y a un besoin de flash supplémentaire. Mais pas plus de code auto-écrit du tout. Si vous avez besoin de fonctionnalités STL, utilisez STL. Il est testé, intelligent et complet fonctionnel sur AVR. Pas besoin d'écrire propre et peut-être un code sujet d'erreur.


Pour la STD :: String and STD :: Vecteur: Vecteur Avez-vous fourni une implémentation pour de nouvelles et supprimées? Ceux-ci devraient être dans la bibliothèque d'exécution et la FAQ AVR-LIBC (point 6) indique qu'il manque.


@Gyorgyszekely Oui, simplement transmettre à Malloc / Gratuit. Rien de spécial ici!