Posons nous dans le cas suivant (oui ça commence direct)
int strange_apply(int (*f)(int) {
return reinterpret_cast<(int(*)(int, int)>(f)(1, 2);
}
Ce code compile avec un warning depuis le dernier gcc, et c'est bien car c'est en fait un undefined behavior (cast entre types de fonctions incompatibles dans le cas présent).
Et pourtant on voit pas mal de code comme ça, p.e. dans les sources de LLVM, sous l'hypothèse que si on utilise pas les derniers arguments, ce n'est pas grave.
Je me suis amusé à créer un nouveau function_cast
qui permet de juste passer les valeurs dont on a besoin pour éviter cet avertissement:
#include <utility>
#include <tuple>
template<typename To, typename From>
class FunctionCast;
template<typename ReturnType, typename... ArgumentTypes, typename FromReturnType, typename... FromArgumentTypes>
class FunctionCast<ReturnType(ArgumentTypes...), FromReturnType(*)(FromArgumentTypes...)> {
using From = FromReturnType (*)(FromArgumentTypes...);
From from;
template<typename Args, std::size_t... Is>
ReturnType apply(Args args, std::index_sequence<Is...>) const {
return from(std::get<Is>(args)...);
}
public:
FunctionCast(From f) : from(f) {}
ReturnType operator()(ArgumentTypes... args) const {
return apply(std::make_tuple(args...), std::make_index_sequence<sizeof...(FromArgumentTypes)>());
}
};
template<typename To, typename From>
FunctionCast<To, From> function_cast(From f) {
return {f};
}
int apply(int (*f)(int)) {
return reinterpret_cast<int(*)(int, int)>(f)(1, 2); // invalid
return function_cast<int(int, int)>(f)(1, 2); // valid
}
lien godbolt illustrant l'idée. Le concept est assez simple : on empaquète les arguments dans un tuple, puis on applique la fonction en dépaquetant uniquement une partie des arguments (au passage, on doit perdre quelques informations de type, std::make_tuple
n'est pas très conservateur à ce sujet).
Et voilà, un nouveau cast qui, malheureusement, ne permet pas de créer de nouveaux pointeurs de fonctions mais un foncteur…
# std::function
Posté par David Demelier (site web personnel) . Évalué à 1.
Et sinon, une raison particulière pour ne pas utiliser
std::function
?git is great because linus did it, mercurial is better because he didn't
[^] # Re: std::function
Posté par Gof (site web personnel) . Évalué à 3.
Car ça ne résout pas le problème
[^] # Re: std::function
Posté par David Demelier (site web personnel) . Évalué à 2. Dernière modification le 08 novembre 2018 à 14:34.
Je ne comprends pas ton problème. Ton contrat c'est de prendre des fonctions qui prennent deux entiers en paramètre. Sauf qu'on t'envoie une fonction qui en prend que un. C'est contraire au contrat indiqué. C'est à l'appelant de se modifier.
Sinon, c'est tout à fait possible avec std::function et une lambda (avec std::bind peut-être)
Note que le std::function n'est nécessaire que si tu souhaite stocker
beurk
pour l'appeler plus tard.git is great because linus did it, mercurial is better because he didn't
[^] # Re: std::function
Posté par serge_sans_paille (site web personnel) . Évalué à 2.
On peut imaginer un système d'enregistrement de plugin avec des paramètres optionnels (je dis pas que c'est le bon design hein) :
Ça permet d'avoir des paramètres optionnels pour les plugins.
# Mensonges !
Posté par Axioplase ıɥs∀ (site web personnel) . Évalué à 2.
Étant donné qu'il manque deux parenthèses fermantes, je doute que ça compile.
Ensuite, concernant LLVM et le "undefined behaviour", ben, vu que LLVM est probablement un compilo qui bootstrappe, ils font ce qui les arrange avec les "undefined behaviours"…
[^] # Re: Mensonges !
Posté par Gof (site web personnel) . Évalué à 2. Dernière modification le 07 novembre 2018 à 14:52.
Euh, non. LLVM est sensé être compilable avec GCC et MSVC.
[^] # Re: Mensonges !
Posté par Gof (site web personnel) . Évalué à 2.
D'ailleurs, aurais tu des exemples dans le code de LLVM ou il font cette erreurs ?
[^] # Re: Mensonges !
Posté par serge_sans_paille (site web personnel) . Évalué à 3.
Un cas proche dans LLVM (bon, plus exactement dans compiler-rt), mais qui porte sur le type de retour :
https://github.com/llvm-mirror/compiler-rt/blob/23b063668e563dd5cc8fc37e00c3199a9afa595f/lib/sanitizer_common/sanitizer_linux.cc#L1715
On retrouve la même dans https://github.com/python/cpython/pull/6008 d'ailleurs.
Un autre cas détecté par GCC qui fait froid dans le dos
[^] # Re: Mensonges !
Posté par Maclag . Évalué à 10. Dernière modification le 07 novembre 2018 à 15:17.
Voilà! C'est pour ça que je n'ai absolument rien compris! c'est parce qu'il manque des parenthèses!
-------> [ ]
# Imbitable
Posté par ff9097 . Évalué à 10.
Langage imbitable
[^] # Re: Imbitable
Posté par Boiethios (site web personnel) . Évalué à 3.
Il y a ça, mais il y a aussi ce que fait l'auteur…
# Crados
Posté par lolop (site web personnel) . Évalué à 7.
Ben c'est crade. Si tu as besoin de ça tu crée une fonction intermédiaire propre qui n'utilise pas le dernier argument et tu l'inline pour les perfs. Mais jouer comme ça, non.
Votez les 30 juin et 7 juillet, en connaissance de cause. http://www.pointal.net/VotesDeputesRN
[^] # Re: Crados
Posté par rewind (Mastodon) . Évalué à 4.
Du coup, c'est exactement ce que fait
function_cast
mais de manière totalement générique.[^] # Re: Crados
Posté par arnaudus . Évalué à 5. Dernière modification le 07 novembre 2018 à 16:58.
Et tu ne pourrais pas profiter de l'explicitissime syntaxe
<int(int, int)>
.Je ne comprends pas pourquoi c'est particulièrement bien qu'un "undefined behavior" compile, au passage.
[^] # Re: Crados
Posté par serge_sans_paille (site web personnel) . Évalué à 2.
Cette vidéo explique très bien un tas de concepts liés : https://www.youtube.com/watch?v=g7entxbQOCc
# extern "C", pointer to member function,
Posté par Gof (site web personnel) . Évalué à 3.
Une autre source de undefined behaviour est quand on a des pointeur
extern "C"
Par exemple:
Il serrait bien aussi de trouver une solution qui pour créé une fonction générique qui fonctionne avec les pointer vers des fonction membre.
Il faut que je fasse un overload pour
my_apply(int(T::*f)(int) const)
.Mais si je quelqu'un veut passer une fonction volatile ? Ah oui, il faut rajouter
my_apply(int(T::*f)(int) volatile)
etmy_apply(int(T::*f)(int) const volatile)
Bon, en C++98 c'est bon, mais en C++11 il faut aussi considérer les "Ref-qualifiers".
Rajoutons donc des overload.
my_apply(int(T::*f)(int) const&)
etmy_apply(int(T::*f)(int) &&)
. Je vais passer tous les équivalent avecvolatile
car bon, qui utilise volatile? mais je rajoute quand mêmemy_apply(int(T::*f)(int) const&&)
pour être sur.Et quand on crois que on a fini, C++17 fait un changement incompatible et le code code ne compile plus si quelqu'un passe une fonction
noexcept
alors il faut que je dédouble le tout.Au final je dois écrire 4 * 3 * 2 = 24 overloads.
[^] # Re: extern "C", pointer to member function,
Posté par serge_sans_paille (site web personnel) . Évalué à 2.
Peux tu détailler l'origine de ce comportement indéfini ? Je pensais que
extern "C"
changeait juste le name mangling, ça changerait aussi la convention d'appel?[^] # Re: extern "C", pointer to member function,
Posté par Gof (site web personnel) . Évalué à 2. Dernière modification le 07 novembre 2018 à 17:20.
Oui, ça peut aussi changer la convention d'appel en principe.
(Bien que sur toute les architecture que je connaisse, c'est la même en C et en C++)
Cf §5.2.2 [expr.call] http://eel.is/c++draft/expr.call
https://docs.oracle.com/cd/E19059-01/wrkshp50/805-4956/bajdcjch/index.html
En particulier, ça veux aussi dire que si on a une ça:
https://stackoverflow.com/questions/7301543/can-a-lambda-have-extern-c-linkage
[^] # Re: extern "C", pointer to member function,
Posté par serge_sans_paille (site web personnel) . Évalué à 2.
Super intéressant, merci !
# Pourquoi?
Posté par devnewton 🍺 (site web personnel) . Évalué à 8.
Quel problème cherches-tu à résoudre?
Le post ci-dessus est une grosse connerie, ne le lisez pas sérieusement.
[^] # Re: Pourquoi?
Posté par Maclag . Évalué à 8.
L'excès de popularité du C++, mais vu la gueule du code, je crois que c'est réglé.
---(Je sais, j'aurais dû rester dehors…)---> [ ]
Suivre le flux des commentaires
Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.