Is it possible to match strings to strings at compile time?

I'm probably trying to achieve the impossible, but StackExchange always surprises me, so please go through:

I need to match the name with an integer. Names (about 2k) are unique. There will be no additions or exceptions to this list, and the values ​​will not change at run time.

Implementing them as const int variables gives me a compile-time check for existence and type. It is also very clear and verbose in code. Errors are easily detected.

Implementing them as std::map<std::string, int> gives me great flexibility to create names for searches using string manipulations. I can use this to provide strings as parameters for functions that can query a list for multiple values ​​by adding prefix / suffixes to this string. I can also iterate over multiple values ​​by creating the digital part of the key name from a loop variable.

Now my question is: is there a way to combine both benefits? Missing compile-time checking (especially for key existence) almost kills the second method for me. (Moreover, std::map silently returns 0 if the key does not exist, which makes it difficult to find errors.) But the possibilities of adding a loop and a preliminary / suffix are so useful.

I would prefer a solution that does not use any additional libraries, such as boost, but please offer them all the same, since I could reimplement them.

An example of what I'm doing with the map:

 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()]); } 

Another goal is that the code shown in the main() procedure remains as simple and detailed as possible. (Non-programmers need to understand.) The init() function will be generated by some tools code. The doSomething(int) functions doSomething(int) fixed, but I can write wrapping functions around them. Helpers like checkName() can be more complex, but they need to be easily debugged.

+6
source share
4 answers

One way to implement your example is to use the enum and token insert, e.g.

 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) ); } 

Regardless of whether this is best, depends on where the names come from.

If you need to maintain a use case for a for loop, consider

 int bob[] = { 0, Bob1, Bob2, Bob3 }; // Values from the enum int main() { for( int i = 1; i <= 3; i++ ) { doSomething( bob[i] ); } } 
+1
source

I'm not sure I understand all your requirements, but what about something like this without using std::map . I assume that you have three lines: "FIRST", "SECOND" and "THIRD", which you want to display 42, 17 and 37 respectively.

 #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; } 

The downside is that you cannot use string variables with LOOKUP . But now you can iterate over the values.

+1
source

Maybe something like this (untested)?

 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>(); } 
+1
source

Using enum, you have both compile-time checking, and you can iterate over it:

How can I iterate over an enumeration?

0
source

Source: https://habr.com/ru/post/952923/


All Articles