2017-10-13 4 views
1

Je fonction constexpr qui compte nombre d'espaces réservés https://godbolt.org/g/JcxSiu,Compile contrôle horaire si sizeof ... (args) correspond à la suite de la fonction constexpr

par exemple: "Hello %1" renvoie 1, et "Hello %1, time is %2" renvoie 2.

Ensuite, je voudrais faire une fonction qui ne compile pas si le nombre d'arguments n'est pas égal au nombre d'espaces réservés.

template <typename... Args> 
inline std::string make(const char* text, Args&&... args) { 
    constexpr static unsigned count = sizeof...(args); 

    // TODO how to compile time check if count == count_placeholders(text)  
    // constexpr static auto np = count_placeholders(text); 

    //static_assert(count == np;, "Wrong number of arguments in make"); 

    return std::to_string(count); 
}; 

afin que make("Hello %1", "World"); compile et

make("Hello %1 %2", "World"); ou make("Hello %1", "World", "John"); ne fonctionne pas.

Je pense que cela peut être fait, je ne sais pas comment. Peut-être avec un peu Magick modèle :)

EDIT

Je reçois presque ce que je veux. https://godbolt.org/g/Y3q2f8

Abandonne maintenant en mode débogage. Il est possible de faire une erreur de compilation?

+0

C'est ** pas une réponse ** car il utilise buf extension buf si cela ne vous dérange pas, vous pouvez utiliser le [modèle littéral de chaîne] (https://wandbox.org/permlink/bSLW5DNEX4gNuDyw). Cela fonctionnera sur gcc et clang mais probablement sur aucun autre compilateur ... –

+0

Ce n'est probablement pas non plus une réponse acceptable, mais voici une solution qui utilise une macro de Boost.Metaparse. [Exemple] (https://godbolt.org/g/tZYzyH). – llonesmiz

Répondre

2

Ma première idée était d'activer/désactiver make() en utilisant SFINAE; quelque chose comme

template <typename... Args> 
auto make(const char* text, Args&&... args) 
    -> std::enable_if_t<sizeof...(args) == count_placeholders(text), 
         std::string> 
{ 
    // ... 
} 

Malheureusement, cela ne compile pas parce que text ne peut pas être utilisé dans un constexpr.

Mais, si vous acceptez que text est un argument de modèle (si connu au moment de la compilation), vous pouvez faire quelque chose comme

template <char const * const text, typename ... Args> 
auto makeS (Args && ... args) 
    -> std::enable_if_t<sizeof...(args) == count_plc(text), std::string> 
{ return std::to_string(sizeof...(args)); } 

Ce qui suit est un complet exemple de travail

#include <string> 
#include <type_traits> 

constexpr std::size_t count_plc (char const * s, 
           std::size_t index = 0U, 
           std::size_t count = 0U) 
{ 
    if (s[index] == '\0') 
     return count; 
    else if ( (s[index] == '%') && (s[index+1U] != '\0') 
      && (s[index+1U] > '0') && (s[index+1U] <= '9')) 
     return count_plc(s, index + 1U, count+1U); 
    else 
     return count_plc(s, index + 1U, count); 
} 

template <char const * const text, typename ... Args> 
auto makeS (Args && ... args) 
    -> std::enable_if_t<sizeof...(args) == count_plc(text), std::string> 
{ return std::to_string(sizeof...(args)); } 

constexpr char const h1[] { "Hello %1" }; 
constexpr char const h2[] { "Hello %1 %2" }; 

int main() 
{ 
    makeS<h1>("World"); // compile 
    //makeS<h2>("World"); // compilation error 
    //makeS<h1>("World", "John"); // compilation error 
} 
+0

En effet: les arguments des fonctions constexpr ne peuvent pas être utilisés comme valeurs constexpr. Deux autres possibilités (si vous ne voulez pas vous limiter à un 'const char *' comme paramètre de template non-type: (1) ['make (texte," Hello "," World ") ; '] (https://godbolt.org/g/QfjRgf) ou (2) (C++ 17?) [' make ([=] {return text;}, "Bonjour", "Monde"); ' ] (https://godbolt.org/g/DHWbpM). – Julius