• Welcome to Valhalla Legends Archive.
 

Declaring Variables

Started by Crypticflare, November 05, 2003, 09:44 PM

Previous topic - Next topic

Zakath

Quote from: MrRaza on November 06, 2003, 10:54 PM
I believe either way works fine, but skywing is correct. By the way, I think most people are getting ahead of themselves, remember, he's just learning to declare variables....

No, either way does not work fine. As has been stated multiple times already, declaring main as returning void is an incorrect, non-standard method that has, unfortunately, been perpetuated by most modern compilers. Also, declaring a main function is the very first thing you learn in C or C++ (at least in all cases I'm aware of). It is not a good idea to allow someone to get used to doing something wrong, then come back later and try to get them to change their habits. In short, always have main return an int.
Quote from: iago on February 02, 2005, 03:07 PM
Yes, you can't have everybody...contributing to the main source repository.  That would be stupid and create chaos.

Opensource projects...would be dumb.

Crypticflare

Thanks a lot everyone, The book can be a bit sketchy at parts its always good to know the local vL forums are always available for help.

Unless your banned =p

Eibro

Quote from: Skywing on November 06, 2003, 08:46 PM
Quote from: Eibro on November 06, 2003, 02:49 PM
Quote from: Kp on November 06, 2003, 09:42 AM
Unfortunately, someone associated with C++/STL decided to create a C++ class named string which handles a great deal of string related functionality.  It should be noted that this is not a built-in type and as such cannot be directly used with system functions (e.g. send, recv, CreateFile).  You can use the c_str() method of the string class to get at its enclosed data for sending, but you'll have to do some extra work if you want to read a string from the system and then put it in the string class.

Prior to the C++ string class, string typically referred to an array of characters, and CupHead/iago already covered how to do that.  Beware that when using a character array, it is your problem to ensure sufficient space is available.  The following will work, but produce bad results.
char x[4]; strcpy (x, "a very long string that doesn't fit");The compiler will (probably) let you compile this, but running it will result in a buffer overrun of x, which will likely cause your program to crash.  Careless string copying is the source of most buffer overflow exploits in programs; afaik, it's difficult/impossible to do this in "managed" languages like Java and C# because the compiler does lots of extra checking on array use.  In C and normal C++, you're responsible for checking that condition manually.
With a little thrusting here and there, you can use std::string just as you would a char* :)
Example of retrieving window text (GetWindowText) straight into an std::string buffer:


// tstring is: typedef std::basic_string<TCHAR> tstring;

tstring IWindow::Text() const
{
  int textLength = GetWindowTextLength( m_hWnd );

  // Neat (icky) trick. Use std::string internal buffer as buffer
  // by reserving specified amount of space and casting const-ness away
  tstring stringBuf( textLength, TCHAR( '\0' ) );

  TCHAR* bufHandle = const_cast<TCHAR*>( stringBuf.c_str() );

  int charsCopied = GetWindowText( m_hWnd, bufHandle, textLength );

  // must resize to actual length, or std::string will flip out
  // when it goes to deallocate
  stringBuf.resize( charsCopied );

  return stringBuf;
}


Then to pass an std::string to functions that want a const char*, do something like:
template <typename T>
class stringEx : public std::basic_string<T>
{
public:
    operator const char*() const { return this->c_str(); }
};
It's flawless!

That's 'const' for a reason.  You're violating the C++ standard.
const_cast is part of the language for a reason. With some extra care, casting away the const-ness has no side effects. Sure beats allocating a temporary buffer, copying it to a tstring and deleting the buffer.
Eibro of Yeti Lovers.

Adron

Isn't there a possible problem with a smart string using the same buffer for two different strings and only allocating a new buffer when you're assigning a new value to one of the strings using one of the string member functions?

Skywing

Quote from: Eibro on November 08, 2003, 06:00 PM
Quote from: Skywing on November 06, 2003, 08:46 PM
Quote from: Eibro on November 06, 2003, 02:49 PM
Quote from: Kp on November 06, 2003, 09:42 AM
Unfortunately, someone associated with C++/STL decided to create a C++ class named string which handles a great deal of string related functionality.  It should be noted that this is not a built-in type and as such cannot be directly used with system functions (e.g. send, recv, CreateFile).  You can use the c_str() method of the string class to get at its enclosed data for sending, but you'll have to do some extra work if you want to read a string from the system and then put it in the string class.

Prior to the C++ string class, string typically referred to an array of characters, and CupHead/iago already covered how to do that.  Beware that when using a character array, it is your problem to ensure sufficient space is available.  The following will work, but produce bad results.
char x[4]; strcpy (x, "a very long string that doesn't fit");The compiler will (probably) let you compile this, but running it will result in a buffer overrun of x, which will likely cause your program to crash.  Careless string copying is the source of most buffer overflow exploits in programs; afaik, it's difficult/impossible to do this in "managed" languages like Java and C# because the compiler does lots of extra checking on array use.  In C and normal C++, you're responsible for checking that condition manually.
With a little thrusting here and there, you can use std::string just as you would a char* :)
Example of retrieving window text (GetWindowText) straight into an std::string buffer:


// tstring is: typedef std::basic_string<TCHAR> tstring;

tstring IWindow::Text() const
{
  int textLength = GetWindowTextLength( m_hWnd );

  // Neat (icky) trick. Use std::string internal buffer as buffer
  // by reserving specified amount of space and casting const-ness away
  tstring stringBuf( textLength, TCHAR( '\0' ) );

  TCHAR* bufHandle = const_cast<TCHAR*>( stringBuf.c_str() );

  int charsCopied = GetWindowText( m_hWnd, bufHandle, textLength );

  // must resize to actual length, or std::string will flip out
  // when it goes to deallocate
  stringBuf.resize( charsCopied );

  return stringBuf;
}


Then to pass an std::string to functions that want a const char*, do something like:
template <typename T>
class stringEx : public std::basic_string<T>
{
public:
    operator const char*() const { return this->c_str(); }
};
It's flawless!

That's 'const' for a reason.  You're violating the C++ standard.
const_cast is part of the language for a reason. With some extra care, casting away the const-ness has no side effects. Sure beats allocating a temporary buffer, copying it to a tstring and deleting the buffer.
Given that the language explicitly says that the buffer returned by c_str is not to ever be modified, I think that your code is a fairly bad idea.

Eibro

#20
Quote from: Adron on November 08, 2003, 07:24 PM
Isn't there a possible problem with a smart string using the same buffer for two different strings and only allocating a new buffer when you're assigning a new value to one of the strings using one of the string member functions?
No doubt, but I don't think this applies to what we're discussing.

QuoteGiven that the language explicitly says that the buffer returned by c_str is not to ever be modified, I think that your code is a fairly bad idea.
I disagree. When the buffer returned by c_str() is of known length, you can safely modify the buffer while taking care not to go out of bounds. Unless there is some situation I have overlooked, this is one instance where i'll not blindly follow the standard.
Eibro of Yeti Lovers.

Skywing

Quote from: Eibro on November 09, 2003, 03:09 PM
Quote from: Adron on November 08, 2003, 07:24 PM
Isn't there a possible problem with a smart string using the same buffer for two different strings and only allocating a new buffer when you're assigning a new value to one of the strings using one of the string member functions?
No doubt, but I don't think this applies to what we're discussing.

QuoteGiven that the language explicitly says that the buffer returned by c_str is not to ever be modified, I think that your code is a fairly bad idea.
I disagree. When the buffer returned by c_str() is of known length, you can safely modify the buffer while taking care not to go out of bounds. Unless there is some situation I have overlooked, this is one instance where i'll not blindly follow the standard.
Yes, there are a number of situations which you have blindly overlooked.  Most blatantly obvious is Adron's point about implementing copy-on-write for std::basic_string.  Bjarne Stroustrup (the creator of C++)'s book, The C++ Programming Language even explictly says something about this:
Quote
As mentioned in §11.12, it is possible to optimize a string so that copying doesn't actually take place until two copies of a string are needed.  The design of the standard string encourages implementations that minimize actual copying.  This makes read-only uses of strings and passing of strings as function arguments much cheaper than one could have naively assumed.
Standards are there for a reason.  Perhaps in the future you'll actually do some research instead of blindly following a completely horrible idea like that, or even worse, teaching it to people.

Eibro

Quote from: Skywing on November 09, 2003, 03:28 PM
Quote from: Eibro on November 09, 2003, 03:09 PM
Quote from: Adron on November 08, 2003, 07:24 PM
Isn't there a possible problem with a smart string using the same buffer for two different strings and only allocating a new buffer when you're assigning a new value to one of the strings using one of the string member functions?
No doubt, but I don't think this applies to what we're discussing.

QuoteGiven that the language explicitly says that the buffer returned by c_str is not to ever be modified, I think that your code is a fairly bad idea.
I disagree. When the buffer returned by c_str() is of known length, you can safely modify the buffer while taking care not to go out of bounds. Unless there is some situation I have overlooked, this is one instance where i'll not blindly follow the standard.
Yes, there are a number of situations which you have blindly overlooked.  Most blatantly obvious is Adron's point about implementing copy-on-write for std::basic_string.  Bjarne Stroustrup (the creator of C++)'s book, The C++ Programming Language even explictly says something about this:
Quote
As mentioned in §11.12, it is possible to optimize a string so that copying doesn't actually take place until two copies of a string are needed.  The design of the standard string encourages implementations that minimize actual copying.  This makes read-only uses of strings and passing of strings as function arguments much cheaper than one could have naively assumed.
Standards are there for a reason.  Perhaps in the future you'll actually do some research instead of blindly following a completely horrible idea like that, or even worse, teaching it to people.
Firstly, where did I try to teach this to anyone? My original post was a lighthearted reply to Kp. I'm not speaking generally when I talk about modifying the buffer returned by c_str(), i'm talking about my code. I don't need to worry about copy-on-write semantics for the above code. It's obviously const for a reason, but sometimes it's okay to bend the rules.

Why did I choose to do it this way? Consider the other options:
1) Have the user pass their own buffer, which may or not be long enough to hold the entire string.
2) Have a static buffer within the function that recieves the data, and return that. This is a problem because repeated calls to the function will modify data returned in previous calls.
3) Allocate a buffer of required length and return it. This is a memory leak waiting to happen.
4) Allocate a buffer, copy it to a tstring, delete the buffer and return the tstring. This is way to inefficient.
Eibro of Yeti Lovers.

Moonshine

#23
I'm going to have to agree with Skywing here; MSDN explicitly says:

Quote
Converts the contents of a string as a C-style, null-terminated string.

const value_type *c_str( ) const;
Return Value
A pointer to the C-style version of the invoking string.

Remarks
... (previous remarks removed here for brevity) ...

The returned C-style string should not be modified, as this could invalidate the pointer to the string, or deleted, as the string has a limited lifetime and is owned by the class string.

(Note the last sentence in that pasting)

Quote1) Have the user pass their own buffer, which may or not be long enough to hold the entire string.

In addition to the documentation, it's not as big of a deal as you say when the user passes their own buffer in the function parameters; for one can always do something along the lines of:

void IWindow::Text(char *pszBuffer, const int iMaxSize) {
  // ...
}


Then just check iMaxSize vs. GetWindowTextLength().  Anyways, that's my take on it, I believe using that method is a much safer bet (even though it might be a little more work, but so is the case with void main()) than violating the standards and having possible unwanted side effects.

Skywing

#24
Note that the buffer returned by c_str() is also only guaranteed to be valid and untouched until the next non-const call to the std::string.  On the next such function call, it's entirely acceptable for the implementation to completely overwrite any changes that you made (improperly, potentially to the wrong buffer).

Just because something "works" on your compiler implementation does not make it a good idea to use it.  Besides simply violating const, you're also making asumptions about how memory for the std::string buffer is allocated that are quite probably not going to hold for a large number of compilers.

Also, note that the purpose of this entire thread was to answer a user's request for information on the C++ language.  Given this, it's fair to assume that somebody might read it to learn something about C++, and possibly pick up the wrong way of doing things from your post.  Inaccuracy is a bad thing to bring into these kinds of situations.

Kp

Quote from: Eibro on November 09, 2003, 07:11 PM1) Have the user pass their own buffer, which may or not be long enough to hold the entire string.
They pass the buffer's length, you check it with GetWindowTextLength, and bail if it isn't big enough.  Then it's their problem to handle the error.

Quote from: Eibro on November 09, 2003, 07:11 PM2) Have a static buffer within the function that recieves the data, and return that. This is a problem because repeated calls to the function will modify data returned in previous calls.
Static data is usually a bad idea (among other problems, it's usually not threadsafe unless you take extra steps).  Never use this option.

Quote from: Eibro on November 09, 2003, 07:11 PM3) Allocate a buffer of required length and return it. This is a memory leak waiting to happen.
That would seem to imply a lack of trust in the user's competency about memory management.  If the user can't be trusted to write good code, why are we trusting them to write code at all?

Quote from: Eibro on November 09, 2003, 07:11 PM4) Allocate a buffer, copy it to a tstring, delete the buffer and return the tstring. This is way to inefficient.
This is why I always tell people not to use std::string if you're going to be passing the data into/out of the OS.
[19:20:23] (BotNet) <[vL]Kp> Any idiot can make a bot with CSB, and many do!

CupHead

Eibro: I think it's time to cut your losses.  Arguing to the bitter end is only ok when you're right or there isn't some glaring standard to say you're wrong.

iago

Quote from: CupHead on November 10, 2003, 05:43 PM
Eibro: I think it's time to cut your losses.  Arguing to the bitter end is only ok when you're right or there isn't some glaring standard to say you're wrong.

And this is coming from Cuphead!
This'll make an interesting test for broken AV:
QuoteX5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*


Eibro

Quote from: CupHead on November 10, 2003, 05:43 PM
Eibro: I think it's time to cut your losses.  Arguing to the bitter end is only ok when you're right or there isn't some glaring standard to say you're wrong.
The "argument" got pretty trivial a few posts ago. I've already said what I needed to say, I don't understand where you're seeing a bitter end.
Eibro of Yeti Lovers.