Est-il légal d'allouer de nouveau un pointeur pour fonctionner?

33

Les pointeurs vers des fonctions ne sont pas de simples pointeurs de données car ils ne peuvent pas être stockés dans un pointeur void *. Néanmoins, il semble que je puisse stocker la copie d'un pointeur de fonction dans la mémoire dynamique (en gcc et clang) comme dans le code ci-dessous. Un tel code est-il légal selon la norme C ++, ou peut-être s'agit-il d'une sorte d'extension de compilateur?

De plus, le pointeur résultant vers le pointeur de fonction se comporte comme un pointeur de données simple: je peux le stocker dans void * et le récupérer de void * par static_cast. Ce comportement est-il garanti par la norme?

int main()
{
  extern void fcn();
  void (*fcnPtr)() = &fcn;
  void (**ptrToFcnPtr)() = nullptr;

  //Make the copy of fcnPtr on the heap:
  ptrToFcnPtr = new decltype(fcnPtr)(fcnPtr);
  //Call the pointed-to function : 
  (**ptrToFcnPtr)();

  //Save the pointer in void* :
  void *ptr = ptrToFcnPtr;
  //retrieve the original ptr: 
  auto myPtr = static_cast< void(**)() > (ptr) ; 
  //free memory:
  delete ptrToFcnPtr ;

}
Adrian
la source
2
Veuillez ne pas utiliser de pointeurs de fonction bruts. Utilisez std::functionplutôt.
Un mec programmeur le
Vous n'avez pas besoin de le newlancer void*. void* ptr = &fcnPtr;fonctionne aussi bien, car fcnPtrest un objet, pas une fonction.
noyer
5
@Someprogrammerdude std::functionest un conteneur de type effacé pour stocker un appel arbitraire, pas vraiment un remplacement pour les pointeurs de fonction…
Michael Kenzel
7
(@Someprogrammerdude) Veuillez ne pas utiliser / recommander aveuglément std::function. C'est génial pour sa capacité à stocker des fonctions "polymorphes" (c'est-à-dire tout ce qui a la bonne signature, même s'il contient un état comme dans le cas de certains lambdas), mais cela ajoute également des frais généraux qui peuvent ne pas être nécessaires. Un pointeur vers une fonction est POD. A std::functionne l'est pas.
Matthew
2
@Matthew Pour être honnête, Adrian demande à propos d'allouer dynamiquement le pointeur à la fonction et de le pointer avec un effacement de type void*, donc dans le contexte de cette question std::functionsemble être exactement ce qu'ils cherchaient. Je suis d'accord que le rejet général par SPD des pointeurs de fonction n'est pas valable.
eerorika

Réponses:

27

Bien que les pointeurs de fonction ne soient pas des pointeurs d'objet, le "pointeur vers une fonction d'un certain type" est toujours un type d'objet [basic.types] / 8 . Ainsi, les pointeurs de fonction sont eux-mêmes des objets, tout ce qu'ils pointent ne l'est pas.

Ainsi, vous pouvez bien sûr créer un objet de type pointeur de fonction via une nouvelle expression…

Michael Kenzel
la source
9

car ils (pointeurs de fonction) ne peuvent pas être stockés dans un pointeur void *.

En fait, le stockage d'un pointeur de fonction en tant que void*est pris en charge sous condition. Cela signifie qu'il peut ou non être stocké en fonction de l'implémentation du langage. Si l'implémentation du langage prend en charge le chargement dynamique, la conversion du pointeur de fonction void*est probablement prise en charge. GCC, Clang et MSVC soutiennent tous ceci:

reinterpret_cast<void*>(&function);

Est-il légal d'allouer de nouveau un pointeur pour fonctionner?

Sûr. Tous les pointeurs, y compris les pointeurs de fonction, sont des objets et tous les objets peuvent être alloués dynamiquement.

De plus, le pointeur résultant vers le pointeur de fonction se comporte comme un pointeur de données simple

Le pointeur de fonction est un objet. Le pointeur vers un pointeur de fonction non seulement "se comporte comme", mais est un pointeur vers un objet.

Je peux le stocker dans void * et le récupérer de void * par static_cast. Ce comportement est-il garanti par la norme?

La conversion entre le pointeur en vide et le pointeur en objet est autorisée, oui. Et la conversion aller-retour est garantie pour donner le pointeur d'origine.

eerorika
la source
Merci. Mais ensuite, pour convertir le pointeur de fonction en void * (ou l'inverse), je dois utiliser reinterpret_cast, non?
Adrian
1
@Adrian Oui. J'ai ajouté un exemple.
eerorika
Si quelqu'un veut probablement une exception: modèle de mémoire dos medium + superpositions. Les pointeurs de fonction sont plus grands que les pointeurs de données dans le modèle moyen, et les superpositions peuvent être utilisées pour réaliser des plugins si vous essayez assez fort.
Joshua