Compile time resolution of a string keyed hash - c++

I am writing a class for generating bitmask from a table of predefined element strings:
const std::unordered_map<std::string, int> flagMap
{ { "bananas", 0x1 }, { "apples", 0x2 }, { "oranges", 0x4 }, { "pears", 0x8 }};
int fruitMask(std::string & fruitName)
{
if(flagMap.count(fruitName) > 0)
return flagMap.at(fruitName);
else
return 0;
}
int fruitMask(const char * fruitName)
{
if(flagMap.count(fruitName) > 0)
return flagMap.at(fruitName);
else
return 0;
}
int fruitMask(std::vector<std::string> fruitNames)
{
int result = 0;
for(auto it=fruitNames.begin(); it!=fruitNames.end(); ++it)
{
if(flagMap.count(*it) > 0)
result = result | flagMap.at(*it);
}
return result;
}
int fruitMask(std::initializer_list<const char*> fruitNames)
{
int result = 0;
for(auto it=fruitNames.begin(); it!=fruitNames.end(); ++it)
{
if(flagMap.count(*it) > 0)
result = result | flagMap.at(*it);
}
return result;
}
When the code using these functions call the const char* or the std::initializer_list<const char*> versions of fruitMask, is there any way to make it work at compile time?
For instance:
constexpr int mask = flagMask({"oranges", "bananas"});
This will not compile because flagMask() is not constexpr, is there any way to make this work? This would require a constexpr unordered_map, I do not even know if this is possible.

Compile time strings were discussed here
Maybe not an answer per se but (hopefully) a helpful hint.
Its not only about unordered map in your case but about your keys, const char* does not really mean compile-time string in general case, would you consider changing key type? Lets consider enum-ed keys (and very not optimal quadratic search, sorry):
#include <utility>
enum class keys : char
{
foo,
bar,
baz,
lol
};
static constexpr std::pair<keys, int> flagMap[] = {
{keys::foo, 42},
{keys::bar, 24},
{keys::baz, 100500},
{keys::lol, 15234}
};
static constexpr int sum(std::initializer_list<keys> target)
{
int res{0};
for (auto key: target) {
for (int i = 0; i < 4; ++i)
{
res += (flagMap[i].first == key) ? flagMap[i].second : 0;
}
}
return res;
}
int main()
{
return sum({keys::foo, keys::baz});
}
Demo yields just
mov eax, 100542
ret
at -O1 and up

Related

Is there any workaround for MSVC producing `Internal compiler error` for this code?

For some reason the below code gives me fatal error C1001: Internal compiler error. with MSVC 19.27, but not with Clang. Any idea how to write it so that the static_assert can be done also on MSVC?
template <typename T, int N, typename K, int M>
constexpr int countIdentifersNotInArray(const T(&identifiers)[N], const K(&array)[M]) {
auto find = [&array](const unsigned char value) {
for (const auto& a : array) {
if (a == value) {
return true;
}
}
return false;
};
int count = 0;
for (const auto& value : identifiers) {
if (!find(value)) {
++count;
}
}
return count;
}
constexpr bool testIt() {
return countIdentifersNotInArray({ 0x01, 0x02 }, { 0x01 });
}
int main() {
static_assert(testIt());
return 0;
}
I would like to use this in a an environment where stl is not available, so solutions without that are most interesting.
As the comment has pointed out, this is an MSVC bug and you should definitely report to Microsoft.
By removing multiple lines until it stops crashing the compiler, I believe the cause is the range-for loops. So, since they're arrays with known size, you can workaround with the classic indexed loops:
template <typename T, int N, typename K, int M>
constexpr int countIdentifersNotInArray(const T(&identifiers)[N], const K(&array)[M]) {
auto find = [&array](const auto value) {
for (int i = 0; i < M; i++) {
if (array[i] == value) {
return true;
}
}
return false;
};
int count = 0;
for (int i = 0; i < N; i++) {
if (!find(identifiers[i])) {
++count;
}
}
return count;
}
It works on MSVC.

A safer way to reference a private member variable in public member function call? [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 4 years ago.
Improve this question
I want to use a single function to interact with multiple private member variables. I've come up with:
class Some_Vectors {
public:
int is_value_in_vector(string vector_name, int value);
void append_value_to_vector(string vector_name, int value)
private:
vector<int> A;
vector<int> B;
};
// returns value's index if it's there, -1 if not
int Some_Vectors::is_value_in_vector(string vector_name, int value) {
vector<int> *selected_vector;
if (vector_name == "A") {selected_vector = &A;}
else if (vector_name == "B") {selected_vector = &B;}
for (int i = 0; i < selected_vector->size(); i++){
if (selected_vector[0][i] == value){
return i;
}
}
return -1;
}
It works, but I feels unsafe/brittle to compare strings like that. Is there a way to specifically reference a private variable in the function call?
Edited to be a (hopefully) less subjective ask. I ended up using RichardCritten's suggestion of multiple public functions that call a single private function.
You can use the unordered_map to achieve your requirements as below.
Declare the unordered_map as below.
unordered_map<string, vector<int>> umap;
Insert the values to map by using [] operator.
umap["A"] = {10,20};
umap["B"] = {30,40};
Search the key value in the unordered_map as below using find function.
string vector_name = "A";
vector_name = "A";
auto it = umap.find(vector_name);
if (it == umap.end())
return -1;
Once you find the key,value pair in the map search the particular int in the vector as below.
std::vector<int>::iterator iter = std::find(it->second.begin(), it->second.end(), 20);
if iter is not pointing the vector end then return the exact position of the int in the vector as below.
if ( iter != it->second.end())
return std::distance(it->second.begin(),iter);
else
return -1;
Your complete sample code may look like below.
int main()
{
unordered_map<string, vector<int>> umap;
// inserting values by using [] operator
umap["A"] = {10,20};
umap["B"] = {30,40};
string vector_name = "A";
vector_name = "A";
auto it = umap.find(vector_name);
if (it == umap.end())
return -1;
std::vector<int>::iterator iter = std::find(it->second.begin(), it->second.end(), 20);
if ( iter != it->second.end())
return std::distance(it->second.begin(),iter);
else
return -1;
}
I have to disagree with the other answers that suggest maps or any kind of solutions involving strings.
Using strings to identify things in code is very fragile. Some major disadvantages are: no autocomplete, no compile-time checks. There are situations where you don't have a better alternative (e.g. you don't know the identifiers at compile time), but this is not one of them.
One solution is to give meaningful names to the functions. Since you provided a toy example I will use A and B but in real life they should be meaningful names:
class X
{
public:
auto foo_a(int value) { return foo(A, value); }
auto foo_b(int value) { return foo(B, value); }
private:
int foo(std::vector<int>& v, int value) { return 24; }
std::vector<int> A;
std::vector<int> B;
};
If you want one function with a parameter to select the vector, you should select the vector with an enum. This way you have autocomplete and compile-time safety (you can't pass an invalid selector - like you could with a string - unless you bend backwards):
class Y
{
public:
enum class Selector { A, B };
auto foo(Selector selector, int value) { return foo(getVector(selector), value); }
private:
std::vector<int>& getVector(Selector selector)
{
switch (selector)
{
case Selector::A:
return A;
case Selector::B:
return B;
}
}
int foo(std::vector<int>& v, int value) { return 24; }
std::vector<int> A;
std::vector<int> B;
};
Y y{};
y.foo(Y::Selector::A, 11);
y.foo(Y::Selector::B, 1024);
First of all, if you have access to C++17 or later versions of compilers the most modern and preferable way of optional return would be using std::optional.
Regarding your question, as #Dai mentioned in the comments, the best way would be(IMHO also) to use
std::unordered_map<std::string, std::vector</*type*/>>
as the member variable and you can do as follows. See Live here
#include <vector>
#include <string>
#include <unordered_map>
#include <iostream>
using uMapType = std::unordered_map<std::string, std::vector<int>>;
class MyClass
{
private:
uMapType _vecMap;
public:
explicit MyClass(const uMapType& vecMap): _vecMap(std::move(vecMap)) {}
int getValue(const std::string &vector_name, const int value)const
{
// std::unordered_map::find the key(vector name)
auto getVec = _vecMap.find(vector_name);
if(getVec != _vecMap.cend()) // if iterator not pointing to map's end
{
const std::vector<int> &selected_vector = getVec->second;
for (std::size_t i = 0; i < selected_vector.size(); ++i)
if (selected_vector[i] == value)
return i;
}
return -1;
}
};
int main()
{
MyClass obj(
{
{"A", {1, 2, 3, 4, 5}},
{"B", {1, 2, 3, 4, 5}}
});
std::cout << obj.getValue("A", 3) << std::endl; // output: 2
std::cout << obj.getValue("B", 5) << std::endl; // output: 5
std::cout << obj.getValue("C", 3) << std::endl; // output: -1
std::cout << obj.getValue("A", 0) << std::endl; // output: -1
return 0;
}
The std::optional sample solution will look like this.
#include <optional>
using uMapType = std::unordered_map<std::string, std::vector<int>>;
class MyClass
{
private:
uMapType _vecMap;
public:
explicit MyClass(const uMapType& vecMap): _vecMap(std::move(vecMap)) {}
std::optional<int> getValue(const std::string &vector_name, const int value)const
{
if(auto getVec = _vecMap.find(vector_name); getVec != _vecMap.cend())
{
for (std::size_t i = 0; i < getVec->second.size(); ++i)
if (getVec->second[i] == value)
return i;
}
return std::nullopt;
}
};

How do I move items from a boost::variant to a multimap?

I'd like to improve the performance of PickPotatoes in the below code by using move instead of copy, but I can't figure out how to do that with insert and a boost::variant. In my actual use case, parsing the data takes about 75% of the time, and the real version of PickPotatoes takes about 25%, due to some slow copies. By improving PickPotatoes I should be able to get that down. Is it possible to move something out of a boost::variant and improve PickPotatoes?
#include <map>
#include "boost/variant.hpp"
#include <string>
#include <vector>
#include <functional>
struct tuber
{
int z;
std::vector<double> r;
};
int getZ(const tuber& t)
{
return t.z;
}
boost::variant<std::string, tuber> GrowPotato()
{
int z = std::rand() / (RAND_MAX / 10);
if (z < 2)
{
return "BAD POTATO";
}
else
{
tuber ret;
ret.z = z;
ret.r.resize(10000);
for (int i = 0;i < 10000;++i)
{
ret.r[i] = std::rand() / (RAND_MAX / 50);
}
return ret;
}
}
std::vector<boost::variant<std::string,tuber>> GrowPotatoes(int n)
{
std::vector<boost::variant<std::string, tuber>> ret;
ret.resize(n);
for (int i = 0; i < n; ++i)
{
ret[i] = GrowPotato();
}
return ret;
}
//could make this more efficient.
std::pair<std::vector<std::string>,std::multimap<int, tuber>> PickPotatoes(std::vector <boost::variant<std::string, tuber>> result)
{
std::pair<std::vector<std::string>,std::multimap<int,tuber>> ret;
int numTypTwo = 0;
for (const auto& item : result)
{
numTypTwo += item.which();
}
ret.first.resize(result.size() - numTypTwo);
int fstSpot = 0;
for (int i = 0; i < result.size();++i)
{
if (result[i].which())
{
ret.second.insert(std::pair<int, tuber>(getZ(boost::get<tuber>(result[i])), boost::get<tuber>(result[i])));
}
else
{
ret.first[fstSpot++] = std::move(boost::get<std::string>(result[i]));
}
}
return ret;
}
int main()
{
std::srand(0);
std::vector<boost::variant<std::string, tuber>> q= GrowPotatoes(5000);
std::pair<std::vector<std::string>, std::multimap<int, tuber>> z = PickPotatoes(q);
return 0;
}
The simplest win would be to move the parameter value:
std::pair<std::vector<std::string>, std::multimap<int, tuber>> z = PickPotatoes(std::move(q));
Indeed, it wins 14% of performance, roughly on my benchmarks. The rest heavily depends on what it all means, how it's going to be used.
Focus on reducing allocations (use a non-nodebased container if you can, e.g. boost::flat_multimap, sort explicitly, use string_view, parse into the desired datastructure instead of intermediate).
BONUS
I was able to shave off about 30% using:
std::pair<std::vector<std::string>, std::multimap<int, tuber> >
PickPotatoes(std::vector<boost::variant<std::string, tuber> >&& result) {
std::pair<std::vector<std::string>, std::multimap<int, tuber> > ret;
ret.first.reserve(result.size());
struct Vis {
using result_type = void;
void operator()(std::string& s) const {
first.emplace_back(std::move(s));
}
void operator()(tuber& tbr) const {
second.emplace(tbr.z, std::move(tbr));
}
std::vector<std::string>& first;
std::multimap<int, tuber>& second;
} visitor { ret.first, ret.second };
for (auto& element : result) {
boost::apply_visitor(visitor, element);
}
return ret;
}
Using emplace, avoiding repeated get<>, avoiding the loop to get the first size etc.

How to make this matching algorithm run faster?

I have two lists of pointers to a data structure X, the algorithm is very simple:
It loops over the first list A and try to find the the first matching element in list B. The requirement is to have at least 50k elements in each list:
#include <iostream>
#include <memory>
#include <chrono>
#include <vector>
#include <algorithm>
#include <string>
struct X {
std::string field_1;
std::string field_2;
std::string field_3;
std::string field_4;
X(std::string f1, std::string f2, std::string f3, std::string f4)
: field_1(f1)
, field_2(f2)
, field_3(f3)
, field_4(f4)
{};
bool equal(const std::shared_ptr<X>& x) {
return (x->field_1 == field_1) &&
(x->field_2 == field_2) &&
(x->field_3 == field_3) &&
(x->field_4 == field_4);
};
X *match = nullptr;
};
typedef std::shared_ptr<X> X_ptr;
class Timer
{
public:
Timer(std::string name) : beg_(clock_::now()), name_(name) {}
~Timer() {
std::cout << "Elapsed(" << name_ << "): " << elapsed() << std::endl;
}
void reset() { beg_ = clock_::now(); }
double elapsed() const {
return std::chrono::duration_cast<second_>
(clock_::now() - beg_).count();
}
private:
typedef std::chrono::high_resolution_clock clock_;
typedef std::chrono::duration<double, std::ratio<1> > second_;
std::chrono::time_point<clock_> beg_;
std::string name_;
};
std::string random_string(size_t length)
{
auto randchar = []() -> char
{
const char charset[] =
"0123456789"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const size_t max_index = (sizeof(charset) - 1);
return charset[rand() % max_index];
};
std::string str(length, 0);
std::generate_n(str.begin(), length, randchar);
return str;
}
int main()
{
Timer t("main");
std::vector <X_ptr> list_A;
std::vector <X_ptr> list_B;
const int MAX_ELEM = 50000;
list_A.reserve(MAX_ELEM);
list_B.reserve(MAX_ELEM);
{
Timer t("insert");
for (int i = 0; i < MAX_ELEM; i++) {
list_A.push_back(X_ptr(new X{ random_string(2), random_string(2), random_string(2), random_string(2) }));
list_B.push_back(X_ptr(new X{ random_string(2), random_string(2), random_string(2), random_string(2) }));
}
}
{
Timer t("match");
std::for_each(list_A.begin(), list_A.end(), [list_B](X_ptr& a) {
auto found_b = std::find_if(list_B.begin(), list_B.end(), [a](const X_ptr& b) {
return a->equal(b);
});
if (found_b != list_B.end()) {
a->match = found_b->get();
std::cout << "match OK \n";
}
});
}
}
on my machine the program is running extremly slow:
Elapsed(insert): 0.05566
Elapsed(match): 98.3739
Elapsed(main): 98.452
Would appreciate it if you can think of any other way to optimize it to run faster.
You are using vectors so each lookup into list_B takes O(n), where n is the number of elements in B. This means the total algorithm is O(m*n), if m is the number of elements in list_A. Thus if m and n a similar in size, you have a O(n^2) algorithm. That is too slow for any large n. To fix this, convert list_B into a unordered_map, (you can do this as part of this algorithm as the conversion is O(n)) where an element in the map's key is an element from list B and the value anything, say 0. You can then perform lookups into the map in O(1) time using find() on the map. Thus your algorithm becomes O(n), way better that O(n^2).
For example
std::unordered_map< X_ptr, int > value_map;
Time r t("match");
std::for_each(list_B.begin(), list_B.end(), [&](X_ptr& b) {
value_map[b] = 0;
});
std::for_each(list_A.begin(), list_A.end(), [value_map](X_ptr& a) {
auto found_b = value_map.find( a );
if ( found_b != value_map.end() )
{
a->match = found_b->first.get();
std::cout << "match OK \n";
}
});
}
Your Version:
Elapsed(insert): 0.0758608
Elapsed(match): 182.899
Elapsed(main): 182.991
New Version:
Elapsed(insert): 0.0719907
Elapsed(match): 0.0388562
Elapsed(main): 0.130884
You may use something like the following:
std::sort(list_B.begin(), list_B.end(), deref_less<X>);
{
Timer t("match");
for (const auto& a : list_A) {
auto it = std::lower_bound(list_B.begin(), list_B.end(), a, deref_less<X>);
if (it != list_B.end() && **it == *a) {
a->match = it->get();
std::cout << "match OK \n";
}
}
}
Live example.

Using template to generate a static lookup table

I have:
const char kLetters[] = "QWERTYUIOPASDFGHJKLZXCVBNM";
I can call kLetters[n] to obtain the nth letter of the Keyboard alphabet in O(1) time. However I will have to iterate through kLetter (taking O(n) or at least O(log n) ) time for the reverse lookup.
I would like to create a reverse lookup table as a compile-time static lookup table using templates and was wondering if there is a ways of doing this.
EDIT - as mentioned in the comments, a reverse lookup would mean I supply 'E' and get back 2. Also my alphabet example was not the best example, I would like to make no assumptions about the order. For that reason I have change the alphabet to keyboard order.
How about something like this? It lets you specify the range rather than a complete string.
#include <iostream>
template <int Start, int End, int N>
struct lookup {
static_assert(Start != End, "Can't have 0 length lookup table");
enum { value = lookup<Start+(Start < End ? 1:-1),End,N-1>::value };
};
template <int Start, int End>
struct lookup<Start,End,0> {
enum { value = Start };
};
template <int Start, int End, int V, int P=0>
struct reverse_lookup {
static_assert(Start != End, "V isn't in the range Start, End");
static_assert(Start != End || !P, "Can't have 0 length range");
enum { value = reverse_lookup<Start+(Start < End ? 1:-1),End,V,P+1>::value };
};
template <int Start, int End, int P>
struct reverse_lookup<Start,End,Start,P> {
enum { value = P };
};
int main() {
std::cout << char(lookup<'A', 'Z', 3>::value) << std::endl;
std::cout << char(lookup<'Z', 'A', 3>::value) << std::endl;
std::cout << int(reverse_lookup<'A','Z','F'>::value) << std::endl;
}
Alright, after knowing what reverse lookup is, I think you can do this:
const char kLetters[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
int get_index(char letter)
{
return letter - 'A';
}
After all, the letter A is at index 0, B at 1, C at 2... and so on. That gives enough hint.
My O(1) solution.
So far other solutions work for non-arbitrary sequence of letters, and #awoodland solution assumes that the letter whose index is to be obtainted is known at compile time which makes it less useful.
But this solution has attempted to solve both limitations; that is, it should work:
With arbitrary sequence of letters, such as
const char Letters[] = "ZBADCEWFVGHIUXJTKSLYQMROPN";
And the letters may be unknown at compile time. The function that gets the index has this signature:
int Index(char letter);
Here is the complete code which uses a technique described by # David Rodríguez in his blog:
#include <iostream>
const char Letters[] = "ZBADCEWFVGHIUXJTKSLYQMROPN";
template<char L> int Index();
template<> int Index<'Z'>() { return 0; }
template<> int Index<'B'>() { return 1; }
template<> int Index<'A'>() { return 2; }
template<> int Index<'D'>() { return 3; }
template<> int Index<'C'>() { return 4; }
template<> int Index<'E'>() { return 5; }
template<> int Index<'W'>() { return 6; }
template<> int Index<'F'>() { return 7; }
template<> int Index<'V'>() { return 8; }
template<> int Index<'G'>() { return 9; }
template<> int Index<'H'>() { return 10; }
template<> int Index<'I'>() { return 11; }
template<> int Index<'U'>() { return 12; }
template<> int Index<'X'>() { return 13; }
template<> int Index<'J'>() { return 14; }
template<> int Index<'T'>() { return 15; }
template<> int Index<'K'>() { return 16; }
template<> int Index<'S'>() { return 17; }
template<> int Index<'L'>() { return 18; }
template<> int Index<'Y'>() { return 19; }
template<> int Index<'Q'>() { return 20; }
template<> int Index<'M'>() { return 21; }
template<> int Index<'R'>() { return 22; }
template<> int Index<'O'>() { return 23; }
template<> int Index<'P'>() { return 24; }
template<> int Index<'N'>() { return 25; }
typedef int (*fptr)();
const int limit = 26;
fptr indexLookup[ limit ];
template <char L>
struct init_indexLookup {
static void init( fptr *indexLookup ) {
indexLookup[ L - 'A' ] = &Index<L>;
init_indexLookup<L-1>::init( indexLookup );
}
};
template <>
struct init_indexLookup<'A'> {
static void init( fptr *indexLookup ) {
indexLookup[ 0 ] = &Index<'A'>;
}
};
const int ignore = (init_indexLookup<'Z'>::init(indexLookup),0);
int Index(char letter)
{
return indexLookup[letter-'A']();
}
And here is the test code:
int main()
{
std::cout << Index('A') << std::endl;
std::cout << Index('Z') << std::endl;
std::cout << Index('B') << std::endl;
std::cout << Index('K') << std::endl;
}
Output:
2
0
1
16
Online demo : http://ideone.com/uzE2t
Well, that actually is two function calls: one to Index(), other to from one in the indexLookup. You can easily avoid first function call by writing (ideone):
int main()
{
std::cout << indexLookup['A'-'A']() << std::endl;
std::cout << indexLookup['Z'-'A']() << std::endl;
std::cout << indexLookup['B'-'A']() << std::endl;
std::cout << indexLookup['K'-'A']() << std::endl;
}
That looks cumbersome, but hey, we can make Index() inline:
inline int Index(char letter)
{
return indexLookup[letter-'A']();
}
That looks fine, and most likely now compiler will make it equivalent to one function call!
Simple yet O(1) solution
Wait. I just realized that the whole solution reduces to a lookup table which is initialized as:
const int indexLookup[] = {2,1,4,3,5,7,9,10,11,14,16,18,21,
25,23,24,20,22,17,15,12,8,6,13,19,0};
inline int Index(char letter)
{
return indexLookup[letter-'A'];
}
which looks unbelievably simple!
If you can use Boost and only need compile-time lookups:
using namespace boost::mpl;
typedef vector_c<char, 'A', 'B', 'C', 'D'> Chars;
// lookup by index:
std::cout << at_c<Chars, 1>::type::value << std::endl; // B
// lookup by value:
typedef find<Chars, integral_c<char, 'C'> >::type Iter;
std::cout << Iter::pos::value << std::endl; // 2
This assumes that 'Z' > 'A', but does not assume letters are contiguous. (Though it takes less memory if they are) I was tempted to put in if (numrLetters>26) conditionals so a smart compiler could use addition rather than the tables for ASCII, but then decided I didn't want to slow the code in the case of less-smart compilers.
const char kLetters[] = "ABCDEFGHJJKLMNOPQRSTUVWXYZ";
const int numLetters = sizeof(kLetters);
const char rkLetters['Z'-'A'] = {};
const int numrLetters = sizeof(rkLetters);
struct LetterInit {
LetterInit() {
for(int i=0; i<numLetters; ++i)
rkLetters[kLetters[i]-'A'] = i;
}
}LetterInitInst;
char findChar(int index) {
assert(index>=0 && index<numLetters);
return kLetters[index];
}
int findIndex(char letter) {
assert(letter>='A' && letter<='Z');
return rkLetters[letter-'A'];
}
As there are several solutions given that don't generate a table but still allow compile time lookup, here is another one
constexpr char kLetters[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
constexpr int get(char const x, int const i = 0) {
return kLetters[i] == x ? i : get(x, i + 1);
}
Use at compile time
int x[get('F')];
static_assert(sizeof(x) == sizeof(int[5]), "");
Specifying a character that doesn't exist will result in an error. If you use the function at runtime, you will get undefined behavior if you specify a character that doesn't exist. Proper checking can be added for those cases.
It yields the index of the first character found. No error is given if a character appears twice in the haystack.
If you can use c++0x (tested with gcc 4.5), this works:
#include<initializer_list>
#include<iostream>
#include<map>
constexpr int getLetterNumber(char a){ return std::map<char,int>({{'a',2},{'b',1},{'c',4}})[a]; }
int main(){
const char ch='b';
std::cout<<ch<<": "<<getLetterNumber(ch)<<std::endl;
}
constexpr enforces evaluation at compile-time.
EDIT: that solution is not correct, as pointed out. constexpr does not enfoce compile-time evaluation. This does does the lookup really at compile-time (similar to solutions posted meanwhile).
#include<iostream>
template<char C> int ch2Num();
#define CHR(c,i) template<> int ch2Num<c>(){ return i; }
CHR('a',2); CHR('b',1); /* ... */
#undef CHR
int main(void){
const char ch='b';
std::cout<<ch<<": "<<ch2Num<ch>()<<std::endl;
};