J'ai une fonction savePotentiometerState (...)
, qui renvoie true
s'il y avait des modifications à enregistrer, et false
si rien n'a été fait. De plus, je sais que lors d'un seul passage dans ma boucle principale, au plus l'un des potentiomètres peut avoir changé (en raison de la façon dont ils sont lus).
C'est sur une plate-forme embarquée très limitée dans le temps, donc il est important (ou du moins important) que je n'appelle pas savePotentiometerState
plus souvent que nécessaire. Cependant, le code que je propose semble naturellement idiot, quelque chose risque de se retrouver au thedailywtf:
const bool retval = savePotentiometerState(pot1) || savePotentiometerState(pot2) || ... || savePotentiometerState(potn);
Une autre façon de faire serait d'utiliser l'évaluation de court-circuit: / p>
if (!savePotentiometerState(pot1)) if (!savePotentiometerState(pot2)) ... if (!savePotentiometerState(potn));
Je suppose que je pourrais même déposer le devoir ici. Mais cela ne me semble pas non plus un bon style, car j'abuse du court-circuit de l'opérateur ||
.
Les différents objets potn
sont des variables membres de la classe contenant, donc il n'y a pas de moyen évident d'écrire cela en boucle.
J'ai l'impression que je manque quelque chose d'évident ici, donc ma question est: y a-t-il un idiomatique / facile à lire la manière de faire ceci, qui ne sacrifie pas l'efficacité? Si cela compte, j'utilise C ++ 17.
3 Réponses :
Loop semble la voie à suivre:
for (auto& pot : {std::ref(pot1), std::ref(pot2), /*..,*/ std::ref(potn)}) { if (savePotentiometerState(pot)) { break; } }
+1, c'est exactement le type d'alternative que je recherchais! Je vais devoir vérifier comment cette boucle se compile, cependant: si le compilateur crée réellement le tableau de références (en tant que pointeurs) sur la pile, cela pourrait être plus lent que d'appeler simplement savePotentiometerState
pour tous les pots chaque temps, et cela prendrait également de l'espace dans la pile (le système a un total de 96K RAM, donc des quantités assez petites de mémoire comptent ici).
Personnellement, j'éviterais l'algorithme que vous utilisez.
Je gardais tout le temps l'état de chaque pot; et suivre les valeurs actuelles et précédentes; puis n'appelez un rappel donné que si la valeur avait changé.
De cette façon, savePotState
est toujours aussi rapide que nécessaire pour un pot donné; et vous n'obtiendrez jamais l'état où pot1 à pot (n-1) peut empêcher potn d'être lu.
Excellent ... Lisible et simple à tester. La famine (causée par une mauvaise utilisation du matériel) peut être très difficile à diagnostiquer, voire à reconnaître. (Été là-bas, fait ça)
Puisque vous pouvez utiliser C ++ 17, vous pouvez tirer parti des expressions de pliage et écrivez une fonction d'aide pour faire l'évaluation à votre place.
if (returtnPotentiometerState(pot1, pot2, ..., potn))
et ensuite vous l'appelleriez comme
template<typename... Args> bool returtnPotentiometerState(Args&&... args) { return (... || savePotentiometerState(args)); }
Cela signifie que vous ne le faites pas ' t avoir une boucle, et vous obtenez un court-circuit.
Je pense que c'est ça: ça ne devrait pas être plus lent que d'écrire les conditions à la main, et je peux nommer la fonction d'assistance pour signaler exactement quelle est l ' intention du code.
@Timo Cela pourrait très bien être plus lent. Puisqu'il faut une référence, il doit le déréférencer pour obtenir sa valeur puisque le compilateur ne peut pas savoir si quelque chose d'autre a fait une modification. Vos instructions const bool retval =
ou if sont probablement les plus rapides. Jarods peut également avoir la même vitesse puisque les références sont locales à la portée afin que le compilateur sache qu'elles ne sont pas modifiées. Vous pouvez utiliser quick bench pour effectuer des tests.
Hmm, j'imagine que puisque les références sont locales, et en supposant que l'assistant soit intégré, il devrait être assez facile pour le compilateur d'éliminer les références ... mais bien sûr, l'hypothèse est la mère de tout **** ** s, alors peut-être que j'écrirai le test de banc rapide demain (merci pour le lien, au fait!).
@Timo Pas de problème. S'il est intégré, cela ne devrait pas poser de problème. Je voulais juste souligner que cela pourrait être plus lent.
J'aime beaucoup cet article et j'ai déjà testé ces solutions sur un cas simple hier, DEMO . Comme NathanOliver l'a suggéré, l'approche en boucle de Jarod semble montrer les meilleures performances. Bien sûr, les fonctions de modèle doivent être intégrées et plutôt compatibles avec la mémoire.
@Hiroki merci d'avoir écrit le test! En effet, la boucle semble encore plus rapide que le naïf ||
ing, assez drôle! D'un autre côté, le passage du compilateur à GCC7.2 rend la manière naïve la plus rapide, avec l'expression de repli une seconde très proche. Dans mon cas, le compilateur est en fait gcc-arm-none-eabi 7.2, donc je vais devoir essayer le benchmark en utilisant cela, car les résultats sont si proches.
pourquoi pensez-vous que ce serait un «abus» de l'opérateur de court-circuit? C'est exactement ce pour quoi c'est bon ...
Au lieu d'avoir
N
variables différentes et distinctes, pourquoi ne pas avoir un tableau ou un vecteur d'élémentsN
? Ensuite, il est facile de faire une boucle du début à la fin et de sortir de la boucle une fois que la fonction retourne true. Cela faciliterait également l'ajout ou la suppression d'objets car le code existant n'a pas besoin d'être modifié pour refléter les variables nouvelles ou supprimées. Et bien sûr, rendez vos cours moins encombrés de membres.@ user463035818 Je suppose parce que je pense en quelque sorte à
||
comme une opération logique , que j'utilise ici directement pour contrôler le déroulement du programme ... mais vous pourriez avoir raison, c'est peut-être la meilleure façon de faire cela.Veuillez fournir un exemple reproductible minimal . Votre question est parsemée de prémisses étranges, ce qui rend difficile la compréhension de votre vrai problème
Tout le monde essaie de vectoriser et vous voulez revenir en boucle?
@Someprogrammerdude Puisque les potentiomètres eux-mêmes sont utilisés de différentes manières à différents endroits, je préfère donc leur donner des noms explicites. Bien sûr, je pourrais les mettre dans un tableau et ensuite utiliser des références nommées, mais cela ressemble encore une fois à l'ajout de passe-partout inutile.
Eh bien, vous pouvez utiliser des constantes nommées (macros ou
const
ouconstexpr
ou mêmeenum
) pour les index dans le tableau. CommePOT_FOR_THING1
ouPOT_FOR_THING2
lorsque vous avez besoin d'obtenir un objet spécifique du tableau. Cela pourrait ajouter du code passe-partout supplémentaire à certains endroits, mais pourrait aussi rendre d'autres parties du code beaucoup plus faciles. À moins que la condition que vous montrez n'inclut pas tous les éléments du tableau?