23 Aug, 2007, Guest wrote in the 1st comment:
Votes: 0
So as part of the mess with GNU's latest "warning that's not really a problem" dance, I was wondering if it would be useful to change all of my do_fun's to use std::string arguments instead. I already did that to my IMC2 module using a function Noplex wrote for me way back when. I've already asked about it on smaugmuds.org but figured I'd post it here too to see what people thought. I've been advised it's a bit inefficient and needs improvement. It does work though.

vector < string > vector_argument( string arg, int chop )
{
vector < string > v;
char cEnd;
int passes = 0;

if( arg.find( '\'' ) < arg.find( '"' ) )
cEnd = '\'';
else
cEnd = '"';

while( !arg.empty( ) )
{
string::size_type space = arg.find( ' ' );

if( space == string::npos )
space = arg.length( );

string::size_type quote = arg.find( cEnd );

if( quote != string::npos && quote < space )
{
arg = arg.substr( quote + 1, arg.length( ) );
if( ( quote = arg.find( cEnd ) ) != string::npos )
space = quote;
else
space = arg.length( );
}

string piece = arg.substr( 0, space );
strip_lspace( piece );
if( !piece.empty( ) )
v.push_back( piece );

if( space < arg.length( ) - 1 )
arg = arg.substr( space + 1, arg.length( ) );
else
break;

/*
* A -1 indicates you want to proceed until arg is exhausted
*/
if( ++passes == chop && chop != -1 )
{
strip_lspace( piece );
v.push_back( arg );
break;
}
}
return v;
}
23 Aug, 2007, kiasyn wrote in the 2nd comment:
Votes: 0
for a tiny bit of optimization you could flip if( ++passes == chop && chop != -1 ) to if( chop != -1 && ++passes == chop )
23 Aug, 2007, Justice wrote in the 3rd comment:
Votes: 0
As a result of chats with Samson the last week or so, I decided to throw together some std::string based one_argument functions.

One is setup as a short term "crutch" as you will… to strip the arguments into a char buffer. It has an optional max length (default: MAX_INPUT_LENGTH) to prevent buffer overruns. This should relieve some of the pain of converting the code to use std::string.

The other is setup for a pure std::string environment.

Both of these have survived basic testing, but feel free to suggest improvements.

mud.h:
void substr(const std::string &input, char *output, std::string::size_type start, std::string::size_type length);
std::string one_argument(const std::string &argument, char *first, std::string::size_type max=MAX_INPUT_LENGTH);
std::string one_argument(const std::string &argument, std::string &first);


interp.c:
void substr(const std::string &input, char *output, std::string::size_type start, std::string::size_type length)
{
std::string::size_type pos, cnt;

pos = start;
cnt = 0;

while(cnt < length)
{
output[cnt] = input[pos];
cnt++;
pos++;
}
output[cnt] = '\0';
}

std::string one_argument(const std::string &argument, char *first, std::string::size_type max)
{
std::string::size_type start, stop, stop2, chk_len, chk_max;
char find;

// Init
start = 0;
chk_max = max-1; // Leave room for '\0'

// Empty?
if (argument.length() < 1)
{
first[0] = '\0';
return "";
}

// Strip leading spaces
if (argument[0] == ' ')
{
start = argument.find_first_not_of(' ');

// Empty?
if (start == argument.npos)
{
first[0] = '\0';
return "";
}
}

// Quotes or space?
switch(argument[start])
{
case '\'':
find = '\'';
start++;
break;
case '\"':
find = '\"';
start++;
break;
default:
find = ' ';
}

// Find end of argument.
stop = argument.find_first_of(find, start);

// Empty leftovers?
if (stop == argument.npos)
{
chk_len = (argument.length()-start);
substr(argument, first, start, UMIN(chk_len, chk_max));
return "";
}

// Update first
chk_len = (stop-start);
substr(argument, first, start, UMIN(chk_len, chk_max));

// Strip leading spaces from leftovers
stop2 = argument.find_first_not_of(' ', stop+1);

// Empty leftovers?
if (stop2 == argument.npos)
return "";

// Return leftovers.
return argument.substr(stop2);
}

// Pick off one argument from a string and return the rest.
std::string one_argument(const std::string &argument, std::string &first)
{
std::string::size_type start, stop, stop2;
char find;

// Init
start = 0;

// Empty?
if (argument.length() < 1)
{
first = "";
return "";
}

// Strip leading spaces
if (argument[0] == ' ')
{
start = argument.find_first_not_of(' ');

// Empty?
if (start == argument.npos)
{
first = "";
return "";
}
}

// Quotes or space?
switch(argument[start])
{
case '\'':
find = '\'';
start++;
break;
case '\"':
find = '\"';
start++;
break;
default:
find = ' ';
}

// Find end of argument.
stop = argument.find_first_of(find, start);

// Empty leftovers?
if (stop == argument.npos)
{
first = argument.substr(start);
return "";
}

// Update first
first = argument.substr(start, (stop-start));

// Strip leading spaces from leftovers
stop2 = argument.find_first_not_of(' ', stop+1);

// Empty leftovers?
if (stop2 == argument.npos)
return "";

// Return leftovers.
return argument.substr(stop2);
}
25 Aug, 2007, Justice wrote in the 4th comment:
Votes: 0
Since I've gone ahead and converted my DO_FUN to use std::string, other utility functions need updated and written. I'll be posting some of these as I write them. As usual, I'm all ears if anyone has ideas on how to improve them.

std::string prefix_argument(const std::string &argument, std::string &prefix)
{
std::string::size_type start, end;

if ((end = argument.find_first_of('.')) == argument.npos)
{
prefix = "";
return argument;
}

start = argument.find_first_not_of(' ');
prefix = argument.substr(start, (end-start));
return argument.substr((end+1));
}

bool is_number(const std::string &argument)
{
std::string::const_iterator it, end;

// Empty
if (argument.length() < 1)
return FALSE;

it = argument.begin();
end = argument.end();
for (; it != end; it++)
{
if (!isdigit(*it))
return FALSE;
}
return TRUE;
}

std::string number_prefix(const std::string &argument, int &num)
{
std::string prefix, ret;

ret = prefix_argument(argument, prefix);
if (is_number(prefix))
{
num = atoi(prefix.c_str());
return ret;
}

num = -1;
return argument;
}
25 Aug, 2007, Tyche wrote in the 5th comment:
Votes: 0
Here's a few that were converted from Merc 2.2…
Murk++ utils.cpp
25 Aug, 2007, Justice wrote in the 6th comment:
Votes: 0
Thanks, some of those will help. Here's a little tolower for your case insensitive str_cmp.

std::transform(buf.begin(), buf.end(), buf.begin(), (int(*)(int))std::tolower);
26 Aug, 2007, Tyche wrote in the 7th comment:
Votes: 0
Hmm… I'd have to create two intermediate strings for the transform though.
Certainly the transform reads better, but which is faster?

// Case insensitive compare
bool str_cmp(const std::string & s1, const std::string & s2)
{
if (s1.size() != s2.size())
return true;

std::string::const_iterator p1 = s1.begin(), p2 = s2.begin();
while(p1 != s1.end() && p2 != s2.end()) {
if(tolower(*p1) != tolower(*p2))
return true;
p1++;
p2++;
}
return false;
}
27 Aug, 2007, Darmond wrote in the 8th comment:
Votes: 0
ok not beeing the best of coders not to mention not haveing a huge background in codeing * I started my world because I was sick of useing pencil and paper for these games with no decent testers so its basacoly a hobby for my I am the dreamer not the weaver of said dream* any ways I wounder if it wasent broken why fix it ? what was so wrong with what was beeing done before and what is the change sapost to do now ?? I am a tad bit confused basacoly as to what error it is saposadly picking up on in the code

P.S. I am a fan of dupt tape I fixed a mouse with dupt tape its been working for the past 2 years like that point here is something dosent need to necisarly follow the standards to work if it works then its all good just make sure that it stays working and dont peal off the tape so to speak
27 Aug, 2007, Tyche wrote in the 9th comment:
Votes: 0
27 Aug, 2007, Dorian wrote in the 10th comment:
Votes: 0
Darmond said:
ok not beeing the best of coders not to mention not haveing a huge background in codeing * I started my world because I was sick of useing pencil and paper for these games with no decent testers so its basacoly a hobby for my I am the dreamer not the weaver of said dream* any ways I wounder if it wasent broken why fix it ? what was so wrong with what was beeing done before and what is the change sapost to do now ?? I am a tad bit confused basacoly as to what error it is saposadly picking up on in the code

P.S. I am a fan of dupt tape I fixed a mouse with dupt tape its been working for the past 2 years like that point here is something dosent need to necisarly follow the standards to work if it works then its all good just make sure that it stays working and dont peal off the tape so to speak


This isn't a case of making a "fix" when something isn't broken. This is like switching from a map and compass to a GPS navigation system because the former technology is quickly becoming a novelty. Sometimes change in the market causes people to change their tool set.
27 Aug, 2007, Guest wrote in the 11th comment:
Votes: 0
Hahaha, that hamster never gets old. :)

This whole thing with the std::string functions was born of GNU's latest ridiculousness with -Wwrite-strings. Rather than put up with the crap I decided it was time to eliminate the crap. So I've been in the process of converting everything in the mud to use std::string, much like Tyche did with Murk++. The more I look into it the more I wonder why I didn't just do this sooner. It saves so much grief compared to C strings.

I also remain convinced that GNU is intentionally trying to cripple straight C with this last "fix". Code that's working fine shouldn't suddenly look like a 3rd grader slapped it together and ignored the compiler. All of the explanations and justifications are falling on deaf ears.
28 Aug, 2007, Omega wrote in the 12th comment:
Votes: 0
replacing everything with strings is a good way to go, I've been himming and hawing over it myself, trying to find a good method for me to work with, and frankly, I've been lacking the time to learn std::strings in full. though once again, it is a good method to go with :)

just like std::maps for large arrays…… Did I say that out-loud?

anyways.. Yeah, that hamster kicks ass, its now my active desktop background… (egads!)

As for GNU crippling straight C, if you haven't noticed, allot of the errors vanish when you compile with gcc apposed to g++ (convient eh) so when compiling straight C in C++ it tends to have more issues. Thats what I've noticed anyways with my work on gcc/g++ related code.

Anywho, limited time, so…

PEACE.
29 Aug, 2007, Justice wrote in the 13th comment:
Votes: 0
Okay… been a busy couple of days, figured I'd drop an update.

Yeah Tyche, I noticed that as soon as I worked to rewrite mine. The tolower code was used in something that stored the strings in a std::map, which was then behaved case-insensitive because I lowercased the search string. Anyway, it's an easy way to "transform" a string to lowercase. Ended up renaming "cmp_nocase" from a book of mine and reusing it for str_cmp and str_prefix.

Darmond, there is a problem but it's subtle. Basically you can modify the value of the argument passed to a do_fun. If you actually modify this, and that argument was originally a string literal, then it should not be possible to modify it. In some cases, modifying this can cause buffer overruns and memory corruption. I'm not too worried about this case, but in a strict sence of "secure" programming, that should really be a const.

That being said, updating to std::string is something I wanted to do anyway, but never got around to. It just works better with the C++ libraries.

Here's my string comparison stuff, basically str_cmp and str_equal check equality, str_prefix and str_starts check if a string starts with another, and has_string is used to replace is_name, but is more flexible. The functions str_cmp and str_prefix use the old -1, 0, +1 return values to represent < = >, while str_equal and str_starts return true/false. Basically… if (str_equal(a,b)) is equiv to if (!str_cmp(a,b)), same goes for str_starts and str_prefix. has_string returns true/false and takes a tokenizer (default: one_argument) and comparer (default: str_prefix).

typedef std::string STR_TOKENIZER(const std::string &input, std::string &next);
typedef int STR_COMPARE(const std::string &astr, const std::string &bstr);

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Comparison

// Compare: astr is equal to bstr.
// Case insensitive.
// – Justice
bool str_equal(const std::string &astr, const std::string bstr)
{
std::string::const_iterator a1, a2, b1, b2;

a1 = astr.begin();
a2 = astr.end();

b1 = bstr.begin();
b2 = bstr.end();

while(a1 != a2 && b1 != b2)
{
if (std::toupper(*a1) != std::toupper(*b1))
return FALSE;
a1++;
b1++;
}
return (bstr.size()==astr.size());
}

// Compare: astr starts with bstr.
// Case insensitive.
// – Justice
bool str_starts(const std::string &astr, const std::string bstr)
{
std::string::const_iterator a1, a2, b1, b2;

a1 = astr.begin();
a2 = astr.end();

b1 = bstr.begin();
b2 = bstr.end();

while(a1 != a2 && b1 != b2)
{
if (std::toupper(*a1) != std::toupper(*b1))
return FALSE;
a1++;
b1++;
}
return TRUE;
}

// Compare: astr ><= bstr.
// <0 = astr < bstr
// 0 = astr == bstr
// >0 = astr > bstr
// Case insensitive.
// – Justice
int str_cmp(const std::string &astr, const std::string &bstr)
{
std::string::const_iterator a1, a2, b1, b2;

a1 = astr.begin();
a2 = astr.end();

b1 = bstr.begin();
b2 = bstr.end();

while(a1 != a2 && b1 != b2)
{
if (std::toupper(*a1) != std::toupper(*b1))
return (std::toupper(*a1)<std::toupper(*b1) ? -1 : 1);
a1++;
b1++;
}
return (bstr.size()==astr.size() ? 0 : (astr.size() < bstr.size() ? -1 : 1));
}

// Compare: astr ><= bstr (astr prefix of bstr)
// <0 = astr < bstr
// 0 = astr == bstr
// >0 = astr > bstr
// Case insensitive.
// Shrinks bstr to equal length if necessary. (prefix)
// – Justice
int str_prefix(const std::string &astr, const std::string &bstr)
{
std::string::const_iterator a1, a2, b1, b2;

a1 = astr.begin();
a2 = astr.end();

b1 = bstr.begin();
b2 = bstr.end();

while(a1 != a2 && b1 != b2)
{
if (std::toupper(*a1) != std::toupper(*b1))
return (std::toupper(*a1)<std::toupper(*b1) ? -1 : 1);
a1++;
b1++;
}
return 0;
}


// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Specialized Searches
bool has_string(const std::string &find, const std::string &text, STR_COMPARE *comp, STR_TOKENIZER *token)
{
std::string buf, cur;
buf = text;

while(!buf.empty())
{
// Strip Keyword
buf = token(buf, cur);

if (!comp(find, cur))
return TRUE;
}
return FALSE;
}


oops, has_string used one_argument instead of calling token, fixed… heh
03 Sep, 2007, David Haley wrote in the 14th comment:
Votes: 0
Justice, how are you handling shared string management in C++? In case you care, or if this is of general interest, I have a C++ shared string library that seems to work quite well. The library is still in "growth mode", in that it has only those features I have needed so far and is still missing some things like [] operators etc., but it does the job of managing shared strings very painlessly.
04 Sep, 2007, Justice wrote in the 15th comment:
Votes: 0
I haven't gotten to shared strings yet. Been working in other areas of the code, the std::string conversion isn't my top priority. In the limited time I spend on the mud lately, I've been working on updating the skill and affect systems. I've also been installing and testing some new search code.

Figured I'd just write a ref-counting object that maintains the string list.
04 Sep, 2007, David Haley wrote in the 16th comment:
Votes: 0
Sounds good. Well, I have mine ready to go and working if you want to use it. I'd be happy to get your opinion on it, too.
04 Sep, 2007, Justice wrote in the 17th comment:
Votes: 0
I took a look at your code, and while it's similar to what I envisioned, you've taken a much different approach. Basically, you're building and maintaining your own string table, as an array of std::list objects. Anyway, ended up writing my own version as I pictured it, using a std::map to maintain the table, with the string as the key and the count as the value. It passed all of my testing quite easily.
04 Sep, 2007, David Haley wrote in the 18th comment:
Votes: 0
Well, in fairness I do a little more than just that… it's actually a generic string table manager that has been implemented as a hash table, in the example provided. It'd be pretty simple to implement it as a std::map, although I suspect that a well-chosen hashing function would be more efficient than a binary search tree (the typical std::map implementation).

(Disclaimer: the hash function I provide is fairly worthless; IIRC it is the fairly poor one that SMAUG uses. I've been meaning to come up with a better one.)
04 Sep, 2007, Justice wrote in the 19th comment:
Votes: 0
Was there something I missed then? Yes, I noticed the hashing, but imho, that's part of maintaining the table.

As for efficiency, that depends on many factors. With enough buckets, well balanced data, and a good hashing function, yes a hashtable would have better performance. Given the similarities of strings used in muds, I suspect that either algorithm will have a tendency to be imbalanced.

In either case, std::map is efficient, stable, and plays nicely with the standard library. Having read your release notes and documentation, I believe this code will be easier to use and maintain.
04 Sep, 2007, kiasyn wrote in the 20th comment:
Votes: 0
Fearitself@Avp gave me some shared string code a long time ago that was really neat, I don't have the specific code anymore, but I remember the concept.

Heres the basic concept, the code below will not work, its just for learning from

class sharedstring
{
class istring
{
public:
std::string str;
int ref;
public istring( std::string s ) : str(s), ref(1) {};
};
static private istring blankString = "";
istring *str;
sharedstring()
sharedstring* operator=( const char *str ) {
if ( –str->ref <= 0 )
delete str;
str = new istring( str );
return *this;
}
sharedstring* operator=( const sharedstring &str ) {
if ( –str->ref <= 0 )
delete str;
str = str.str;
str-ref++;
}
}


obviously this needs a lot of work, you overload the operators to the string, everytime the internal string is created, you split off and make a new string. cool stuff i always thought, no tables, no maps :) would mess up gdb a bit I think though, I don't really remember.

UPDATE: Fear has given me some better code, posted below
This code is untested in compile or execution - it is based (mostly) on my own working and very stable code, however it is modified so as not to be dependent on other parts of my code.
Permission is granted to Kiasyn@Talon to repost this elsewhere, if the entirety (including this header) is copied verbatim. Bugfixes are permitted.



// SharedString.h


// Code by Chris Jacobson (FearItself @ AvP)
// Based on code from LexiMUD (Aliens vs Predator: The MUD, and Raze the Dead)
// Modified to remove namespaces, target std::string, and eliminate dependency on my own shared object system
// Permission to use in your own code, so long as this header remains intact.


class SharedString
{
public:
SharedString() : m_Shared(GetBlank()) { m_Shared->Retain(); }
explicit SharedString(const char *str) : m_Shared(NULL) { Assign(str); }
SharedString(const std::string &str) : m_Shared(NULL) { Assign(str.c_str()); }
SharedString(const SharedString &str) : m_Shared(str.m_Shared) { m_Shared->Retain(); }

void operator=(const char *str) { Assign(str); }
void operator=(const std::string &str) { Assign(str.c_str()); }
void operator=(const SharedString &str) { m_Shared->Release(); m_Shared = str.m_Shared; m_Shared->Retain(); }

bool operator==(const SharedString &ss) const { return *static_cast<std::string *>(m_Shared) == *ss.m_Shared; }
bool operator==(const std::string &str) const { return *static_cast<std::string *>(m_Shared) == *str; }
bool operator==(const char *str) const { return *static_cast<std::string *>(m_Shared) == str; }

operator const std::string &() const { return *m_Shared; }
const char * c_str() const { return m_Shared->c_str(); }
bool empty() const { return m_Shared->empty(); }
size_type length() const { return m_Shared->length(); }

// This is a dangerous function; it is useful if you want to update everything at once.
// Note that LexiMUD does NOT use a hashtable for sharing, as that can cause very poor
// performance in a MUD that uses a lot of dynamic names or dynamic object name changing
std::string & GetStringRef() { return *m_Shared; }

private:
class Implementation : public std::string
{
public:
Implementation(const char *str) : std::string(str), m_Refs(1) {}
Implementation(const std::string &str) : std::string(str), m_Refs(1) {}

void Retain() { ++m_Refs; }
void Release() { if (–m_Refs == 0) delete this; }

private:
/* unimp */ Implementation();
/* unimp */ Implementation(const Implementation &);
int m_Refs;
};

void Assign(const char *str);

Implementation * m_Shared;
static Implementation * GetBlank();
};



// SharedString.cpp

// Code by Chris Jacobson (FearItself @ AvP)
// Based on code from LexiMUD (Aliens vs Predator: The MUD, and Raze the Dead)
// Modified to remove namespaces and target std::string.
// Permission to use in your own code, so long as this header remains intact.


// Due to static initialization order, the safest way to get a static is by a function that returns
// a pointer or reference to it. Anything global/static-initialized could potentially initialize before
// another static/global object that it depends on. Using a function guarantees the object gets
// initialized because it will be initialized when the function is called, not during global static
// initializers
SharedString::Implementation *SharedString::GetBlank()
{
static SharedString::Implementation s_Blank("");

return &s_Blank;
}


void SharedString::Assign(const char *str)
{
if (m_Shared) m_Shared->Release(); // This will always happen EXCEPT on new objects under construction

if (str && *str) m_Shared = new Implementation(str);
else
{
m_Shared = GetBlank();
m_Shared->Retain();
}
}
0.0/49