6
votes

Inversez l'ordre de X et Y dans MACRO (X) (Y)

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?


8 commentaires

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 type B même si A est false . 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, appeler bar est une erreur grave.


@Barry Si j'ai bien lu, l'utilisation de fast_enable_if_t force l'instanciation de hard_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 ...


4 Réponses :


4
votes

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>)


2 commentaires

Nice hack, mais IDE ne sera pas content.


Sensationnel. 😳😳😳😳😳



3
votes

Pourquoi n'utilisez-vous pas simplement quelque chose comme

template<class T,bool B>
using reverse_enable_if_t=enable_if_t<B,T>;


0 commentaires

1
votes

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>


0 commentaires

0
votes

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 )


1 commentaires

Merci. J'essaie d'éviter d'avoir à envelopper la déclaration dans une macro EVAL .