Switching on strings...

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • slick_shoes

    Switching on strings...

    What would be the best way to switch program flow based on a string the user
    enters? I found out the hard way that char*'s can't be used in a switch
    statement, so i've resorted to stringing if-else statements together. Is
    there a more efficient way to do this?

    Thanks
    slick_shoes


  • Patrick Frankenberger

    #2
    Re: Switching on strings...

    "slick_shoe s" wrote:[color=blue]
    > What would be the best way to switch program flow based on a string the[/color]
    user[color=blue]
    > enters? I found out the hard way that char*'s can't be used in a switch
    > statement, so i've resorted to stringing if-else statements together. Is
    > there a more efficient way to do this?[/color]

    You can use std::map<std::s tring, functionpointer > to do this:

    std::map<std::s tring, void(*)(void)> x;
    x["bar"]=&function1;
    x["foo"]=&function2; //and so on

    //in input routine:
    std::string input;
    if(x.find(input )==x.end()) error(); // invalid input
    x[input](); // calls the associated function


    Comment

    • Allan Bruce

      #3
      Re: Switching on strings...


      "slick_shoe s" <slick_shoes@pu nkass.com> wrote in message
      news:YmfSa.5977 $q6.482@news.ra ndori.com...[color=blue]
      > What would be the best way to switch program flow based on a string the[/color]
      user[color=blue]
      > enters? I found out the hard way that char*'s can't be used in a switch
      > statement, so i've resorted to stringing if-else statements together. Is
      > there a more efficient way to do this?
      >
      > Thanks
      > slick_shoes
      >
      >[/color]

      You *could* do a switch statement, e.g.

      char MyString[100]; // or whatever

      switch (MyString[0])
      {
      case 'A':
      // do whatever
      break;
      case 'B':
      // do whatever
      break;
      case 'C':
      switch (MyString[1])
      {
      case 'A':
      // do whatever
      break;
      case 'B':
      // do whatever
      break;
      }
      }

      Although this is quite messy. You could also set up an array of char[]
      which may contain the first few letters of the string to decide which way to
      switch, then loop through these with strncmp() and get the index, then
      switch on the index
      Allan



      Comment

      • Cedric LEMAIRE

        #4
        Re: Switching on strings...

        "slick_shoe s" <slick_shoes@pu nkass.com> wrote in message news:<YmfSa.597 7$q6.482@news.r andori.com>...[color=blue]
        > What would be the best way to switch program flow based on a string the user
        > enters? I found out the hard way that char*'s can't be used in a switch
        > statement, so i've resorted to stringing if-else statements together. Is
        > there a more efficient way to do this?[/color]
        To emulate a switch statement on string, I propose you a solution that
        consists of:
        - converting a string to a (non unique) integral value,
        - using a switch statement on the value,
        - resolving value-equivalence conflicts,
        with no hand-typed code to implement these points. Just specify the
        case labels and type the specific code for each of them.

        To do it simpler, we'll suppose that each case block ends with a break
        statement.

        You'll just have to type the following lines by hand:
        //##markup##"swit ch(<your-string-expression>)"
        //##data##
        //<case-label-1>
        //...
        //<case-label-n>
        //##data##
        and you'll obtain by code generation:
        //##markup##"swit ch(<your-string-expression>)"
        //##data##
        //<case-label-1>
        //...
        //<case-label-n>
        //##data##
        //##begin##"switc h(<your-string-expression>)"
        {
        // Computes a hashcode for <your-string-expression>
        int iHashCode = 0;
        std::string sKey = <your-string-expression>;
        for (int i = 0; i < sKey.size(); i++) {
        unsigned char c = sKey[i];
        iHashCode = (31*iHashCode + (c%31)) % 64000000;
        }
        // if true, no case label have matched
        bool bDefault = false;
        //
        switch(iHashCod e) {
        case <case-value-1>: // "<case-label-1>"
        if (sKey == "<case-label-1>") {
        //##protect##"cas e \"<case-label-1>\":"
        //##protect##"cas e \"<case-label-1>\":"
        } else { bDefault = true;
        }
        break;
        ...
        case <case-value-n>: // "<case-label-n>"
        if (sKey == "<case-label-n>") {
        //##protect##"cas e \"<case-label-n>\":"
        //##protect##"cas e \"<case-label-n>\":"
        } else { bDefault = true;
        }
        break;
        default:
        bDefault = true;
        }
        if (bDefault) {
        //##protect##"def ault:"
        //##protect##"def ault:"
        }
        }
        //##markup##"swit ch(<your-string-expression>)"

        Example:
        * Suppose that you want to write something like it:
        switch(sText) {
        case "Product":
        ...
        break;
        case "Customer":
        ...
        break;
        case "Figuring":
        ...
        break;
        default:
        ...
        }

        * Then write:
        //##markup##"swit ch(sText)"
        //##data##
        //Product
        //Customer
        //Figurine
        //##data##


        * Which becomes:
        //##markup##"swit ch(sText)"
        //##data##
        //Product
        //Customer
        //Figurine
        //##data##
        //##begin##"switc h(sText)"
        {
        int iHashCode = 0;
        std::string sKey = sText;
        for (int i = 0; i < sKey.size(); i++) {
        unsigned char c = sKey[i];
        iHashCode = (31*iHashCode + (c%31)) % 64000000;
        }
        bool bDefault = false;
        switch(iHashCod e) {
        case 17133617: // "Product"
        if (sKey == "Product") {
        //##protect##"cas e \"Product\": "
        //##protect##"cas e \"Product\": "
        } else { bDefault = true;
        }
        break;
        case 26793087: // "Customer"
        if (sKey == "Customer") {
        //##protect##"cas e \"Customer\" :"
        //##protect##"cas e \"Customer\" :"
        } else { bDefault = true;
        }
        break;
        case 20050752: // "Figurine"
        if (sKey == "Figurine") {
        //##protect##"cas e \"Figurine\" :"
        //##protect##"cas e \"Figurine\" :"
        } else { bDefault = true;
        }
        break;
        default:
        bDefault = true;
        }
        if (bDefault) {
        //##protect##"def ault:"
        //##protect##"def ault:"
        }
        }
        //##end##"switch( sText)"


        The specific code for each case must be written in protected
        areas (bounded with //##protect##...) so that the generator
        preserves them from a generation to another.

        But what about the generator? We'll use a LGPL tool devoted
        to work on generative programming and called 'CodeWorker',
        available at "http://www.codeworker. org".

        We'll write our tailor-made code generation in the scripting
        language of CodeWorker. The following script looks like
        a server page syntax: raw text with instructions inlayed into.
        Here, instructions of the scripting language are put between
        the symbol '@'. If you are interested in understanding the
        script in detail, I could give you some explanations about it.
        We'll call the script "script.gen ":
        {
        int iHashCode = 0;
        std::string sKey = @coreString(get MarkupKey(), 7, 1)@;
        for (int i = 0; i < sKey.size(); i++) {
        unsigned char c = sKey[i];
        iHashCode = (31*iHashCode + (c%31)) % 64000000;
        }
        bool bDefault = false;
        switch(iHashCod e) {
        @
        local codes;
        local sData = getMarkupValue( );
        while sData {
        local iIndex = sData.findStrin g('\n');
        if $iIndex < 0$ || !sData.startStr ing("//") error("syntax error");
        local sKey = sData.midString (2, $iIndex - 2$);
        if sKey.endString( '\r') set sKey = sKey.rsubString (1);
        local iHashCode = 0;
        local i = 0;
        while $i < sKey.length()$ {
        local c = sKey.charAt(i);
        iHashCode = $(31*iHashCode + (c.charToInt()% 31)) % 64000000$;
        increment(i);
        }
        pushItem codes[iHashCode].keys = "\"" + composeCLikeStr ing(sKey) +
        "\"";
        set sData = sData.subString ($iIndex + 1$);
        }
        foreach i in codes {
        @ case @key(i)@: // @
        foreach j in i.keys {
        if !first(j) {
        @, @
        } @@j@@
        }
        @
        @
        foreach j in i.keys {
        @ @
        if !first(j) {
        // resolve hashcode conflicts between 2 labels
        @} else @
        }
        @if (sKey == @j@) {
        @
        setProtectedAre a("case " + j + ":");
        }
        @ } else {
        bDefault = true;
        }
        break;
        @
        }
        @ default:
        bDefault = true;
        }
        if (bDefault) {
        @
        setProtectedAre a("default:") ;
        @ }
        }

        Now, just type the following line on the console:
        CodeWorker -expand script.gen <your-C++-file>

        CodeWorker expands the lines you have typed at the beginning
        in the developed form, following the directives of "script.gen ".
        You can add/remove as many case labels as you want, even after having
        expanded the C++ file, with no loss of source code. You just have
        to type the precedent command line to take it into account.
        If you change a label, don't forget to change the name of the
        corresponding protected area as well.

        Now, we can extend easily our switch statement for not being obliged
        to have a break statement at the end of each case block:
        - in "script.gen ", replace:
        bool bDefault = false;
        by:
        int iRank;
        - each if statement implements the line:
        iRank = <rank-of-case-label>; // [1..n]
        - replace:
        bDefault = true;
        by:
        iRank = 0;
        - add a second switch behind the first one, applied on iRank.
        - moves the protected areas to each case and to default.

        -- Cedric

        Comment

        • fred
          New Member
          • Feb 2006
          • 1

          #5
          switching on strings

          below is a C++ program that shows a very easy way to switch on strings:

          //---- Switch/case statments on strings - a workable solution (2/2006):
          #include <iostream.h>
          #include <stdlib.h>
          using namespace std;

          //------------------------------------------------------------------------------
          //---- Given an array of string pointers, compare a subject string against
          // that array. Function returns with the index to the list of subject
          // strings.
          // If a match is
          // 1) found : return the index of the matched string plus 1
          // 2) not found: return "0"
          int sw(char *table[], const char *str, const int &entries)
          {char **bp;
          int i;
          for(i=0,bp=&tab le[0]; i<entries; ++i,++bp) // scan all test strings
          {if(*str != **bp)continue; // 1st char's must match
          if(strcmp(str,* bp)==0)return i+1; // match whole string
          }
          return 0;
          }
          //------------------------------------------------------------------------------
          int main(int argc, char *argv[])
          {int i;
          char in[64];

          #define SIZE 6
          char *array[SIZE]={"first","seco nd","third","fo urth","fifth"," sixth"};

          while (true)
          {cout << "Please enter a string (\"enter\" only to terminate): ";
          cin.getline(in, 64);

          i=sw(array, in, SIZE);
          switch(i)
          {case 1:cout << in << "=" << i << endl;break;
          case 2:cout << in << "=" << i << endl;break;
          case 3:cout << in << "=" << i << endl;break;
          case 4:cout << in << "=" << i << endl;break;
          case 5:cout << in << "=" << i << endl;break;
          case 6:cout << in << "=" << i << endl;break;
          default:
          if(strlen(in)<1 )exit(0);
          cout << "ERROR: " << "\"" << in << "\"" << endl;
          }
          }
          }

          Comment

          Working...