Orderly Mayhem
May 21, 2012, 01:32:29 PM *
Welcome, Guest. Please login or register.

Login with username, password and session length
News:
 
   Home   Help Search Contact Staff List Login Register  
Pages: [1]   Go Down
  Print  
Author Topic: Crazy C++ switch-case for strings  (Read 1936 times)
BS-er
Moderator of Mayhem
Administrator
*****
Offline Offline

Posts: 2150


View Profile
« on: March 18, 2008, 06:02:29 PM »

Awhile back I created a crazy set of defines for implementing a switch-case structure using strings.  It's ugly macros, and some wierdness, but I admit I use it in a few places.  Here it is:

#pragma once
// ----------------------------------------------------------------------------
// string_switch_case.h
//
// These macros together implement switch-case functionality for C strings and
// std::string.  When first encountered, the switch-case structure does two
// passes.  The first pass is a one-time initialization of a std::map with the
// strings as keys, and line numbers as values, to get the unique keys needed
// for a numeric switch-case.  All subsequent passes use the std::map to
// retrieve an unsigned integer for a given string, that was mapped during the
// first pass, to use for a numeric switch case.  This switch-case approach can
// be used repeatedly and even nested.  An example of the usage is as follows:
//
// switch_string(a)
// {
// case_string("first string")
//    printf("first string");
//    break;
// case_string("second string")
//    printf("second string falls through to...");
// case_string("third string")
//    printf("third string");
//    break;
// default_case_string
//    printf("default");
// }
// end_switch_string
//
// The end_switch_string macro is required after the closing brace of the
// switch-case structure.  Each case_string statement must be on a unique line.
// Each case_string has its own local scope.  A case_string statement is
// ignored if declared within scope brackets beneath a sibling case statement.
// ----------------------------------------------------------------------------

#include <map>
#include <string>


// switch macro
#define switch_string(a) \
while (true) { \
   static std::map<std::string, unsigned int> _string_lookup_; \
   static bool _init_ = true; \
   unsigned int _val_ = 0; \
   if (!_init_) { \
      std::map<std::string, unsigned int>::iterator _iter_ = \
         _string_lookup_.find(a); \
         if (_iter_ != _string_lookup_.end()) { \
            _val_ = (*_iter_).second; } \
         else { \
            _val_ = 0xffffffff; \
         } \
   } \
   switch(_val_) { \
   case 0:


// case macro
#define case_string(a) \
      } \
   case __LINE__: \
      if (_init_) \
         _string_lookup_.insert \
            (std::pair<std::string, unsigned int>(a, __LINE__)); \
      else {


// default case macro
#define default_case_string \
      } \
   default: \
      if (!_init_) {


// required macro for the end of the switch-case
#define end_switch_string \
   } \
   if (_init_) { \
      _init_ = false; \
   } \
   else { \
      break; \
   } \
}
Logged
OvermindDL1
Casual Support Developers
*
Offline Offline

Gender: Male
Posts: 278

Programmer


View Profile WWW
« Reply #1 on: March 19, 2008, 01:49:05 AM »

Interesting.  How about using a hash_map instead of a map though, might be a little bit faster for larger versions.
Normally I would just create my own jump table, or if it is something where the case statements are a single line of code (any complexity, just as long as it is a single line, like a function call or setting something) then it should be pretty easy to make up a few templated commands to do it, probably a mock-up of something like this:
Code:
// This would be the preferred syntax:
str_switch(testString)
[
str_case<"someLiteral", fallthrough>(commandToPerform_LambdaWouldBeFine),
str_case<"anotherLiteral", break>(anotherCommandToPerform_LambdaWouldBeFine),
str_default_case(theDefaultCommandToPerform_LambdaWouldBeFine)
]

But as the current C++ does not support character array template params then you could cast the pointer to an int and pass that in and just let it convert it back, hiding the nasties in a macro probably (D's templates supports string literals just fine, grrr, C++'s templates *really* need to be more like D's).  Or maybe convert the above to the equivalent, but much more nasty:
Code:
str_switch(testString)
[
str_case<fallthrough, 's','o','m','e','L','i','t','e','r','a','l'>(commandToPerform_LambdaWouldBeFine),
str_case<break, 'a','n','o','t','h','e','r','L','i','t','e','r','a','l'>(anotherCommandToPerform_LambdaWouldBeFine),
str_default_case(theDefaultCommandToPerform_LambdaWouldBeFine)
]
Which would not be hard to do at all actually, but yea... great fun with that syntax, and I cannot off-hand think of any way to have the preprocessor (or the horrible string template support) to convert a nice string into a character template array like that...

The nice thing about the above two forms is that you could pre-process the hash of the strings before execution so that the testString could be parsed as normal and matched at run-time, with a test if the hash matches to confirm, else dump to the default.

The D language (I would quite honestly use if it had the rich amount of libraries that C++ has or some interaction with them, which it does, but only with C externaled code, no C++ class external support thanks to C++'s lack of standardization in its external support) supports string in switches, which it has highly optimized so it is just about as quick as a hash of the incoming string, the rest of the work is at compile time, enhanced example from the D site:
Code:
char[] name;
/* code to set name to something */
switch (name)
{
    case "hello":
        // do something
        // fallthrough
    case "world":
        // do something else
        break;
    case "fred":
        // do something yet more
        // then do the things in the default label
        goto default;
    case "sally":
        // do yet even more somethings
        // goto the case "world" as well
        goto case "world";
    default:
        // do the default thing
}
« Last Edit: March 19, 2008, 02:03:49 AM by OvermindDL1 » Logged

BS-er
Moderator of Mayhem
Administrator
*****
Offline Offline

Posts: 2150


View Profile
« Reply #2 on: March 19, 2008, 09:02:28 AM »

Interesting.  How about using a hash_map instead of a map though, might be a little bit faster for larger versions.

Yeah I did consider hash_map as well.  It should just drop in so that's an easy change.

D's templates supports string literals just fine, grrr, C++'s templates *really* need to be more like D's).

Yes C++ is a very good language that's starting to show its age.  In fact its all the good aspects of it that are making it hard to move away from it.

I've been hearing good things about D.  I imagine converting Ogre into D would be some work though.  I don't even want to think about side-projects right now Smiley.
Logged
JonathanS
Administrator
*****
Offline Offline

Gender: Male
Posts: 278



View Profile
« Reply #3 on: March 20, 2008, 03:53:50 PM »

I've been hearing good things about D.  I imagine converting Ogre into D would be some work though.  I don't even want to think about side-projects right now Smiley.

You mean there's a D?!?!
Logged

JonathanS


OvermindDL1
Casual Support Developers
*
Offline Offline

Gender: Male
Posts: 278

Programmer


View Profile WWW
« Reply #4 on: March 20, 2008, 10:04:16 PM »

Yep, made by digital mars (who makes a few compilers, including a C++ compiler), designed to be as fast as C++, but much much easier to use (and I do mean much).
Logged

Pages: [1]   Go Up
  Print  
 
Jump to:  

Powered by MySQL Powered by PHP Powered by SMF 1.1 RC3 | SMF © 2001-2006, Lewis Media Valid XHTML 1.0! Valid CSS!


Google visited last this page Today at 06:02:26 AM