IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C++ Discussion :

template expression, foncteurs et static_assert/enable_if


Sujet :

C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Expert éminent

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 202
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 202
    Par défaut template expression, foncteurs et static_assert/enable_if
    Bonjour à tous!

    Ma question est assez simple à formuler: Comment valider les paramètres (de types) d'un operateur + combinant deux fonctions en une seule, dans le cadre d'expression template.

    Mon objectif est d'avoir une template de la forme suivante:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    template <typename F1, typename F2>
    auto operator+(F1 f1, F2 f2) {
        static_assert(F1 et F2 s'appelle avec les mêmes arguments);
        static_assert(F1 et F2 produisent des types additionnables);
        return my::plus<F1, F2>{f1, f2};
    }
    La difficulté étant que la template doit pouvoir être instanciée pour plusieurs signatures, par exemple, deux fonctions int(int) ou deux fonctions double(double, double).
    J'essaie d'avoir un message clair si les types ne sont pas des fonctions/foncteurs compatible.
    Par contre, je peux certainement rajouter des enable_if pour m'assurer d'avoir des foncteurs/fonctions, comme types. Cependant qu'en sera-t-il des lambda généralisées?

    A priori, j'ai raté une astuce dans <type_trait>.
    Quel serait le mécanisme pour vérifier qu'une expression est valide? un équivalent de is_compilable(f1+f2).

  2. #2
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Salut,

    Pour le premier cas tu peux vérifier si deux types sont identiques à l'aide de std::is_same
    Pour le deuxième cas, si tu te limites à l'addition "mathématique" (comprend : que tu ne voudras par exemple pas pouvoir utiliser std::string qui présente un opérateur +), tu peux te tourner vers std::is_arithmetic
    Au final (et pour autant que j'ai bien compris l'idée du deuxième cas), tu devrais pouvoir t'en sortir avec un code proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    template <typename F1, typename F2>
    auto operator+(F1 f1, F2 f2) {
        static_assert(std::is_same<F1, F2>::value, 
                             "first and second parameters should be same type");
        static_assert(std::is_arithmetic<F1>,
                             "parameters should be arithmetics type");
        return my::plus<F1, F2>{f1, f2};
    }
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  3. #3
    Expert éminent

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 202
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 202
    Par défaut
    Je n'ai pas été très clair: F1 et F2 sont deux types de foncteurs.

    Par exemple, on pourrait avoir ce code-ci:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    auto f = [](int x) {return -x;};
    auto g = [](int x) {return 2*x;} + [](int x) {return x+1;};//g équivalente de [](x){return (2*x)+(x+1);}
    auto h = f + [](int x) {return -x;};
    Avec la technique expression template, h aurai pour type plus< lambda_f, plus<lambda_1, lambda_2> >.
    J'ai une version un peu moins optimale à base de std::function, que j'optimiserai par la suite.

    Ce que j'essaie d'avoir, c'est une meta qui permette (dans mon exemple) de vérifier que les deux lambdas sont compatibles (pour produire un opérateur qui convient).
    Le soucis étant d'avoir une erreur de compilation non pas sur l'appel de my::plus::operator(), mais à la construction h = f+g.

    std::is_same n'est pas satisfaisant dans ce cas là, car en effet, les types de f et g sont compatible.
    Qui plus est, je risque d'avoir le même probleme avec la composition de fonctions (f(g))(x).

  4. #4
    Expert confirmé
    Avatar de Luc Hermitte
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2003
    Messages
    5 296
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Août 2003
    Messages : 5 296
    Par défaut
    Une idée, non testée, n'est-il pas possible de jouer avec SFINAE + decltype pour détecter une situation qui ne compile pas au plus tôt ?

    PS: attention avec les expressions template et auto et autre mémorisation. Il faut reconnaitre certains cas pour ne pas prendre de référence dessus, mais dupliquer ce qui est derrière.
    Blog|FAQ C++|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS|Bons livres sur le C++
    Les MP ne sont pas une hotline. Je ne réponds à aucune question technique par le biais de ce média. Et de toutes façons, ma BAL sur dvpz est pleine...

  5. #5
    Membre Expert

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Avril 2013
    Messages
    610
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Finance

    Informations forums :
    Inscription : Avril 2013
    Messages : 610
    Billets dans le blog
    21
    Par défaut
    Voici ce que je propose, sachant que cela ne permet pas de résoudre les cas où il y a une ambigüité intrinsèque, c'est-à-dire lorsque les types que tu additionnes sont des foncteurs qui surchargent l'opérateur de fonction:
    - Une structure Callable_traits permettant de récupérer le type des arguments (stockés dans un tuple) et le type de retour. Il manque des spécialisations: pointeurs de fonction, et un certain nombre de surcharges const/non const, etc. A toi de polir l'outil...
    - utiliser is_same sur les types d'arguments récupérés par la structure (donc stockés dans le tuple)
    - et SFINAE c++11 style pour l'addition:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    #include <iostream>
    #include <tuple>
    #include <type_traits>
     
    template <typename Callable>
        struct Callable_traits : public Callable_traits<decltype(&Callable::operator())> {}; // on passe le type de l'opérateur de fonction
    template <typename Ret, typename... Args>
        struct Callable_traits<Ret(Args...)> { // le cas de la fonction
            using Return_type = Ret;
            using Arguments_type = std::tuple<Args...>; // le tuple de stockage
        };
    template <typename Ret, typename Class, typename... Args>
        struct Callable_traits<Ret(Class::*)(Args...) const> { // le cas de la fonction membre, qui récupère donc l'opérateur de fonction
            using Return_type = Ret;
            using Arguments_type = std::tuple<Args...>;
        };
     
    template <typename A, typename B>
    constexpr auto is_additionable_impl(int) -> decltype( std::declval<A>() + std::declval<B>(), bool() ) { // ici la magie decltype
        return true;
    }
    template <typename A, typename B>
    constexpr bool is_additionable_impl(...) {
        return false;
    }
    template <typename A, typename B>
    constexpr bool is_additionable(){ // on recoure SFINAE pour faire plus joli
        return is_additionable_impl<A, B>(0);
    }
     
     
    int main() {
        auto l1 = [](int i) { return i * 2; };
        auto l2 = [](int i) { return i + 1; };
        using L1_info = Callable_traits<decltype(l1)>;
        using L2_info = Callable_traits<decltype(l2)>;
        std::cout << std::boolalpha;
        std::cout << std::is_same<L1_info::Arguments_type, 
                                  L2_info::Arguments_type>::value; // true
        std::cout << std::endl;
        std::cout << is_additionable<L1_info::Return_type, L2_info::Return_type>(); // true
    }
    EDIT: Il faudrait peut-être réfléchir à employer common_type plutôt que is_same, mais d'après la doc il n'est garanti "SFINAE friendly" qu'à partir de C++17. Donc à tester...

  6. #6
    Expert éminent

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 202
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 202
    Par défaut
    Ca m'a tout l'air de correspondre, en effet.
    Je vais aussi essayer grouper les déclarations de is_additionnable et des futures is_substractable, is_divideable, is_multipliable, is_moduloable, is_composable en utilisant un foncteur en parametre (std::plus, std::minus, etc)

    Je fermerai dès que j'aurai testé.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 15
    Dernier message: 02/07/2007, 08h23
  2. Foncteur, classes templates et héritage
    Par Floréal dans le forum C++
    Réponses: 8
    Dernier message: 17/06/2007, 21h56
  3. Problème expression templates
    Par Bakura dans le forum C++
    Réponses: 5
    Dernier message: 06/06/2007, 20h08
  4. [Débutant]Foncteur et operator() template
    Par Sub dans le forum Langage
    Réponses: 14
    Dernier message: 27/03/2007, 13h51
  5. Réponses: 12
    Dernier message: 27/01/2007, 12h32

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo