Using strings in switch statements - where do we stand with C++17?
Asked Answered
G

10

15

Every one of us has (probably) had the childhood dream of writing:

switch(my_std_string) {
case "foo":  do_stuff(); break;
case "bar":  do_other_stuff(); break;
default:     just_give_up();
}

but this is not possible, as is explained in the answers to this question from the olden days (2009):

Why the switch statement cannot be applied on strings?

Since then we've seen the advent of C++11, which lets us go as far as:

switch (my_hash::hash(my_std_string)) {
case "foo"_hash:  do_stuff(); break;
case "bar"_hash:  do_other_stuff(); break;
default:          just_give_up();
}

as described in an answer to Compile time string hashing - which is not so bad, although it doesn't actually do exactly what we wanted - there's a chance of collision.

My question is: Has the development of the language since then (mostly C++14 I suppose) affected the way one would write a sort-of-a string case statement? Or simplified the nuts-and-bolts for achieving the above?

Specifically, with the conclusion of the C++17 standard being just around the corner - I'm interested in the answer given what we can assume the standard will contain.

Galasyn answered 26/12, 2016 at 23:25 Comment(23)
Hi, c++17 won't change anything in this particular field. We have to stick to hashed values.Minter
I can't say I've ever had such a dream. The hash example you give has a non-zero probability of giving a false hit (i.e. there is a non-zero probability of two non-equal strings having an equal hash). Given the origins of switch (i.e. exploiting machine instructions that allowed constructing a jump table based on integral values) I would be surprised if any version of C++ ever supported non-integral switch/case.Docker
Agree, with @Peter, the hash solution is NOT formally correct... unless -maybe- an additional check is made to verify the equality, so no clear benefit. OTOH, having a switch for strings in the language, what would be the benefit, really?Cercaria
@Peter: I didn't suggest the hash example is what we want; it's a sort of an approximation. But I guess it's important to point out the possibility of collision. As for the origins of switch - think about the origins of if, and how we now have constexpr-if ...Galasyn
@A.S.H: Switch'ing over strings is very intuitive. If you have if (my_std_string == "foo") do_stuff() else do_other_stuff(); you would expect to be able to extend that to multiple cases without a sequence of if-then-else's.Galasyn
@OP if one needs really a switch, a possible solution would be to use some static std::map<string, int> and make the switch on the associated int. This solution would be formally correct and intuitive.Cercaria
@A.S.H: Would the code look closer to the original than the hash 'solution'? That's what I'm interested in, in this question. Also - I'm not debating whether or not it's a good idea...Galasyn
I think so, why not. You would simply write switch myMap[my_std_string] ... case "myMap["foo"]` etc..Cercaria
I rarely switch on integers. I've never understood why people like switch statements, given all the issues with things like fall-through. ability to only use constants in cases, etc. that they have.Boykins
The chance of a hash collision is for Java small enough to not be considered at all.Fall
@RadLexus still, the solution is not formally correct.Cercaria
A case statement with break is the same number of keystrokes as an "else if" clause.Exacerbate
if else lets you order tests in order of frequency of occurrence if performance is an issue. If the set of possible arguments is known and matches the case statements then all hash collisions would be detected during compile and that would be rare. You can use message digests if you have a particularly large collection of strings to test against. Risk of collision will always remain if the switch argument set includes collisions but is effectively zero with a high quality message digest and they are also pretty immune to selected argument attack whereas regular hash's are not.Crossbar
@peter - If I have that dream I would wake up screaming.Quietism
@RadLexus Your link shows the exact opposite. Your link shows that the chance of hash collisions was considered, and that's why after checking the hash code, the string is compared to the expected value specifically so that hash collisions do not form a problem.Mezereon
@Ed Heal - I suspect I would too.Docker
@einpoklum: It's not clear what this question is asking. Are you asking if there's a way to use switch directly with string literals? Or are you asking for any mechanism, language or library, that would give you most of the effects of switching over string literals?Fickle
@Cercaria Why bother with the intermediate int, instead have a map<string, function<void()>>? (with some finesse for the default action)Victorious
@Victorious this is also possible, although it imposes to write a function for each switch case, you wouldn't be able to write direct statements.Cercaria
@Boykins People like switch statements because of their efficiency; the jump to the specific case if relatively deterministic and consistent across a large number of cases when compared to an if..then..else construct where tests must be made sequentially. The overhead of setting up the jump at run-time is normally overcome where statements have ~3 or more cases to deal with. It also improves clarity where only a specific variable is involved in each case. Knowing when to choose the right construct for the right circumstances is an important skill.Dysteleology
@cosimo193: I don't think that actually make a lot of sense, because compilers should be optimizing switch statements and if-then-else chains into the same thing. For me at least it's more of a stylistic preference.Galasyn
@Galasyn Compilers can only optimise these into the same thing once they've decided that all the conditions in an if-then-else chain depend only on a single variable, and every conditions is a comparison between that one variable and a constant expression (or a series of constant expressions, if a range is being tested). If these circumstances are true, you should be using a switch statement as it makes the intention clear to the reader of your code (unless it's a big range - IMO C++ needs to allow ranges in case expression like Ada has since before 1983!).Dysteleology
@Galasyn For reference to the "case" statement (equivalent of "switch") in Ada, see archive.adaic.com/standards/83lrm/html/lrm-05-04.htmlDysteleology
C
5

My proposal is possible with C++14, but with if constexpr and std::string_view it is a little esier to write.

First - we need constexpr string - like this one:

template <char... c>
using ConstString = std::integer_sequence<char, c...>;

template <char ...c>
constexpr auto operator ""_cstr ()
{
    return  ConstString<c...>{};
}

operator == is also easier to write with template-less construction of tuple and with the fact that tuple has now constexpr operator ==:

template <char... c1, char ...c2>
constexpr bool operator == (ConstString<c1...>, ConstString<c2...>)
{
    if constexpr (sizeof...(c1) == sizeof...(c2)) // c++17 only
    {
        return tuple{c1...} == tuple{c2...};  // c++17 only
    }
    else
    {
        return false;
    }
}

Next thing - define switch-case code:

template <typename Callable, typename Key>
class StringSwitchCase;

template <typename Callable, char ...c>
struct StringSwitchCase<Callable, ConstString<c...>>
{
    constexpr bool operator == (const std::string_view& str) // c++17 only
    {
        constexpr char val[] = {c..., '\0'};
        return val == str;
    }
    Callable call;
    static constexpr ConstString<c...> key{};
};

template <typename Callable, char ...c>
constexpr auto makeStringSwitchCase(CString<c...>, Callable call)
{
    return StringSwitchCase<Callable, ConstString<c...>>{call};
}

Default case would be also needed:

template <typename Callable>
struct StringSwitchDefaultCase
{
    constexpr bool operator == (const std::string_view&)
    {
        return true;
    }
    Callable call;
};

template <typename Callable>
constexpr auto makeStringSwitchDefaultCase(Callable call)
{
    return StringSwitchDefaultCase<Callable>{call};
}

So, the StringSwitch - actually, it is if () {} else if () {} ... else {} construction:

template <typename ...Cases>
class StringSwitch
{
public:
    StringSwitch(Cases&&... cases) : cases(std::forward<Cases>(cases)...) {}

    constexpr auto call(const std::string_view& str)
    {
        return call<0u>(str);
    }
private:
    template <std::size_t idx>
    constexpr auto call(const std::string_view& str)
    {
        if constexpr (idx < sizeof...(Cases))
        {
            if (std::get<idx>(cases) == str)
            {
                return std::get<idx>(cases).call();
            }
            return call<idx + 1>(str);
        }
        else
        {
            return;
        }
    }

    std::tuple<Cases...> cases;
};

And possible usage:

StringSwitch cstrSwitch(   
    makeStringSwitchCase(234_cstr, 
                          [] { 
                              cout << "234\n"; 
                          }),
    makeStringSwitchCase(ConstString<'a', 'b', 'c'>{}, // only C++ standard committee know why I cannot write "abc"_cstr  
                          [] { 
                              cout << "abc\n"; 
                          }),
    makeStringSwitchDefaultCase([] { 
                              cout << "Default\n"; 
                          }));

cstrSwitch.call("abc"s);

Working demo.


I manage to do ConstString in much easier way, basing on this post. Working demo2.

The added part is as follows:

#include <boost/preprocessor/repetition/repeat.hpp>
#include <boost/preprocessor/comma_if.hpp>

#define ELEMENT_OR_NULL(z, n, text) BOOST_PP_COMMA_IF(n) (n < sizeof(text)) ? text[n] : 0
#define CONST_STRING(value) typename ExpandConstString<ConstString<BOOST_PP_REPEAT(20, ELEMENT_OR_NULL, #value)>, \
                                                       ConstString<>, sizeof(#value) - 1>::type

template <typename S, typename R, int N>
struct ExpandConstString;
template <char S1, char ...S, char ...R, int N>
struct ExpandConstString<ConstString<S1, S...>, ConstString<R...>, N> :
       ExpandConstString<ConstString<S...>, ConstString<R..., S1>, N - 1>
{};
template <char S1, char ...S, char ...R>
struct ExpandConstString<ConstString<S1, S...>, ConstString<R...>, 0>
{
    using type = ConstString<R...>;
};

By changing first parameter (20) in BOOST_PP_REPEAT(20, ELEMENT_OR_NULL, #value) we can control the maximum possible size of ConstString - and the usage is as follows:

int main() {
    StringSwitch cstrSwitch(
        makeStringSwitchCase(CONST_STRING(234){}, 
                              [] { 
                                  cout << "234\n"; 
                              }),
        makeStringSwitchCase(CONST_STRING(abc){}, 
                              [] { 
                                  cout << "abc\n"; 
                              }),
        makeStringSwitchDefaultCase([] { 
                                  cout << "Default\n"; 
                              }));

    cstrSwitch.call("abc"s);
}
Caterinacatering answered 27/12, 2016 at 10:27 Comment(1)
Hmm, nice. I'm going to tweak this a little to make it a bit more like the "naive" syntax.Galasyn
A
4

It would be easy-ish to write

switcher(expr)->*
caser(case0)->*[&]{
}->*
caser(case1)->*[&]{
};

to build a statically sized hash table of case0 through caseN, populate it dynamically, test for collisions with ==, do the lookup via expr, and run the corresponding lambda.

Even caser(case3)->*caser(case4)->*lambda and ->*fallthrough could be supported.

I do not see a compelling need.

I see no advantage to writing this in C++17 either.

Adham answered 27/12, 2016 at 3:9 Comment(1)
Is there any sample code to show how it can be implemented? how this solution is compared with this solution: hbfs.wordpress.com/2017/01/10/…Helbonnas
R
4

Since C++11 you can use smilingthax/cttrie (cf. C/C++: switch for non-integers - esp. Update 2016):

#include "cttrie.h"
...
const char *str = ...;

  TRIE(str)
    std::cout << "Not found\n";
  CASE("abc")
    std::cout << "Found abc\n";
  CASE("bcd")
    std::cout << "Found bcd\n";
  ENDTRIE;

Internally, a Trie is created at compile time and stored as a type. At runtime it is traversed according to str. The code blocks are wrapped in lambdas and executed at the corresponding leafs.

Rosenzweig answered 6/9, 2018 at 12:56 Comment(2)
So, this looks much nicer than the other macro solution, but will this work for non-char* strings? Which are not known at compile time?Galasyn
You can also use std::string str = ...; – in fact every class that has .c_str() and .size() is supposed to work (minor bug in the stringview implementation is now fixed).Rosenzweig
E
2

Here is a simple solution for simulating switch case in C/C++.

UPDATE: Including continue version. Earlier version cannot use continue statement within a loop. Regular switch-case block can perform continue, as expected, when used in a loop. But since we use for loop in our SWITCH-CASE macros, continue just brings out of the SWITCH-CASE block but not out of the loop, in which it is being used.

Here are the macros to be used:

#ifndef SWITCH_CASE_INIT
#define SWITCH_CASE_INIT
    char __switch_continue__;

    #define SWITCH(X)   __switch_continue__=0; \
                    for (char* __switch_p__=X, __switch_next__=1; __switch_p__!=0 ; __switch_next__=2) { \
                        if (__switch_next__==2) { __switch_continue__=1; break;
    #define CASE(X)         } if (!__switch_next__ || !(__switch_next__ = strcmp(__switch_p__, X))) {
    #define DEFAULT         } {
    #define END         __switch_p__=0; }}
    #define CONTINUE    __switch_p__=0; }} if (__switch_continue__) { continue; }
#endif

EXAMPLE: SWITCH-CASE with continue

EXECUTE

If the SWITCH block is used in a loop and we happen to use continue within the SWITCH, we need to end the SWITCH with CONTINUE (rather than END)

#include <stdio.h>
#include <string.h>

#ifndef SWITCH_CASE_INIT
#define SWITCH_CASE_INIT
    char __switch_continue__;

    #define SWITCH(X)   __switch_continue__=0; \
                        for (char* __switch_p__=X, __switch_next__=1; __switch_p__!=0 ; __switch_next__=2) { \
                            if (__switch_next__==2) { __switch_continue__=1; break;
    #define CASE(X)         } if (!__switch_next__ || !(__switch_next__ = strcmp(__switch_p__, X))) {
    #define DEFAULT         } {
    #define END         __switch_p__=0; }}
    #define CONTINUE    __switch_p__=0; }} if (__switch_continue__) { continue; }
#endif


int main()
{
    char* str = "def";
    char* str1 = "xyz";

    while (1) {
        SWITCH (str)
            CASE ("abc")
                printf ("in abc\n");
                break;

            CASE ("def")                                
                printf("in def (continuing)\n");
                str = "ghi";
                continue;                               // <== Notice: Usage of continue (back to enclosing while loop)

            CASE ("ghi")                                // <== Notice: break; not given for this case. Rolls over to subsequent CASEs through DEFAULT
                printf ("in ghi (not breaking)\n");

            DEFAULT
                printf("in DEFAULT\n");

        CONTINUE                                        // <== Notice: Need to end the SWITCH with CONTINUE

        break; // break while(1)
    }
}

OUTPUT:

in def (continuing)
in ghi (not breaking)
in DEFAULT
  • Need to use SWITCH..CASE..CONTINUE inside a loop (that too if continue is required within the switch)

  • Need to use SWITCH..CASE..END by default

  • Can use reverse string comparison. Like

    SWITCH ("abc") CASE(str1) END

This kind of comparison can open a whole lot of comparison options and avoid clumsy if-else chains. String comparison cannot be made without character-by-character comparison and so cannot avoid if-else chains. At least code looks cute with SWITCH-CASE. But the bottleneck is it uses

  • 3 extra variables
  • 5 extra assignments and
  • 1 extra (bool) comparison for each CASE

So itz on developers' discretion of opting between if-else to SWITCH-CASE

Expansion answered 3/8, 2018 at 11:45 Comment(3)
1. How is this different from chaining if-then-else's? 2. Why is the for loop necessary?Galasyn
Thank you for this code snippet, which might provide some limited, immediate help. A proper explanation would greatly improve its long-term value by showing why this is a good solution to the problem, and would make it more useful to future readers with other, similar questions. Please edit your answer to add some explanation, including the assumptions you've made.Rolandorolandson
@Galasyn 1). Well if-then-else can be chained for ordinary switch-case as well, but why is the need of switch-case? Itz just for simplicity, wherein retyping same variable name with == and if-else makes the code clumsy. By the way, this approach can be used for reverse string switch as well like: SWITCH ("abc") CASE (str1) END which is not possible in switch-case; means a variable cannot be used within a case clause. 2). Yes for loop is needed to simulate break; statement and avoid roll over to subsequent CASEs through DEFAULT.Expansion
G
1

A minor modification of @PiotrNycz's interesting answer, to make the syntax a bit more similar to the 'naive' switch, allows us to write this:

switch_(my_std_string, 
case_(234_cstr, [] {     
    std::cout << "do stuff with the string \"234\" \n"; 
}),
case_(ConstString<'a', 'b', 'c'> { }, [] { 
    std::cout << "do other stuff with the string \"abc\"\n";
}),
default_( [] { 
    std::cout << "just give up.\n"; 
})      

The full implementation:

#include <iostream>
#include <array>
#include <tuple>
#include <string>
#include <type_traits>
#include <utility>


template<char ... c>
using ConstString = std::integer_sequence<char, c...>;

template <char ...c>
constexpr auto operator ""_cstr ()
{
    return ConstString<c...> {};
}

template<char ... c1, char ...c2>
constexpr bool operator == (ConstString<c1...>, ConstString<c2...>) 
{
    if constexpr (sizeof...(c1) == sizeof...(c2)) {
        return std::tuple {c1...} == std::tuple {c2...};
    }
    else { return false; }
}

template<typename Callable, typename Key>
class SwitchCase;

template<typename Callable, char ...c>
struct SwitchCase<Callable, ConstString<c...>> {
    constexpr bool operator == (const std::string_view& str) {
        constexpr char val[] = { c..., '\0' };
        return val == str;
    }
    const ConstString<c...> key;
    Callable call;
};

template<typename Callable, char ...c>
constexpr auto case_(ConstString<c...> key, Callable call) 
{
    return SwitchCase<Callable, ConstString<c...>> { key, call };
}

template<typename Callable>
struct SwitchDefaultCase {
    constexpr bool operator == (const std::string_view&) { return true; }
    Callable call;
};

template<typename Callable>
constexpr auto default_(Callable call) 
{
    return SwitchDefaultCase<Callable> { call };
}

template<typename ...Cases>
class switch_ {
public:
    // I thought of leaving this enabled, but it clashes with the second ctor somehow
    // switch_(Cases&&... cases) : cases(std::forward<Cases>(cases)...) {}

    constexpr auto call(const std::string_view& str) {
        return call<0u>(str);
    }

    switch_(const std::string_view&& str, Cases&&... cases) :
            cases(std::forward<Cases>(cases)...) {
        call<0u>(str);
    }

private:
    template<std::size_t idx>
    constexpr auto call(const std::string_view& str) {
        if constexpr (idx < sizeof...(Cases)) {
            if (std::get<idx>(cases) == str) {
                return std::get<idx>(cases).call();
            }
            return call<idx + 1>(str);
        }
        else { return; }
    }

    std::tuple<Cases...> cases;
};

int main() {
    std::string my_std_string("abc");
    std::cout << "What is \"" << my_std_string << "\"?\n";

    switch_(my_std_string, 
    case_(234_cstr, [] {     
        std::cout << "do stuff\n"; 
    }),
    case_(ConstString<'a', 'b', 'c'> { }, [] { 
        std::cout << "do other stuff\n";
    }),
    default_( [] { 
        std::cout << "just give up\n"; 
    })      
    );
}

And a similar working demo. Now what we would really need is constructing ConstStrings from "abcd" -type literals.

Galasyn answered 27/12, 2016 at 11:40 Comment(6)
what is 234_cstr in this code? Can I use a case like this case_("My test string")?Helbonnas
@mans: It's basically "234". Edited to clarify and see also PiotrNycz's answer.Galasyn
Thanks for quick reply. So how can I use it for checking "my test string" can I use it, please note that it has a space in string.Helbonnas
@mans: You can see how to use this mechanism in the main() function. It doesn't matter whether the string has spaces or not.Galasyn
Can I run it in VS 2015? or does it need VS2017 ?Helbonnas
@mans: You can run this in any compiler which supports C++17. I don't think either of these does, but I wouldn't know. It will run with gcc 7 or clang 5, or higher versions, if you use the --std=c++17 switch. These can also run on Windows platforms IIANM.Galasyn
R
0

The original reason for the switch statement is that it can be mapped by the compiler to a similar machine operation. For switches with a large amount of cases, this produces very efficient machine code.

For strings, because of the needed comparison, this is not possible, so the implementation would be far less efficient; not any different from if/else/else-if clauses. The C and C++ language family still has the target to allow to produce very efficient machine code without any overhead, so a switch on strings is not something that would be a useful extension - there are more efficient ways to code that, if you really need it to be more efficient. It would also imply to add a 'strcmp' into the language syntax, with all its variations and vagaries - not a good idea.

I doubt that this would be a good extension at any time for any version of C++.

Restore answered 27/12, 2016 at 14:17 Comment(3)
You're making several claims here which I don't agree with, but regardless of that - you're not answering the question. I specifically said I'm not asking about the merits of doing this, just about the extent to which it's close to being possible.Galasyn
How it's implemented under the hood is irrelevant; it's the clarity of the source code that's of interest here, particularly as numerous other languages (C#, Java, VB) support switching on string values. docs.oracle.com/javase/7/docs/technotes/guides/language/… specifically mentions that "The Java compiler generates generally more efficient bytecode from switch statements that use String objects than from chained if-then-else statements", so I don't see any reason why C++ could not also provide this.Dysteleology
What are the more efficient ways to code that?Manolo
E
0

Here is another solution. But this version also uses a series of comparisons.

  • 3 assignments (including an Array of all the CASE string pointers)
  • string Comparisons until a match is found
  • increments on an integer until a match is found

DEMO

#include <stdio.h>
#include <string.h>

#define SWITCH(X, ...) \
            char * __switch_case_ ## X ## _decl[] = {__VA_ARGS__}; \
            int __switch_case_ ## X ## _i=0, __switch_case_ ## X ## _size = sizeof(__switch_case_ ## X ## _decl)/sizeof(char*); \
            while (__switch_case_ ## X ## _i < __switch_case_ ## X ## _size && strcmp(X, __switch_case_ ## X ## _decl[__switch_case_ ## X ## _i])){ __switch_case_ ## X ## _i++; } \
            switch (__switch_case_ ## X ## _i)


int main()
{
    char * str = "def";

    SWITCH (str, "abc", "def", "ghi", "jkl")
    {
    case 0:
        printf (str);
        break;
    case 1:
        printf (str);
        break;
    case 2:
        printf (str);
        break;
    case 3:
        printf (str);
        break;
    default:
        printf ("default");
    }

    return 0;
}

OUTPUT:

def
Expansion answered 24/8, 2018 at 12:58 Comment(1)
Well, it's a solution with macros, which I dislike; but regardless of that - what would you say are the benefits here relative to your other suggestion?Galasyn
C
0

In C++17, I take advantage of std::find as part of the <algorithm> group. The idea is to keep all case value strings together inside a searchable container (such as std::vector). We will try to locate the string being searched and, then, switch based on the int index found.

So, let's start creating a finder template such as:

template<typename T>
int find_case(std::vector<T> vHaystack, T tNeedle)
{
    int nPos(-1);
    typename std::vector<T>::iterator it =
        std::find(vHaystack.begin(), vHaystack.end(), tNeedle);
    if (it != vHaystack.cend())
        nPos = std::distance(vHaystack.begin(), it);
    return nPos;
}

find_case will return -1 when the needle is not found inside the haystack, or the non-negative index, if the needle is found.

These are usage examples:

std::vector<std::string> v1 { "Hello", "How", "are", "you" };
int n1(find_case(v1, "How"));    // Will return 1
int n2(find_case(v1, "Bye"));    // Will return -1

One advantage of being a template is that we could also work with other types, such as std::wstring.

Now, let's have a look at the switch on the index:

// vCases is the haystack, the vector with all case strings
// strKey is the needle, the value to be searched
switch (int nIndex; nIndex = find_case(vCases, strKey))
{
    case 0:   ...; break;
    case 1:   ...; break;
    case 2:   ...; break;
    ...
    default:
        // User gave a wrong/unexpected key
        if (nIndex < 0)
            std::cout << "Unknown case " << strKey << std::endl;
        // Our list of switch cases is missing one, at least
        else
            std::cerr << "INTERNAL: No case for " << strKey << std::endl;
}

Do not forget to include <vector> and <algorithm> headers.

Out of the scope of this answer, there are much more powerful implementations using std searchers in combination with std::any that will let us have string and integer type cases together under a common switch statement.

Cullin answered 19/5, 2023 at 13:19 Comment(0)
S
0

If your C++ compiler supports the C++17 standard, it is possible to calculate the hash of a string_view literal with a constexpr function at compile time for switch...case statements.

#include <string_view>
using namespace std::literals::string_view_literals;
#include <string>
using namespace std::literals::string_literals;

//
// The following code ported from Microsoft C++ STL std::hash<std::string_view>
// make it a 'constexpr' and working for string_view literals at compile-time
//

// These FNV-1a utility functions are extremely performance sensitive,
// check examples like that in VSO-653642 before making changes.
#if defined(_WIN64)
static inline constexpr std::size_t __FNV_basis = 14695981039346656037ULL;
static inline constexpr std::size_t __FNV_prime = 1099511628211ULL;
#else
static inline constexpr std::size_t __FNV_basis = 2166136261U;
static inline constexpr std::size_t __FNV_prime = 16777619U;
#endif

static inline constexpr std::size_t __FNV_hash(const std::string_view sv)
{
    std::size_t hash_{ __FNV_basis };
    for (std::size_t i = 0; i < sv.size(); ++i)
    {
        hash_ ^= static_cast<std::size_t>(sv.at(i));
        hash_ *= __FNV_prime;
    }
    return hash_;
}

int main(int argc, char *argv[])
{
    std::string str(argv[0]);

    switch (__FNV_hash(str)) {
    case __FNV_hash("ls"sv):
        break;
    case __FNV_hash("chmod"sv):
        break;
    case __FNV_hash("cp"sv):
        break;
    default:
        break;
    }

    return 0;
}
Stoush answered 21/3 at 3:45 Comment(1)
That's an interesting comment, but doesn't quite answer my question.Galasyn
S
-1

Late to the party, here's a solution I came up with some time ago, which completely abides to the requested syntax and works also with c++11.

#include <uberswitch/uberswitch.hpp>

uswitch(my_std_string) {
ucase ("foo"): do_stuff(); break;
ucase ("bar"): do_other_stuff(); break;
default:       just_give_up();
}

The only differences to be noticed are the usage of uswitch in place of switch and ucase in place of case, with the added parenthesis around the value (needed, because that's a macro).

Here's the code: https://github.com/falemagn/uberswitch

Sideways answered 29/8, 2020 at 16:2 Comment(3)
Replacing "case" with a macro is absolutely not acceptable. Why don't you use case_ or ucase (for uber-case) etc? Also, how is your solution different than @Ramu's ?Galasyn
I used case because syntax highlighters like it much better than any other name I could give it, but if you've got such a strong opinion about it, changing the name of the macro is easier than writing a comment on Stack Overflow, I believe? :)Sideways
This solution is different than @Ramu's in that... both the syntax and the implementations and functionalities are different, which seemed quite obvious to me?Sideways

© 2022 - 2024 — McMap. All rights reserved.