J'ai un DSL basé sur une macro C ++ qui définit une macro comme celle-ci:
template<class T> auto some_function(T t) -> enable_if_t<SomeConcept<T>, int>
Ceci est pour une utilisation comme:
template<class T> auto some_function(T t) -> enable_if_t<int, SomeConcept<T>>
Ce qui se développe en:
template<class T> auto some_function(T t) -> RETURNS(int) (requires SomeConcept<T>)
(Lorsque les concepts C ++ 20 sont activés, cela se développe en une vraie clause requires
.)
Je préférerais que l'ordre des paramètres soit inversé. Autrement dit, je voudrais qu'il génère ceci:
#define RETURNS(...) \ enable_if_t<__VA_ARGS__ WHEN #define WHEN(...) \ , EAT_ ## __VA_ARGS__ > #define EAT_requires
Je pense que ce n'est pas possible. Un pirate informatique intelligent peut-il me prouver le contraire?
4 Réponses :
S'il est tolérable d'omettre un paren ouvert, vous pouvez le réaliser comme ceci:
template<class T> auto some_function(T t) -> enable_if_t< SomeConcept<T, int>, pair<int, int> >
Entrée:
template<class T> auto some_function(T t) -> RETURNS(pair<int, int>) requires SomeConcept<T, int>)
Sortie:
#define UNWRAP(...) __VA_ARGS__ #define RETURNS(...) \ WHEN ((__VA_ARGS__), #define WHEN(x, ...) \ enable_if_t<EAT_ ## __VA_ARGS__, UNWRAP x> #define EAT_requires template<class T> auto some_function(T t) -> RETURNS(pair<int, int>) requires SomeConcept<T, int>)
Nice hack, mais IDE ne sera pas content.
Sensationnel. 😳😳😳😳😳
Pourquoi n'utilisez-vous pas simplement quelque chose comme
template<class T,bool B> using reverse_enable_if_t=enable_if_t<B,T>;
Une autre approche à ce sujet est:
template<class T> auto some_function(T t) -> RETURNS(pair<int, int> REQUIRES SomeConcept<T, int>);
qui permet la syntaxe équilibrée parens suivante:
#define RETURNS(...) REVERSE_ENABLE_IF_T((__VA_ARGS__)) #define REQUIRES ),( #define UNWRAP(...) __VA_ARGS__ #define REVERSE_ENABLE_IF_T(PT,PB) enable_if_t<UNWRAP PB,UNWRAP PT>
Voici une solution qui ne nécessite pas de parenthèses sans correspondance, mais nécessite l'ajout d'une macro EVAL autour de l'ensemble:
template<class T> auto some_function( T t ) -> enable_if_t<SomeConcept<T>, int>
Vous pouvez alors écrire ce qui suit:
EVAL( template<class T> auto some_function(T t) -> RETURNS(int) (requires SomeConcept<T>) )
et il produit:
#define EVAL( ... ) EVAL1( __VA_ARGS__ ) #define EVAL1( ... ) EVAL0( EVAL0( EVAL0( __VA_ARGS__ ) ) ) #define EVAL0( ... ) __VA_ARGS__ #define EMPTY() #define DEFER( ... ) __VA_ARGS__ EMPTY() #define EAT_requires #define SWAP( X, Y ) enable_if_t<EAT_##Y, X> #define RETURNS( X ) DEFER( SWAP )( X, ADD_PAREN #define ADD_PAREN( Y ) Y )
Merci. J'essaie d'éviter d'avoir à envelopper la déclaration dans une macro EVAL
.
Pourquoi les macros? Il existe presque toujours de meilleures façons.
Est-ce que la définition d'un nouveau
template enable_if_t_reversed : enable_if_t
fonctionnerait?" Pourquoi des macros?" - On dirait que vous avez manqué le fait qu'il s'agit d'une macro de portabilité qui s'étend à autre chose lorsque les concepts C ++ 20 sont pris en charge. "Est-ce que la définition d'un nouveau
template enable_if_t_reversed : enable_if_t
fonctionnerait?" - C'est ce que je fais actuellement, et ce n'est pas terrible, mais cela force l'évaluation de typeB
même siA
estfalse
. Si je peux trouver un moyen de réorganiser les arguments, je peux améliorer les temps de compilation.@EricNiebler mais, il n'y a pas de court-circuit à l'intérieur de
enable_if_t
- l'inversion des arguments ne change rien.@Quentin C'est ce que je pensais aussi, et pourtant, voir cet exemple . Je l'ai légèrement modifié à partir de ce qu'Eric m'a montré à l'origine. Appeler
foo
est très bien, appelerbar
est une erreur grave.@Barry Si j'ai bien lu,
l'utilisation de fast_enable_if_t
force l'instanciation dehard_error_t
. C'est vraiment déroutant, mais à moins que mon cerveau ne soit frit, cela est indépendant de l'ordre des arguments, n'est-ce pas?@Quentin Oui, ce n'est pas l'ordre des arguments basé. C'est que
fast_enable_if :: sfinae
est déjà un échec de substitution - et nous instancions de gauche à droite. Mettre la condition en premier vous permet de vous en sortir. (Dans ce cas également, nous n'essayons pas d'éviter l'instanciation en raison d'erreurs matérielles, nous essayons d'éviter l'instanciation en raison du temps de compilation supplémentaire)@Barry oooooh Je vois, ça a cliqué. Je ne vois toujours pas pourquoi le simple fait de mentionner
hard_error_t
l'instancie, mais d'accord. En particulier, il n'a même pas besoin d'être défini ...