2013-09-01 2 views
6

Я, вероятно, пытаюсь достичь невозможного, но StackExchange всегда меня удивляет, поэтому, пожалуйста, пройдите по этому адресу:Возможно ли сопоставление времени между строками во время компиляции?

Мне нужно нанести имя имени на целое число. Имена (около 2k) уникальны. В этот список не будет никаких добавлений и исключений, и значения во время выполнения не изменятся.

Реализуя их как переменные const int, дает мне проверку времени на компиляцию для существования и типа. Также это очень ясный и подробный код. Ошибки легко обнаруживаются.

Реализация их как std::map<std::string, int> дает мне большую гибкость при построении имен для поиска с помощью строковых манипуляций. Я могу использовать это, чтобы предоставить строки в качестве параметров для функций, которые могут запрашивать список для нескольких значений, добавляя префикс/суффиксы к этой строке. Я также могу перебрать несколько значений, создав цифровую часть имени ключа из переменной цикла.

Теперь мой вопрос: существует ли способ сочетания обоих преимуществ? Отсутствующая проверка времени компиляции (особенно для ключевого существования) почти убивает второй метод для меня. (Особенно, поскольку std::map молча возвращает 0, если ключ не существует, что затрудняет поиск ошибок.) Но возможности добавления циклов и префикса/суффикса настолько полезны.

Я бы предпочел решение, которое не использует никаких дополнительных библиотек, таких как boost, но, пожалуйста, предложите им, тем не менее, так как я мог бы их повторно реализовать.

Пример того, что я делаю с картой:

void init(std::map<std::string, int> &labels) 
{   
    labels.insert(std::make_pair("Bob1" , 45)); 
    labels.insert(std::make_pair("Bob2" , 8758)); 
    labels.insert(std::make_pair("Bob3" , 436)); 
    labels.insert(std::make_pair("Alice_first" , 9224)); 
    labels.insert(std::make_pair("Alice_last" , 3510)); 
} 

int main() 
{  
    std::map<std::string, int> labels; 
    init(labels); 

    for (int i=1; i<=3; i++) 
    { 
    std::stringstream key; 
    key << "Bob" << i; 
    doSomething(labels[key.str()]); 
    } 

    checkName("Alice"); 
} 

void checkName(std::string name) 
{ 
    std::stringstream key1,key2; 
    key1 << name << "_first"; 
    key2 << name << "_last"; 
    doFirstToLast(labels[key1.str()], labels[key2.str()]); 
} 

Другая цель состоит в том, что код, показанный в main() рутинных пребывания, как легко и многословен, насколько это возможно. (Требуется понять не программистов.) Функция init() будет сгенерирована кодом некоторыми инструментами. Функции doSomething(int) фиксированы, но я могу писать вокруг них обертки. Помощники вроде checkName() могут быть более сложными, но их нужно легко отлаживать.

+1

Это звучит, как вы хотите, чтобы построить строки во время выполнения, но как-то это проверяется во время компиляции? –

+0

Есть способы конвертировать перечисления в соответствующие строки, поэтому это должно быть возможно. Хотя трудно понять, как это время компиляции, если вы создаете строки во время выполнения –

+0

'std :: map', безусловно, имеет возможность сказать вы вставили ли элемент. Один из способов - «вставить». – chris

ответ

1

Один из способов реализовать пример использует перечисление и маркер вставки, как этот

enum { 
    Bob1 = 45, 
    Bob2 = 8758, 
    Bob3 = 436, 
    Alice_first = 9224, 
    Alice_last = 3510 
}; 

#define LABEL(a, b) (a ## b) 

int main() 
{  

    doSomething(LABEL(Bob,1)); 
    doSomething(LABEL(Bob,2)); 
    doSomething(LABEL(Bob,3)); 
} 


void checkName() 
{ 
    doFirstToLast(LABEL(Alice,_first), LABEL(Alice,_last)); 
} 

ли не лучше всего это зависит от того, где имена берутся.

Если вам необходимо поддерживать для контура потребительной случае, а затем рассмотреть

int bob[] = { 0, Bob1, Bob2, Bob3 }; // Values from the enum 

int main() 
{  
    for(int i = 1; i <= 3; i++) { 
    doSomething(bob[i]); 
    } 
} 
+0

Даже при использовании препроцессора (который поставляется с собственными проблемами при отладке) это решает проблему довольно красиво и дает довольно многословный и понятный код. Мне нужно будет проверить внутри реального кода, если он помечает все поля, но мне это уже нравится. –

+0

Невозможно сделать for-loops, хотя это все равно будет доступно только во время выполнения. Или существует не злая трюка для циклов с препроцессором? –

1

Я не уверен, что понимаю все ваши требования, но как насчет чего-то подобного, без использования std::map. Я предполагаю, что у вас есть три строки: «FIRST», «SECOND» и «THIRD», которые вы хотите нанести на 42, 17 и 37 соответственно.

#include <stdio.h> 

const int m_FIRST = 0; 
const int m_SECOND = 1; 
const int m_THIRD = 2; 

const int map[] = {42, 17, 37}; 

#define LOOKUP(s) (map[m_ ## s]) 

int main() 
{ 
    printf("%d\n", LOOKUP(FIRST)); 
    printf("%d\n", LOOKUP(SECOND)); 
    return 0; 
} 

Недостаток заключается в том, что вы не можете использовать переменные строки с LOOKUP. Но теперь вы можете перебирать значения.

+0

Интересная идея. Ограничивает цикл до последовательных значений, но это на самом деле вполне приемлемо. К сожалению, его также трудно отладить (попробуйте найти 1276-е значение в массиве). –

0

Использование перечисления вы как во время компиляции проверки, и вы можете петлю над ним:

How can I iterate over an enum?

+0

Ссылка говорит, что я не могу перебрать ее, если я использую произвольные числа для ENUM, что будет иметь место, если я использую перечисление для хранения карты имени-в-int. –

1

Может быть что-то вроде этого (непроверенные)?

struct Bob { 
    static constexpr int values[3] = { 45, 8758, 436 }; 
}; 

struct Alice { 
    struct first { 
     static const int value = 9224; 
    }; 
    struct last { 
     static const int value = 3510; 
    }; 
}; 

template <typename NAME> 
void checkName() 
{ 
    doFirstToLast(NAME::first::value, NAME::last::value); 
} 

...

constexpr int Bob::values[3]; // need a definition in exactly one TU 

int main() 
{ 
    for (int i=1; i<=3; i++) 
    { 
     doSomething(Bob::values[i]); 
    } 

    checkName<Alice>(); 
} 
+0

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