Шаблонное метапрограммирование с массивами символов в качестве параметров - PullRequest
12 голосов
/ 03 апреля 2009

Я играю с TMP в полу-реализации GCC 4.3.2 на C ++ 11, и мне было интересно, есть ли способ как-то сделать следующее:

template <char x, char... c>
struct mystruct {
...
};

int main () {

   mystruct<"asdf">::go();

}

Это, очевидно, не позволит мне сделать это просто так, и я подумал, что мне повезет, если использовать пользовательские литералы для преобразования строки «asdf» во время компиляции, но GCC 4.3 не поддерживает пользователя определяемые литералы ...

Есть предложения? Я бы предпочел не делать «а», «s», «d», «f», так как это сильно мешает моим планам на этот проект.

Ответы [ 9 ]

10 голосов
/ 03 апреля 2009

Я решил проблему, подобную этой. Нам нужен был другой тип для каждого имени

template< const char* the_name >
class A
{
    public:
    const char* name( void )
    {
        return the_name;
    }
};

extern const char g_unique_name[]; // defined elsewhere
typedef A<g_unique_name> A_unique;

Это даст вам во время компиляции доступ к имени и уникальному экземпляру. Однако он не позволит вам получить доступ к отдельным персонажам во время выполнения.

Если вы хотите получить доступ к индивидуальному персонажу, единственный способ добиться этого - использовать пользовательский литерал. C ++ 0x будет расширен, чтобы разрешить синтаксис в вашей главной функции выше, но он все равно будет привязывать шаблон к символьному указателю, а не к массиву символов времени компиляции.

9 голосов
/ 03 апреля 2009

К сожалению, вам все равно придется разделить его на отдельные символы, например:

myTemplate<'s','t','r','i','n','g'>

По моему скромному мнению, это огромный упущение в новом стандарте. Некоторые другие согласились и попытались реализовать поведение в GCC с довольно хорошими результатами. Вы можете найти эту тему здесь .

Изменить: Некоторые странные проблемы со ссылкой, поэтому вырезать и вставить из этого:

http://groups.google.com/group/comp.lang.c++.moderated/browse_thread/thread/9b0edd169ba2ce3c

3 голосов
/ 22 октября 2009

" string " недавно был добавлен в Boost.MPL, что позволяет написать:

typedef mpl::string<'asdf'> asdf;
typedef mpl::push_back<asdf, mpl::char_<'!'> >::type asdf_bang;

BOOST_ASSERT(0 == std::strcmp(mpl::c_str<asdf_bang>::value, "asdf!"));

Обратите внимание, что приведенный выше пример немного надуман, так как «строки», состоящие из более чем 4 символов, должны быть разделены. Например:

typedef mpl::string<'hell','o wo','rld'> hello;
3 голосов
/ 20 апреля 2009

Попробуйте это:

extern const char SOMESTRING[] = "stringhere"; //extern linkage required!

template<const char * const STR>
struct MyStruct
{
  static std::string doThis() { return STR; }
};



MyStruct<SOMESTRING>   testObj; //ok!

Chris

2 голосов
/ 17 сентября 2012

В C ++ 11 нет способа хранить временную строку где-либо во время компиляции. Поэтому я могу предложить вам такой подход: (Это быстро сделанный набросок, но хорошо описательный)

#include <stdio.h>

template <char...>
struct StringTuple;

template <char TargHead>
struct StringTuple<TargHead> {
    static constexpr char kSymbol = TargHead;

    static void print() {
        printf(kSymbol ? "%c\n" : "\n", kSymbol);
    }
};

template <char TargHead, char... TargTail>
struct StringTuple<TargHead, TargTail...> {
    using Next = StringTuple<TargTail...>;
    static constexpr char kSymbol = TargHead;

    static void print() {
        if (kSymbol) {
            printf("%c", kSymbol);
            Next::print();
        } else {
            printf("\n");
        }
    }
};

constexpr int length(char *string) {
    return (string[0] == 0) ? 1 : (length(string + 1) + 1);
}

constexpr char get(char *string, int i) {
    return i < length(string) ? string[i] : 0;
}

#define ST(string) \
    StringTuple< \
    get(string, 0), \
    get(string, 1), \
    get(string, 2), \
    get(string, 3), \
    get(string, 4), \
    get(string, 5), \
    get(string, 6), \
    get(string, 7), \
    get(string, 8), \
    get(string, 9), \
    get(string, 10), \
    get(string, 11), \
    get(string, 12), \
    get(string, 13), \
    get(string, 14), \
    get(string, 15), \
    get(string, 16), \
    get(string, 17), \
    get(string, 18), \
    get(string, 19), \
    get(string, 20), \
    get(string, 21), \
    get(string, 22), \
    get(string, 23), \
    get(string, 24), \
    get(string, 25), \
    get(string, 26), \
    get(string, 27), \
    get(string, 28), \
    get(string, 29), \
    get(string, 30), \
    get(string, 31), \
    get(string, 32), \
    get(string, 33), \
    get(string, 34), \
    get(string, 35), \
    get(string, 36), \
    get(string, 37), \
    get(string, 38), \
    get(string, 39), \
    get(string, 40), \
    get(string, 41), \
    get(string, 42) \
    >

int main() {
    ST("Hello, compile-time world!")::print();
}

Код Bash для генерации части макроса:

for i in `seq 0 42`; do echo "    get(string, $i), \\"; done

Вы должны передать большое число (1000 или более) этому генератору для поддержки всех ваших строк, и вы должны сделать статическое утверждение, если строка превышает этот предел.


Я использую такие сгенерированные макросы в своих научных проектах. Я знаю, это кажется грязным, но это работает. Пример использования сгенерированного макроса:

#define PRINT(a) print(a);
FOREACH_MACRO(PRINT, a, b, c) // print(a);print(b);print(c);

Я постараюсь найти более симпатичное решение, но сначала я воспользуюсь этим.

0 голосов
/ 17 сентября 2012

Мотти не прав.

К сожалению, текущий стандарт (C ++ 11) не поддерживает литеральный оператор шаблона переменной для строковых параметров, он возможен только для чисел.

template <char... Args>
operator ""_op();

...

1212_op; // legal, calls operator ""_op<'1','2','1','2'>;
"1212"_op; // illegal

Я не понял цели этого ограничения.

0 голосов
/ 03 апреля 2009

Цитата из нового стандартного проекта:

14.3.2 Шаблонные нетиповые аргументы [temp.arg.nontype]

2 Примечание: строковый литерал (2.13.4) не удовлетворяет требованиям любая из этих категорий и, следовательно, не приемлемый шаблон-аргумент.

Пример:

template<class T, char* p>
class X 
{ 
X(); 
X(const char* q) { /... / } 
}; 

X<int, "Studebaker"> x1; // error: string literal as template-argument char p[] = "Vivisectionist";
X<int,p> x2; // OK

Попробуйте, но я не уверен, потому что http://gcc.gnu.org/gcc-4.3/cxx0x_status.html ничего не говорит об этой функции.

0 голосов
/ 03 апреля 2009

Вы не можете этого сделать. С 14.3.2 в стандарте:

Аргумент шаблона для нетипичного, не шаблонного параметра шаблона должен быть одним из:

  • целочисленная константа-выражение целого или перечислимого типа; или
  • имя нетипового шаблона-параметра; или
  • адрес объекта или функции с внешней связью, включая шаблоны функций и идентификаторы шаблонов функций но исключая нестатические члены класса, выраженные как & id-выражение, где символ & является необязательным, если имя относится к
  • функция или массив, или если соответствующий шаблон-параметр является ссылкой; или
  • константное выражение, которое оценивается как нулевое значение указателя (4.10); или
  • константное выражение, которое оценивается как нулевое значение указателя на член (4.11); или
  • указатель на член, выраженный как описано в 5.3.1.
  • [Примечание: строковый литерал (2.13.4) не удовлетворяет требованиям какой-либо из этих категорий и, следовательно, не является приемлемым аргументом шаблона
0 голосов
/ 03 апреля 2009

Я не уверен, чего вы хотите достичь, но когда вы передаете «asdf» шаблону, его тип - это char *, а значение - адрес строки. Таким образом, простой подход, подобный изложенному, потерпит неудачу. Трудно что-либо порекомендовать, не зная, какую проблему вы пытаетесь решить.

...