Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Returning string constants in switch-case block with std::string

Tags:

c++

c++14

NOTE: This is not about using a string for choosing the execution path in a switch-case block.

A common pattern in C++ is to use a switch-case block for converting integer constants to strings. This looks like:

char const * to_string(codes code)
{
    switch (code)
    {
        case codes::foo: return "foo";
        case codes::bar: return "bar";
    }
}

However, we are in C++, so using std::string is more appropriate:

std::string to_string(codes code)
{
    switch (code)
    {
        case codes::foo: return "foo";
        case codes::bar: return "bar";
    }
}

This however copies the string literal. Perhaps a better approach would be instead:

std::string const & to_string(codes code)
{
    switch (code)
    {
        case codes::foo: { static std::string str = "foo"; return str; }
        case codes::bar: { static std::string str = "bar"; return str; }
    }
}

But this is kinda ugly, and involves more boilerplate.

What is considered the cleanest and most efficient solution for this problem using C++14?

like image 290
dv_ Avatar asked Feb 04 '19 14:02

dv_


People also ask

Can I use string in switch case C++?

You cannot use string in either switch or case .

Can I use string in switch case?

It is recommended to use String values in a switch statement if the data you are dealing with is also Strings. The expression in the switch cases must not be null else, a NullPointerException is thrown (Run-time). Comparison of Strings in switch statement is case sensitive.

Is string allowed in switch case in C?

No you can't.

How do I return STD strings?

Try this: std::string * sp; std::string func() { std::string s("bla"); sp = &s; return s; } int main() { std::string s = func(); if(sp == &s) std::cout << "YAY"; else std::cout << "BOO"; } -- On my compiler (VS) It prints YAY.


2 Answers

This however copies the string literal.

Yes and no. It will copy the string literal indeed, but don't necessarily allocate memory. Check your implementation SSO limit.


You could use std::string_view:

constexpr std::string_view to_string(codes code) {
    switch (code) {
        case codes::foo: return "foo";
        case codes::bar: return "bar";
    }
}

You can find many backported versions like this one

However, sometimes a char const* is the right abstraction. For example, if you were to forward that string into an API that require a null terminated string, you'd be better off returning it a c style string.

like image 84
Guillaume Racicot Avatar answered Oct 16 '22 11:10

Guillaume Racicot


But this is kinda ugly, and involves more boilerplate.

What is considered the cleanest and most efficient solution for this problem using C++14?

To answer the above, as @SamerTufail pointed out (and as I do it myself at work also), I would use enums and std::map like this.

   typedef enum {
        foo = 1,
        bar = 2,
    } Key;

std::map<Key, std::string> hash_map = { {Key::foo ,"foo"}, { Key::bar,"bar"} };

And then in main() you could get the value like this,

std::cout << hash_map.find(Key::foo)->second;

I would create a function for returning the second, where you would check the iterator for end(), otherwise the interator would be invalid and using it would be UB.


EDIT: As others have pointed out in the comments and as per this question, you could replace std::map, with std::unordered_map provided you do not need to keep elements in order.

And as per my experience, I always create such maps as static const. Therefore create them one time and use them many times to amortize the cost of creation.

like image 25
Duck Dodgers Avatar answered Oct 16 '22 13:10

Duck Dodgers