c ++: генерация строковых литералов из параметров шаблона - PullRequest
6 голосов
/ 04 ноября 2010
template < unsigned int i >
struct t {
  static const char *s;
};
template < unsigned int i >
const char* t<i>::s = ...;

, где ... - это «0 1 2 ... i-1», например, «0 1 2 3 4» для i == 5.

Возможно ли это? (нет решений, делающих это во время выполнения, пожалуйста!)

  • Вопрос из любопытства (было бы легко выполнить макросы / константы препроцессора, но как насчет параметров шаблона)?
  • Значение: строковый литерал, сгенерированный во время компиляции. Теперь я вижу, что const не форсирует это, но может потребовать любую вычисляемую во время выполнения функцию для генерации строки.

Ответы [ 7 ]

6 голосов
/ 04 ноября 2010

Это технически возможно, просто очень, очень уродливо.Вот пример, который генерирует строковый литерал для беззнакового целого.Он (пока) не создает строку вида «1 2 3 ... i-1», однако я уверен, что это возможно, если вы готовы потратить усилия.

#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;
}
3 голосов
/ 04 ноября 2010

Нет, но это возможно:

template < unsigned int i >
struct t {
  static std::string s;

  static std::string ConvertIntToString()
  {
    std::stringstream ss;
    ss << i;
    return ss.str();
  }
};

template< unsigned int i >
std::string t< i >::s = t<i>::ConvertIntToStr();

Кстати, почему вы используете строки c?C ++ имеет класс std :: string, который является превосходящим.

EDIT

Я думаю, вы могли бы использовать специализацию шаблона:

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";
1 голос
/ 15 января 2011

Я бы подумал, что может быть выполнимым с помощью шаблонов с переменным числом аргументов. У меня нет компилятора для тестирования, но я представляю что-то вроде этого может работать.

template < char ... RHS,  unsigned int i> 
struct t { 
    static const char s[] = t<' ', char(i+'0'), RHS, i-1>::s;
}; 

template <char ... RHS > 
struct t<RHS, 0> { 
    static const char s[] = {'0', RHS, '\0'};
}; 

void main() {
    std::cout << t<5>::s; // {'0',' ','1',' ','2',' ','3',' ','4',' ','5','\0'}
}
1 голос
/ 04 ноября 2010

Код, который вы предоставляете, ...

template < unsigned int i >
struct t {
  static const char *s;
};
static const char* t::s = ...;

... является недействительным .t::s должен иметь внешнюю связь.Кроме того, определение должно быть шаблонным.

Исправление прямых проблем с кодом, например ...

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

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

... затем инициализация T<i>::s с любой желаемой строкойтривиальный.

Итак, по модулю ошибок в вашем коде, ответ «да, это не только возможно, это тривиально».тривиальная вещь?

1 голос
/ 04 ноября 2010

невозможно.

Поскольку расширение шаблона выполняется во время компиляции, когда компилятор может работать только с постоянным значением, которое он знает. Любая операция, связанная с выделением памяти (например, инициализация строки), в настоящее время невозможна, но только во время выполнения.

0 голосов
/ 13 мая 2018
//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 голосов
/ 04 ноября 2010

Это невозможно при использовании шаблона. Но используя stringstream, создание таких string тривиально. Вот псевдокод:

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
}

Более подробную информацию о stringstream можно найти здесь .

...