7
votes

C ++: générer des littéraux de chaîne à partir de paramètres de modèle

template < unsigned int i >
struct t {
  static const char *s;
};
template < unsigned int i >
const char* t<i>::s = ...;
where  ... is "0 1 2 ... i-1", for example "0 1 2 3 4" for i == 5.Is this possible?  (no solutions doing this at run-time, please!)
Question asked out of curiosity (doing it with preprocessor macros / constants would be easy, but how about template parameters)?
The meaning is: compile-time generated string literal. I see now that const does not force this, but could take any run-time evaluated function for string generation.

3 commentaires

Ceci n'est tout simplement pas possible. Qu'est-ce que vous essayez d'atteindre par cela?


@MOO: Curiosity, pas encore d'utilisation. Si i était une constante de préprocesseur, ce serait facile, mais avec des modèles que je ne pouvais pas penser à un moyen de résoudre ce problème.


@Reno: modèle, car il pourrait y avoir plusieurs instanciations pour divers i ; Comment pourrais-je réécrire ceci sans structure? Je ne pense pas que le modèle const char * s = ...; compilerait.


8 Réponses :


3
votes

Non, mais c'est possible:

template < unsigned int i >
struct t;

template <>
struct t<0>
{
  static const char * const s;
};
const char* const t<0>::s = "abc";

template <>
struct t<1>
{
  static const char * const s;
};
const char* const t<1>::s = "123";


5 commentaires

Une solution au moment de l'exécution est bien sûr facile à trouver, mais ce n'était pas ma question; Le point était le const .


Et bien sûr, je pourrais initialiser un std :: chaîne avec la chaîne constante, mais il doit d'abord être généré en quelque sorte!


La spécialisation des modèles ne fournirait pas de solution générale à moins qu'il ne soit récursif (mais je ne vois pas un moyen de faire la réursion, pourtant))


Si vous initialisez std :: string avec une chaîne constante, vous utilisez tous les avantages de la statique du temps de compilation. L'avantage le plus important de la THA, c'est qu'il n'y a pas de fiasco d'initialisation statique pour eux.


@Johannes Schaub - Litb Je évite les membres statiques de la classe pour cette raison, et généralement si j'ai besoin d'une telle chose, je crée une fonction statique renvoyant une référence, comme cette structure A {statique int & a () {statique int v = 0; retour v;}};



1
votes

impossible.

Parce que l'expansion du modèle est effectuée à l'heure de la compilation lorsque le compilateur ne peut que traiter de la valeur constante, elle sait. Toute opération impliquant la répartition de la mémoire (par exemple l'initialisation d'une chaîne) n'est pas possible pour le moment, mais uniquement au moment de l'exécution.


2 commentaires

Mais le compilateur connaît la valeur de modèle constante et aucune allocation de mémoire (dynamique) n'est nécessaire.


Mais il ne connaît pas la longueur de char S, qui doit être * généré en mémoire. C'est pourquoi la chaîne n'est pas autorisée en tant que paramètre de modèle.



0
votes

Ce n'est pas possible à l'aide de gabarit. Mais en utilisant stringstream code>, créer une chaîne code> est trivial. Voici le pseudo code:

string makeit(int i)
{
    stringstream sstr;

    for (int x = 0; x < i-1; x++)
        put x and ' ' in sstr;
    put i in sstr;
    return sstr contents converted to string
}


0 commentaires

1
votes

Le code que vous présentez, ...

template < unsigned int i >
struct T
{
  static const char * const s;
};

template< unsigned i >
const char* const T<i>::s = ...;


13 commentaires

Vous avez raison, le code ne doit démontrer que l'idée, mais c'était invalide (avec C ++ 0x, je pouvais directement avoir écrit statique Const * S = ...; à l'intérieur de la structure). Je corrigerai le code ci-dessus. Néanmoins, je ne vois pas comment initialiser la chaîne (et répondre à ma question)!


@THOMAS: Après le = , vous pouvez écrire n'importe quel appel de fonction. Cela répond à votre question initiale. La question supplémentaire que vous avez posée plus tard dans les commentaires, comment faire cela à l'heure de la compilation, est un peu plus délicate (si elle est possible), mais votre motivation pour ce faire est basée sur une hypothèse invalide sur const nécessitant une évaluation de l'heure de compilation. Il est donc utile de se concentrer sur le problème réel à résoudre (que vous n'avez même pas mentionné), pas les problèmes vaguement et incorrectement perçus de la solution de tentative vaguement perçue. Acclamations et hth.,


@Alf: Où vous attendez-vous à la fonction d'allouer la chaîne de résultat? En tas ou en pile?


@Alf: Ma pensée derrière la question était de générer la chaîne à l'heure de la compilation (c'est pourquoi je l'ai appelé "littéral") - mon erreur consistait à supposer que const pourrait forcer ceci.


@Vlad: Vraiment, c'est une question idiote. Premièrement, la chaîne de résultats ne peut pas être stockée "dans la pile", sauf si vous voulez un comportement indéfini. Mais en dehors de cela, il peut être stocké statiquement ou il peut être attribué de manière dynamique. Par exemple. Si la chaîne est stockée en tant que statique std :: string , cela dépend de la mise en œuvre std :: string (qui pourrait faire une petite optimisation tampon) et la taille, et peut-être la phase de la lune. Mais, comme le programmeur peut prendre en charge cela, c'est donc à vous de choisir (sauf que vous ne choisissez pas UB). Acclamations et hth.,


@Alf: Vous ne pouvez pas faire de stockage statique, car il existe potentiellement un nombre non négligé d'instanciations de modèle possibles. Par conséquent, la seule alternative est une allocation dynamique, qui sera dans le meilleur cas, vient de déclarer comme une fuite de mémoire. De plus, cette fonction ne peut être optimisée par le compilateur afin d'éviter que la nécessité d'initialiser (avec la possibilité d'échouer) au moment de l'exécution.


@Vlad: Désolé, aucun de ce que vous écrivez est significatif. C'est de la foutaise.


@Alf: Essayez simplement de faire un exemple de "tout appel de fonction" après = , vous verrez le problème vous-même. :-P Par exemple, vous pouvez essayer de mettre en œuvre ce que l'OP a réellement demandé.


@Vlad: S'il vous plaît, arrêtez de faire des commentaires non-sens. Aucun de ce que vous avez écrit jusqu'à présent n'a aucun sens que ce soit. Les choses que vous dites sont impossibles sont triviales; Le raisonnement que vous présentez est invalide; Donc, c'est des ordures.


@Alf: Si cela avait été si trivial, vous pouvez présenter un exemple. BTW, Ce lien pourrait être intéressant pour vous. Être sûr que vous n'êtes pas familier avec le concept sous le lien, je traite notre prochaine communication indésirable.


@Vlad: S'il vous plaît, arrêtez de poster des commentaires non-sens, des revendications non-sens et des défis non-sens. Je ne sais pas ce que vous avez suivi, que ce soit, que ce soit de l'âge ou de la dominance ou d'avoir d'autres faire vos devoirs pour vous, mais je ne le ferai pas. Je vous demande, suffisamment de plaisir, de cesser de sensibles et de désinformation, s'il vous plaît. Tia.,


@Alf Il peut être trivial pour les initialiser pendant la phase d'initialisation dynamique. Mais ce faisant, donc ils sont statistiques initialisés n'est guère possible, je pense. Si c'est le cas, alors vous êtes libre d'ordre d'initialisation statique fiasco, ce qui constituerait un bon objectif.


@Johannes: droite, phase d'initialisation statique est un ballon totalement différent. Le problème fondamental avec l'initialisation à la phase d'initialisation statique est de produire un tableau initialisé (quel que soit le type) avec du contenu en fonction des paramètres de modèle. Trivial pour la phase d'initialisation dynamique, mais je pense impossible dans la norme portable C ++ pour la phase d'init statique. OTOH ., Je ne pense pas qu'une solution portable à cela aiderait à l'ordre d'initialisation statique fiasco. Ce problème est lié à la phase d'initialisation dynamique. Acclamations,



6
votes

Ceci est techniquement possible, c'est tout simplement très laid. Voici un échantillon qui génère un littéral à chaîne pour un Int non signé. Il n'a pas (encore) une chaîne de formulaire "1 2 3 ... I-1", mais je suis sûr que cela est possible si vous êtes prêt à passer les efforts.

#include <iostream>
#include <string>
#include <limits>

///////////////////////////////////////////////////////////////////////////////
// exponentiation calculations
template <int accum, int base, int exp> struct POWER_CORE : POWER_CORE<accum * base, base, exp - 1>{};

template <int accum, int base>
struct POWER_CORE<accum, base, 0>
{
    enum : int { val = accum };
};

template <int base, int exp> struct POWER : POWER_CORE<1, base, exp>{};

///////////////////////////////////////////////////////////////////////////////
// # of digit calculations
template <int depth, unsigned int i> struct NUM_DIGITS_CORE : NUM_DIGITS_CORE<depth + 1, i / 10>{};

template <int depth>
struct NUM_DIGITS_CORE<depth, 0>
{
    enum : int { val = depth};
};

template <int i> struct NUM_DIGITS : NUM_DIGITS_CORE<0, i>{};

template <>
struct NUM_DIGITS<0>
{
    enum : int { val = 1 };
};

///////////////////////////////////////////////////////////////////////////////
// Convert digit to character (1 -> '1')
template <int i>
struct DIGIT_TO_CHAR
{
    enum : char{ val = i + 48 };
};

///////////////////////////////////////////////////////////////////////////////
// Find the digit at a given offset into a number of the form 0000000017
template <unsigned int i, int place> // place -> [0 .. 10]
struct DIGIT_AT
{
    enum : char{ val = (i / POWER<10, place>::val) % 10 };
};

struct NULL_CHAR
{
    enum : char{ val = '\0' };
};

///////////////////////////////////////////////////////////////////////////////
// Convert the digit at a given offset into a number of the form '0000000017' to a character
template <unsigned int i, int place> // place -> [0 .. 9]
    struct ALT_CHAR : DIGIT_TO_CHAR< DIGIT_AT<i, place>::val >{};

///////////////////////////////////////////////////////////////////////////////
// Convert the digit at a given offset into a number of the form '17' to a character

// Template description, with specialization to generate null characters for out of range offsets
template <unsigned int i, int offset, int numDigits, bool inRange>  
    struct OFFSET_CHAR_CORE_CHECKED{};
template <unsigned int i, int offset, int numDigits>                
    struct OFFSET_CHAR_CORE_CHECKED<i, offset, numDigits, false> : NULL_CHAR{};
template <unsigned int i, int offset, int numDigits>                
    struct OFFSET_CHAR_CORE_CHECKED<i, offset, numDigits, true>  : ALT_CHAR<i, (numDigits - offset) - 1 >{};

// Perform the range check and pass it on
template <unsigned int i, int offset, int numDigits>
    struct OFFSET_CHAR_CORE : OFFSET_CHAR_CORE_CHECKED<i, offset, numDigits, offset < numDigits>{};

// Calc the number of digits and pass it on
template <unsigned int i, int offset>
    struct OFFSET_CHAR : OFFSET_CHAR_CORE<i, offset, NUM_DIGITS<i>::val>{};

///////////////////////////////////////////////////////////////////////////////
// Integer to char* template. Works on unsigned ints.
template <unsigned int i>
struct IntToStr
{
    const static char str[];
};

template <unsigned int i>
const char IntToStr<i>::str[] = 
{
    OFFSET_CHAR<i, 0>::val,
    OFFSET_CHAR<i, 1>::val,
    OFFSET_CHAR<i, 2>::val,
    OFFSET_CHAR<i, 3>::val,
    OFFSET_CHAR<i, 4>::val,
    OFFSET_CHAR<i, 5>::val,
    OFFSET_CHAR<i, 6>::val,
    OFFSET_CHAR<i, 7>::val,
    OFFSET_CHAR<i, 8>::val,
    OFFSET_CHAR<i, 9>::val,
    NULL_CHAR::val
};


///////////////////////////////////////////////////////////////////////////////
// Tests
int _tmain(int argc, _TCHAR* argv[])
{
    std::wcout << IntToStr<17>::str << std::endl;
    std::wcout << IntToStr<173457>::str << std::endl;
    std::wcout << IntToStr< INT_MAX >::str << std::endl;
    std::wcout << IntToStr<0>::str << std::endl;
    std::wcout << IntToStr<1>::str << std::endl;
    std::wcout << IntToStr<-1>::str << std::endl;

    return 0;
}


2 commentaires

Le problème de l'OP appelle une matrice de taille au moins proportionnelle à la valeur de l'argument de modèle. Je pense que cela est impossible à faire en standard portable C ++ à la phase initiale statique (bien que triviale à la phase init dynamique). Mais je pourrais avoir tort ... :-) acclamations,


@Alf en C ++ 0x Il est trivial à faire pour la phase initiale statique. Dommage que nous soyons toujours en 2010!



1
votes

Je penserais que pourrait être faisable avec des modèles variadiques. Je n'ai pas de compilateur à tester avec, mais j'imagine que quelque chose sur les lignes de ce pourrait travail. XXX


0 commentaires

0
votes
//using lambda
#include <sstream>
template<size_t i372> struct T369 {
    static std::string s;
};
template<size_t i372> std::string T369<i372>::s = [](){std::stringstream ss; 
for (int j = 0; j < i372; j++) { ss << "," << j; }; return ss.str(); }();

0 commentaires

0
votes

avec moderne C ++ Ceci est maintenant possible.

Je pense que cela peut être fait avec C ++ 17, mais cette solution utilise certaines caractéristiques C ++ 20: P>

#include <iostream>
#include <concepts>

template <char... Cs>
struct char_pack {
    using self = char_pack<Cs...>;

    static constexpr size_t size = sizeof...(Cs);

   private:
    // This allows us to use ::concat on types that inherit from char_pack<...>,
    // such as int_to_char_pack.
    // We need this because Cs (below) can't be deduced from a derived type.
    //
    // Ex:
    // char_pack<'a', 'b', 'c'>::concat<int_to_char_pack<123>>
    template <typename Other>
    struct concat_impl : concat_impl<typename Other::self> {};

    template <char... OtherCs>
    struct concat_impl<char_pack<OtherCs...>> : char_pack<Cs..., OtherCs...> {};

   public:
    // Using a type alias means we don't have to write ::self in
    // certain places that we otherwise would have needed to due
    // to some quirks in the template evaluation system.
    template <typename Other>
    using concat = concat_impl<Other>;

    template <char... OtherCs>
    using append = char_pack<Cs..., OtherCs...>;

    static constexpr const char to_string[size + 1] = {Cs..., '\0'};
};

template <auto I>
struct int_to_char_pack : char_pack<> {};

template <std::integral IT, IT I>
requires(I >= 10)
struct int_to_char_pack<I> : int_to_char_pack<I / 10>::append<'0' + (I % 10)> {};

template <std::integral IT, IT I>
requires(I < 10 && I >= 0)
struct int_to_char_pack<I> : char_pack<'0' + (I % 10)> {};

template <std::integral IT, IT I>
requires(I < 0)
struct int_to_char_pack<I> : char_pack<'-'>::concat<int_to_char_pack<-I>> {};

template <int I>
struct num_list : num_list<I - 1>::append<' '>::concat<int_to_char_pack<I>> {};

template <>
struct num_list<0> : char_pack<'0'> {};

int main() {
    std::cout << num_list<10>::to_string;
}


0 commentaires