Partage
  • Partager sur Facebook
  • Partager sur Twitter

Qui a raison?

Un construct à base de lambda

7 septembre 2018 à 19:05:16

Voilà j'ai une classe NbgItem qui a cette gueule là (j'ai simplifié), elle est construite par lecture d'un sous-fichier binaire, c'est une classe de stockage de très bas niveau, à plus haut niveau je construis des classes spécifiques pour extraire le contenu.

class NbgItem
{
public:

   /* des trucs sans importance pour le problème */

   bool isMemory() const;
   bool isSimu() const;

};

Je stocke ces machins dans un vector tout ce qu'il y a de plus classique

using NbgInstance = std::vector<NbgItem>;

Maintenant j'ai une fonction de sélection des NbgItem qui m'intéressent qui ressemble à ça 

template<class P>
std::vector<NbgItem const *> 
select(NbgInstance const & set, P p)
{
   std::vector<NbgItem const *> result {};
   for( auto const & i : set){
      if (p(i)){
         result.push_back(&i);
      }
   }
   return result;
}

Dans une classe qui formate l'affichage de ces bidules, les simu et les memory s"affichent exactement de la même façon, c'est leur signification qui est différente, aussi je n'ai qu'une seule classe pour afficher les deux, j'affiche simplement le seul sous-ensemble qui m'intéresse via une fonction update

void update(NbgInstance const & inst,bool mem)
{
   auto f = mem ? [] (NbgItem const & i){return i.isMemory();}
                : [] (NbgItem const & i){return i.isSimu();}

   auto result = select(inst,f);
   // La suite ...
}

Cette fonction compile avec gcc 5.3 mais pas avec VS2017, pour passer avec VS 2017, je dois la transformer en 

void update(NbgInstance const & inst,bool mem)
{
   auto f = std::function<bool (NbgItem const &)>;
   if (mem){
      f  = [](NbgItem const & i ){return i.isMemory();};
   } else {
      f = [](NbgItem const & i ){return i.isSimu();};
   }

   auto result = select(inst,f);

   // la suite ...
}

Ce n'est pas que ça me gêne de devoir passer par un if plutôt que par le ternaire, mais je trouve la solution du ternaire plus élégante, est ce que la solution du ternaire vous paraît aller à l'encontre de la norme ?







-
Edité par int21h 7 septembre 2018 à 19:19:27

  • Partager sur Facebook
  • Partager sur Twitter
Mettre à jour le MinGW Gcc sur Code::Blocks. Du code qui n'existe pas ne contient pas de bug
7 septembre 2018 à 19:12:18

Lu'!

De mémoire, le type d'une lambda est quelque chose de non-spécifié par la norme. Donc je dirai qu'il n'y a pas de raison que le compilateur soit obligé de donner le même type à tes deux lambdas, donc la ternaire devient ill-formed parce que le type dans les deux branches n'est pas le même.

  • Partager sur Facebook
  • Partager sur Twitter

Posez vos questions ou discutez informatique, sur le Discord NaN | Tuto : Preuve de programmes C

7 septembre 2018 à 19:18:01

Merci pour l'info :)

  • Partager sur Facebook
  • Partager sur Twitter
Mettre à jour le MinGW Gcc sur Code::Blocks. Du code qui n'existe pas ne contient pas de bug
7 septembre 2018 à 19:19:07

(C'est mon interprétation hein, j'en mettrais pas ma main au feu à couper dans la gueule du loup).

  • Partager sur Facebook
  • Partager sur Twitter

Posez vos questions ou discutez informatique, sur le Discord NaN | Tuto : Preuve de programmes C

7 septembre 2018 à 19:23:22

Quand j'ai écris le code, j'étais sur GCC, et j'étais pas sûr que ce soit valide. Je l'ai tenté quand même et c'est passé. C'est quand j'ai voulu le passer sur VS que ça ne voulait plus compiler. Quoi qu'il en soit la solution avec des if passe sur les deux compilos et donne les résultats attendus, ça me va bien :)
  • Partager sur Facebook
  • Partager sur Twitter
Mettre à jour le MinGW Gcc sur Code::Blocks. Du code qui n'existe pas ne contient pas de bug
7 septembre 2018 à 19:23:28

Ca me semble etre possible comme explication. Il suffit que MSVC donne un nom différent en interne (et pas juste la signature) pour qu'il puisse considérer que c'est 2 fonctions differentes.

A noter, dans le second code, std::function n'est pas le type correspondant a une lambda, c'est un type qui peut encapsuler une lambda. Il y a un cast pour mettre une lambda dans un std::function. Tu peux utiliser le ternaire, mais il faudra forcer le type.

auto f = mem ? 
  std::function<bool (NbgItem const &)>([](NbgItem const & i ){return i.isMemory();}) :
  std::function<bool (NbgItem const &)>([](NbgItem const & i ){return i.isSimu();});

Ca devrait fonctionner aussi avec un pointer de fonction.

  • Partager sur Facebook
  • Partager sur Twitter
7 septembre 2018 à 20:47:06

Vc ne sait pas faire de double conversions vers un sous type. Ici, il y a 2 lambdas donc 2 types différents. Une lambda sans contexte de capture peut être implicitement convertit en pointeur de fonction et clang / gcc font bien la conversion vers le type commun. Vc se vautre.

Ceci devrait fonctionner sans utiliser std::function.

auto* f = [](NbgItem const & i ){return i.isMemory();};
if (!mem){
  f = [](NbgItem const & i ){return i.isSimu();};
}

Au pire, tu mets la ternaire dans la condition, ce ne sera pas pire qu'utiliser un pointeur de fonction.

-
Edité par jo_link_noir 7 septembre 2018 à 20:47:22

  • Partager sur Facebook
  • Partager sur Twitter
7 septembre 2018 à 23:13:39

Salut, pour compléter la réponse de jo_link_noir, il faut savoir que les lambda sans contexte de capture possède l'opérateur "+" qui permet de convertir une lambda en pointeur de fonction.

Du coup quelque chose comme :

auto f = mem ? +[] (NbgItem const & i){return i.isMemory();}
             : +[] (NbgItem const & i){return i.isSimu();}

Devrait fonctionner et faire l'affaire.

-
Edité par Qnope 7 septembre 2018 à 23:15:00

  • Partager sur Facebook
  • Partager sur Twitter
http://cpp-rendering.io : Vous trouverez tout ce dont vous avez besoin sur Vulkan / OpenGL et le rendu 3D !
7 septembre 2018 à 23:38:39

Pour compléter la réponse de Qnope, cela ne fonctionne pas avec vc :D

  • Partager sur Facebook
  • Partager sur Twitter
7 septembre 2018 à 23:52:57

je ne connaissais pas ce trick.

Bon, en fait, c'est pas un opérateur spéciale pour les lambdas, c'est l'operateur uniaire +, qui a une surchage qui prend un pointeur de fonction et retourne un pointeur de fonction. Et la lambda est casté en pointeur de fonction pour appeler cet opérateur, ce qui permet le cast.

(Le code est valide, mais dire "les lambdas possédent un operateur +" est bof bof)

  • Partager sur Facebook
  • Partager sur Twitter
8 septembre 2018 à 0:32:40

@jo_link_noir 

Le code compile sous MSVC, du moins avec la dernière version (15.8.3).

Effectivement, c'est mal dit et même faux ^^. Merci pour ta précision gbdivers ;)

-
Edité par Qnope 8 septembre 2018 à 0:43:12

  • Partager sur Facebook
  • Partager sur Twitter
http://cpp-rendering.io : Vous trouverez tout ce dont vous avez besoin sur Vulkan / OpenGL et le rendu 3D !
8 septembre 2018 à 1:24:16

Ah oui, c'est effectivement corrigé, bon à savoir.

  • Partager sur Facebook
  • Partager sur Twitter
8 septembre 2018 à 6:45:46

L'opérateur unaire + est un trick classique pour forcer une conversion:
#include<iostream>

int main()
{
    char a = 'a';

    // affiche a
    std::cout << a << '\n';

    // affiche 97 (cast en int)
    std::cout << +a;

    return 0;
}
Par contre, j'ignorais qu'on pouvait faire ça sur une lambda ^^
  • Partager sur Facebook
  • Partager sur Twitter
Mettre à jour le MinGW Gcc sur Code::Blocks. Du code qui n'existe pas ne contient pas de bug