[Home] [Downloads] [Search] [Help/forum]


Register forum user name Search FAQ

Gammon Forum

Notice: Any messages purporting to come from this site telling you that your password has expired, or that you need to "verify" your details, making threats, or asking for money, are spam. We do not email users with any such messages. If you have lost your password you can obtain a new one by using the password reset link.
 Entire forum ➜ Programming ➜ STL ➜ Newer, Better, Cooler Bitmask-Flags

Newer, Better, Cooler Bitmask-Flags

It is now over 60 days since the last post. This thread is closed.     Refresh page


Pages: 1  2 

Posted by Nick Gammon   Australia  (23,046 posts)  Bio   Forum Administrator
Date Reply #15 on Sun 27 Jun 2004 11:39 PM (UTC)
Message
Er, yes, it does look a bit of a mess. Why do you need all that complication? To reverse the bits, you can use the reverse algorithm on the string, like this:


#include <bitset>
#include <string>
#include <sstream>
#include <iostream>
#include <algorithm>

using namespace std;

// this define lets you create a string on-the-fly
// you can use output modifiers in the string, eg.
// string mystr = MAKE_STRING (
//    "hello " << boolalpha << true << hex << 1234 << octal << 3456 << dec
//    << 123456 << scientific << 123.45 << fixed << 1234.567 " );

#define MAKE_STRING(msg) \
   (((ostringstream&) (ostringstream() << boolalpha << msg)).str())


int main (void)
  {
  bitset<50> mybits;

  mybits.set (1);
  mybits.set (5);
  mybits.set (40);

string s;

  s = MAKE_STRING (mybits);

  cout << "bits = " << s << endl;
  reverse (s.begin (), s.end ());
  cout << "reversed bits = " << s << endl;

  }



Output

bits = 00000000010000000000000000000000000000000000100010
reversed bits = 01000100000000000000000000000000000000001000000000


- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Samson   USA  (683 posts)  Bio
Date Reply #16 on Mon 28 Jun 2004 03:58 AM (UTC)
Message

char *bitset_string( std::basic_string< char, std::char_traits<char>, std::allocator<char> > flags, int flagmax, char *const flagarray[] )
{
   static char buf[MSL];
   int x;

   reverse( flags.begin(), flags.end() );

   buf[0] = '\0';
   for( x = 0; x < flagmax; x++ )
	if( flags[x] == '1' )
	{
	   mudstrlcat( buf, flagarray[x], MSL );
         /* don't catenate a blank if the last char is blank  --Gorog */
         if( buf[0] != '\0' && ' ' != buf[strlen(buf)-1] )
            mudstrlcat( buf, " ", MSL );
	}

   if( ( x = strlen( buf ) ) > 0 )
      buf[--x] = '\0';

   return buf;
}


Mainly..... because I don't know that much about C++ yet. I'm delving into it bit by bit ( no pun intended! ) so every little thing helps. Didn't know about the reverse algorithm. Certainly made that particular portion of things much easier to work with though.
Top

Posted by Nick Gammon   Australia  (23,046 posts)  Bio   Forum Administrator
Date Reply #17 on Mon 28 Jun 2004 05:01 AM (UTC)
Message
OK, good. Now what does this routine do exactly? Turn a bitset into a string? Can't help thinking it can be replaced by one or two lines.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Samson   USA  (683 posts)  Bio
Date Reply #18 on Mon 28 Jun 2004 12:12 PM (UTC)
Message
Yes. It's purpose is to take a std::bitset and turn it into a string of names to represent the bits which are set. Much like flag_string does for the BVxx type, and ext_flag_string does for the EXT_BV types. Used for display in stats screens, identify spells, and file formats etc.

If it can be done in 2 lines I'd be rather impressed with that.
Top

Posted by Nick Gammon   Australia  (23,046 posts)  Bio   Forum Administrator
Date Reply #19 on Mon 28 Jun 2004 10:52 PM (UTC)

Amended on Mon 28 Jun 2004 10:53 PM (UTC) by Nick Gammon

Message
Ah, I see. Converting a bitset to a string of names. Well, maybe 3 lines. :)

This still looks neater:


#ifdef WIN32
  #pragma warning( disable : 4786)
#endif

#include <bitset>
#include <vector>
#include <string>
#include <iostream>
#include <algorithm>

using namespace std;

char *  const   npc_race        [] =
{
"human", "elf", "dwarf", "halfling", "pixie", "vampire", "half-ogre",
"half-orc", "half-troll", "half-elf", "gith", "drow", "sea-elf",
"lizardman", "gnome", "r5", "r6", "r7", "r8", "troll",
"ant", "ape", "baboon", "bat", "bear", "bee",
"beetle", "boar", "bugbear", "cat", "dog", "dragon", "ferret", "fly",
"gargoyle", "gelatin", "ghoul", "gnoll", "gnome", "goblin", "golem",
"gorgon", "harpy", "hobgoblin", "kobold", "lizardman", "locust",
"lycanthrope", "minotaur", "mold", "mule", "neanderthal", "ooze", "orc",
"rat", "rustmonster", "shadow", "shapeshifter", "shrew", "shrieker",
"skeleton", "slime", "snake", "spider", "stirge", "thoul", "troglodyte",
"undead", "wight", "wolf", "worm", "zombie", "bovine", "canine", "feline",
"porcine", "mammal", "rodent", "avis", "reptile", "amphibian", "fish",
"crustacean", "insect", "spirit", "magical", "horse", "animal", "humanoid",
"monster", "god"
};


#define NUMITEMS(arg) (sizeof (arg) / sizeof (arg [0]))


template <class T>
string bitset_to_string (T bits, vector<string> names)
  {

  string s;

  for (size_t i = 0; i < bits.size (); i++)
    if (bits [i])
      s += names [i] + " ";
  

  return s;
  } // end of bitset_to_string

int main (void)
  {

  vector<string> npc_race_vector;
  bitset<NUMITEMS (npc_race)> race_flags;

  race_flags.set (5);
  race_flags.set (6);
  race_flags.set (70);

  copy (npc_race,
        &npc_race [ NUMITEMS (npc_race) ],
        back_inserter (npc_race_vector));

  cout << "result = " << bitset_to_string (race_flags, npc_race_vector) << endl;

  }



Output

result = vampire half-ogre worm




The important 3 lines are in bold.

What I have done here is use the NUMITEMS macro (to find how many flags are in the array), and convert the char * array into a vector (for simplicity, although this probably isn't necessary).

Then I used a templated function to save having to pass down the number of bits in the array.

Also by using:

bitset<NUMITEMS (npc_race)> race_flags;

I could let the bitset adjust itself to the number of flags actually needed, although there may be reasons to not do that.

Finally the bitset_to_string function scans the array of bits, and for each one set, copies the appropriate name into the string, appending a space.

The only problem is you will have a trailing space (unless no bits match) but you can always do a test and shorten the resulting string by one if necessary.


- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Samson   USA  (683 posts)  Bio
Date Reply #20 on Tue 06 Jul 2004 08:28 AM (UTC)
Message
Going by what you've posted above, I went on to modify my bitset_string function as follows:


template <class T>
char *bitset_string( T bits, char *const flagarray[] )
{
   static char buf[MSL];
   size_t x;

   buf[0] = '\0';
   for( x = 0; x < bits.size(); x++ )
   {
      if( bits.test(x) )
	{
	   mudstrlcat( buf, flagarray[x], MSL );
         /* don't catenate a blank if the last char is blank  --Gorog */
         if( buf[0] != '\0' && ' ' != buf[strlen(buf)-1] )
            mudstrlcat( buf, " ", MSL );
	}
   }

   if( ( x = strlen( buf ) ) > 0 )
      buf[--x] = '\0';

   return buf;
}


This has a prototype in mud.h as such:


template <class T>
char *bitset_string( T bits, char *const flagarray[] );


Called as such, for various different bitsets I want to turn into strings for the purpose of saving/displaying their contents:


	fprintf( fpout, "%s~\n", bitset_string( pMobIndex->actflags, act_flags ) );
      fprintf( fpout, "%s~\n", bitset_string( pMobIndex->affected_by, a_flags ) );


I was rather excited watching the results, it was compiling along just fine until it hit the linking stage where I got spammed silly with dozens of these:


o/act_info.o(.text+0x277c): In function `do_look':
/usr/include/c++/3.3.3/bitset:1064: undefined reference to `char* bitset_string<std::bitset<(unsigned)12> >(std::bitset<(unsigned)12>, char* const*)'
o/act_info.o(.text+0x27a4): In function `do_look':
/home/samson/Alsherok/src/act_info.c:1061: undefined reference to `char* bitset_string<std::bitset<(unsigned)42> >(std::bitset<(unsigned)42>, char* const*)'
o/act_wiz.o(.text+0x13e0): In function `do_rstat':
/home/samson/Alsherok/src/act_wiz.c:953: undefined reference to `char* bitset_string<std::bitset<(unsigned)42> >(std::bitset<(unsigned)42>, char* const*)'


Now, obviously I'm doing something wrong. I can't seriously expect that the compiler wants me to make 5 dozen different copies of bitset_string just in case. It seems to me all I'm missing is how to properly pass a std::bitset as an argument to a general function. There has to be away, but none of the web references I've found will tell me.
Top

Posted by Nick Gammon   Australia  (23,046 posts)  Bio   Forum Administrator
Date Reply #21 on Tue 06 Jul 2004 10:52 PM (UTC)
Message
I was caught by this a while ago, and the answer can be a bit obscure when you are not expecting it. :)

The way templates work is that the compiler generates the code required when it sees an instance of the templated code (not the template declaration).

Thus, you cannot prototype the function like you have done - the compiler is accepting the prototype, but is not aware to generate the *instance* required for each of the 5 cases.

The correct, and only, solution is to put the entire templated function into a header file, and include that in the places where it is wanted. This is how STL does it. If you have this:

#include <map>

(for example)

Then that has the entire code for generating maps inside the include file, and that has to be in the front of any module that wants to use it. And, there is no map.cpp file. It is all in the header.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Samson   USA  (683 posts)  Bio
Date Reply #22 on Tue 06 Jul 2004 11:11 PM (UTC)
Message
Ah, sweetness. That fixed it up nicely. No kidding on the not expecting it part. I sure didn't think it needed to be in a header file. But I'm still feeling my way through this for the most part. Thanks for the heads up :)
Top

Posted by Nick Gammon   Australia  (23,046 posts)  Bio   Forum Administrator
Date Reply #23 on Wed 07 Jul 2004 12:23 AM (UTC)

Amended on Wed 07 Jul 2004 12:30 AM (UTC) by Nick Gammon

Message
Now that you are using STL and C++, it might be a good idea to look at moving away from static buffers for strings.

Depending on what MSL is defined as (4096 in my version) then you are either:


  • Using too much memory, and wasting (say) 4,000 bytes of RAM which is never used; or

  • Not allowing enough, in which case one day it will overflow and crash


The version below takes the same arguments as yours, and still returns a const char * string, but uses a string internally, which can never overflow. I have declared it as static so it doesn't go out of scope too soon ...



#ifdef WIN32
  #pragma warning( disable : 4786)
#endif

#include <bitset>
#include <string>
#include <iostream>

#include <stdio.h>

using namespace std;

char *  const   npc_race        [] =
{
"human", "elf", "dwarf", "halfling", "pixie", "vampire", "half-ogre",
"half-orc", "half-troll", "half-elf", "gith", "drow", "sea-elf",
"lizardman", "gnome", "r5", "r6", "r7", "r8", "troll",
"ant", "ape", "baboon", "bat", "bear", "bee",
"beetle", "boar", "bugbear", "cat", "dog", "dragon", "ferret", "fly",
"gargoyle", "gelatin", "ghoul", "gnoll", "gnome", "goblin", "golem",
"gorgon", "harpy", "hobgoblin", "kobold", "lizardman", "locust",
"lycanthrope", "minotaur", "mold", "mule", "neanderthal", "ooze", "orc",
"rat", "rustmonster", "shadow", "shapeshifter", "shrew", "shrieker",
"skeleton", "slime", "snake", "spider", "stirge", "thoul", "troglodyte",
"undead", "wight", "wolf", "worm", "zombie", "bovine", "canine", "feline",
"porcine", "mammal", "rodent", "avis", "reptile", "amphibian", "fish",
"crustacean", "insect", "spirit", "magical", "horse", "animal", "humanoid",
"monster", "god"
};


template <class T>
const char * bitset_to_string (T bits, char *const flagarray[] )
  {

  static string s;

  s.clear ();

  for (size_t i = 0; i < bits.size (); i++)
    if (bits [i])
      {
      if (!s.empty ())
        s += " ";  // separate multiple ones by a space
      s += flagarray [i];
      }

  return s.c_str ();
  } // end of bitset_to_string

int main (void)
  {

  bitset<100> race_flags;

  race_flags.set (5);
  race_flags.set (6);
  race_flags.set (70);

  printf ("results = '%s'\n", bitset_to_string (race_flags, npc_race) );

  }



The only problem with the code above is that it doesn't handle spaces inside your constants, which I think your code does. The version below which introduces a couple more routines that I wrote to trim spaces, will handle spaces at either end of the constants (see the spaces I added to "worm" below) ...


#ifdef WIN32
  #pragma warning( disable : 4786)
#endif

#include <bitset>
#include <string>
#include <iostream>

#include <stdio.h>

using namespace std;

char *  const   npc_race        [] =
{
"human", "elf", "dwarf", "halfling", "pixie", "vampire", "half-ogre",
"half-orc", "half-troll", "half-elf", "gith", "drow", "sea-elf",
"lizardman", "gnome", "r5", "r6", "r7", "r8", "troll",
"ant", "ape", "baboon", "bat", "bear", "bee",
"beetle", "boar", "bugbear", "cat", "dog", "dragon", "ferret", "fly",
"gargoyle", "gelatin", "ghoul", "gnoll", "gnome", "goblin", "golem",
"gorgon", "harpy", "hobgoblin", "kobold", "lizardman", "locust",
"lycanthrope", "minotaur", "mold", "mule", "neanderthal", "ooze", "orc",
"rat", "rustmonster", "shadow", "shapeshifter", "shrew", "shrieker",
"skeleton", "slime", "snake", "spider", "stirge", "thoul", "troglodyte",
"undead", "wight", "wolf", " worm ", "zombie", "bovine", "canine", "feline",
"porcine", "mammal", "rodent", "avis", "reptile", "amphibian", "fish",
"crustacean", "insect", "spirit", "magical", "horse", "animal", "humanoid",
"monster", "god"
};

#define SPACES " \t\r\n"  // default "spaces" for trimming strings

// trim spaces from right (you can define what spaces are)
string trim_right (const string & s, const string & t = SPACES);
// trim spaces from left
string trim_left (const string & s, const string & t = SPACES);
// trim spaces from both sides
string trim (const string & s, const string & t = SPACES);

inline string trim_right (const string & s, const string & t)
  {
  string d (s);
  string::size_type i (d.find_last_not_of (t));
  if (i == string::npos)
    return "";
  else
   return d.erase (d.find_last_not_of (t) + 1) ;
  }  // end of trim_right

inline string trim_left (const string & s, const string & t)
  {
  string d (s);
  return d.erase (0, s.find_first_not_of (t)) ;
  }  // end of trim_left

inline string trim (const string & s, const string & t)
  {
  string d (s);
  return trim_left (trim_right (d, t), t) ;
  }  // end of trim

template <class T>
const char * bitset_to_string (T bits, char *const flagarray[] )
  {

  static string s;

  s.clear ();

  for (size_t i = 0; i < bits.size (); i++)
    if (bits [i])
      s += trim (flagarray [i]) + " ";

  s = trim_right (s);  // get rid of final space

  return s.c_str ();
  } // end of bitset_to_string

int main (void)
  {

  bitset<100> race_flags;

  race_flags.set (5);
  race_flags.set (6);
  race_flags.set (70);

  printf ("results = '%s'\n", bitset_to_string (race_flags, npc_race) );

  }



Output

results = 'vampire half-ogre worm'




In the second example above, the implementations of trim, trim_left and trim_right *can* be moved into a cpp file (not a header file) because they are straight functions, not templated functions.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

The dates and times for posts above are shown in Universal Co-ordinated Time (UTC).

To show them in your local time you can join the forum, and then set the 'time correction' field in your profile to the number of hours difference between your location and UTC time.


66,613 views.

This is page 2, subject is 2 pages long:  [Previous page]  1  2 

It is now over 60 days since the last post. This thread is closed.     Refresh page

Go to topic:           Search the forum


[Go to top] top

Quick links: MUSHclient. MUSHclient help. Forum shortcuts. Posting templates. Lua modules. Lua documentation.

Information and images on this site are licensed under the Creative Commons Attribution 3.0 Australia License unless stated otherwise.

[Home]