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

Langage C++ Discussion :

Transtypage de pointeurs sur fonction


Sujet :

Langage C++

  1. #1
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Juin 2009
    Messages
    4 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 481
    Points : 13 679
    Points
    13 679
    Billets dans le blog
    1
    Par défaut Transtypage de pointeurs sur fonction
    Salut !

    Je continue à jouer avec des API en C utilisant des void* pour les wrapper dans des classes C++.

    Aujourd'hui, j'ai une fonction C qui attend en paramètre un pointeur sur une fonction prenant un void* en paramètre et ne retournant rien. Je crée un wrapper en C++ dans lequel j'arrive à m'assurer que ce void* sera en fait l'adresse d'un objet. Je souhaite donc écrire un code comme ceci :

    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
    // -----------------------
    // C world
    using C_callback = void(*)(void*);
     
    void registerCallback(C_callback c) {
    	(void) c;
    }
     
    // -----------------------
    // C++ world
    class Foo {
    public:
    	using Cpp_callback = void(*)(Foo*);
     
    	Foo(Cpp_callback cpp) {
    		registerCallback(reinterpret_cast<C_callback>(cpp));
    	}
    };
    Sans reinterpret_cast<>, j'ai l'erreur suivante, qui me parait tout à fait logique : error: invalid conversion from 'void (*)(Foo*)' to 'void (*)(void*)' [-fpermissive]. Le compilateur ne peut pas du tout assurer que ce void* sera en fait un Foo*. Si je peux le garantir (de part la construction de mon programme), est-ce qu'un tel cast est OK ?

    Question bonus : que fait la 2e ligne par rapport à la première ?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    using C_callback = void(*)(void*);
    using what_is_it= void(void*);

  2. #2
    Membre confirmé
    Avatar de gb_68
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2006
    Messages
    232
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France, Haut Rhin (Alsace)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2006
    Messages : 232
    Points : 546
    Points
    546
    Par défaut
    Bonjour,

    Il est possible de transtyper un pointeur de fonction vers un autre type fonction (et revenir vers le premier type en toute sécurité) mais effectuer un appel vers la fonction alors que le type du pointeur n'est pas celui d'origine tombe dans l'Undefined Behavior (cf. 7)).


    Dans les faits, ce type de cast (avec un paramètre effectivement de type Foo*), fonctionnera sans doute sur la plupart des compilateurs d'autant plus si l'implémentation de la fonction effectuant l'appel au pointeur de fonction n'est pas "visible" du compilateur ayant fait le cast (ex : bibliothèque externe).

    Mais afin de ne pas provoquer les éventuels démons nasaux, il vaut mieux dans la mesure du possible conserver la signature de la fonction et effectuer le transtypage sur le paramètre void *
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    using C_callback = void(*)(void*);
    // souvent le paramètre void * est enregistré en même temps
    void registerCallback(C_callback c, void * data);
     
    // -----------------------
    // C++ world
    template<typename T, void(T::* member)()>
    void callback_for_c_api(void * ptr)
    { (static_cast<T*>(ptr)->*member)(); }
     
    template<typename T, void(T::* member)() = &T::operator()>
    void registerCallbackFor(T * data)
    { ::registerCallback(&callback_for_c_api<T, member>, data); }
    Pour la partie bonus :

    C_callback définit un type pointeur vers une fonction prenant un paramètre void * et ne retournant rien
    what_is_it définit un type fonction prenant un paramètre void * et ne retournant rien

    et l'on a la relation "static_assert(std::is_same_v<C_callback, what_is_it *>, "ok");"
    (what_is_it est à C_callback ce que int est à int *)

    Outre le fait qu'il soit possible de définir des prototypes de fonction à l'aide de what_is_it
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    what_is_it a;
    what_is_it b;
    La principale raison d'être de ce genre de type se trouve dans les déductions template
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    template<typename T>
    void bar(T * ptr);
     
    int foo(char);
    bar(&foo); // T == int(char), ptr de type T * == int(*)(char)
    Et il existe pire afin de gérer les fonctions membres : les Abominable Function Types
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    using regular    = void();
    using abominable = void() const volatile &&;

  3. #3
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Juin 2009
    Messages
    4 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 481
    Points : 13 679
    Points
    13 679
    Billets dans le blog
    1
    Par défaut
    Tsss.... Maudits UB...

    Du coup j'ai utilisé ton astuce pour faire des trucs carrément cools, notamment j'ai désormais using Cpp_callback = std::function<void(Foo&)>; au lieu de using Cpp_callback = void(*)(Foo*);. Et ça c'est bien ! Le code ressemble à ça :
    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
    43
    44
    45
    46
    47
    48
    #include <iostream>
     
    // -----------------------
    // C world
    using C_callback = void(*)(void*);
     
    void registerCallback(C_callback c, void* handle) {
    	std::cout << __PRETTY_FUNCTION__ << &c << handle << std::endl;
    	c(handle); // this call is supposed to be asynchronous
    }
     
    // -----------------------
    // C++ world
    #include <functional>
     
    class Foo {
    public:
    	using Cpp_callback = std::function<void(Foo&)>;
     
    	Foo(Cpp_callback cpp) :
    			cpp_m(cpp) {
    		registerCallback(&Foo::executeFoo, this);
    	}
     
    private:
    	Cpp_callback cpp_m;
     
    	void execute() {
    		cpp_m(*this);
    	}
     
    	static void executeFoo(void* ptr) {
    		static_cast<Foo*>(ptr)->execute();
    	}
    };
     
    //---------------
    // Usage
    #include <cassert>
     
    int main(int argc, char* argv[]) {
     
    	auto callback = [&argc, &argv](Foo& timer) {
    		std::cout << __PRETTY_FUNCTION__ << &timer << argc << argv <<std::endl;
    	};
     
    	Foo st(callback);
    }
    Merci !

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

Discussions similaires

  1. Transtypage Pointeur sur Fonction
    Par oupslelapin dans le forum C++
    Réponses: 2
    Dernier message: 30/05/2007, 11h54
  2. Réponses: 5
    Dernier message: 12/01/2005, 20h58
  3. pointeurs sur fonction en C++
    Par cemoi dans le forum C++
    Réponses: 7
    Dernier message: 29/11/2004, 13h19
  4. [langage] Pointeur sur fonction
    Par Fanch.g dans le forum Langage
    Réponses: 2
    Dernier message: 02/10/2004, 10h43
  5. Declaration de fonction retournant un pointeur sur fonction
    Par pseudokifaitladifférence dans le forum C
    Réponses: 5
    Dernier message: 11/08/2003, 19h37

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